Files
starruler-linux/scripts/menu/new_game.as
T
2018-07-17 14:15:37 +02:00

3207 lines
85 KiB
ActionScript

import menus;
import elements.BaseGuiElement;
import elements.GuiButton;
import elements.GuiPanel;
import elements.GuiOverlay;
import elements.GuiSprite;
import elements.GuiText;
import elements.GuiTextbox;
import elements.GuiSpinbox;
import elements.GuiCheckbox;
import elements.GuiDropdown;
import elements.GuiContextMenu;
import elements.GuiIconGrid;
import elements.GuiEmpire;
import elements.GuiMarkupText;
import elements.MarkupTooltip;
import elements.GuiBackgroundPanel;
import dialogs.SaveDialog;
import dialogs.LoadDialog;
import dialogs.MessageDialog;
import dialogs.QuestionDialog;
import util.settings_page;
import empire_data;
import traits;
import icons;
from util.draw_model import drawLitModel;
import void showMultiplayer() from "multiplayer_menu";
from maps import Map, maps, mapCount, getMap;
import settings.game_settings;
import util.game_options;
const int EMPIRE_SETUP_HEIGHT = 96;
const int GALAXY_SETUP_HEIGHT = 200;
const int REC_MAX_PEREMP = 25;
const int REC_MAX_OPTIMAL = 150;
const int REC_MAX_BAD = 400;
const int REC_MAX_OHGOD = 1000;
const array<Color> QDIFF_COLORS = {Color(0x00ff00ff), Color(0x1197e0ff), Color(0xff0000ff)};
const array<string> QDIFF_NAMES = {locale::AI_DIFF_EASY, locale::AI_DIFF_NORMAL, locale::AI_DIFF_HARD};
const array<string> QDIFF_DESC = {locale::AI_DIFF_EASY_DESC, locale::AI_DIFF_NORMAL_DESC, locale::AI_DIFF_HARD_DESC};
const array<Sprite> QDIFF_ICONS = {Sprite(spritesheet::AIDifficulty, 0), Sprite(spritesheet::AIDifficulty, 1), Sprite(spritesheet::AIDifficulty, 2)};
NameGenerator empireNames;
bool empireNamesInitialized = false;
class ConfirmStart : QuestionDialogCallback {
void questionCallback(QuestionDialog@ dialog, int answer) {
if(answer == QA_Yes) {
new_game.start();
hideNewGame(true);
}
}
};
class NewGame : BaseGuiElement {
GameSettings settings;
GuiBackgroundPanel@ empireBG;
GuiBackgroundPanel@ gameBG;
GuiBackgroundPanel@ chatBG;
GuiButton@ backButton;
GuiButton@ inviteButton;
GuiButton@ playButton;
EmpirePortraitCreation portraits;
int nextEmpNum = 1;
GuiPanel@ empirePanel;
EmpireSetup@[] empires;
GuiButton@ addAIButton;
GuiSkinElement@ gameHeader;
GuiButton@ mapsButton;
array<GuiButton@> settingsButtons;
array<GuiPanel@> settingsPanels;
GuiButton@ resetButton;
GuiPanel@ galaxyPanel;
GalaxySetup@[] galaxies;
GuiButton@ addGalaxyButton;
GuiPanel@ mapPanel;
GuiText@ mapHeader;
GuiListbox@ mapList;
GuiPanel@ chatPanel;
GuiMarkupText@ chatLog;
GuiTextbox@ chatBox;
bool animating = false;
bool hide = false;
bool fromMP = false;
bool choosingMap = false;
string chatMessages;
NewGame() {
super(null, recti());
@empireBG = GuiBackgroundPanel(this, Alignment(
Left+0.05f, Top+0.1f, Left+0.5f-6, Bottom-0.1f));
empireBG.title = locale::MENU_EMPIRES;
empireBG.titleColor = Color(0x00ffe9ff);
@gameBG = GuiBackgroundPanel(this, Alignment(
Left+0.5f+6, Top+0.1f, Left+0.95f, Bottom-0.1f));
@gameHeader = GuiSkinElement(gameBG, Alignment(Left+1, Top+1, Right-2, Top+41), SS_FullTitle);
@mapsButton = GuiButton(gameHeader, Alignment(Left, Top+1, Width=200, Height=38));
mapsButton.text = locale::MENU_GALAXIES;
mapsButton.buttonIcon = Sprite(material::SystemUnderAttack);
mapsButton.toggleButton = true;
mapsButton.font = FT_Medium;
mapsButton.pressed = true;
mapsButton.style = SS_TabButton;
@chatBG = GuiBackgroundPanel(this, Alignment(
Left+0.05f, Bottom-0.1f-250, Left+0.5f-6, Bottom-0.1f));
chatBG.title = locale::CHAT;
chatBG.titleColor = Color(0xff8000ff);
chatBG.visible = false;
//Empire list
@empirePanel = GuiPanel(empireBG,
Alignment(Left, Top+34, Right, Bottom-4));
@addAIButton = GuiButton(empirePanel,
recti_area(vec2i(), vec2i(200, 36)),
locale::ADD_AI);
addAIButton.buttonIcon = icons::Add;
//Game settings
for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i) {
auto@ panel = GuiPanel(gameBG,
Alignment(Left, Top+46, Right, Bottom-40));
panel.visible = false;
settingsPanels.insertLast(panel);
auto@ page = GAME_SETTINGS_PAGES[i];
page.create(panel);
auto@ button = GuiButton(gameHeader, Alignment(Left+200+(i*200), Top+1, Width=200, Height=38));
button.text = page.header;
button.buttonIcon = page.icon;
button.toggleButton = true;
button.pressed = false;
button.font = FT_Medium;
button.style = SS_TabButton;
settingsButtons.insertLast(button);
}
@resetButton = GuiButton(gameBG, Alignment(Left+0.5f-120, Bottom-40, Width=240, Height=35), locale::NG_RESET);
resetButton.color = Color(0xff8080ff);
resetButton.buttonIcon = icons::Reset;
resetButton.visible = false;
//Galaxy list
@galaxyPanel = GuiPanel(gameBG,
Alignment(Left, Top+46, Right, Bottom-4));
galaxyPanel.visible = false;
@addGalaxyButton = GuiButton(galaxyPanel,
recti_area(vec2i(), vec2i(260, 36)),
locale::ADD_GALAXY);
addGalaxyButton.buttonIcon = Sprite(spritesheet::CardCategoryIcons, 3);
//Maps choice list
@mapPanel = GuiPanel(gameBG,
Alignment(Left, Top+46, Right, Bottom-4));
mapPanel.visible = true;
choosingMap = true;
@mapHeader = GuiText(mapPanel, Alignment(Left, Top, Right, Top+30));
mapHeader.font = FT_Medium;
mapHeader.horizAlign = 0.5;
mapHeader.stroke = colors::Black;
mapHeader.text = locale::CHOOSE_MAP;
@mapList = GuiListbox(mapPanel,
Alignment(Left+4, Top+34, Right-4, Bottom-4));
mapList.itemStyle = SS_DropdownListItem;
mapList.itemHeight = 100;
updateMapList();
//Chat
@chatPanel = GuiPanel(chatBG, Alignment(Left+8, Top+34, Right-8, Bottom-38));
@chatLog = GuiMarkupText(chatPanel, recti_area(0, 0, 100, 100));
@chatBox = GuiTextbox(chatBG, Alignment(Left+6, Bottom-36, Right-6, Bottom-6));
//Actions
@playButton = GuiButton(this, Alignment(
Right-0.05f-200, Bottom-0.1f+6, Width=200, Height=46),
locale::START_GAME);
playButton.buttonIcon = Sprite(spritesheet::MenuIcons, 9);
@backButton = GuiButton(this, Alignment(
Left+0.05f, Bottom-0.1f+6, Width=200, Height=46),
locale::BACK);
backButton.buttonIcon = Sprite(spritesheet::MenuIcons, 11);
@inviteButton = GuiButton(this, Alignment(
Left+0.05f+208, Bottom-0.1f+6, Width=200, Height=46),
locale::INVITE_FRIEND);
inviteButton.buttonIcon = Sprite(spritesheet::MenuIcons, 13);
inviteButton.visible = cloud::inLobby;
updateAbsolutePosition();
}
void updateMapList() {
mapList.clearItems();
for(uint i = 0, cnt = mapCount; i < cnt; ++i) {
auto@ mp = getMap(i);
if(mp.isUnique) {
bool found = false;
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) {
if(galaxies[i].mp.id == mp.id) {
found = true;
break;
}
}
if(found)
continue;
}
if(mp.isListed && !mp.isScenario && (mp.dlc.length == 0 || hasDLC(mp.dlc)))
mapList.addItem(MapElement(mp));
}
}
void init() {
if(!empireNamesInitialized) {
empireNames.read("data/empire_names.txt");
empireNames.useGeneration = false;
empireNamesInitialized = true;
}
portraits.reset();
clearEmpires();
addEmpire(true, getRacePreset(0));
if(!mpServer && !fromMP) {
addEmpire(false);
addEmpire(false);
RaceChooser(empires[0], true);
}
updateAbsolutePosition();
switchPage(0);
if(fromMP) {
mapPanel.visible = false;
galaxyPanel.visible = true;
choosingMap = false;
}
else {
mapPanel.visible = true;
galaxyPanel.visible = false;
choosingMap = true;
updateMapList();
}
addGalaxyButton.visible = !fromMP;
addAIButton.visible = !fromMP;
chatMessages = "";
if(fromMP) {
playButton.text = locale::MP_NOT_READY;
playButton.color = colors::Orange;
}
else {
playButton.text = locale::START_GAME;
playButton.color = colors::White;
}
}
void addChat(const string& str) {
chatMessages += str+"\n";
bool wasBottom = chatPanel.vert.pos >= (chatPanel.vert.end - chatPanel.vert.page);
chatLog.text = chatMessages;
chatPanel.updateAbsolutePosition();
if(wasBottom) {
chatPanel.vert.pos = max(0.0, chatPanel.vert.end - chatPanel.vert.page);
chatPanel.updateAbsolutePosition();
}
}
void resetAIColors() {
for(uint i = 0, cnt = empires.length; i < cnt; ++i) {
auto@ setup = empires[i];
if(setup.player)
continue;
setup.settings.color = colors::Invisible;
}
for(uint i = 0, cnt = empires.length; i < cnt; ++i) {
auto@ setup = empires[i];
if(setup.player)
continue;
setUniqueColor(setup);
}
}
void resetAIRaces() {
for(uint i = 0, cnt = empires.length; i < cnt; ++i) {
auto@ setup = empires[i];
if(setup.player)
continue;
setup.settings.raceName = "";
}
for(uint i = 0, cnt = empires.length; i < cnt; ++i) {
auto@ setup = empires[i];
if(setup.player)
continue;
setup.applyRace(getUniquePreset());
}
}
RacePreset@ getUniquePreset() {
uint index = randomi(0, getRacePresetCount() - 1);
for(uint i = 0, cnt = getRacePresetCount(); i < cnt; ++i) {
auto@ preset = getRacePreset((index+i) % cnt);
if(preset.dlc.length != 0 && !hasDLC(preset.dlc))
continue;
bool has = false;
for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) {
if(empires[n].settings.raceName == preset.name) {
has = true;
break;
}
}
if(!has) {
return preset;
}
}
for(uint i = 0, cnt = getRacePresetCount(); i < cnt; ++i) {
auto@ preset = getRacePreset((index+i) % cnt);
if(preset.dlc.length != 0 && !hasDLC(preset.dlc))
continue;
return preset;
}
return getRacePreset(index);
}
void setUniqueColor(EmpireSetup@ setup) {
bool found = false;
Color setColor;
for(uint i = 0, cnt = getEmpireColorCount(); i < cnt; ++i) {
Color col = getEmpireColor(i).color;
bool has = false;
for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) {
if(empires[n] !is setup && empires[n].settings.color.color == col.color) {
has = true;
break;
}
}
if(!has) {
found = true;
setColor = col;
break;
}
}
if(!found) {
Colorf rnd;
rnd.fromHSV(randomd(0, 360.0), randomd(0.5, 1.0), 1.0);
setColor = Color(rnd);
}
setup.settings.color = setColor;
setup.update();
}
void tick(double time) {
if(mapIcons.length == 0) {
mapIcons.length = mapCount;
for(uint i = 0, cnt = mapCount; i < cnt; ++i) {
auto@ mp = getMap(i);
if(mp.isListed && !mp.isScenario && mp.icon.length != 0)
mapIcons[i].load(mp.icon);
}
}
inviteButton.visible = cloud::inLobby;
addAIButton.disabled = empires.length >= 28;
if(mpServer) {
bool allReady = true;
for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) {
auto@ emp = empires[n];
if(emp.playerId != -1 && emp.playerId != CURRENT_PLAYER.id) {
emp.found = false;
if(!emp.settings.ready)
allReady = false;
}
}
array<Player@>@ players = getPlayers();
for(uint i = 0, cnt = players.length; i < cnt; ++i) {
Player@ pl = players[i];
if(pl == CURRENT_PLAYER)
continue;
//Find if we already have an empire
bool found = false;
for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) {
auto@ emp = empires[n];
if(emp.playerId == pl.id) {
emp.found = true;
found = true;
if(pl.name.length != 0 && emp.name.text.length == 0)
emp.name.text = pl.name;
}
}
if(!found) {
auto@ emp = addEmpire(false, getRacePreset(0));
emp.name.text = pl.name;
emp.address = pl.address;
emp.setPlayer(pl.id);
}
}
//Prune disconnected players
for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) {
auto@ emp = empires[n];
if(emp.playerId != -1 && !emp.found) {
removeEmpire(emp);
--n; --ncnt;
}
}
//Update play button
if(allReady)
playButton.color = colors::Green;
else
playButton.color = colors::Orange;
}
else if(fromMP) {
if(game_running) {
hideNewGame(true);
switchToMenu(main_menu, snap=true);
return;
}
if(awaitingGalaxy) {
hideNewGame(true);
switchToMenu(main_menu, snap=true);
showMultiplayer();
return;
}
auto@ pl = findPlayer(CURRENT_PLAYER.id);
if(pl !is null && pl.settings.ready) {
playButton.text = locale::MP_READY;
playButton.color = colors::Green;
}
else {
playButton.text = locale::MP_NOT_READY;
playButton.color = colors::Orange;
}
if(!mpIsConnected()) {
message("Lost connection to server:\n "
+localize("DISCONNECT_"+uint(mpDisconnectReason)));
hideNewGame(true);
switchToMenu(main_menu, snap=true);
showMultiplayer();
}
}
}
EmpireSetup@ addEmpire(bool player = false, const RacePreset@ preset = null) {
if(empires.length >= 28)
return null;
uint y = empires.length * (EMPIRE_SETUP_HEIGHT + 8) + 8;
EmpireSetup@ emp = EmpireSetup(this,
Alignment(Left+4, Top+y, Right-4, Top+y + EMPIRE_SETUP_HEIGHT),
player);
portraits.randomize(emp.settings);
if(player && settings::sNickname.length != 0)
emp.name.text = settings::sNickname;
else
emp.name.text = "Empire "+(nextEmpNum++);
if(preset is null) {
if(player)
@preset = getRacePreset(0);
else
@preset = getUniquePreset();
}
emp.defaultName = emp.name.text;
addAIButton.position = vec2i((empirePanel.size.width - addAIButton.size.width)/2, y + EMPIRE_SETUP_HEIGHT + 4);
emp.update();
empires.insertLast(emp);
empirePanel.updateAbsolutePosition();
if(preset !is null)
emp.applyRace(preset);
else if(!player)
emp.resetName();
if(!player)
setUniqueColor(emp);
return emp;
}
EmpireSetup@ findPlayer(int id) {
for(uint i = 0, cnt = empires.length; i < cnt; ++i) {
if(empires[i].playerId == id)
return empires[i];
}
return null;
}
void clearEmpires() {
for(uint i = 0, cnt = empires.length; i < cnt; ++i)
empires[i].remove();
empires.length = 0;
nextEmpNum = 2;
updateEmpirePositions();
}
void removeEmpire(EmpireSetup@ emp) {
emp.remove();
empires.remove(emp);
updateEmpirePositions();
}
void updateEmpirePositions() {
uint cnt = empires.length;
for(uint i = 0; i < cnt; ++i) {
EmpireSetup@ emp = empires[i];
emp.alignment.top.pixels = i * (EMPIRE_SETUP_HEIGHT + 8) + 8;
emp.alignment.bottom.pixels = emp.alignment.top.pixels + EMPIRE_SETUP_HEIGHT;
emp.updateAbsolutePosition();
}
addAIButton.position = vec2i((empirePanel.size.width - addAIButton.size.width)/2, cnt * (EMPIRE_SETUP_HEIGHT + 8) + 6);
}
GalaxySetup@ addGalaxy(Map@ mp) {
uint y = galaxies.length * (GALAXY_SETUP_HEIGHT + 8) + 8;
GalaxySetup@ glx = GalaxySetup(this,
Alignment(Left+8, Top+y, Right-8, Top+y + GALAXY_SETUP_HEIGHT),
mp);
if(mp.eatsPlayers) {
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i)
galaxies[i].setHomeworlds(false);
}
else {
bool haveEating = false;
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) {
if(galaxies[i].mp.eatsPlayers) {
haveEating = true;
}
}
if(haveEating)
glx.setHomeworlds(false);
}
addGalaxyButton.position = vec2i((galaxyPanel.size.width - addGalaxyButton.size.width)/2, y + GALAXY_SETUP_HEIGHT);
galaxies.insertLast(glx);
galaxyPanel.updateAbsolutePosition();
updateGalaxyPositions();
return glx;
}
void removeGalaxy(GalaxySetup@ glx) {
glx.remove();
galaxies.remove(glx);
updateGalaxyPositions();
if(glx.mp.eatsPlayers) {
bool haveEating = false;
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) {
if(galaxies[i].mp.eatsPlayers) {
haveEating = true;
}
}
if(!haveEating) {
for(uint i = 0, cnt = galaxies.length; i < cnt; ++i) {
galaxies[i].setHomeworlds(true);
}
}
}
if(galaxies.length == 0) {
mapHeader.text = locale::CHOOSE_MAP;
mapPanel.visible = true;
galaxyPanel.visible = false;
choosingMap = true;
updateMapList();
}
}
void updateGalaxyPositions() {
uint cnt = galaxies.length;
for(uint i = 0; i < cnt; ++i) {
GalaxySetup@ glx = galaxies[i];
glx.alignment.top.pixels = i * (GALAXY_SETUP_HEIGHT + 8) + 8;
glx.alignment.bottom.pixels = glx.alignment.top.pixels + GALAXY_SETUP_HEIGHT;
glx.updateAbsolutePosition();
}
addGalaxyButton.position = vec2i((galaxyPanel.size.width - addGalaxyButton.size.width)/2, cnt * (GALAXY_SETUP_HEIGHT + 8) + 6);
galaxyPanel.updateAbsolutePosition();
}
void apply() {
apply(settings);
}
void reset() {
uint newCnt = settings.empires.length;
uint oldCnt = empires.length;
for(uint i = newCnt; i < oldCnt; ++i) {
removeEmpire(empires[i]);
--i; --oldCnt;
}
for(uint i = 0; i < newCnt; ++i) {
EmpireSetup@ setup;
if(i >= oldCnt)
@setup = addEmpire();
else
@setup = empires[i];
auto@ sett = settings.empires[i];
if(setup.playerId == sett.playerId
&& setup.playerId == CURRENT_PLAYER.id) {
if(setup.settings.delta > sett.delta)
setup.apply(settings.empires[i]);
else
setup.load(settings.empires[i]);
}
else {
setup.load(settings.empires[i]);
}
}
updateEmpirePositions();
newCnt = settings.galaxies.length;
oldCnt = galaxies.length;
for(uint i = newCnt; i < oldCnt; ++i) {
removeGalaxy(galaxies[i]);
--i; --oldCnt;
}
for(uint i = 0; i < newCnt; ++i) {
GalaxySetup@ setup;
if(i >= oldCnt)
@setup = addGalaxy(getMap(settings.galaxies[i].map_id));
else
@setup = galaxies[i];
setup.load(settings.galaxies[i]);
}
updateGalaxyPositions();
for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i)
GAME_SETTINGS_PAGES[i].load(settings);
addGalaxyButton.visible = !mpClient;
addAIButton.visible = !mpClient;
}
void reset(GameSettings& settings) {
this.settings = settings;
reset();
}
void apply(GameSettings& settings) {
uint empCnt = empires.length;
settings.empires.length = empCnt;
for(uint i = 0; i < empCnt; ++i) {
settings.empires[i].index = i;
empires[i].apply(settings.empires[i]);
}
uint glxCnt = galaxies.length;
settings.galaxies.length = glxCnt;
for(uint i = 0; i < glxCnt; ++i)
galaxies[i].apply(settings.galaxies[i]);
for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i)
GAME_SETTINGS_PAGES[i].apply(settings);
}
void start(){
apply();
Message msg;
settings.write(msg);
startNewGame(msg);
}
void switchPage(uint page) {
mapsButton.pressed = page == 0;
galaxyPanel.visible = page == 0 && !choosingMap;
mapPanel.visible = page == 0 && choosingMap;
if(mapPanel.visible)
updateMapList();
//if(page == 0)
// gameHeader.color = Color(0xff003fff);
resetButton.visible = page != 0 && !mpClient;
for(uint i = 0, cnt = settingsButtons.length; i < cnt; ++i) {
settingsButtons[i].pressed = page == i+1;
settingsPanels[i].visible = page == i+1;
//if(page == i+1)
// gameHeader.color = GAME_SETTINGS_PAGES[i].color;
}
}
bool onGuiEvent(const GuiEvent& event) {
switch(event.type) {
case GUI_Clicked:
if(event.caller is playButton) {
if(fromMP) {
auto@ pl = findPlayer(CURRENT_PLAYER.id);
if(pl !is null) {
pl.settings.ready = !pl.settings.ready;
pl.submit();
}
}
else {
if(mpServer) {
bool allReady = true;
for(uint n = 0, ncnt = empires.length; n < ncnt; ++n) {
auto@ emp = empires[n];
if(emp.playerId != -1 && emp.playerId != CURRENT_PLAYER.id) {
if(!emp.settings.ready)
allReady = false;
}
}
if(!allReady) {
question(locale::MP_CONFIRM_NOT_READY, ConfirmStart());
return true;
}
}
else {
uint sysCount = 0;
apply();
for(uint i = 0, cnt = settings.galaxies.length; i < cnt; ++i)
sysCount += settings.galaxies[i].systemCount * settings.galaxies[i].galaxyCount;
uint empCount = empires.length;
if(sysCount > REC_MAX_OHGOD) {
question(locale::NG_WARN_OHGOD, ConfirmStart());
return true;
}
else if(sysCount > REC_MAX_BAD) {
question(locale::NG_WARN_BAD, ConfirmStart());
return true;
}
else if(sysCount > REC_MAX_OPTIMAL) {
question(locale::NG_WARN_OPTIMAL, ConfirmStart());
return true;
}
else if(sysCount > REC_MAX_PEREMP * empCount) {
question(locale::NG_WARN_PEREMP, ConfirmStart());
return true;
}
}
start();
hideNewGame(true);
}
return true;
}
else if(event.caller is backButton) {
if(!game_running)
mpDisconnect();
hideNewGame();
return true;
}
else if(event.caller is inviteButton) {
cloud::inviteFriend();
return true;
}
else if(event.caller is addAIButton) {
addEmpire();
return true;
}
else if(event.caller is addGalaxyButton) {
mapHeader.text = locale::ADD_GALAXY;
mapPanel.visible = true;
galaxyPanel.visible = false;
updateMapList();
choosingMap = true;
return true;
}
else if(event.caller is resetButton) {
for(uint i = 0, cnt = GAME_SETTINGS_PAGES.length; i < cnt; ++i) {
if(settingsPanels[i].visible)
GAME_SETTINGS_PAGES[i].reset();
}
return true;
}
else if(event.caller is mapsButton) {
switchPage(0);
return true;
}
else {
for(uint i = 0, cnt = settingsButtons.length; i < cnt; ++i) {
if(event.caller is settingsButtons[i]) {
switchPage(i+1);
return true;
}
}
}
break;
case GUI_Confirmed:
if(event.caller is chatBox) {
string message = chatBox.text;
if(message.length != 0)
menuChat(message);
chatBox.text = "";
}
break;
case GUI_Changed:
if(event.caller is mapList) {
if(mapList.selected != -1)
addGalaxy(cast<MapElement>(mapList.selectedItem).mp);
if(galaxies.length != 0) {
mapList.clearSelection();
mapPanel.visible = false;
galaxyPanel.visible = true;
choosingMap = false;
}
return true;
}
break;
case GUI_Animation_Complete:
animating = false;
return true;
}
return BaseGuiElement::onGuiEvent(event);
}
void updateAbsolutePosition() {
if(!animating) {
if(!hide) {
size = parent.size;
position = vec2i(0, 0);
}
else {
size = parent.size;
position = vec2i(size.x, 0);
}
}
if(fromMP || mpServer) {
chatBG.visible = true;
chatLog.size = vec2i(chatPanel.size.width-20, chatLog.size.height);
empireBG.alignment.bottom.pixels = 262;
}
else {
chatBG.visible = false;
empireBG.alignment.bottom.pixels = 0;
}
addAIButton.position = vec2i((empirePanel.size.width - addAIButton.size.width)/2, addAIButton.position.y);
addGalaxyButton.position = vec2i((galaxyPanel.size.width - addGalaxyButton.size.width)/2, addGalaxyButton.position.y);
BaseGuiElement::updateAbsolutePosition();
}
void animateIn() {
animating = true;
hide = false;
rect = recti_area(vec2i(parent.size.x, 0), parent.size);
animate_time(this, recti_area(vec2i(), parent.size), MSLIDE_TIME);
}
void animateOut() {
animating = true;
hide = true;
rect = recti_area(vec2i(), parent.size);
animate_time(this, recti_area(vec2i(parent.size.x, 0), parent.size), MSLIDE_TIME);
}
};
void drawRace(const Skin@ skin, const recti& absPos, const string& name,
const string& portrait, const array<const Trait@>@ traits = null, bool showTraits = true) {
const Font@ normal = skin.getFont(FT_Normal);
const Font@ bold = skin.getFont(FT_Bold);
recti namePos = recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.width * 0.35, absPos.height));
//Portrait
auto@ prt = getEmpirePortrait(portrait);
if(prt !is null) {
prt.portrait.draw(recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.height, absPos.height)));
namePos.topLeft.x += absPos.height+8;
}
//Race name
bold.draw(pos=namePos, text=name);
//FTL Method
recti ftlPos = recti_area(absPos.topLeft + vec2i(absPos.width*0.35 + 16, 0),
vec2i(absPos.width * 0.35, absPos.height));
//Traits
if(traits !is null) {
recti pos = recti_area(vec2i(absPos.botRight.x - 32, absPos.topLeft.y + 3), vec2i(24, 24));
for(uint i = 0, cnt = traits.length; i < cnt; ++i) {
auto@ trait = traits[i];
if(trait.unique == "FTL") {
trait.icon.draw(recti_area(ftlPos.topLeft, vec2i(absPos.height, absPos.height)).aspectAligned(trait.icon.aspect));
ftlPos.topLeft.x += absPos.height+8;
normal.draw(text=trait.name, pos=ftlPos);
}
else if(showTraits) {
traits[i].icon.draw(pos.aspectAligned(traits[i].icon.aspect));
pos -= vec2i(24, 0);
}
}
}
}
Color colorFromNumber(int num) {
float hue = (num*26534371)%360;
Colorf col;
col.fromHSV(hue, 1.f, 1.f);
return Color(col);
}
class RaceElement : GuiListElement {
const RacePreset@ preset;
RaceElement(const RacePreset@ preset) {
@this.preset = preset;
}
void draw(GuiListbox@ ele, uint flags, const recti& absPos) {
drawRace(ele.skin, absPos, preset.name, preset.portrait, preset.traits);
}
};
class CustomRaceElement : GuiListElement {
const EmpireSettings@ settings;
CustomRaceElement(const EmpireSettings@ settings) {
@this.settings = settings;
}
void draw(GuiListbox@ ele, uint flags, const recti& absPos) {
drawRace(ele.skin, absPos, settings.raceName, settings.portrait, settings.traits);
}
};
class CurrentRaceElement : GuiListElement {
EmpireSettings@ settings;
bool valid = true;
CurrentRaceElement(EmpireSettings@ settings) {
@this.settings = settings;
}
void update() {
valid = settings.getTraitPoints() >= 0 && !settings.hasTraitConflicts();
}
void draw(GuiListbox@ ele, uint flags, const recti& absPos) {
if(!valid) {
Color color(0xff0000ff);
color.a = abs((frameTime % 1.0) - 0.5) * 2.0 * 255.0;
ele.skin.draw(SS_Button, SF_Normal, absPos.padded(-5, -3), color);
}
drawRace(ele.skin, absPos, settings.raceName, settings.portrait, traits=settings.traits, showTraits=false);
}
};
class CustomizeOption : GuiListElement {
void draw(GuiListbox@ ele, uint flags, const recti& absPos) {
const Font@ bold = ele.skin.getFont(FT_Bold);
recti namePos = recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.width * 0.95, absPos.height));
icons::Customize.draw(recti_area(absPos.topLeft + vec2i(8, 0), vec2i(absPos.height, absPos.height)));
namePos.topLeft.x += absPos.height+8;
bold.draw(pos=namePos, text=locale::CUSTOMIZE_RACE, color=Color(0xff8000ff));
}
};
class TraitList : GuiIconGrid {
array<const Trait@> traits;
TraitList(IGuiElement@ parent, Alignment@ align) {
super(parent, align);
MarkupTooltip tt(350, 0.f, true, true);
tt.Lazy = true;
tt.LazyUpdate = false;
tt.Padding = 4;
@tooltipObject = tt;
}
uint get_length() override {
return traits.length;
}
string get_tooltip() override {
if(hovered < 0 || hovered >= int(length))
return "";
auto@ trait = traits[hovered];
return format("[color=$1][b]$2[/b][/color]\n$3",
toString(trait.color), trait.name, trait.description);
}
void drawElement(uint i, const recti& pos) override {
traits[i].icon.draw(pos.aspectAligned(traits[i].icon.aspect));
}
};
class ChangeWelfare : GuiContextOption {
ChangeWelfare(const string& text, uint index) {
value = int(index);
this.text = text;
icon = Sprite(spritesheet::ConvertIcon, index);
}
void call(GuiContextMenu@ menu) override {
playerEmpire.WelfareMode = uint(value);
}
};
const Sprite[] DIFF_SPRITES = {
Sprite(material::HappyFace),
Sprite(material::StatusPeace),
Sprite(material::StatusWar),
Sprite(material::StatusCeaseFire),
Sprite(spritesheet::AttributeIcons, 3),
Sprite(spritesheet::AttributeIcons, 0),
Sprite(spritesheet::VoteIcons, 3),
Sprite(spritesheet::VoteIcons, 3, colors::Red)
};
const Color[] DIFF_COLORS = {
colors::Green,
colors::White,
colors::White,
colors::White,
colors::Orange,
colors::Red,
colors::Red,
colors::Red
};
const string[] DIFF_TOOLTIPS = {
locale::DIFF_PASSIVE,
locale::DIFF_EASY,
locale::DIFF_NORMAL,
locale::DIFF_HARD,
locale::DIFF_MURDEROUS,
locale::DIFF_CHEATING,
locale::DIFF_SAVAGE,
locale::DIFF_BARBARIC,
};
class ChangeDifficulty : GuiMarkupContextOption {
int level;
EmpireSetup@ setup;
ChangeDifficulty(EmpireSetup@ setup, int value, const string& text) {
level = value;
set(text);
@this.setup = setup;
}
void call(GuiContextMenu@ menu) override {
setup.settings.difficulty = level;
setup.update();
}
};
class ChangeTeam : GuiMarkupContextOption {
int team;
EmpireSetup@ setup;
ChangeTeam(EmpireSetup@ setup, int value) {
team = value;
if(value >= 0)
set(format("[b][color=$2]$1[/color][/b]", format(locale::TEAM_TEXT, toString(value)),
toString(colorFromNumber(value))));
else
set(format("[b][color=#aaa]$1...[/color][/b]", locale::NO_TEAM));
@this.setup = setup;
}
void call(GuiContextMenu@ menu) override {
setup.settings.team = team;
setup.submit();
}
};
class Chooser : GuiIconGrid {
Color spriteColor;
array<Color> colors;
array<Sprite> sprites;
uint selected = 0;
Chooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) {
super(parent, align);
horizAlign = 0.5;
vertAlign = 0.0;
iconSize = itemSize;
updateAbsolutePosition();
}
void add(const Color& col) {
colors.insertLast(col);
}
void add(const Sprite& sprt) {
sprites.insertLast(sprt);
}
uint get_length() override {
return max(colors.length, sprites.length);
}
void drawElement(uint index, const recti& pos) override {
if(uint(selected) == index)
drawRectangle(pos, Color(0xffffff30));
if(uint(hovered) == index)
drawRectangle(pos, Color(0xffffff30));
if(index < colors.length)
drawRectangle(pos.padded(5), colors[index]);
if(index < sprites.length)
sprites[index].draw(pos, spriteColor);
}
};
class RaceChooser : GuiOverlay {
EmpireSetup@ setup;
GuiSkinElement@ panel;
GuiText@ header;
GuiPanel@ list;
const RacePreset@ selectedRace;
array<GuiButton@> presetButtons;
array<const RacePreset@> racePresets;
GuiSprite@ portrait;
GuiSprite@ flag;
GuiSprite@ bgDisplay;
GuiPanel@ descScroll;
GuiMarkupText@ description;
GuiPanel@ loreScroll;
GuiMarkupText@ lore;
GuiButton@ playButton;
GuiButton@ customizeButton;
GuiButton@ loadButton;
GuiButton@ backButton;
bool isInitial;
bool hasChosenRace = false;
bool chosenShipset = false;
Chooser@ flags;
Chooser@ colors;
ShipsetChooser@ shipsets;
RaceChooser(EmpireSetup@ setup, bool isInitial = false) {
@this.setup = setup;
this.isInitial = isInitial;
super(null);
closeSelf = false;
@panel = GuiSkinElement(this, Alignment(Left-4, Top+0.05f, Right+4, Bottom-0.05f), SS_Panel);
@customizeButton = GuiButton(panel, Alignment(Right-232, Bottom-78, Width=220, Height=33));
customizeButton.text = locale::CUSTOMIZE_RACE;
customizeButton.setIcon(icons::Edit);
@loadButton = GuiButton(panel, Alignment(Right-232, Bottom-78+33, Width=220, Height=33));
loadButton.text = locale::LOAD_CUSTOM_RACE;
loadButton.setIcon(icons::Load);
int w = 250, h = 140;
int off = max((size.width - (getRacePresetCount() * w)) / 2 - 20, 0);
GuiSkinElement listBG(panel, Alignment(Left-4, Top+12, Right+4, Top+154), SS_PlainBox);
@list = GuiPanel(panel, Alignment(Left+off, Top+12, Right-off, Top+174));
updateAbsolutePosition();
vec2i pos;
uint curSelection = 0;
for(uint i = 0, cnt = getRacePresetCount(); i < cnt; ++i) {
auto@ preset = getRacePreset(i);
if(preset.dlc.length != 0 && !hasDLC(preset.dlc))
continue;
racePresets.insertLast(preset);
GuiButton btn(list, recti_area(pos.x, pos.y, w, h));
btn.toggleButton = true;
btn.style = SS_GlowButton;
btn.pressed = i == 0;
GuiSprite icon(btn, recti_area(2, 2, w*0.75, h-4));
icon.horizAlign = 0.0;
icon.vertAlign = 1.0;
icon.desc = getSprite(preset.portrait);
GuiText name(btn, recti_area(0, 0, w-4, h));
name.font = FT_Big;
name.stroke = colors::Black;
name.text = preset.name;
name.vertAlign = 0.4;
name.horizAlign = 0.9;
GuiSkinElement tagbar(btn, recti_area(1, h-28, w-3, 24), SS_PlainBox);
tagbar.color = Color(0xffffff80);
GuiText tagline(btn, recti_area(0, h-30, w-4, 24));
tagline.font = FT_Italic;
tagline.stroke = colors::Black;
tagline.color = Color(0xaaaaaaff);
tagline.text = preset.tagline;
tagline.horizAlign = 1.0;
TraitList traits(btn, Alignment(Left, Bottom-56, Right, Bottom-28));
traits.iconSize = vec2i(24, 24);
traits.horizAlign = 1.0;
traits.fallThrough = true;
traits.traits = preset.traits;
if(preset.equals(setup.settings)) {
curSelection = i;
hasChosenRace = true;
}
if(!setup.player && !preset.aiSupport) {
icon.saturation = 0.f;
traits.visible = false;
btn.disabled = true;
btn.color = Color(0xffffffaa);
name.color = Color(0xaa3030ff);
setMarkupTooltip(btn, locale::AI_CANNOT_PLAY);
}
presetButtons.insertLast(btn);
pos.x += w;
}
BaseGuiElement leftBG(panel, Alignment(Left+12, Top+174, Left+0.33f-6, Bottom-90));
int y = 0;
GuiSkinElement portBG(leftBG, Alignment(Left, Top+y, Right, Bottom), SS_PlainBox);
@bgDisplay = GuiSprite(portBG, Alignment().padded(2), Sprite(getEmpireColor(setup.settings.color).background));
bgDisplay.color = Color(0xffffff80);
bgDisplay.stretchOutside = true;
@portrait = GuiSprite(portBG, Alignment(Left+2, Top, Right-2, Height=232));
portrait.horizAlign = 0.0;
portrait.vertAlign = 1.0;
@flag = GuiSprite(portBG, Alignment(Right-164, Top+4, Width=160, Height=160));
flag.horizAlign = 1.0;
flag.vertAlign = 0.0;
flag.color = setup.settings.color;
flag.color.a = 0xc0;
flag.desc = getSprite(setup.settings.flag);
y += 220 + 12;
GuiSkinElement colBG(leftBG, Alignment(Left, Top+y, Right, Height=34), SS_PlainBox);
@colors = Chooser(colBG, Alignment().padded(8, 0), vec2i(48, 32));
for(uint i = 0, cnt = getEmpireColorCount(); i < cnt; ++i) {
Color color = getEmpireColor(i).color;
colors.add(color);
if(color.color == setup.settings.color.color)
colors.selected = i;
}
updateAbsolutePosition();
y += 34 + 12;
GuiSkinElement flagBG(leftBG, Alignment(Left, Top+y, Right, Height=110), SS_PlainBox);
@flags = Chooser(flagBG, Alignment().padded(8, 0), vec2i(48, 48));
flags.spriteColor = setup.settings.color;
for(uint i = 0, cnt = getEmpireFlagCount(); i < cnt; ++i) {
string flag = getSpriteDesc(Sprite(getEmpireFlag(i).flag));
flags.add(getSprite(flag));
if(flag == setup.settings.flag)
flags.selected = i;
}
y += 110 + 12;
GuiSkinElement shipsetBG(leftBG, Alignment(Left, Top+y, Right, Height=150), SS_PlainBox);
@shipsets = ShipsetChooser(shipsetBG, Alignment().padded(8, 0), vec2i(160, 70));
shipsets.selectedColor = setup.settings.color;
shipsets.selected = 0;
shipsets.horizAlign = 0.0;
for(uint i = 0, cnt = getShipsetCount(); i < cnt; ++i) {
auto@ ss = getShipset(i);
if(ss.available && (ss.dlc.length == 0 || hasDLC(ss.dlc)))
shipsets.add(ss);
if(ss.ident == setup.settings.shipset)
shipsets.selected = shipsets.length-1;
}
GuiSkinElement loreBox(panel, Alignment(Left+0.33f+6, Top+174, Left+0.66f-6, Bottom-90), SS_PlainBox);
@loreScroll = GuiPanel(loreBox, Alignment().fill());
@lore = GuiMarkupText(loreScroll, recti_area(12, 12, 376, 100));
lore.fitWidth = true;
GuiSkinElement descBox(panel, Alignment(Left+0.66f+6, Top+174, Right-12, Bottom-90), SS_PlainBox);
@descScroll = GuiPanel(descBox, Alignment().fill());
@description = GuiMarkupText(descScroll, recti_area(12, 12, 376, 100));
description.fitWidth = true;
@playButton = GuiButton(panel, Alignment(Left+0.5f-150, Bottom-78, Left+0.5f+150, Bottom-12));
playButton.font = FT_Medium;
playButton.color = Color(0x00c0ffff);
@backButton = GuiButton(panel, Alignment(Left+12, Bottom-78, Left+220, Bottom-12), locale::BACK);
backButton.font = FT_Medium;
backButton.buttonIcon = icons::Back;
selectRace(curSelection);
updateAbsolutePosition();
updateAbsolutePosition();
}
void updateAbsolutePosition() {
if(shipsets !is null && shipsets.parent !is null)
shipsets.parent.visible = screenSize.height >= 900;
BaseGuiElement::updateAbsolutePosition();
}
void close() override {
if(isInitial)
return;
GuiOverlay::close();
}
void selectRace(uint select) {
for(uint i = 0, cnt = presetButtons.length; i < cnt; ++i)
presetButtons[i].pressed = i == select;
auto@ preset = racePresets[select];
string desc;
if(preset.isHard)
desc += format("[font=Subtitle][color=#ffc000]$1[/color][/font]", locale::RACE_IS_HARD);
for(uint i = 0, cnt = preset.traits.length; i < cnt; ++i) {
if(desc.length != 0)
desc += "\n\n";
desc += format("[font=Medium]$1[/font][vspace=4/]\n[offset=20]", preset.traits[i].name);
desc += preset.traits[i].description;
desc += "[/offset]";
}
description.text = desc;
string txt = format("[font=Big]$1[/font]\n", preset.name);
txt += format("[right][font=Medium][color=#aaa]$1[/color][/font][/right]\n\n", preset.tagline);
txt += preset.lore;
lore.text = txt;
if(isInitial)
playButton.text = format(locale::PLAY_AS_RACE, preset.name);
else
playButton.text = format(locale::CHOOSE_A_RACE, preset.name);
playButton.buttonIcon = getSprite(preset.portrait);
portrait.desc = getSprite(preset.portrait);
loreScroll.updateAbsolutePosition();
descScroll.updateAbsolutePosition();
@selectedRace = preset;
if(!chosenShipset) {
setup.settings.shipset = preset.shipset;
for(uint i = 0, cnt = shipsets.length; i < cnt; ++i) {
if(shipsets.items[i].ident == preset.shipset) {
shipsets.selected = i;
break;
}
}
}
}
bool onGuiEvent(const GuiEvent& evt) {
if(evt.type == GUI_Clicked) {
if(evt.caller is flags) {
uint sel = flags.hovered;
if(sel != uint(-1)) {
string sprt = getSpriteDesc(Sprite(getEmpireFlag(sel).flag));
setup.settings.flag = sprt;
flag.desc = getSprite(sprt);
flags.selected = sel;
}
return true;
}
else if(evt.caller is colors) {
uint sel = colors.hovered;
if(sel != uint(-1)) {
auto empCol = getEmpireColor(sel);
Color col = empCol.color;
setup.settings.color = col;
bgDisplay.desc = Sprite(empCol.background);
flag.color = col;
flag.color.a = 0xc0;
flags.spriteColor = col;
shipsets.selectedColor = col;
colors.selected = sel;
}
return true;
}
else if(evt.caller is shipsets) {
uint sel = shipsets.hovered;
if(sel != uint(-1)) {
chosenShipset = true;
setup.settings.shipset = shipsets.items[sel].ident;
shipsets.selected = sel;
}
return true;
}
else if(evt.caller is customizeButton) {
isInitial = false;
if(hasChosenRace) {
setup.applyRace(selectedRace);
setup.submit();
}
setup.openRaceWindow();
close();
return true;
}
else if(evt.caller is backButton) {
if(isInitial) {
isInitial = false;
new_game.backButton.emitClicked();
close();
return true;
}
close();
return true;
}
else if(evt.caller is loadButton) {
isInitial = false;
LoadRaceDialog(null, setup.settings, setup);
close();
return true;
}
else if(evt.caller is playButton) {
setup.applyRace(selectedRace);
setup.submit();
if(isInitial) {
isInitial = false;
setup.resetName();
setup.ng.resetAIColors();
setup.ng.resetAIRaces();
}
close();
return true;
}
else {
for(uint i = 0, cnt = presetButtons.length; i < cnt; ++i) {
if(evt.caller.isChildOf(presetButtons[i])) {
hasChosenRace = true;
selectRace(i);
return true;
}
}
}
}
return GuiOverlay::onGuiEvent(evt);
}
};
class EmpireSetup : BaseGuiElement, IGuiCallback {
GuiButton@ portraitButton;
GuiEmpire@ portrait;
EmpireSettings settings;
NewGame@ ng;
GuiTextbox@ name;
GuiButton@ removeButton;
GuiText@ handicapLabel;
GuiSpinbox@ handicap;
GuiButton@ raceBox;
GameAddress address;
GuiButton@ colorButton;
GuiButton@ flagButton;
GuiButton@ difficulty;
GuiButton@ aiSettings;
GuiSprite@ aiIcon;
GuiText@ aiText;
GuiButton@ teamButton;
GuiText@ raceName;
GuiSprite@ raceFTLIcon;
GuiText@ raceFTL;
TraitList@ traitList;
bool player;
bool found = true;
int playerId = -1;
ChoosePopup@ popup;
GuiSprite@ readyness;
string defaultName;
EmpireSetup(NewGame@ menu, Alignment@ align, bool Player = false) {
super(menu.empirePanel, align);
@portraitButton = GuiButton(this, Alignment(Left+8, Top+4, Left+EMPIRE_SETUP_HEIGHT, Bottom-4));
portraitButton.style = SS_NULL;
@portrait = GuiEmpire(portraitButton, Alignment().fill());
@portrait.settings = settings;
@ng = menu;
@name = GuiTextbox(this, Alignment(Left+EMPIRE_SETUP_HEIGHT+8, Top+14, Right-310, Top+0.5f-4));
name.font = FT_Subtitle;
name.style = SS_HoverTextbox;
name.selectionColor = Color(0xffffff40);
@colorButton = GuiButton(this, Alignment(Right-302, Top+14, Width=50, Height=30));
colorButton.style = SS_HoverButton;
@flagButton = GuiButton(this, Alignment(Right-244, Top+14, Width=50, Height=30));
flagButton.style = SS_HoverButton;
@teamButton = GuiButton(this, Alignment(Right-186, Top+14, Width=50, Height=30));
teamButton.style = SS_HoverButton;
@difficulty = GuiButton(this, Alignment(Right-128, Top+14, Width=50, Height=30));
difficulty.style = SS_HoverButton;
difficulty.visible = false;
@aiSettings = GuiButton(this, Alignment(Right-128, Top+10, Width=66, Height=38));
aiSettings.style = SS_HoverButton;
aiSettings.visible = false;
@aiIcon = GuiSprite(aiSettings, Alignment().padded(1, 1, 1, 5));
@aiText = GuiText(aiSettings, Alignment());
aiText.horizAlign = 0.5;
aiText.vertAlign = 0.2;
aiText.font = FT_Small;
aiText.stroke = colors::Black;
@raceBox = GuiButton(this, Alignment(Left+EMPIRE_SETUP_HEIGHT+8, Top+0.5f+4, Right-8, Bottom-14));
raceBox.style = SS_HoverButton;
@raceName = GuiText(raceBox, Alignment(Left+8, Top, Left+0.35f, Bottom));
raceName.font = FT_Bold;
@raceFTLIcon = GuiSprite(raceBox, Alignment(Left+0.4f, Top, Left+0.4f+22, Bottom));
@raceFTL = GuiText(raceBox, Alignment(Left+0.4f+26, Top, Right-0.3f, Bottom));
@traitList = TraitList(raceBox, Alignment(Right-0.3f, Top+2, Right-30, Bottom));
traitList.iconSize = vec2i(24, 24);
traitList.horizAlign = 1.0;
traitList.fallThrough = true;
player = Player;
@removeButton = GuiButton(this,
Alignment(Right-50, Top, Right, Top+30));
removeButton.color = colors::Red;
removeButton.setIcon(icons::Remove);
if(!player) {
removeButton.visible = true;
aiSettings.visible = true;
}
else {
removeButton.visible = false;
playerId = 1;
}
@readyness = GuiSprite(portrait, Alignment(Right-40, Bottom-40, Right, Bottom));
readyness.visible = false;
applyRace(getRacePreset(randomi(0, getRacePresetCount()-1)));
updateAbsolutePosition();
}
void showDifficulties() {
GuiContextMenu menu(mousePos);
menu.itemHeight = 54;
menu.addOption(ChangeDifficulty(this, 0, locale::DIFF_PASSIVE));
menu.addOption(ChangeDifficulty(this, 1, locale::DIFF_EASY));
menu.addOption(ChangeDifficulty(this, 2, locale::DIFF_NORMAL));
menu.addOption(ChangeDifficulty(this, 3, locale::DIFF_HARD));
menu.addOption(ChangeDifficulty(this, 4, locale::DIFF_MURDEROUS));
menu.addOption(ChangeDifficulty(this, 5, locale::DIFF_CHEATING));
menu.addOption(ChangeDifficulty(this, 6, locale::DIFF_SAVAGE));
menu.addOption(ChangeDifficulty(this, 7, locale::DIFF_BARBARIC));
menu.updateAbsolutePosition();
}
void showAISettings() {
AIPopup popup(aiSettings, this);
aiSettings.Hovered = false;
aiSettings.Pressed = false;
}
void showTeams() {
GuiContextMenu menu(mousePos);
menu.itemHeight = 30;
//Figure out how many distinct teams we have
uint distinctTeams = 0;
uint teamMask = 0;
int maxTeam = 0;
for(uint i = 0, cnt = ng.empires.length; i < cnt; ++i) {
int team = ng.empires[i].settings.team;
if(team < 0)
continue;
maxTeam = max(maxTeam, team);
uint mask = 1<<(team-1);
if(mask & teamMask == 0) {
teamMask |= mask;
++distinctTeams;
}
}
//Add more teams than we currently have
menu.addOption(ChangeTeam(this, -1));
for(uint i = 1; i <= min(max(distinctTeams+5, maxTeam+1), 30); ++i)
menu.addOption(ChangeTeam(this, i));
menu.updateAbsolutePosition();
}
void forceAITraits(EmpireSettings& settings) {
for(uint i = 0, cnt = settings.traits.length; i < cnt; ++i) {
auto@ trait = settings.traits[i];
if(!trait.aiSupport) {
if(trait.unique.length == 0) {
settings.traits.removeAt(i);
--cnt; --i;
}
else {
const Trait@ repl;
uint replCount = 0;
for(uint n = 0, ncnt = getTraitCount(); n < ncnt; ++n) {
auto@ other = getTrait(n);
if(other.unique == trait.unique && other.aiSupport && other.hasDLC) {
replCount += 1;
if(randomd() < 1.0 / double(replCount))
@repl = other;
}
}
if(repl !is null) {
@settings.traits[i] = repl;
}
else {
settings.traits.removeAt(i);
--cnt; --i;
}
}
}
}
}
void applyRace(const RacePreset@ preset) {
preset.apply(settings);
if(!player) {
forceAITraits(settings);
if(defaultName == name.text)
resetName();
}
update();
}
void applyRace(const EmpireSettings@ custom) {
settings.copyRaceFrom(custom);
if(!player) {
forceAITraits(settings);
if(defaultName == name.text)
resetName();
}
}
void resetName() {
string race = settings.raceName;
if(race.startswith_nocase("the "))
race = race.substr(4);
name.text = format(localize(empireNames.generate()), race);
defaultName = name.text;
}
void setPlayer(int id) {
player = id != -1;
playerId = id;
name.disabled = player || !mpClient;
removeButton.visible = !mpClient && (!player || id != CURRENT_PLAYER.id);
aiSettings.visible = !player;
readyness.visible = player && id != 1;
bool editable = id == CURRENT_PLAYER.id || (!mpClient && id == -1);
raceBox.disabled = !editable;
colorButton.disabled = !editable;
flagButton.disabled = !editable;
teamButton.disabled = !editable;
aiSettings.disabled = !editable;
}
void openRaceWindow() {
TraitsWindow win(this);
}
void update() {
updateTraits();
if(difficulty.visible) {
difficulty.color = DIFF_COLORS[settings.difficulty];
setMarkupTooltip(difficulty, locale::TT_DIFF+"\n"+DIFF_TOOLTIPS[settings.difficulty], width=300);
if(difficulty.color.color != colors::White.color)
difficulty.style = SS_Button;
else
difficulty.style = SS_HoverButton;
}
if(aiSettings.visible) {
aiIcon.desc = QDIFF_ICONS[clamp(settings.difficulty, 0, 2)];
aiText.color = QDIFF_COLORS[clamp(settings.difficulty, 0, 2)];
aiText.text = getAIName(settings);
}
name.textColor = settings.color;
raceName.text = settings.raceName;
for(uint i = 0, cnt = settings.traits.length; i < cnt; ++i) {
auto@ trait = settings.traits[i];
if(trait.unique == "FTL") {
raceFTLIcon.desc = trait.icon;
raceFTL.text = trait.name;
}
}
}
void updateTraits() {
traitList.traits.length = 0;
for(uint i = 0, cnt = getTraitCount(); i < cnt; ++i) {
auto@ trait = getTrait(i);
if(settings.hasTrait(trait) && trait.unique != "FTL")
traitList.traits.insertLast(trait);
}
if(settings.ready) {
readyness.tooltip = locale::MP_PLAYER_READY;
readyness.desc = icons::Ready;
}
else {
readyness.tooltip = locale::MP_PLAYER_NOT_READY;
readyness.desc = icons::NotReady;
}
}
void submit() {
if(mpClient)
changeEmpireSettings(settings);
if(!player)
forceAITraits(settings);
settings.delta += 1;
update();
}
bool onGuiEvent(const GuiEvent& evt) {
switch(evt.type) {
case GUI_Clicked:
if(evt.caller is removeButton) {
if(player)
mpKick(playerId);
else
ng.removeEmpire(this);
return true;
}
else if(evt.caller is difficulty) {
showDifficulties();
return true;
}
else if(evt.caller is aiSettings) {
showAISettings();
return true;
}
else if(evt.caller is teamButton) {
showTeams();
return true;
}
else if(evt.caller is raceBox || evt.caller is portraitButton) {
RaceChooser(this);
raceBox.Hovered = false;
return true;
}
else if(evt.caller is colorButton) {
vec2i pos(evt.caller.absolutePosition.topLeft.x,
evt.caller.absolutePosition.botRight.y);
uint cnt = getEmpireColorCount();
vec2i size(220, ceil(double(cnt)/4.0) * 38.0);
@popup = ChoosePopup(pos, size, vec2i(48, 32));
@popup.callback = this;
popup.extraHeight = 60;
ColorPicker picker(popup.overlay, recti_area(pos+vec2i(20,size.y+2), vec2i(size.x-40, 50)));
@picker.callback = this;
for(uint i = 0; i < cnt; ++i)
popup.add(getEmpireColor(i).color);
return true;
}
else if(evt.caller is flagButton) {
vec2i pos(evt.caller.absolutePosition.topLeft.x,
evt.caller.absolutePosition.botRight.y);
uint cnt = getEmpireFlagCount();
vec2i size(220, ceil(double(cnt)/4.0) * 52.0);
@popup = ChoosePopup(pos, size, vec2i(48, 48));
@popup.callback = this;
popup.spriteColor = settings.color;
for(uint i = 0; i < cnt; ++i)
popup.add(Sprite(getEmpireFlag(i).flag));
return true;
}
else if(evt.caller is popup) {
}
break;
case GUI_Confirmed:
if(evt.caller is popup) {
if(popup.colors.length > 0)
settings.color = getEmpireColor(evt.value).color;
else if(popup.sprites.length > 0)
settings.flag = getEmpireFlag(evt.value).flagDef;
@popup = null;
submit();
return true;
}
if(cast<ColorPicker>(evt.caller) !is null) {
settings.color = cast<ColorPicker>(evt.caller).picked;
popup.remove();
@popup = null;
submit();
}
break;
}
return BaseGuiElement::onGuiEvent(evt);
}
void apply(EmpireSettings& es) {
es = settings;
es.name = name.text;
es.playerId = playerId;
if(player || playerId != -1)
es.type = ET_Player;
else if(es.type == ET_Player)
es.type = ET_WeaselAI;
}
void load(EmpireSettings& es) {
name.text = es.name;
player = es.type == uint(ET_Player);
setPlayer(es.playerId);
settings = es;
update();
}
void draw() {
Color color = settings.color;
skin.draw(SS_EmpireSetupItem, SF_Normal, AbsolutePosition.padded(-10, 0), color);
BaseGuiElement::draw();
if(colorButton.visible) {
setClip(colorButton.absoluteClipRect);
drawRectangle(colorButton.absolutePosition.padded(6), color);
}
auto@ flag = getEmpireFlag(settings.flag);
if(flag !is null && flagButton.visible) {
setClip(flagButton.absoluteClipRect);
flag.flag.draw(recti_centered(flagButton.absolutePosition,
vec2i(flagButton.size.height, flagButton.size.height)),
color);
}
if(difficulty.visible) {
setClip(difficulty.absoluteClipRect);
DIFF_SPRITES[settings.difficulty].draw(recti_centered(difficulty.absolutePosition,
vec2i(difficulty.size.height, difficulty.size.height)));
}
if(teamButton.visible) {
setClip(teamButton.absoluteClipRect);
if(settings.team >= 0) {
material::TabDiplomacy.draw(recti_centered(teamButton.absolutePosition,
vec2i(teamButton.size.height, teamButton.size.height)));
skin.getFont(FT_Small).draw(
pos=teamButton.absolutePosition,
text=locale::TEAM,
horizAlign=0.5, vertAlign=0.0,
stroke=colors::Black,
color=colors::White);
skin.getFont(FT_Medium).draw(
pos=teamButton.absolutePosition,
text=toString(settings.team),
horizAlign=0.5, vertAlign=1.0,
stroke=colors::Black,
color=colorFromNumber(settings.team));
}
else {
shader::SATURATION_LEVEL = 0.f;
material::TabDiplomacy.draw(recti_centered(teamButton.absolutePosition,
vec2i(teamButton.size.height, teamButton.size.height)),
Color(0xffffff80), shader::Desaturate);
}
}
}
};
string getAIName(EmpireSettings& settings) {
string text;
text = QDIFF_NAMES[clamp(settings.difficulty, 0, 2)];
if(settings.aiFlags & AIF_Passive != 0)
text += "|";
if(settings.aiFlags & AIF_Aggressive != 0)
text += "@";
if(settings.aiFlags & AIF_Biased != 0)
text += "^";
if(settings.aiFlags & AIF_CheatPrivileged != 0)
text += "$";
if(settings.type == ET_BumAI)
text += "?";
int cheatLevel = 0;
if(settings.cheatWealth > 0)
cheatLevel += ceil(double(settings.cheatWealth) / 10.0);
if(settings.cheatStrength > 0)
cheatLevel += settings.cheatStrength;
if(settings.cheatAbundance > 0)
cheatLevel += settings.cheatAbundance;
if(cheatLevel > 0) {
if(cheatLevel > 3)
cheatLevel = 3;
while(cheatLevel > 0) {
text += "+";
cheatLevel -= 1;
}
}
return text;
}
class AIPopup : BaseGuiElement {
GuiOverlay@ overlay;
GuiListbox@ difficulties;
GuiText@ behaveHeading;
GuiText@ cheatHeading;
EmpireSetup@ setup;
GuiCheckbox@ aggressive;
GuiCheckbox@ passive;
GuiCheckbox@ biased;
GuiCheckbox@ legacy;
GuiCheckbox@ wealth;
GuiSpinbox@ wealthAmt;
GuiCheckbox@ strength;
GuiSpinbox@ strengthAmt;
GuiCheckbox@ abundance;
GuiSpinbox@ abundanceAmt;
GuiCheckbox@ privileged;
GuiButton@ okButton;
AIPopup(IGuiElement@ around, EmpireSetup@ setup) {
@overlay = GuiOverlay(null);
overlay.closeSelf = false;
overlay.fade.a = 0;
@this.setup = setup;
recti pos = recti_area(
vec2i(around.absolutePosition.botRight.x, around.absolutePosition.topLeft.y),
vec2i(600, 200));
if(pos.botRight.y > screenSize.y)
pos += vec2i(0, screenSize.y - pos.botRight.y);
if(pos.botRight.x > screenSize.x)
pos += vec2i(screenSize.x - pos.botRight.x, 0);
super(overlay, pos);
updateAbsolutePosition();
setGuiFocus(this);
@difficulties = GuiListbox(this, Alignment(Left+4, Top+4, Left+250, Bottom-4));
difficulties.required = true;
difficulties.itemHeight = 64;
for(uint i = 0; i < 3; ++i) {
difficulties.addItem(GuiMarkupListText(
format("[color=$1][font=Medium][stroke=#000]$2[/stroke][/font][/color]\n[color=#aaa][i]$3[/i][/color]",
toString(QDIFF_COLORS[i]), QDIFF_NAMES[i], QDIFF_DESC[i])));
}
@behaveHeading = GuiText(this, Alignment(Left+260, Top+6, Left+260+170, Top+36));
behaveHeading.font = FT_Medium;
behaveHeading.stroke = colors::Black;
behaveHeading.text = locale::AI_BEHAVIOR;
pos = recti_area(vec2i(260, 36), vec2i(170, 30));
@aggressive = GuiCheckbox(this, pos, locale::AI_AGGRESSIVE);
setMarkupTooltip(aggressive, locale::AI_AGGRESSIVE_DESC);
pos += vec2i(0, 30);
@passive = GuiCheckbox(this, pos, locale::AI_PASSIVE);
setMarkupTooltip(passive, locale::AI_PASSIVE_DESC);
pos += vec2i(0, 30);
@biased = GuiCheckbox(this, pos, locale::AI_BIASED);
setMarkupTooltip(biased, locale::AI_BIASED_DESC);
pos += vec2i(0, 30);
@legacy = GuiCheckbox(this, pos, locale::AI_LEGACY);
legacy.textColor = Color(0xaaaaaaff);
legacy.visible = !hasDLC("Heralds");
setMarkupTooltip(legacy, locale::AI_LEGACY_DESC);
pos += vec2i(0, 30);
@cheatHeading = GuiText(this, Alignment(Left+260+165, Top+6, Right-12, Top+36));
cheatHeading.font = FT_Medium;
cheatHeading.stroke = colors::Black;
cheatHeading.text = locale::AI_CHEATS;
pos = recti_area(vec2i(260+165, 36), vec2i(170, 30));
@wealth = GuiCheckbox(this, recti_area(pos.topLeft, vec2i(110, 30)), locale::AI_WEALTH);
setMarkupTooltip(wealth, locale::AI_WEALTH_DESC);
@wealthAmt = GuiSpinbox(this, recti_area(pos.topLeft+vec2i(115, 0), vec2i(50, 30)), 10, 0, 1000, 1, 0);
pos += vec2i(0, 30);
@strength = GuiCheckbox(this, recti_area(pos.topLeft, vec2i(110, 30)), locale::AI_STRENGTH);
setMarkupTooltip(strength, locale::AI_STRENGTH_DESC);
@strengthAmt = GuiSpinbox(this, recti_area(pos.topLeft+vec2i(115, 0), vec2i(50, 30)), 1, 0, 100, 1, 0);
pos += vec2i(0, 30);
@abundance = GuiCheckbox(this, recti_area(pos.topLeft, vec2i(110, 30)), locale::AI_ABUNDANCE);
setMarkupTooltip(abundance, locale::AI_ABUNDANCE_DESC);
@abundanceAmt = GuiSpinbox(this, recti_area(pos.topLeft+vec2i(115, 0), vec2i(50, 30)), 1, 0, 100, 1, 0);
pos += vec2i(0, 30);
@privileged = GuiCheckbox(this, pos, locale::AI_PRIVILEGED);
setMarkupTooltip(privileged, locale::AI_PRIVILEGED_DESC);
pos += vec2i(0, 30);
@okButton = GuiButton(this, Alignment(Left+260+135, Bottom-34, Width=70, Height=30), locale::OK);
reset();
}
void reset() {
difficulties.selected = clamp(setup.settings.difficulty, 0, 2);
aggressive.checked = setup.settings.aiFlags & AIF_Aggressive != 0;
passive.checked = setup.settings.aiFlags & AIF_Passive != 0;
biased.checked = setup.settings.aiFlags & AIF_Biased != 0;
privileged.checked = setup.settings.aiFlags & AIF_CheatPrivileged != 0;
legacy.checked = setup.settings.type == ET_BumAI;
if(legacy.checked)
legacy.visible = true;
wealth.checked = setup.settings.cheatWealth > 0;
wealthAmt.visible = wealth.checked;
if(wealth.checked)
wealthAmt.value = setup.settings.cheatWealth;
strength.checked = setup.settings.cheatStrength > 0;
strengthAmt.visible = strength.checked;
if(strength.checked)
strengthAmt.value = setup.settings.cheatStrength;
abundance.checked = setup.settings.cheatAbundance > 0;
abundanceAmt.visible = abundance.checked;
if(abundance.checked)
abundanceAmt.value = setup.settings.cheatAbundance;
}
void apply() {
uint flags = 0;
if(aggressive.checked)
flags |= AIF_Aggressive;
if(passive.checked)
flags |= AIF_Passive;
if(biased.checked)
flags |= AIF_Biased;
if(privileged.checked)
flags |= AIF_CheatPrivileged;
if(legacy.checked)
setup.settings.type = ET_BumAI;
else
setup.settings.type = ET_WeaselAI;
wealthAmt.visible = wealth.checked;
if(wealthAmt.visible)
setup.settings.cheatWealth = wealthAmt.value;
else
setup.settings.cheatWealth = 0;
strengthAmt.visible = strength.checked;
if(strengthAmt.visible)
setup.settings.cheatStrength = strengthAmt.value;
else
setup.settings.cheatStrength = 0;
abundanceAmt.visible = abundance.checked;
if(abundanceAmt.visible)
setup.settings.cheatAbundance = abundanceAmt.value;
else
setup.settings.cheatAbundance = 0;
setup.settings.difficulty = difficulties.selected;
setup.settings.aiFlags = flags;
setup.submit();
}
bool onGuiEvent(const GuiEvent& evt) override {
if(evt.type == GUI_Changed) {
if(evt.caller is passive) {
if(passive.checked)
aggressive.checked = false;
apply();
return true;
}
if(evt.caller is aggressive) {
if(aggressive.checked)
passive.checked = false;
apply();
return true;
}
apply();
}
if(evt.type == GUI_Clicked) {
if(evt.caller is okButton) {
apply();
remove();
return true;
}
}
return BaseGuiElement::onGuiEvent(evt);
}
void remove() {
overlay.remove();
@overlay = null;
BaseGuiElement::remove();
}
void draw() override {
clearClip();
skin.draw(SS_Panel, SF_Normal, AbsolutePosition);
BaseGuiElement::draw();
}
};
class ChoosePopup : GuiIconGrid {
GuiOverlay@ overlay;
int extraHeight = 0;
Color spriteColor;
array<Color> colors;
array<Sprite> sprites;
ChoosePopup(const vec2i& pos, const vec2i& size, const vec2i& itemSize) {
@overlay = GuiOverlay(null);
overlay.closeSelf = false;
overlay.fade.a = 0;
super(overlay, recti_area(pos, size));
horizAlign = 0.5;
vertAlign = 0.0;
iconSize = itemSize;
updateAbsolutePosition();
}
bool onGuiEvent(const GuiEvent& evt) override {
if(evt.caller is this && evt.type == GUI_Clicked) {
if(hovered != -1)
emitConfirmed(uint(hovered));
overlay.close();
return true;
}
return GuiIconGrid::onGuiEvent(evt);
}
void remove() {
overlay.remove();
@overlay = null;
GuiIconGrid::remove();
}
void add(const Color& col) {
colors.insertLast(col);
}
void add(const Sprite& sprt) {
sprites.insertLast(sprt);
}
uint get_length() override {
return max(colors.length, sprites.length);
}
void drawElement(uint index, const recti& pos) override {
if(uint(hovered) == index)
drawRectangle(pos, Color(0xffffff30));
if(index < colors.length)
drawRectangle(pos.padded(5), colors[index]);
if(index < sprites.length)
sprites[index].draw(pos, spriteColor);
}
void draw() override {
clearClip();
skin.draw(SS_Panel, SF_Normal, AbsolutePosition.padded(0,0,0,-extraHeight));
GuiIconGrid::draw();
}
};
class ColorPicker : BaseGuiElement {
Color picked;
bool pressed = false;
ColorPicker(IGuiElement@ parent, const recti& pos) {
super(parent, pos);
updateAbsolutePosition();
}
void draw() {
shader::HSV_VALUE = 1.f;
shader::HSV_SAT_START = 0.5f;
shader::HSV_SAT_END = 1.f;
drawRectangle(AbsolutePosition, material::HSVPalette, Color());
if(AbsolutePosition.isWithin(mousePos)) {
clearClip();
recti area = recti_area(mousePos-vec2i(10), vec2i(20));
drawRectangle(area.padded(-1), colors::Black);
drawRectangle(area, getColor(mousePos-AbsolutePosition.topLeft));
}
BaseGuiElement::draw();
}
Color getColor(vec2i offset) {
Colorf col;
float hue = float(offset.x) / float(AbsolutePosition.width) * 360.f;
float sat = (1.f - float(offset.y) / float(AbsolutePosition.height)) * 0.5f + 0.5f;
col.fromHSV(hue, sat, 1.f);
col.a = 1.f;
return Color(col);
}
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
if(event.type == MET_Button_Down || (event.type == MET_Moved && pressed)) {
pressed = true;
picked = getColor(mousePos - AbsolutePosition.topLeft);
GuiEvent evt;
@evt.caller = this;
evt.type = GUI_Changed;
onGuiEvent(evt);
return true;
}
else if(pressed && event.type == MET_Button_Up) {
pressed = false;
emitConfirmed();
return true;
}
return BaseGuiElement::onMouseEvent(event, source);
}
};
class PortraitChooser : GuiIconGrid {
array<Sprite> sprites;
uint selected = 0;
Color selectedColor;
PortraitChooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) {
super(parent, align);
horizAlign = 0.5;
vertAlign = 0.0;
iconSize = itemSize;
updateAbsolutePosition();
}
void add(const Sprite& sprt) {
sprites.insertLast(sprt);
}
uint get_length() override {
return sprites.length;
}
void drawElement(uint index, const recti& pos) override {
if(selected == index)
drawRectangle(pos, selectedColor);
if(uint(hovered) == index)
drawRectangle(pos, Color(0xffffff30));
if(index < sprites.length)
sprites[index].draw(pos);
}
};
class ShipsetChooser : GuiIconGrid {
array<const Shipset@> items;
uint selected = 0;
Color selectedColor;
ShipsetChooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) {
super(parent, align);
horizAlign = 0.5;
vertAlign = 0.0;
iconSize = itemSize;
updateAbsolutePosition();
}
void add(const Shipset@ shipset) {
items.insertLast(shipset);
}
uint get_length() override {
return items.length;
}
void drawElement(uint index, const recti& pos) override {
if(selected == index) {
Color col = selectedColor;
col.a = 0x15;
drawRectangle(pos, col);
}
if(uint(hovered) == index)
drawRectangle(pos, Color(0xffffff15));
if(index < items.length) {
const Shipset@ shipset = items[index];
const Hull@ hull = shipset.hulls[0];
if(hull !is null) {
quaterniond rot;
rot = quaterniond_fromAxisAngle(vec3d_front(), -0.9);
rot *= quaterniond_fromAxisAngle(vec3d_up(), 0.6);
rot *= quaterniond_fromAxisAngle(vec3d_right(), -0.5);
setClip(pos);
Color lightColor = colors::White;
if(selected == index) {
NODE_COLOR = Colorf(selectedColor);
lightColor = selectedColor;
}
else
NODE_COLOR = Colorf(1.f, 1.f, 1.f, 1.f);
drawLitModel(hull.model, hull.material, pos+vec2i(-4,0), rot, 1.9, lightColor=lightColor);
clearClip();
}
const Font@ ft = skin.getFont(FT_Bold);
if(selected == index || uint(hovered) == index)
ft.draw(text=shipset.name, pos=pos.padded(0,4),
horizAlign=0.5, vertAlign=0.0, stroke=colors::Black,
color=(selected == index ? selectedColor : colors::White));
}
}
};
class WeaponSkinChooser : GuiIconGrid {
array<const EmpireWeaponSkin@> items;
uint selected = 0;
Color selectedColor;
WeaponSkinChooser(IGuiElement@ parent, Alignment@ align, const vec2i& itemSize) {
super(parent, align);
horizAlign = 0.5;
vertAlign = 0.0;
iconSize = itemSize;
updateAbsolutePosition();
}
void add(const EmpireWeaponSkin@ it) {
items.insertLast(it);
}
uint get_length() override {
return items.length;
}
void drawElement(uint index, const recti& pos) override {
if(selected == index) {
Color col = selectedColor;
col.a = 0x15;
drawRectangle(pos, col);
}
if(uint(hovered) == index)
drawRectangle(pos, Color(0xffffff15));
if(index < items.length)
items[index].icon.draw(pos);
}
};
class TraitDisplay : BaseGuiElement {
const Trait@ trait;
GuiSprite@ icon;
GuiMarkupText@ name;
GuiMarkupText@ description;
GuiText@ points;
GuiText@ conflicts;
GuiCheckbox@ check;
bool hovered = false;
bool conflict = false;
TraitDisplay(IGuiElement@ parent) {
super(parent, recti());
@icon = GuiSprite(this, Alignment(Left+20, Top+12, Left+52, Bottom-12));
@name = GuiMarkupText(this, Alignment(Left+65, Top+8, Right-168, Top+38));
name.defaultFont = FT_Medium;
name.defaultStroke = colors::Black;
@description = GuiMarkupText(this, Alignment(Left+124, Top+34, Right-168, Bottom-8));
@conflicts = GuiText(this, Alignment(Right-360, Top+8, Right-56, Bottom-8));
conflicts.vertAlign = 0.1;
conflicts.horizAlign = 1.0;
@points = GuiText(this, Alignment(Right-160, Top+8, Right-56, Bottom-8));
points.horizAlign = 1.0;
points.font = FT_Subtitle;
@check = GuiCheckbox(this, Alignment(Right-48, Top+0.5f-20, Right-8, Top+0.5f+20), "");
}
bool onGuiEvent(const GuiEvent& evt) override {
switch(evt.type) {
case GUI_Mouse_Entered:
hovered = true;
break;
case GUI_Mouse_Left:
hovered = false;
break;
case GUI_Changed:
if(evt.caller is check) {
check.checked = !check.checked;
emitClicked();
return true;
}
break;
}
return BaseGuiElement::onGuiEvent(evt);
}
bool onMouseEvent(const MouseEvent& evt, IGuiElement@ caller) override {
switch(evt.type) {
case MET_Button_Down:
if(evt.button == 0)
return true;
break;
case MET_Button_Up:
if(evt.button == 0) {
emitClicked();
return true;
}
break;
}
return BaseGuiElement::onMouseEvent(evt, caller);
}
void set(const Trait@ trait, bool selected, bool conflict) {
@this.trait = trait;
this.conflict = conflict;
description.text = trait.description;
icon.desc = trait.icon;
name.defaultColor = trait.color;
if(trait.gives > 0) {
points.text = format(locale::RACE_POINTS_POS, toString(trait.gives));
points.color = colors::Green;
points.visible = true;
}
else if(trait.cost > 0) {
points.text = format(locale::RACE_POINTS_NEG, toString(trait.cost));
points.color = colors::Red;
points.visible = true;
}
else {
points.text = locale::RACE_POINTS_NEU;
points.color = Color(0xaaaaaaff);
points.visible = false;
}
bool displayConflicts = false;
if(trait.conflicts.length > 0) {
if(conflict) {
conflicts.color = colors::Red;
conflicts.font = FT_Bold;
conflicts.vertAlign = 0.2;
}
else {
conflicts.color = Color(0xaaaaaaff);
conflicts.font = FT_Italic;
conflicts.vertAlign = 0.1;
}
string str = locale::CONFLICTS+" ";
for(uint i = 0, cnt = trait.conflicts.length; i < cnt; ++i) {
if(!trait.conflicts[i].available)
continue;
if(i != 0)
str += ", ";
str += trait.conflicts[i].name;
displayConflicts = true;
}
conflicts.text = str;
}
if(displayConflicts) {
conflicts.visible = true;
points.vertAlign = 0.7;
}
else {
conflicts.visible = false;
points.vertAlign = 0.5;
}
if(trait.unique.length != 0) {
check.style = SS_Radiobox;
if(description.alignment.right.pixels != 52) {
description.alignment.right.pixels = 52;
description.updateAbsolutePosition();
}
}
else {
check.style = SS_Checkbox;
if(description.alignment.right.pixels != 168) {
description.alignment.right.pixels = 168;
description.updateAbsolutePosition();
}
}
name.text = trait.name;
check.checked = selected;
}
void draw() {
if(check.checked)
skin.draw(SS_Glow, SF_Normal, AbsolutePosition, trait.color);
skin.draw(SS_Panel, SF_Normal, AbsolutePosition.padded(4), trait.color);
if(hovered)
drawRectangle(AbsolutePosition.padded(8), Color(0xffffff10));
BaseGuiElement::draw();
}
};
class SaveRaceDialog : SaveDialog {
EmpireSettings settings;
EmpireSetup@ setup;
SaveRaceDialog(IGuiElement@ bind, EmpireSettings@ settings, EmpireSetup@ setup) {
this.settings = settings;
@this.setup = setup;
super(bind, modProfile["races"], settings.raceName+".race");
}
void clickConfirm() override {
exportRace(settings, path);
}
};
class LoadRaceDialog : LoadDialog {
EmpireSettings settings;
EmpireSetup@ setup;
TraitsWindow@ win;
LoadRaceDialog(TraitsWindow@ win, EmpireSettings@ settings, EmpireSetup@ setup) {
this.settings = settings;
@this.setup = setup;
@this.win = win;
super(win, modProfile["races"]);
}
void clickConfirm() override {
importRace(setup.settings, path);
if(win !is null)
win.update();
setup.submit();
}
};
class TraitElement : GuiListElement {
const Trait@ trait;
void draw(GuiListbox@ ele, uint flags, const recti& absPos) {
recti iconPos = recti_area(absPos.topLeft+vec2i(10, 5), vec2i(absPos.height-10, absPos.height-10));
trait.icon.draw(iconPos);
recti textPos = absPos.padded(absPos.height + 10, 0, 10, 4);
ele.skin.getFont(FT_Medium).draw(
text=trait.name, pos=textPos);
}
string get_tooltipText() {
return format("[color=$1][b]$2[/b][/color]\n$3",
toString(trait.color), trait.name, trait.description);
}
};
class TraitsWindow : BaseGuiElement {
GuiOverlay@ overlay;
EmpireSetup@ setup;
GuiBackgroundPanel@ bg;
GuiListbox@ categories;
array<const TraitCategory@> usedCategories;
GuiPanel@ profilePanel;
GuiText@ nameLabel;
GuiTextbox@ name;
GuiText@ portraitLabel;
PortraitChooser@ portrait;
GuiText@ shipsetLabel;
ShipsetChooser@ shipset;
GuiText@ weaponSkinLabel;
WeaponSkinChooser@ weaponSkin;
GuiText@ traitsLabel;
GuiListbox@ traitList;
GuiText@ pointsLabel;
GuiPanel@ traitPanel;
GuiText@ noTraits;
array<TraitDisplay@> traits;
GuiButton@ acceptButton;
GuiButton@ saveButton;
GuiButton@ loadButton;
TraitsWindow(EmpireSetup@ setup) {
@this.setup = setup;
@overlay = GuiOverlay(null);
overlay.closeSelf = false;
super(overlay, Alignment(Left+0.11f, Top+0.11f, Right-0.11f, Bottom-0.11f));
updateAbsolutePosition();
@bg = GuiBackgroundPanel(this, Alignment().fill());
bg.titleColor = Color(0xff8000ff);
bg.title = locale::CUSTOMIZE_RACE;
@categories = GuiListbox(bg, Alignment(Left+4, Top+32, Left+250, Bottom-4));
categories.itemHeight = 44;
categories.style = SS_PlainOverlay;
categories.itemStyle = SS_TabButton;
categories.addItem(GuiMarkupListText(locale::RACE_PROFILE));
categories.required = true;
for(uint i = 0, cnt = getTraitCategoryCount(); i < cnt; ++i) {
auto@ cat = getTraitCategory(i);
bool hasTraits = false;
for(uint n = 0, ncnt = getTraitCount(); n < ncnt; ++n) {
if(getTrait(n).category is cat && getTrait(n).available && getTrait(n).hasDLC) {
hasTraits = true;
break;
}
}
if(hasTraits) {
categories.addItem(GuiMarkupListText(cat.name));
usedCategories.insertLast(cat);
}
}
@acceptButton = GuiButton(bg, Alignment(Right-140, Bottom-40, Right-3, Bottom-3), locale::ACCEPT);
@loadButton = GuiButton(bg, Alignment(Right-274, Bottom-40, Right-154, Bottom-3), locale::LOAD);
@saveButton = GuiButton(bg, Alignment(Right-400, Bottom-40, Right-280, Bottom-3), locale::SAVE);
@pointsLabel = GuiText(bg, Alignment(Left+264, Bottom-40, Right-410, Bottom-3));
pointsLabel.font = FT_Medium;
Alignment panelAlign(Left+258, Top+32, Right-4, Bottom-40);
@profilePanel = GuiPanel(bg, panelAlign);
@traitPanel = GuiPanel(bg, panelAlign);
traitPanel.visible = false;
int y = 8;
@nameLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::RACE_NAME, FT_Bold);
@name = GuiTextbox(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+30), setup.settings.raceName);
y += 38;
int h = 80 + (getEmpirePortraitCount() / ((size.width - 200) / 70)) * 80;
@portraitLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::PORTRAIT, FT_Bold);
@portrait = PortraitChooser(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+h), vec2i(70, 70));
portrait.selectedColor = setup.settings.color;
portrait.selected = randomi(0, getEmpirePortraitCount()-1);
portrait.horizAlign = 0.0;
for(uint i = 0, cnt = getEmpirePortraitCount(); i < cnt; ++i) {
auto@ img = getEmpirePortrait(i);
portrait.add(Sprite(img.portrait));
if(img.ident == setup.settings.portrait)
portrait.selected = i;
}
y += h+8;
h = 80 + (getShipsetCount() / ((size.width - 200) / 150)) * 80;
@shipsetLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::SHIPSET, FT_Bold);
@shipset = ShipsetChooser(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+h), vec2i(150, 70));
shipset.selectedColor = setup.settings.color;
shipset.selected = 0;
shipset.horizAlign = 0.0;
for(uint i = 0, cnt = getShipsetCount(); i < cnt; ++i) {
auto@ ss = getShipset(i);
if(ss.available && (ss.dlc.length == 0 || hasDLC(ss.dlc)))
shipset.add(ss);
if(ss.ident == setup.settings.shipset)
shipset.selected = shipset.length-1;
}
y += h+8;
@weaponSkinLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::WEAPON_SKIN, FT_Bold);
@weaponSkin = WeaponSkinChooser(profilePanel, Alignment(Left+200, Top+y, Right-12, Top+y+80), vec2i(120, 70));
weaponSkin.selectedColor = setup.settings.color;
weaponSkin.selected = 0;
weaponSkin.horizAlign = 0.0;
for(uint i = 0, cnt = getEmpireWeaponSkinCount(); i < cnt; ++i) {
auto@ skin = getEmpireWeaponSkin(i);
weaponSkin.add(skin);
if(skin.ident == setup.settings.effectorSkin)
weaponSkin.selected = weaponSkin.length-1;
}
y += 88;
@traitsLabel = GuiText(profilePanel, Alignment(Left+12, Top+y, Left+200, Top+y+30), locale::TRAITS, FT_Bold);
@traitList = GuiListbox(profilePanel, Alignment(Left+200, Top+y, Right-12, Bottom-8));
traitList.itemStyle = SS_StaticListboxItem;
traitList.itemHeight = 50;
addLazyMarkupTooltip(traitList);
@noTraits = GuiText(profilePanel, Alignment(Left+240, Top+y+10, Right-12, Top+y+50), locale::NO_TRAITS);
noTraits.color = Color(0xaaaaaaff);
noTraits.vertAlign = 0.0;
y += 58;
update();
updateAbsolutePosition();
}
void update() {
int sel = categories.selected;
profilePanel.visible = sel == 0;
traitPanel.visible = sel != 0;
uint index = 0;
const TraitCategory@ cat;
if(sel > 0)
@cat = usedCategories[sel - 1];
int points = STARTING_TRAIT_POINTS;
for(uint i = 0, cnt = setup.settings.traits.length; i < cnt; ++i) {
points += setup.settings.traits[i].gives;
points -= setup.settings.traits[i].cost;
}
if(traitPanel.visible) {
int y = 0;
array<const Trait@> list;
for(uint i = 0, cnt = getTraitCount(); i < cnt; ++i) {
auto@ trait = getTrait(i);
if(cat !is null && cat !is trait.category)
continue;
if(!setup.player && !trait.aiSupport)
continue;
if(!trait.available)
continue;
if(!trait.hasDLC)
continue;
list.insertLast(trait);
}
list.sortAsc();
for(uint i = 0, cnt = list.length; i < cnt; ++i) {
auto@ trait = list[i];
TraitDisplay@ disp;
if(index < traits.length) {
@disp = traits[index];
}
else {
@disp = TraitDisplay(traitPanel);
traits.insertLast(disp);
}
disp.set(trait, setup.settings.hasTrait(trait), trait.hasConflicts(setup.settings.traits));
disp.alignment.set(Left, Top+y, Right, Top+y+140);
disp.updateAbsolutePosition();
int needH = disp.description.renderer.height+48;
if(needH != 140) {
disp.alignment.set(Left, Top+y, Right, Top+y+needH);
disp.updateAbsolutePosition();
}
++index;
y += needH;
}
for(uint i = index, cnt = traits.length; i < cnt; ++i)
traits[i].remove();
traits.length = index;
traitPanel.updateAbsolutePosition();
}
if(profilePanel.visible) {
uint cnt = setup.settings.traits.length;
traitList.removeItemsFrom(cnt);
for(uint i = 0; i < cnt; ++i) {
auto@ item = cast<TraitElement>(traitList.getItemElement(i));
if(item is null) {
@item = TraitElement();
traitList.addItem(item);
}
@item.trait = setup.settings.traits[i];
}
noTraits.visible = cnt == 0;
}
if(points > 0) {
pointsLabel.color = colors::Green;
pointsLabel.text = format(locale::RACE_POINTS_AVAIL_POS, toString(points));
pointsLabel.visible = true;
}
else if(points < 0) {
pointsLabel.color = colors::Red;
pointsLabel.text = format(locale::RACE_POINTS_AVAIL_NEG, toString(-points));
pointsLabel.visible = true;
}
else {
pointsLabel.color = Color(0xaaaaaaff);
pointsLabel.text = format(locale::RACE_POINTS_AVAIL_POS, toString(points));
pointsLabel.visible = false;
}
if(points >= 0 && !setup.settings.hasTraitConflicts())
acceptButton.color = colors::Green;
else
acceptButton.color = colors::Red;
}
bool onGuiEvent(const GuiEvent& evt) override {
if(evt.caller is acceptButton) {
if(evt.type == GUI_Clicked) {
overlay.close();
return true;
}
}
if(evt.caller is saveButton) {
if(evt.type == GUI_Clicked) {
SaveRaceDialog(this, setup.settings, setup);
return true;
}
}
else if(evt.caller is loadButton) {
if(evt.type == GUI_Clicked) {
LoadRaceDialog(this, setup.settings, setup);
return true;
}
}
if(evt.type == GUI_Clicked) {
if(evt.caller is portrait) {
int hov = portrait.hovered;
if(hov >= 0) {
setup.settings.portrait = getEmpirePortrait(hov).ident;
portrait.selected = hov;
}
setup.submit();
return true;
}
if(evt.caller is shipset) {
int hov = shipset.hovered;
if(hov >= 0) {
setup.settings.shipset = shipset.items[hov].ident;
shipset.selected = hov;
}
setup.submit();
return true;
}
if(evt.caller is weaponSkin) {
int hov = weaponSkin.hovered;
if(hov >= 0) {
setup.settings.effectorSkin = weaponSkin.items[hov].ident;
weaponSkin.selected = hov;
}
setup.submit();
return true;
}
auto@ disp = cast<TraitDisplay>(evt.caller);
if(disp !is null) {
if(disp.trait.unique.length != 0)
setup.settings.chooseTrait(disp.trait);
else if(setup.settings.hasTrait(disp.trait))
setup.settings.removeTrait(disp.trait);
else
setup.settings.addTrait(disp.trait);
update();
setup.submit();
}
}
if(evt.type == GUI_Changed) {
if(evt.caller is name) {
setup.settings.raceName = name.text;
setup.submit();
return true;
}
if(evt.caller is categories) {
update();
return true;
}
}
return BaseGuiElement::onGuiEvent(evt);
}
void draw() override {
BaseGuiElement::draw();
}
};
class GalaxySetup : BaseGuiElement {
Map@ mp;
NewGame@ ng;
GuiText@ name;
GuiText@ timesLabel;
GuiSpinbox@ timesBox;
GuiPanel@ settings;
GuiButton@ removeButton;
GuiButton@ hwButton;
GuiSprite@ hwX;
GalaxySetup(NewGame@ menu, Alignment@ align, Map@ fromMap) {
super(menu.galaxyPanel, align);
@mp = fromMap.create();
@ng = menu;
@name = GuiText(this, Alignment(Left+6, Top+5, Right-262, Height=28));
name.text = mp.name;
name.font = FT_Medium;
name.color = mp.color;
name.stroke = colors::Black;
@timesBox = GuiSpinbox(this, Alignment(Right-190, Top+7, Width=52, Height=22), 1.0);
timesBox.min = 1.0;
timesBox.max = 100.0;
timesBox.decimals = 0;
timesBox.color = Color(0xffffff60);
@timesLabel = GuiText(this, Alignment(Right-135, Top+7, Width=25, Height=22), "x");
timesBox.visible = !mp.isUnique;
timesLabel.visible = !mp.isUnique;
@removeButton = GuiButton(this, Alignment(Right-84, Top+4, Right-25, Top+34));
removeButton.setIcon(icons::Remove);
removeButton.color = colors::Red;
@hwButton = GuiButton(this, Alignment(Right-230, Top+5, Width=26, Height=26));
hwButton.setIcon(Sprite(spritesheet::PlanetType, 2, Color(0xffffffaa)), padding=0);
hwButton.toggleButton = true;
hwButton.pressed = false;
hwButton.style = SS_IconButton;
hwButton.color = Color(0xff0000ff);
setMarkupTooltip(hwButton, locale::NGTT_MAP_HW);
@hwX = GuiSprite(hwButton, Alignment(), Sprite(spritesheet::QuickbarIcons, 3, Color(0xffffff80)));
hwX.visible = false;
@settings = GuiPanel(this,
Alignment(Left, Top+42, Right, Bottom-4));
mp.create(settings);
}
void setHomeworlds(bool value) {
hwButton.pressed = !value;
hwX.visible = hwButton.pressed;
if(hwButton.pressed)
hwButton.fullIcon.color = Color(0xffffffff);
else
hwButton.fullIcon.color = Color(0xffffffaa);
}
void apply(MapSettings& set) {
set.map_id = mp.id;
set.galaxyCount = timesBox.value;
@set.parent = ng.settings;
set.allowHomeworlds = !hwButton.pressed;
mp.apply(set);
}
void load(MapSettings& set) {
auto@ _map = getMap(set.map_id);
if(getClass(mp) !is getClass(_map))
@mp = cast<Map>(getClass(_map).create());
timesBox.value = set.galaxyCount;
hwButton.pressed = !set.allowHomeworlds;
hwX.visible = hwButton.pressed;
if(hwButton.pressed)
hwButton.fullIcon.color = Color(0xffffffff);
else
hwButton.fullIcon.color = Color(0xffffffaa);
mp.load(set);
}
bool onGuiEvent(const GuiEvent& evt) {
switch(evt.type) {
case GUI_Clicked:
if(evt.caller is removeButton) {
ng.removeGalaxy(this);
return true;
}
else if(evt.caller is hwButton) {
setHomeworlds(!hwButton.pressed);
return true;
}
break;
}
return BaseGuiElement::onGuiEvent(evt);
}
void draw() {
recti bgPos = AbsolutePosition.padded(-5,0,-4,0);
clipParent(bgPos);
skin.draw(SS_GalaxySetupItem, SF_Normal, bgPos.padded(-4,0), mp.color);
resetClip();
auto@ icon = mapIcons[mp.index];
if(mp.icon.length != 0 && icon.isLoaded(0)) {
recti pos = AbsolutePosition.padded(0,42,0,0).aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0);
icon.draw(pos, Color(0xffffff80));
}
BaseGuiElement::draw();
}
};
class MapElement : GuiListElement {
Map@ mp;
MapElement(Map@ _map) {
@mp = _map;
}
void draw(GuiListbox@ ele, uint flags, const recti& absPos) override {
const Font@ title = ele.skin.getFont(FT_Subtitle);
const Font@ normal = ele.skin.getFont(FT_Normal);
ele.skin.draw(SS_ListboxItem, flags, absPos, mp.color);
auto@ icon = mapIcons[mp.index];
if(mp.icon.length != 0 && icon.isLoaded(0)) {
recti pos = absPos.aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0);
icon.draw(pos, Color(0xffffff80));
}
title.draw(pos=absPos.resized(0, 32).padded(12,4),
text=mp.name, color=mp.color, stroke=colors::Black);
normal.draw(pos=absPos.padded(12,36,12+absPos.height,0), offset=vec2i(),
lineHeight=-1, text=mp.description, color=colors::White);
}
};
class Quickstart : ConsoleCommand {
void execute(const string& args) {
new_game.start();
}
};
NewGame@ new_game;
array<DynamicTexture> mapIcons;
void init() {
@new_game = NewGame();
new_game.visible = false;
addConsoleCommand("quickstart", Quickstart());
}
array<Player@> connectedPlayers;
set_int connectedSet;
void tick(double time) {
if(new_game.visible)
new_game.tick(time);
if(!game_running && mpServer) {
array<Player@>@ players = getPlayers();
//Send connect events
for(uint i = 0, cnt = players.length; i < cnt; ++i) {
Player@ pl = players[i];
if(pl.id == CURRENT_PLAYER.id)
continue;
string name = pl.name;
if(name.length == 0)
continue;
if(!connectedSet.contains(pl.id)) {
string msg = format("[color=#aaa]* "+locale::MP_CONNECT_EVENT+"[/color]",
format("[b]$1[/b]", bbescape(name)));
recvMenuJoin(ALL_PLAYERS, msg);
connectedPlayers.insertLast(pl);
connectedSet.insert(pl.id);
}
}
connectedSet.clear();
for(uint i = 0, cnt = players.length; i < cnt; ++i)
connectedSet.insert(players[i].id);
//Send disconnect events
for(uint i = 0, cnt = connectedPlayers.length; i < cnt; ++i) {
if(!connectedSet.contains(connectedPlayers[i].id)) {
Color color;
string name = connectedPlayers[i].name;
string msg = format("[color=#aaa]* "+locale::MP_DISCONNECT_EVENT+"[/color]",
format("[b]$2[/b]", toString(color), bbescape(name)));
recvMenuLeave(ALL_PLAYERS, msg);
connectedPlayers.removeAt(i);
--i; --cnt;
}
}
}
}
void showNewGame(bool fromMP = false) {
new_game.visible = true;
new_game.fromMP = fromMP;
new_game.init();
menu_container.visible = false;
menu_container.animateOut();
new_game.animateIn();
}
void hideNewGame(bool snap = false) {
new_game.fromMP = false;
menu_container.visible = true;
if(!snap) {
menu_container.animateIn();
new_game.animateOut();
}
else {
animate_remove(new_game);
new_game.visible = false;
menu_container.show();
}
}
void changeEmpireSettings_client(Player& pl, EmpireSettings@ settings) {
auto@ emp = new_game.findPlayer(pl.id);
emp.settings.raceName = settings.raceName;
emp.settings.traits = settings.traits;
emp.settings.portrait = settings.portrait;
emp.settings.shipset = settings.shipset;
emp.settings.effectorSkin = settings.effectorSkin;
emp.settings.color = settings.color;
emp.settings.flag = settings.flag;
emp.settings.ready = settings.ready;
emp.settings.team = settings.team;
emp.update();
}
bool sendPeriodic(Message& msg) {
if(game_running)
return false;
new_game.apply();
msg << new_game.settings;
return true;
}
void recvPeriodic(Message& msg) {
msg >> new_game.settings;
new_game.reset();
new_game.updateAbsolutePosition();
}
void chatMessage(Player& pl, string text) {
auto@ emp = new_game.findPlayer(pl.id);
Color color = emp.settings.color;
string msg = format("[b][color=$1]$2[/color][/b] [offset=100]$3[/offset]",
toString(color), bbescape(emp.name.text), bbescape(text));
recvMenuChat(ALL_PLAYERS, msg);
}
void chatMessage_client(string text) {
new_game.addChat(text);
sound::generic_click.play();
}
void chatJoin_client(string text) {
new_game.addChat(text);
sound::generic_ok.play();
}
void chatLeave_client(string text) {
new_game.addChat(text);
sound::generic_warn.play();
}