2044 lines
56 KiB
ActionScript
2044 lines
56 KiB
ActionScript
import elements.BaseGuiElement;
|
|
import elements.GuiSkinElement;
|
|
import elements.GuiText;
|
|
import elements.GuiSprite;
|
|
import elements.GuiButton;
|
|
import elements.GuiOverlay;
|
|
import elements.GuiAccordion;
|
|
import elements.GuiListbox;
|
|
import elements.GuiProgressbar;
|
|
import elements.GuiPanel;
|
|
import elements.GuiMarkupText;
|
|
import elements.GuiContextMenu;
|
|
import elements.GuiCargoDisplay;
|
|
import elements.MarkupTooltip;
|
|
import constructible;
|
|
import ship_groups;
|
|
import buildings;
|
|
import constructions;
|
|
import orbitals;
|
|
import targeting.PointTarget;
|
|
import targeting.ObjectTarget;
|
|
import systems;
|
|
import cargo;
|
|
from obj_selection import hoveredObject;
|
|
from gui import animate_time;
|
|
from overlays.ContextMenu import FinanceDryDock;
|
|
|
|
export ConstructionParent, ConstructionDisplay;
|
|
export BuildElement;
|
|
|
|
const double ANIM1_TIME = 0.15;
|
|
const double ANIM2_TIME = 0.001;
|
|
const uint Q_HEIGHT = 250;
|
|
const uint WIDTH = 500;
|
|
interface ConstructionParent : IGuiElement {
|
|
void close();
|
|
void startBuild(const BuildingType@ type);
|
|
Object@ get_object();
|
|
Object@ get_slaved();
|
|
void triggerUpdate();
|
|
};
|
|
|
|
class ConstructionOverlay : GuiOverlay, ConstructionParent {
|
|
ConstructionDisplay@ construction;
|
|
Object@ obj;
|
|
Object@ slave;
|
|
|
|
ConstructionOverlay(IGuiElement@ parent, Object@ obj) {
|
|
if(obj.isOrbital) {
|
|
Orbital@ orb = cast<Orbital>(obj);
|
|
if(orb.hasMaster()) {
|
|
@slave = obj;
|
|
@obj = orb.getMaster();
|
|
}
|
|
}
|
|
|
|
@this.obj = obj;
|
|
super(parent);
|
|
|
|
@construction = ConstructionDisplay(this, vec2i(parent.size.width/2, parent.size.height),
|
|
Alignment(Right-0.5f-250, Top+20, Right-0.5f+250, Bottom-20));
|
|
construction.animate(ANIM1_TIME);
|
|
}
|
|
|
|
bool onGuiEvent(const GuiEvent& evt) override {
|
|
switch(evt.type) {
|
|
case GUI_Confirmed:
|
|
triggerUpdate();
|
|
break;
|
|
}
|
|
return GuiOverlay::onGuiEvent(evt);
|
|
}
|
|
|
|
void startBuild(const BuildingType@ type) {
|
|
}
|
|
|
|
Object@ get_object() {
|
|
return obj;
|
|
}
|
|
|
|
Object@ get_slaved() {
|
|
return slave;
|
|
}
|
|
|
|
void triggerUpdate() {
|
|
}
|
|
|
|
void update(double time) {
|
|
construction.update(time);
|
|
}
|
|
};
|
|
|
|
class DisplayBox : BaseGuiElement {
|
|
ConstructionParent@ overlay;
|
|
Alignment@ targetAlign;
|
|
|
|
DisplayBox(ConstructionParent@ ov, vec2i origin, Alignment@ target) {
|
|
@overlay = ov;
|
|
@targetAlign = target;
|
|
super(overlay, recti_area(origin, vec2i(1,1)));
|
|
visible = false;
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void animate(double animTime = ANIM2_TIME) {
|
|
visible = true;
|
|
animate_time(this, targetAlign.resolve(overlay.size), animTime);
|
|
}
|
|
|
|
bool onGuiEvent(const GuiEvent& evt) override {
|
|
switch(evt.type) {
|
|
case GUI_Animation_Complete:
|
|
@alignment = targetAlign;
|
|
return true;
|
|
}
|
|
return BaseGuiElement::onGuiEvent(evt);
|
|
}
|
|
|
|
bool onMouseEvent(const MouseEvent& evt, IGuiElement@ source) override {
|
|
switch(evt.type) {
|
|
case MET_Button_Down:
|
|
if(evt.button == 0)
|
|
return true;
|
|
break;
|
|
case MET_Button_Up:
|
|
if(evt.button == 0)
|
|
return true;
|
|
break;
|
|
}
|
|
|
|
return BaseGuiElement::onMouseEvent(evt, source);
|
|
}
|
|
|
|
void remove() override {
|
|
@overlay = null;
|
|
BaseGuiElement::remove();
|
|
}
|
|
|
|
void update(double time) {
|
|
}
|
|
|
|
void draw() {
|
|
skin.draw(SS_OverlayBox, SF_Normal, AbsolutePosition, Color(0x888888ff));
|
|
BaseGuiElement::draw();
|
|
}
|
|
};
|
|
|
|
class ConstructionDisplay : DisplayBox {
|
|
Object@ obj;
|
|
Object@ slaved;
|
|
|
|
GuiSkinElement@ nameBox;
|
|
GuiText@ name;
|
|
|
|
GuiSkinElement@ laborBox;
|
|
GuiText@ labor;
|
|
GuiSprite@ laborIcon;
|
|
|
|
Constructible[] cons;
|
|
GuiSkinElement@ queueBox;
|
|
GuiPanel@ queue;
|
|
|
|
QueueItem@[] queueItems;
|
|
QueueItem@ draggingItem;
|
|
QueueItem@ hoveredItem;
|
|
|
|
GuiPanel@ buildPanel;
|
|
BaseGuiElement@ buttonBox;
|
|
GuiButton@ buildingsButton;
|
|
GuiButton@ orbitalsButton;
|
|
GuiButton@ shipsButton;
|
|
GuiButton@ modulesButton;
|
|
GuiButton@ constructionsButton;
|
|
GuiAccordion@ buildingsList;
|
|
GuiAccordion@ orbitalsList;
|
|
GuiAccordion@ shipsList;
|
|
GuiAccordion@ modulesList;
|
|
GuiAccordion@ constructionsList;
|
|
|
|
GuiProgressbar@ storageBox;
|
|
GuiButton@ repeatButton;
|
|
GuiButton@ drydockButton;
|
|
|
|
GuiCargoDisplay@ cargo;
|
|
|
|
bool hasBuildings = false;
|
|
bool hasOrbitals = false;
|
|
bool hasShips = false;
|
|
bool hasConstructions = false;
|
|
bool isBuildingFlagship = false;
|
|
|
|
ConstructionDisplay(ConstructionParent@ ov, vec2i origin, Alignment@ target) {
|
|
super(ov, origin, target);
|
|
@obj = ov.object;
|
|
@slaved = ov.slaved;
|
|
|
|
//Header
|
|
@nameBox = GuiSkinElement(this, Alignment(Left+8, Top+8, Left+0.5f-4, Top+42), SS_PlainOverlay);
|
|
@name = GuiText(nameBox, Alignment().padded(8, 0), locale::CONSTRUCTION);
|
|
name.font = FT_Medium;
|
|
|
|
@laborBox = GuiSkinElement(this, Alignment(Left+0.5f+4, Top+8, Right-8, Top+42), SS_PlainOverlay);
|
|
@laborIcon = GuiSprite(laborBox, Alignment(Left+8, Top+5, Width=24, Height=24));
|
|
laborIcon.desc = Sprite(spritesheet::ResourceIcon, 6);
|
|
@labor = GuiText(laborBox, Alignment(Left+40, Top, Right-8, Bottom));
|
|
labor.font = FT_Medium;
|
|
|
|
//Queue section
|
|
@queueBox = GuiSkinElement(this, Alignment(Left+8, Top+50, Right-8, Top+Q_HEIGHT), SS_QueueBackground);
|
|
GuiSkinElement bottomBar(queueBox, Alignment(Left+4, Bottom-34, Right-4, Bottom), SS_PlainBox);
|
|
@queue = GuiPanel(queueBox, Alignment(Left, Top, Right, Bottom-34));
|
|
@storageBox = GuiProgressbar(queueBox, recti_area(4, Q_HEIGHT-34-50, WIDTH-16-12-80, 34));
|
|
storageBox.visible = false;
|
|
storageBox.frontColor = Color(0x836000ff);
|
|
storageBox.backColor = Color(0xffffff80);
|
|
@cargo = GuiCargoDisplay(queueBox, recti_area((WIDTH-16)/2, Q_HEIGHT-34-50, (WIDTH-16)/2-12-80, 34));
|
|
cargo.visible = false;
|
|
|
|
@repeatButton = GuiButton(queueBox, Alignment(Right-8-32, Bottom-32, Width=30, Height=30));
|
|
repeatButton.style = SS_IconToggle;
|
|
repeatButton.color = Color(0x00ff00ff);
|
|
repeatButton.toggleButton = true;
|
|
repeatButton.setIcon(icons::Repeat);
|
|
setMarkupTooltip(repeatButton, locale::TT_REPEAT_QUEUE, width=300);
|
|
|
|
@drydockButton = GuiButton(queueBox, Alignment(Right-8-32-34, Bottom-32, Width=30, Height=30));
|
|
drydockButton.style = SS_IconButton;
|
|
drydockButton.color = Color(0x0080ffff);
|
|
drydockButton.setIcon(Sprite(spritesheet::GuiOrbitalIcons, 3, playerEmpire.color));
|
|
setMarkupTooltip(drydockButton, locale::TT_DRY_DOCK, width=300);
|
|
|
|
//Build panel buttons
|
|
@buttonBox = BaseGuiElement(this, Alignment(Left+8, Top+Q_HEIGHT+8, Right-8, Top+Q_HEIGHT+44));
|
|
@buildingsButton = GuiButton(buttonBox, Alignment(Left, Top, Left, Bottom));
|
|
buildingsButton.text = locale::BUILDINGS;
|
|
buildingsButton.toggleButton = true;
|
|
buildingsButton.disabled = true;
|
|
buildingsButton.style = SS_TabButton;
|
|
buildingsButton.buttonIcon = icons::Building;
|
|
|
|
@orbitalsButton = GuiButton(buttonBox, Alignment(Left, Top, Left, Bottom));
|
|
orbitalsButton.text = locale::ORBITALS;
|
|
orbitalsButton.toggleButton = true;
|
|
orbitalsButton.disabled = true;
|
|
orbitalsButton.style = SS_TabButton;
|
|
orbitalsButton.buttonIcon = icons::Orbital;
|
|
|
|
@shipsButton = GuiButton(buttonBox, Alignment(Left, Top, Left, Bottom));
|
|
shipsButton.text = locale::SHIPS;
|
|
shipsButton.toggleButton = true;
|
|
shipsButton.pressed = true;
|
|
shipsButton.disabled = true;
|
|
shipsButton.style = SS_TabButton;
|
|
shipsButton.buttonIcon = icons::Ship;
|
|
|
|
@modulesButton = GuiButton(buttonBox, Alignment(Left, Top, Left, Bottom));
|
|
modulesButton.text = locale::ORBITAL_MODULES;
|
|
modulesButton.toggleButton = true;
|
|
modulesButton.disabled = true;
|
|
modulesButton.visible = false;
|
|
modulesButton.style = SS_TabButton;
|
|
|
|
@constructionsButton = GuiButton(buttonBox, Alignment(Left, Top, Left, Bottom));
|
|
constructionsButton.text = locale::PROJECTS;
|
|
constructionsButton.toggleButton = true;
|
|
constructionsButton.disabled = true;
|
|
constructionsButton.style = SS_TabButton;
|
|
constructionsButton.buttonIcon = icons::Project;
|
|
|
|
//Build panel section
|
|
@buildPanel = GuiPanel(this, Alignment(Left+8, Top+Q_HEIGHT+44, Right-8, Bottom-8));
|
|
buildPanel.horizType = ST_Never;
|
|
|
|
@buildingsList = GuiAccordion(buildPanel, recti(0, 0, WIDTH-16, 50));
|
|
buildingsList.multiple = true;
|
|
buildingsList.clickableHeaders = false;
|
|
buildingsList.visible = false;
|
|
|
|
@orbitalsList = GuiAccordion(buildPanel, recti(0, 0, WIDTH-16, 50));
|
|
orbitalsList.multiple = true;
|
|
orbitalsList.clickableHeaders = false;
|
|
orbitalsList.visible = false;
|
|
|
|
@shipsList = GuiAccordion(buildPanel, recti(-1, 0, WIDTH-16, 50));
|
|
shipsList.multiple = true;
|
|
shipsList.clickableHeaders = false;
|
|
|
|
@modulesList = GuiAccordion(buildPanel, recti(-1, 0, WIDTH-16, 50));
|
|
modulesList.multiple = true;
|
|
modulesList.clickableHeaders = false;
|
|
modulesList.visible = false;
|
|
|
|
@constructionsList = GuiAccordion(buildPanel, recti(-1, 0, WIDTH-16, 50));
|
|
constructionsList.multiple = true;
|
|
constructionsList.clickableHeaders = false;
|
|
constructionsList.visible = false;
|
|
|
|
if(obj.isOrbital && !obj.hasConstruction) {
|
|
shipsList.visible = false;
|
|
shipsButton.pressed = false;
|
|
modulesList.visible = true;
|
|
modulesButton.pressed = true;
|
|
}
|
|
|
|
updateBuildList(true);
|
|
}
|
|
|
|
void draw() {
|
|
DisplayBox::draw();
|
|
if(draggingItem !is null) {
|
|
clearClip();
|
|
if(hoveredItem !is null) {
|
|
int y = hoveredItem.AbsolutePosition.topLeft.y;
|
|
bool moved = hoveredItem !is draggingItem;
|
|
if(mousePos.y > hoveredItem.AbsolutePosition.center.y) {
|
|
int ind = queueItems.find(hoveredItem) + 1;
|
|
if(ind >= 0 && uint(ind) < queueItems.length) {
|
|
y = queueItems[ind].AbsolutePosition.topLeft.y;
|
|
moved = true;
|
|
}
|
|
else if(queueItems[queueItems.length-1] !is draggingItem){
|
|
y = queueItems[queueItems.length-1].AbsolutePosition.botRight.y+4;
|
|
moved = true;
|
|
}
|
|
}
|
|
if(moved) {
|
|
drawLine(vec2i(queueBox.AbsolutePosition.topLeft.x, y),
|
|
vec2i(queueBox.AbsolutePosition.botRight.x, y),
|
|
Color(0xff8080ff), 3);
|
|
}
|
|
}
|
|
else if(queueItems.length != 0 && mousePos.y < queueItems[0].absolutePosition.botRight.y - 10) {
|
|
if(queueItems[0] !is draggingItem) {
|
|
int y = queueBox.AbsolutePosition.topLeft.y;
|
|
drawLine(vec2i(queueBox.AbsolutePosition.topLeft.x, y),
|
|
vec2i(queueBox.AbsolutePosition.botRight.x, y),
|
|
Color(0xff8080ff), 3);
|
|
}
|
|
}
|
|
else {
|
|
if(queueItems.length != 0 && queueItems[queueItems.length-1] !is draggingItem) {
|
|
int y = queueBox.AbsolutePosition.topLeft.y;
|
|
if(queueItems.length != 0)
|
|
y = queueItems[queueItems.length-1].AbsolutePosition.botRight.y+4;
|
|
drawLine(vec2i(queueBox.AbsolutePosition.topLeft.x, y),
|
|
vec2i(queueBox.AbsolutePosition.botRight.x, y),
|
|
Color(0xff8080ff), 3);
|
|
}
|
|
}
|
|
if(!mouseLeft)
|
|
@draggingItem = null;
|
|
else
|
|
draggingItem.draw(recti_area(mousePos - draggingItem.dragOffset, vec2i(500, 29)));
|
|
}
|
|
}
|
|
|
|
void updateAbsolutePosition() {
|
|
DisplayBox::updateAbsolutePosition();
|
|
if(shipsList !is null) {
|
|
if(buildPanel.vert.visible) {
|
|
shipsList.size = vec2i(buildPanel.size.width - 20, shipsList.size.height);
|
|
orbitalsList.size = vec2i(buildPanel.size.width - 20, orbitalsList.size.height);
|
|
buildingsList.size = vec2i(buildPanel.size.width - 20, buildingsList.size.height);
|
|
constructionsList.size = vec2i(buildPanel.size.width - 20, constructionsList.size.height);
|
|
modulesList.size = vec2i(buildPanel.size.width - 20, modulesList.size.height);
|
|
}
|
|
else {
|
|
shipsList.size = vec2i(buildPanel.size.width, shipsList.size.height);
|
|
orbitalsList.size = vec2i(buildPanel.size.width, orbitalsList.size.height);
|
|
buildingsList.size = vec2i(buildPanel.size.width, buildingsList.size.height);
|
|
constructionsList.size = vec2i(buildPanel.size.width, constructionsList.size.height);
|
|
modulesList.size = vec2i(buildPanel.size.width, modulesList.size.height);
|
|
}
|
|
}
|
|
}
|
|
|
|
void deselect() {
|
|
for(uint i = 0, cnt = buildingsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(buildingsList.items[i]);
|
|
list.clearSelection();
|
|
}
|
|
for(uint i = 0, cnt = orbitalsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(orbitalsList.items[i]);
|
|
list.clearSelection();
|
|
}
|
|
for(uint i = 0, cnt = shipsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(shipsList.items[i]);
|
|
list.clearSelection();
|
|
}
|
|
for(uint i = 0, cnt = modulesList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(modulesList.items[i]);
|
|
list.clearSelection();
|
|
}
|
|
for(uint i = 0, cnt = constructionsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(constructionsList.items[i]);
|
|
list.clearSelection();
|
|
}
|
|
}
|
|
|
|
void updateBuildList(bool full = false) {
|
|
if(full) {
|
|
buildingsList.clearSections();
|
|
constructionsList.clearSections();
|
|
orbitalsList.clearSections();
|
|
shipsList.clearSections();
|
|
modulesList.clearSections();
|
|
if(obj is null || obj.owner !is playerEmpire)
|
|
return;
|
|
if(obj.hasConstruction && obj.hasSurfaceComponent && obj.owner.ImperialBldConstructionRate > 0.001) {
|
|
array<GuiListbox@> cats;
|
|
array<string> catNames;
|
|
|
|
for(uint i = 0, cnt = getBuildingTypeCount(); i < cnt; ++i) {
|
|
const BuildingType@ type = getBuildingType(i);
|
|
if(type.civilian)
|
|
continue;
|
|
if(!type.canBuildOn(obj, ignoreState = true))
|
|
continue;
|
|
|
|
GuiListbox@ list;
|
|
for(uint n = 0, ncnt = cats.length; n < ncnt; ++n) {
|
|
if(catNames[n] == type.category) {
|
|
@list = cats[n];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(list is null) {
|
|
@list = GuiListbox(this, recti(0, 0, 100, 20));
|
|
list.style = SS_NULL;
|
|
list.itemHeight = 30;
|
|
|
|
MarkupTooltip tt(400, 0.f, true, true);
|
|
tt.Lazy = true;
|
|
tt.LazyUpdate = false;
|
|
tt.Padding = 4;
|
|
@list.tooltipObject = tt;
|
|
|
|
catNames.insertLast(type.category);
|
|
cats.insertLast(list);
|
|
}
|
|
|
|
list.addItem(BuildElement(this, type, obj));
|
|
}
|
|
|
|
for(uint i = 0, cnt = cats.length; i < cnt; ++i) {
|
|
auto@ list = cats[i];
|
|
list.sortDesc();
|
|
|
|
string title = localize("#BCAT_"+catNames[i]);
|
|
if(title[0] == '#')
|
|
title = catNames[i];
|
|
|
|
uint sec = buildingsList.addSection(title, list);
|
|
buildingsList.openSection(sec);
|
|
list.updateHover();
|
|
}
|
|
|
|
hasBuildings = true;
|
|
buildingsButton.disabled = false;
|
|
}
|
|
else {
|
|
hasBuildings = false;
|
|
buildingsButton.disabled = true;
|
|
}
|
|
if(obj.hasConstruction && obj.canBuildOrbitals) {
|
|
GuiListbox@ list = GuiListbox(this, recti(0, 0, 100, 20));
|
|
list.style = SS_NULL;
|
|
list.itemHeight = 30;
|
|
|
|
MarkupTooltip tt(400, 0.f, true, true);
|
|
tt.Lazy = true;
|
|
tt.LazyUpdate = false;
|
|
tt.Padding = 4;
|
|
@list.tooltipObject = tt;
|
|
|
|
auto@ drydock = getOrbitalModule("DryDock");
|
|
for(uint i = 0, cnt = getOrbitalModuleCount(); i < cnt; ++i) {
|
|
auto@ def = getOrbitalModule(i);
|
|
if(def is drydock)
|
|
continue;
|
|
if(def.canBuildBy(obj))
|
|
list.addItem(BuildElement(this, def, obj));
|
|
}
|
|
list.sortDesc();
|
|
uint sec = orbitalsList.addSection(locale::ORBITALS, list);
|
|
orbitalsList.openSection(sec);
|
|
addShipsToBuildList(orbitalsList, stations=true);
|
|
hasOrbitals = true;
|
|
orbitalsButton.disabled = false;
|
|
list.updateHover();
|
|
}
|
|
else {
|
|
hasOrbitals = false;
|
|
orbitalsButton.disabled = true;
|
|
}
|
|
if(obj.isOrbital && !cast<Orbital>(obj).isStandalone) {
|
|
GuiListbox@ list = GuiListbox(this, recti(0, 0, 100, 20));
|
|
list.style = SS_NULL;
|
|
list.itemHeight = 30;
|
|
|
|
MarkupTooltip tt(400, 0.f, true, true);
|
|
tt.Lazy = true;
|
|
tt.LazyUpdate = false;
|
|
tt.Padding = 4;
|
|
@list.tooltipObject = tt;
|
|
|
|
for(uint i = 0, cnt = getOrbitalModuleCount(); i < cnt; ++i) {
|
|
auto@ def = getOrbitalModule(i);
|
|
if(def.canBuildOn(cast<Orbital>(obj)))
|
|
list.addItem(BuildElement(this, def, obj));
|
|
}
|
|
list.sortDesc();
|
|
uint sec = modulesList.addSection(locale::ORBITAL_MODULES, list);
|
|
modulesList.openSection(sec);
|
|
modulesButton.disabled = false;
|
|
list.updateHover();
|
|
}
|
|
else {
|
|
modulesButton.disabled = true;
|
|
}
|
|
if(obj.hasConstruction && obj.canBuildShips) {
|
|
addShipsToBuildList(shipsList, stations=false);
|
|
hasShips = true;
|
|
shipsButton.disabled = false;
|
|
}
|
|
else {
|
|
hasShips = false;
|
|
shipsButton.disabled = true;
|
|
}
|
|
if(obj.hasConstruction) {
|
|
array<GuiListbox@> cats;
|
|
array<string> catNames;
|
|
|
|
for(uint i = 0, cnt = getConstructionTypeCount(); i < cnt; ++i) {
|
|
const ConstructionType@ type = getConstructionType(i);
|
|
if(!type.canBuild(obj, ignoreCost=true))
|
|
continue;
|
|
|
|
GuiListbox@ list;
|
|
for(uint n = 0, ncnt = cats.length; n < ncnt; ++n) {
|
|
if(catNames[n] == type.category) {
|
|
@list = cats[n];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(list is null) {
|
|
@list = GuiListbox(this, recti(0, 0, 100, 20));
|
|
list.style = SS_NULL;
|
|
list.itemHeight = 30;
|
|
|
|
MarkupTooltip tt(400, 0.f, true, true);
|
|
tt.Lazy = true;
|
|
tt.LazyUpdate = false;
|
|
tt.Padding = 4;
|
|
@list.tooltipObject = tt;
|
|
|
|
catNames.insertLast(type.category);
|
|
cats.insertLast(list);
|
|
}
|
|
|
|
list.addItem(BuildElement(this, type, obj));
|
|
}
|
|
|
|
for(uint i = 0, cnt = cats.length; i < cnt; ++i) {
|
|
auto@ list = cats[i];
|
|
list.sortDesc();
|
|
|
|
string title = localize("#BCAT_"+catNames[i]);
|
|
if(title[0] == '#')
|
|
title = catNames[i];
|
|
|
|
uint sec = constructionsList.addSection(title, list);
|
|
constructionsList.openSection(sec);
|
|
list.updateHover();
|
|
}
|
|
|
|
if(cats.length != 0) {
|
|
hasConstructions = true;
|
|
constructionsButton.disabled = false;
|
|
}
|
|
else {
|
|
hasConstructions = false;
|
|
constructionsButton.disabled = true;
|
|
}
|
|
}
|
|
else {
|
|
hasConstructions = false;
|
|
constructionsButton.disabled = true;
|
|
}
|
|
if(obj.laborIncome == 0 && hasConstructions && shipsButton.pressed) {
|
|
shipsList.visible = false;
|
|
shipsButton.pressed = false;
|
|
constructionsButton.pressed = true;
|
|
constructionsList.visible = true;
|
|
}
|
|
else if(shipsButton.disabled && shipsButton.pressed) {
|
|
shipsList.visible = false;
|
|
shipsButton.pressed = false;
|
|
if(!orbitalsButton.disabled) {
|
|
orbitalsButton.pressed = true;
|
|
orbitalsList.visible = true;
|
|
}
|
|
else if(!buildingsButton.disabled) {
|
|
buildingsButton.pressed = true;
|
|
buildingsList.visible = true;
|
|
}
|
|
else if(!constructionsButton.disabled) {
|
|
constructionsButton.pressed = true;
|
|
constructionsList.visible = true;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for(uint i = 0, cnt = buildingsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(buildingsList.items[i]);
|
|
for(uint j = 0, jcnt = list.itemCount; j < jcnt; ++j) {
|
|
GuiListElement@ ele = list.getItemElement(j);
|
|
|
|
if(cast<BuildElement>(ele) !is null)
|
|
cast<BuildElement>(ele).update(this);
|
|
}
|
|
}
|
|
for(uint i = 0, cnt = orbitalsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(orbitalsList.items[i]);
|
|
for(uint j = 0, jcnt = list.itemCount; j < jcnt; ++j) {
|
|
GuiListElement@ ele = list.getItemElement(j);
|
|
|
|
if(cast<BuildElement>(ele) !is null)
|
|
cast<BuildElement>(ele).update(this);
|
|
}
|
|
}
|
|
for(uint i = 0, cnt = shipsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(shipsList.items[i]);
|
|
for(uint j = 0, jcnt = list.itemCount; j < jcnt; ++j) {
|
|
GuiListElement@ ele = list.getItemElement(j);
|
|
|
|
if(cast<BuildElement>(ele) !is null)
|
|
cast<BuildElement>(ele).update(this);
|
|
}
|
|
}
|
|
for(uint i = 0, cnt = constructionsList.items.length; i < cnt; ++i) {
|
|
GuiListbox@ list = cast<GuiListbox>(constructionsList.items[i]);
|
|
for(uint j = 0, jcnt = list.itemCount; j < jcnt; ++j) {
|
|
GuiListElement@ ele = list.getItemElement(j);
|
|
|
|
if(cast<BuildElement>(ele) !is null)
|
|
cast<BuildElement>(ele).update(this);
|
|
}
|
|
}
|
|
}
|
|
updateAbsolutePosition();
|
|
gui_root.updateHover();
|
|
}
|
|
|
|
void addShipsToBuildList(GuiAccordion@ acc, bool stations = false) {
|
|
//Add ship classes
|
|
ReadLock lock(playerEmpire.designMutex);
|
|
uint clsCount = playerEmpire.designClassCount;
|
|
for(uint i = 0; i < clsCount; ++i) {
|
|
const DesignClass@ cls = playerEmpire.getDesignClass(i);
|
|
|
|
//Check if we should display this class at all
|
|
bool hasShips = false;
|
|
for(uint j = 0, jcnt = cls.designCount; j < jcnt; ++j) {
|
|
const Design@ dsg = cls.designs[j];
|
|
if(dsg.obsolete)
|
|
continue;
|
|
if(dsg.hasTag(ST_Station) != stations)
|
|
continue;
|
|
if(dsg.hasTag(ST_Satellite))
|
|
continue;
|
|
hasShips = true;
|
|
break;
|
|
}
|
|
|
|
if(!hasShips)
|
|
continue;
|
|
|
|
//Create the box
|
|
GuiListbox@ list = GuiListbox(this, recti(0, 0, 100, 20));
|
|
list.style = SS_NULL;
|
|
list.itemHeight = 30;
|
|
|
|
MarkupTooltip tt(400, 0.f, true, true);
|
|
tt.Lazy = true;
|
|
tt.LazyUpdate = false;
|
|
tt.Padding = 4;
|
|
@list.tooltipObject = tt;
|
|
|
|
for(uint j = 0, jcnt = cls.designCount; j < jcnt; ++j) {
|
|
const Design@ dsg = cls.designs[j];
|
|
if(dsg.obsolete)
|
|
continue;
|
|
if(dsg.hasTag(ST_Station) != stations)
|
|
continue;
|
|
if(dsg.hasTag(ST_Satellite))
|
|
continue;
|
|
list.addItem(BuildElement(this, dsg, obj));
|
|
}
|
|
list.sortDesc();
|
|
|
|
uint sec = acc.addSection(cls.name, list);
|
|
acc.openSection(sec);
|
|
list.updateHover();
|
|
}
|
|
|
|
}
|
|
|
|
bool onMouseEvent(const MouseEvent& evt, IGuiElement@ source) override {
|
|
switch(evt.type) {
|
|
case MET_Button_Down:
|
|
if(source is queue && evt.button == 1)
|
|
return true;
|
|
break;
|
|
case MET_Button_Up:
|
|
if(evt.button == 0) {
|
|
if(draggingItem !is null) {
|
|
if(hoveredItem !is null) {
|
|
if(mousePos.y > hoveredItem.AbsolutePosition.center.y) {
|
|
int ind = queueItems.find(hoveredItem) + 1;
|
|
if(ind >= 0 && uint(ind) < queueItems.length) {
|
|
obj.moveConstruction(draggingItem.cons.id, queueItems[ind].cons.id);
|
|
}
|
|
else {
|
|
obj.moveConstruction(draggingItem.cons.id, -1);
|
|
}
|
|
}
|
|
else if(hoveredItem !is draggingItem) {
|
|
obj.moveConstruction(draggingItem.cons.id, hoveredItem.cons.id);
|
|
}
|
|
}
|
|
else if(queueItems.length != 0 && mousePos.y < queueItems[0].absolutePosition.botRight.y - 10) {
|
|
if(queueItems[0] !is draggingItem)
|
|
obj.moveConstruction(draggingItem.cons.id, queueItems[0].cons.id);
|
|
}
|
|
else {
|
|
if(queueItems.length != 0 && queueItems[queueItems.length-1] !is draggingItem)
|
|
obj.moveConstruction(draggingItem.cons.id, -1);
|
|
}
|
|
@draggingItem = null;
|
|
updateQueue();
|
|
updateTimer = 0.15;
|
|
return true;
|
|
}
|
|
if(source is queue)
|
|
selectQueue(null);
|
|
return true;
|
|
}
|
|
else if(evt.button == 1) {
|
|
if(source !is queue)
|
|
overlay.close();
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
return DisplayBox::onMouseEvent(evt, source);
|
|
}
|
|
|
|
|
|
QueueItem@ selQueue;
|
|
void selectQueue(QueueItem@ it) {
|
|
@selQueue = it;
|
|
}
|
|
|
|
void updateQueue() {
|
|
if(obj.owner !is playerEmpire
|
|
|| !obj.hasConstruction) {
|
|
cons.length = 0;
|
|
return;
|
|
}
|
|
if(draggingItem !is null)
|
|
return;
|
|
|
|
cons.syncFrom(obj.getConstructionQueue());
|
|
uint oldCnt = queueItems.length;
|
|
uint newCnt = cons.length;
|
|
|
|
for(uint i = newCnt; i < oldCnt; ++i)
|
|
queueItems[i].remove();
|
|
queueItems.length = newCnt;
|
|
|
|
int selId = -1;
|
|
if(selQueue !is null)
|
|
selId = selQueue.cons.id;
|
|
@selQueue = null;
|
|
|
|
int y = 0;
|
|
isBuildingFlagship = false;
|
|
for(uint i = 0; i < newCnt; ++i) {
|
|
QueueItem@ ele = queueItems[i];
|
|
if(ele is null) {
|
|
@ele = QueueItem(queue, this);
|
|
@queueItems[i] = ele;
|
|
}
|
|
|
|
if(cons[i].dsg !is null)
|
|
isBuildingFlagship = true;
|
|
|
|
ele.first = i == 0;
|
|
ele.update(cons[i], obj);
|
|
ele.position = vec2i(0, y);
|
|
y += ele.size.height;
|
|
|
|
if(ele.cons.id == selId)
|
|
@selQueue = ele;
|
|
}
|
|
}
|
|
|
|
double updateTimer = 0.0;
|
|
double longTimer = 5.0;
|
|
void update(double time) override {
|
|
updateTimer -= time;
|
|
longTimer -= time;
|
|
if(updateTimer <= 0) {
|
|
updateTimer = randomd(0.1,0.9);
|
|
|
|
updateQueue();
|
|
|
|
if(longTimer <= 0) {
|
|
updateBuildList(true);
|
|
longTimer = 5.0;
|
|
}
|
|
else {
|
|
if(obj.owner is playerEmpire) {
|
|
if(hasShips != obj.hasConstruction && obj.canBuildShips
|
|
|| hasOrbitals != obj.hasConstruction && obj.canBuildOrbitals
|
|
|| hasBuildings != obj.hasConstruction && obj.hasSurfaceComponent && obj.owner.ImperialBldConstructionRate > 0.01)
|
|
updateBuildList(true);
|
|
else
|
|
updateBuildList(false);
|
|
}
|
|
}
|
|
|
|
int btnCount = 0;
|
|
if(obj.hasConstruction) {
|
|
nameBox.visible = true;
|
|
laborBox.visible = true;
|
|
queueBox.visible = true;
|
|
|
|
buttonBox.alignment.top.pixels = Q_HEIGHT+8;
|
|
buttonBox.alignment.bottom.pixels = Q_HEIGHT+44;
|
|
|
|
if(obj.hasSurfaceComponent) {
|
|
buildingsButton.visible = true;
|
|
btnCount += 1;
|
|
}
|
|
else {
|
|
buildingsButton.visible = false;
|
|
}
|
|
|
|
shipsButton.visible = true;
|
|
orbitalsButton.visible = true;
|
|
btnCount += 2;
|
|
}
|
|
else {
|
|
nameBox.visible = false;
|
|
laborBox.visible = false;
|
|
queueBox.visible = false;
|
|
|
|
buttonBox.alignment.top.pixels = 8;
|
|
buttonBox.alignment.bottom.pixels = 44;
|
|
buildPanel.alignment.top.pixels = 44;
|
|
|
|
buildingsButton.visible = false;
|
|
shipsButton.visible = false;
|
|
orbitalsButton.visible = false;
|
|
}
|
|
/*if(obj.isOrbital) {*/
|
|
/* modulesButton.visible = true;*/
|
|
/* btnCount += 1;*/
|
|
/*}*/
|
|
/*else {*/
|
|
modulesButton.visible = false;
|
|
/*}*/
|
|
if(hasConstructions) {
|
|
constructionsButton.visible = true;
|
|
btnCount += 1;
|
|
}
|
|
else {
|
|
constructionsButton.visible = false;
|
|
}
|
|
|
|
if(btnCount <= 1) {
|
|
buttonBox.visible = false;
|
|
buildPanel.alignment.top.pixels = buttonBox.alignment.top.pixels;
|
|
}
|
|
else {
|
|
buttonBox.visible = true;
|
|
buildPanel.alignment.top.pixels = buttonBox.alignment.bottom.pixels;
|
|
|
|
float pos = 0.f, step = 1.f / float(btnCount);
|
|
if(modulesButton.visible) {
|
|
modulesButton.alignment.left.percent = pos;
|
|
pos += step;
|
|
modulesButton.alignment.right.percent = pos;
|
|
}
|
|
if(buildingsButton.visible) {
|
|
buildingsButton.alignment.left.percent = pos;
|
|
pos += step;
|
|
buildingsButton.alignment.right.percent = pos;
|
|
}
|
|
if(orbitalsButton.visible) {
|
|
orbitalsButton.alignment.left.percent = pos;
|
|
pos += step;
|
|
orbitalsButton.alignment.right.percent = pos;
|
|
}
|
|
if(shipsButton.visible) {
|
|
shipsButton.alignment.left.percent = pos;
|
|
pos += step;
|
|
shipsButton.alignment.right.percent = pos;
|
|
}
|
|
if(constructionsButton.visible) {
|
|
constructionsButton.alignment.left.percent = pos;
|
|
pos += step;
|
|
constructionsButton.alignment.right.percent = pos;
|
|
}
|
|
}
|
|
|
|
double curLabor = 0;
|
|
if(obj.owner is playerEmpire)
|
|
curLabor = obj.laborIncome;
|
|
labor.text = formatMinuteRate(curLabor, " "+locale::RESOURCE_LABOR);
|
|
|
|
double curStored = 0;
|
|
if(obj.owner is playerEmpire)
|
|
curStored = obj.currentLaborStored;
|
|
double storCap = 0;
|
|
if(obj.owner is playerEmpire)
|
|
storCap = obj.laborStorageCapacity;
|
|
if(obj.hasConstruction && (curStored > 0.001 || storCap > 0.001)) {
|
|
storageBox.visible = true;
|
|
|
|
float pct = 1.f;
|
|
if(storCap > 0)
|
|
pct = min(1.f, curStored / storCap);
|
|
storageBox.progress = pct;
|
|
storageBox.text = format(locale::STORED_LABOR, toString(curStored,0), toString(storCap,0));
|
|
cargo.rect = recti_area((WIDTH-16)/2, Q_HEIGHT-34-50, (WIDTH-16)/2-12-80, 34);
|
|
}
|
|
else {
|
|
storageBox.visible = false;
|
|
cargo.rect = recti_area(4, Q_HEIGHT-34-50, (WIDTH-8)-12-80, 34);
|
|
}
|
|
|
|
//Update cargo
|
|
cargo.visible = obj.hasCargo && obj.cargoTypes > 0;
|
|
if(cargo.visible) {
|
|
cargo.update(obj);
|
|
storageBox.size = vec2i((WIDTH-16)/2-8, 34);
|
|
}
|
|
else {
|
|
storageBox.size = vec2i(WIDTH-16-12-80, 34);
|
|
}
|
|
|
|
repeatButton.pressed = obj.owner is playerEmpire && obj.isRepeating;
|
|
|
|
auto@ drydock = getOrbitalModule("DryDock");
|
|
drydockButton.visible = obj.owner is playerEmpire && obj.canBuildShips && obj.canBuildOrbitals && drydock !is null && drydock.canBuildBy(obj);
|
|
|
|
updateAbsolutePosition();
|
|
}
|
|
}
|
|
|
|
void openDrydockMenu(Empire@ forEmp = playerEmpire) {
|
|
GuiContextMenu menu(mousePos);
|
|
menu.itemHeight = 36;
|
|
menu.flexWidth = false;
|
|
menu.width = 400;
|
|
|
|
uint cnt = playerEmpire.designCount;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ dsg = playerEmpire.designs[i];
|
|
if(dsg.obsolete || dsg.newer !is null || dsg.updated !is null)
|
|
continue;
|
|
if(dsg.hasTag(ST_Support))
|
|
continue;
|
|
if(dsg.hasTag(ST_Station))
|
|
continue;
|
|
if(dsg.hasTag(ST_Satellite))
|
|
continue;
|
|
menu.addOption(FlagshipDrydock(obj, dsg, forEmp));
|
|
}
|
|
|
|
menu.list.sortDesc();
|
|
menu.updateAbsolutePosition();
|
|
}
|
|
|
|
bool onGuiEvent(const GuiEvent& evt) override {
|
|
switch(evt.type) {
|
|
case GUI_Changed: {
|
|
GuiListbox@ list = cast<GuiListbox>(evt.caller);
|
|
if(list !is null && list.isChildOf(buildPanel)) {
|
|
if(obj.owner !is playerEmpire)
|
|
return true;
|
|
|
|
BuildElement@ ele = cast<BuildElement>(list.selectedItem);
|
|
if(ele !is null) {
|
|
if(ele.dsg !is null) {
|
|
if(ele.dsg.hasTag(ST_IsSupport)) {
|
|
if(!obj.canBuildShips)
|
|
return true;
|
|
int selCons = -1;
|
|
if(selQueue !is null)
|
|
selCons = selQueue.cons.id;
|
|
obj.addSupportShipConstruction(selCons, ele.dsg, shiftKey ? 1 : 10);
|
|
}
|
|
else if(ele.dsg.hasTag(ST_Station)) {
|
|
if(!obj.canBuildOrbitals)
|
|
return true;
|
|
list.clearSelection();
|
|
overlay.close();
|
|
targetPoint(OrbitalTarget(obj, null, ele.dsg, constructFrom=slaved));
|
|
}
|
|
else {
|
|
if(!obj.canBuildShips)
|
|
return true;
|
|
auto@ drydock = getOrbitalModule("DryDock");
|
|
bool canDrydock = obj.canBuildOrbitals && drydock !is null && drydock.canBuildBy(obj);
|
|
if(ctrlKey && canDrydock) {
|
|
FinanceDryDock(ele.dsg, obj);
|
|
}
|
|
else {
|
|
int cost = getBuildCost(ele.dsg, buildAt=obj);
|
|
if(!playerEmpire.canPay(cost) && canDrydock)
|
|
FinanceDryDock(ele.dsg, obj);
|
|
else
|
|
obj.buildFlagship(ele.dsg, constructFrom=slaved);
|
|
}
|
|
}
|
|
list.clearSelection();
|
|
}
|
|
else if(ele.orb !is null) {
|
|
if(ele.orb.isCore) {
|
|
if(!obj.canBuildOrbitals)
|
|
return true;
|
|
list.clearSelection();
|
|
overlay.close();
|
|
targetPoint(OrbitalTarget(obj, ele.orb, constructFrom=slaved));
|
|
}
|
|
else {
|
|
Orbital@ orb = cast<Orbital>(obj);
|
|
if(orb is null)
|
|
return true;
|
|
orb.buildModule(ele.orb.id);
|
|
updateTimer = 0.0;
|
|
longTimer = 0.0;
|
|
overlay.triggerUpdate();
|
|
}
|
|
}
|
|
else if(ele.building !is null) {
|
|
overlay.startBuild(ele.building);
|
|
list.clearSelection();
|
|
}
|
|
else if(ele.construction !is null) {
|
|
if(ele.construction.targets.length == 0) {
|
|
obj.buildConstruction(ele.construction.id);
|
|
}
|
|
else if(ele.construction.targets[0].type == TT_Object) {
|
|
targetObject(ConstructionTargetObject(obj, ele.construction));
|
|
overlay.close();
|
|
}
|
|
else if(ele.construction.targets[0].type == TT_Point) {
|
|
targetPoint(ConstructionTargetPoint(obj, ele.construction));
|
|
overlay.close();
|
|
}
|
|
list.clearSelection();
|
|
}
|
|
|
|
updateBuildList(true);
|
|
updateTimer = 0.0;
|
|
}
|
|
}
|
|
} break;
|
|
case GUI_Clicked:
|
|
if(evt.caller is repeatButton) {
|
|
obj.setRepeating(!obj.isRepeating);
|
|
return true;
|
|
}
|
|
else if(evt.caller is drydockButton) {
|
|
openDrydockMenu();
|
|
return true;
|
|
}
|
|
else if(cast<QueueItem>(evt.caller) !is null) {
|
|
updateTimer = 0.0;
|
|
return true;
|
|
}
|
|
else if(evt.caller is buildingsButton) {
|
|
buildingsButton.pressed = true;
|
|
buildingsList.visible = true;
|
|
|
|
orbitalsList.visible = false;
|
|
orbitalsButton.pressed = false;
|
|
shipsList.visible = false;
|
|
shipsButton.pressed = false;
|
|
modulesList.visible = false;
|
|
modulesButton.pressed = false;
|
|
constructionsList.visible = false;
|
|
constructionsButton.pressed = false;
|
|
updateAbsolutePosition();
|
|
}
|
|
else if(evt.caller is orbitalsButton) {
|
|
orbitalsButton.pressed = true;
|
|
orbitalsList.visible = true;
|
|
|
|
buildingsList.visible = false;
|
|
buildingsButton.pressed = false;
|
|
shipsList.visible = false;
|
|
shipsButton.pressed = false;
|
|
modulesList.visible = false;
|
|
modulesButton.pressed = false;
|
|
constructionsList.visible = false;
|
|
constructionsButton.pressed = false;
|
|
updateAbsolutePosition();
|
|
}
|
|
else if(evt.caller is shipsButton) {
|
|
shipsButton.pressed = true;
|
|
shipsList.visible = true;
|
|
|
|
orbitalsList.visible = false;
|
|
orbitalsButton.pressed = false;
|
|
buildingsList.visible = false;
|
|
buildingsButton.pressed = false;
|
|
modulesList.visible = false;
|
|
modulesButton.pressed = false;
|
|
constructionsList.visible = false;
|
|
constructionsButton.pressed = false;
|
|
updateAbsolutePosition();
|
|
}
|
|
else if(evt.caller is modulesButton) {
|
|
modulesButton.pressed = true;
|
|
modulesList.visible = true;
|
|
|
|
orbitalsList.visible = false;
|
|
orbitalsButton.pressed = false;
|
|
buildingsList.visible = false;
|
|
buildingsButton.pressed = false;
|
|
shipsList.visible = false;
|
|
shipsButton.pressed = false;
|
|
constructionsList.visible = false;
|
|
constructionsButton.pressed = false;
|
|
updateAbsolutePosition();
|
|
}
|
|
else if(evt.caller is constructionsButton) {
|
|
constructionsList.visible = true;
|
|
constructionsButton.pressed = true;
|
|
|
|
orbitalsList.visible = false;
|
|
orbitalsButton.pressed = false;
|
|
buildingsList.visible = false;
|
|
buildingsButton.pressed = false;
|
|
shipsList.visible = false;
|
|
shipsButton.pressed = false;
|
|
modulesList.visible = false;
|
|
modulesButton.pressed = false;
|
|
updateAbsolutePosition();
|
|
}
|
|
break;
|
|
}
|
|
return DisplayBox::onGuiEvent(evt);
|
|
}
|
|
};
|
|
|
|
class OrbitalTarget : PointTargeting {
|
|
Object@ obj;
|
|
Object@ constructFrom;
|
|
const OrbitalModule@ def;
|
|
const Design@ dsg;
|
|
bool wasShifted;
|
|
|
|
OrbitalTarget(Object@ obj, const OrbitalModule@ mod, const Design@ dsg = null, bool wasShifted = false, Object@ constructFrom = null) {
|
|
@this.obj = obj;
|
|
this.wasShifted = wasShifted;
|
|
@this.dsg = dsg;
|
|
@this.constructFrom = constructFrom;
|
|
@def = mod;
|
|
if(dsg !is null)
|
|
icon = dsg.icon;
|
|
else if(def.icon.valid)
|
|
icon = def.icon;
|
|
else
|
|
icon = icons::Labor;
|
|
}
|
|
|
|
bool valid(const vec3d& pos) override {
|
|
if(wasShifted && !shiftKey) {
|
|
cancelTargeting();
|
|
return false;
|
|
}
|
|
if(constructFrom !is null) {
|
|
if(!canBuildOrbital(constructFrom, pos, true))
|
|
return false;
|
|
}
|
|
else {
|
|
if(!canBuildOrbital(obj, pos, true))
|
|
return false;
|
|
}
|
|
if(def !is null) {
|
|
if(!def.canBuildBy(obj))
|
|
return false;
|
|
if(!def.canBuildAt(obj, pos))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void call(const vec3d& pos) override {
|
|
if(dsg !is null)
|
|
obj.buildStation(dsg, pos, frame=hoveredObject, constructFrom=constructFrom);
|
|
else
|
|
obj.buildOrbital(def.id, pos, frame=hoveredObject, constructFrom=constructFrom);
|
|
if(shiftKey)
|
|
targetPoint(OrbitalTarget(obj, def, dsg, true, constructFrom=constructFrom));
|
|
}
|
|
|
|
string message(const vec3d& pos, bool valid) {
|
|
if(dsg !is null)
|
|
return dsg.name;
|
|
return def.name;
|
|
}
|
|
|
|
string desc(const vec3d& pos, bool valid) {
|
|
if(valid) {
|
|
double laborCost = 0;
|
|
if(dsg !is null) {
|
|
laborCost = getLaborCost(dsg);
|
|
if(dsg.hasTag(ST_Station))
|
|
laborCost *= obj.owner.OrbitalLaborCostFactor;
|
|
}
|
|
else if(def !is null)
|
|
laborCost = def.laborCost * obj.owner.OrbitalLaborCostFactor;
|
|
double laborIncome = max(obj.laborIncome, 0.01);
|
|
|
|
Orbital@ orbFrame = cast<Orbital>(hoveredObject);
|
|
if(orbFrame !is null && orbFrame.getValue(OV_FRAME_Usable) == 0.0)
|
|
@orbFrame = null;
|
|
if(orbFrame !is null && orbFrame.owner !is obj.owner)
|
|
@orbFrame = null;
|
|
|
|
double penFact = 1.0;
|
|
if(orbFrame !is null) {
|
|
laborCost *= orbFrame.getValue(OV_FRAME_LaborFactor);
|
|
penFact = orbFrame.getValue(OV_FRAME_LaborPenaltyFactor);
|
|
}
|
|
|
|
TradePath path(obj.owner);
|
|
Region@ target = getRegion(pos);
|
|
if(target is null)
|
|
return "";
|
|
path.generate(getSystem(obj.region), getSystem(target));
|
|
laborCost *= 1.0 + config::ORBITAL_LABOR_COST_STEP * penFact * double(path.pathSize - 1);
|
|
|
|
return toString(laborCost,0)+" "+locale::RESOURCE_LABOR+", "+formatTime(laborCost / laborIncome);
|
|
}
|
|
Region@ reg = getRegion(pos);
|
|
if(reg is null)
|
|
return locale::OERR_SYSTEM;
|
|
if(reg.MemoryMask & obj.owner.mask == 0 && reg.VisionMask & obj.owner.visionMask == 0)
|
|
return locale::OERR_VISION;
|
|
if(def !is null) {
|
|
for(uint i = 0, cnt = def.hooks.length; i < cnt; ++i) {
|
|
if(!def.hooks[i].canBuildBy(obj) || !def.hooks[i].canBuildAt(obj, pos))
|
|
return def.hooks[i].getBuildError(obj, pos);
|
|
}
|
|
}
|
|
return locale::OERR_TRADE;
|
|
}
|
|
};
|
|
|
|
class ConstructionTargetPoint : PointTargeting {
|
|
Object@ obj;
|
|
const ConstructionType@ def;
|
|
Targets targets;
|
|
bool wasShifted = false;
|
|
|
|
ConstructionTargetPoint(Object@ obj, const ConstructionType@ type, bool wasShifted = false) {
|
|
@this.obj = obj;
|
|
@def = type;
|
|
targets = type.targets;
|
|
this.wasShifted = wasShifted;
|
|
}
|
|
|
|
bool valid(const vec3d& pos) override {
|
|
if(wasShifted && !shiftKey) {
|
|
cancelTargeting();
|
|
return false;
|
|
}
|
|
targets[0].filled = true;
|
|
targets[0].point = pos;
|
|
return def.canBuild(obj, targets);
|
|
}
|
|
|
|
void call(const vec3d& pos) override {
|
|
obj.buildConstruction(def.id, pointTarg=pos);
|
|
if(shiftKey)
|
|
targetPoint(ConstructionTargetPoint(obj, def, wasShifted=true));
|
|
}
|
|
|
|
string message(const vec3d& pos, bool valid) {
|
|
return def.name;
|
|
}
|
|
|
|
string desc(const vec3d& pos, bool valid) {
|
|
if(valid)
|
|
return def.formatCosts(obj, targets);
|
|
return def.getTargetError(obj, targets);
|
|
}
|
|
};
|
|
|
|
class ConstructionTargetObject : ObjectTargeting {
|
|
Object@ obj;
|
|
const ConstructionType@ def;
|
|
Targets targets;
|
|
bool wasShifted = false;
|
|
|
|
ConstructionTargetObject(Object@ obj, const ConstructionType@ type, bool wasShifted = false) {
|
|
@this.obj = obj;
|
|
@def = type;
|
|
targets = type.targets;
|
|
this.wasShifted = wasShifted;
|
|
}
|
|
|
|
bool valid(Object@ target) override {
|
|
if(wasShifted && !shiftKey) {
|
|
cancelTargeting();
|
|
return false;
|
|
}
|
|
targets[0].filled = true;
|
|
@targets[0].obj = target;
|
|
return def.canBuild(obj, targets);
|
|
}
|
|
|
|
void call(Object@ target) override {
|
|
obj.buildConstruction(def.id, objTarg=target);
|
|
if(shiftKey)
|
|
targetObject(ConstructionTargetObject(obj, def, wasShifted=true));
|
|
}
|
|
|
|
string message(Object@ target, bool valid) {
|
|
return def.name;
|
|
}
|
|
|
|
string desc(Object@ target, bool valid) {
|
|
if(valid)
|
|
return def.formatCosts(obj, targets);
|
|
return def.getTargetError(obj, targets);
|
|
}
|
|
};
|
|
|
|
const string slashStr("/");
|
|
class BuildElement : GuiListElement {
|
|
const Design@ dsg;
|
|
const OrbitalModule@ orb;
|
|
const BuildingType@ building;
|
|
const ConstructionType@ construction;
|
|
Object@ buildAt;
|
|
Color color;
|
|
string nameText;
|
|
string costText;
|
|
string maintainText;
|
|
string ttText;
|
|
string timeText;
|
|
string laborText;
|
|
string energyText;
|
|
Sprite icon;
|
|
Color nameColor;
|
|
Color iconColor = colors::White;
|
|
bool isSupport = false;
|
|
bool incomplete = false;
|
|
array<Sprite> extraIcons;
|
|
array<string> extraCosts;
|
|
|
|
|
|
BuildElement(ConstructionDisplay@ disp, const Design@ Dsg, Object@ at) {
|
|
@dsg = Dsg;
|
|
@buildAt = at;
|
|
update(disp);
|
|
}
|
|
|
|
BuildElement(ConstructionDisplay@ disp, const OrbitalModule@ Orb, Object@ at) {
|
|
@orb = Orb;
|
|
@buildAt = at;
|
|
update(disp);
|
|
}
|
|
|
|
BuildElement(ConstructionDisplay@ disp, const BuildingType@ Type, Object@ at) {
|
|
@building = Type;
|
|
@buildAt = at;
|
|
update(disp);
|
|
}
|
|
|
|
BuildElement(ConstructionDisplay@ disp, const ConstructionType@ Type, Object@ at) {
|
|
@construction = Type;
|
|
@buildAt = at;
|
|
update(disp);
|
|
}
|
|
|
|
string get_tooltipText() {
|
|
return ttText;
|
|
}
|
|
|
|
int opCmp(const GuiListElement@ other) const {
|
|
const BuildElement@ be = cast<const BuildElement@>(other);
|
|
if(be is null)
|
|
return 0;
|
|
if(dsg !is null) {
|
|
if(be.dsg is null)
|
|
return 0;
|
|
if(dsg.size > be.dsg.size)
|
|
return 1;
|
|
if(dsg.size < be.dsg.size)
|
|
return -1;
|
|
}
|
|
else if(orb !is null) {
|
|
if(orb.totalRequirementCount > be.orb.totalRequirementCount)
|
|
return 1;
|
|
if(orb.totalRequirementCount < be.orb.totalRequirementCount)
|
|
return -1;
|
|
if(orb.buildCost > be.orb.buildCost)
|
|
return 1;
|
|
if(orb.buildCost < be.orb.buildCost)
|
|
return -1;
|
|
}
|
|
else if(building !is null) {
|
|
if(building.laborCost > be.building.laborCost)
|
|
return -1;
|
|
if(building.laborCost < be.building.laborCost)
|
|
return 1;
|
|
if(building.buildCostEst > be.building.buildCostEst)
|
|
return 1;
|
|
if(building.buildCostEst < be.building.buildCostEst)
|
|
return -1;
|
|
if(building.maintainCostEst > be.building.maintainCostEst)
|
|
return 1;
|
|
if(building.maintainCostEst < be.building.maintainCostEst)
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void update(ConstructionDisplay@ disp) {
|
|
int build = 0, maintain = 0;
|
|
double labor = 0, time = 0, energy = 0;
|
|
bool hasError = false, hasWarning = false;
|
|
extraIcons.length = 0;
|
|
extraCosts.length = 0;
|
|
|
|
if(dsg !is null) {
|
|
nameText = dsg.name+" ("+toString(dsg.size, 0)+")";
|
|
ttText = "";
|
|
getBuildCost(dsg, build, maintain, labor, -1, buildAt);
|
|
icon = dsg.icon;
|
|
|
|
double multiply = 1.0;
|
|
if(dsg.hasTag(ST_Support)) {
|
|
build *= 10;
|
|
maintain *= 10;
|
|
labor *= 10;
|
|
multiply = 10.0;
|
|
isSupport = true;
|
|
iconColor = dsg.color;
|
|
}
|
|
else if(dsg.hasTag(ST_Station)) {
|
|
labor *= buildAt.owner.OrbitalLaborCostFactor;
|
|
build *= buildAt.owner.OrbitalBuildCostFactor;
|
|
}
|
|
else {
|
|
isSupport = false;
|
|
iconColor = dsg.dullColor;
|
|
}
|
|
|
|
for(uint i = 0, cnt = getCargoTypeCount(); i < cnt; ++i) {
|
|
auto@ cargo = getCargoType(i);
|
|
if(int(cargo.variable) == -1)
|
|
continue;
|
|
double amt = dsg.total(cargo.variable) * multiply;
|
|
if(amt > 0) {
|
|
extraIcons.insertLast(cargo.icon);
|
|
extraCosts.insertLast(standardize(amt, true));
|
|
if(buildAt is null || !buildAt.hasCargo || buildAt.getCargoStored(cargo.id) < amt)
|
|
hasError = true;
|
|
}
|
|
}
|
|
|
|
{
|
|
double energyCost = dsg.total(SV_EnergyBuildCost);
|
|
if(energyCost > 0) {
|
|
extraIcons.insertLast(icons::Energy);
|
|
extraCosts.insertLast(standardize(energyCost, true));
|
|
if(buildAt is null || buildAt.owner.EnergyStored < energyCost)
|
|
hasError = true;
|
|
}
|
|
}
|
|
|
|
{
|
|
int influenceCost = dsg.total(SV_InfluenceBuildCost);
|
|
if(influenceCost > 0) {
|
|
extraIcons.insertLast(icons::Influence);
|
|
extraCosts.insertLast(toString(influenceCost, 0));
|
|
if(buildAt is null || buildAt.owner.Influence < influenceCost)
|
|
hasError = true;
|
|
}
|
|
}
|
|
|
|
{
|
|
double ftlCost = dsg.total(SV_FTLBuildCost);
|
|
if(ftlCost > 0) {
|
|
extraIcons.insertLast(icons::FTL);
|
|
extraCosts.insertLast(standardize(ftlCost, true));
|
|
if(buildAt is null || buildAt.owner.FTLStored < ftlCost)
|
|
hasError = true;
|
|
}
|
|
}
|
|
}
|
|
else if(orb !is null) {
|
|
color = Color(0xffffffff);
|
|
nameText = orb.name;
|
|
ttText = orb.getTooltip();
|
|
build = orb.buildCost;
|
|
build *= buildAt.constructionCostMod;
|
|
build *= buildAt.owner.OrbitalBuildCostFactor;
|
|
labor = orb.laborCost;
|
|
labor *= buildAt.owner.OrbitalLaborCostFactor;
|
|
maintain = orb.maintenance;
|
|
energy = 0;
|
|
icon = orb.icon;
|
|
isSupport = false;
|
|
|
|
if(!orb.canBuildBy(buildAt, ignoreCost=false))
|
|
hasError = true;
|
|
|
|
Sprite exIcon; string exCost;
|
|
for(uint i = 0, cnt = orb.hooks.length; i < cnt; ++i) {
|
|
if(orb.hooks[i].getCost(buildAt, exCost, exIcon)) {
|
|
extraCosts.insertLast(exCost);
|
|
extraIcons.insertLast(exIcon);
|
|
}
|
|
}
|
|
}
|
|
else if(building !is null) {
|
|
nameText = building.name;
|
|
build = building.buildCostEst * buildAt.owner.BuildingCostFactor;
|
|
build *= buildAt.constructionCostMod;
|
|
maintain = building.maintainCostEst;
|
|
if(building.laborCost > 0)
|
|
labor = building.laborCost;
|
|
else
|
|
time = building.getBuildTime(buildAt) / (buildAt.buildingConstructRate * buildAt.owner.BuildingConstructRate * buildAt.owner.ImperialBldConstructionRate);
|
|
icon = building.sprite;
|
|
iconColor = colors::White;
|
|
isSupport = false;
|
|
|
|
ttText = building.getTooltip(valueObject=buildAt, isOption=true);
|
|
|
|
if(!building.canBuildOn(buildAt))
|
|
hasError = true;
|
|
|
|
Sprite exIcon; string exCost;
|
|
for(uint i = 0, cnt = building.hooks.length; i < cnt; ++i) {
|
|
if(building.hooks[i].getCost(buildAt, exCost, exIcon)) {
|
|
extraCosts.insertLast(exCost);
|
|
extraIcons.insertLast(exIcon);
|
|
}
|
|
}
|
|
}
|
|
else if(construction !is null) {
|
|
nameText = construction.name;
|
|
build = construction.getBuildCost(buildAt);
|
|
maintain = construction.getMaintainCost(buildAt);
|
|
labor = construction.getLaborCost(buildAt);
|
|
time = construction.getTimeCost(buildAt);
|
|
icon = construction.icon;
|
|
iconColor = colors::White;
|
|
isSupport = false;
|
|
ttText = construction.formatTooltip(buildAt);
|
|
|
|
if(!construction.canBuild(buildAt, null))
|
|
hasError = true;
|
|
|
|
Sprite exIcon; string exCost;
|
|
for(uint i = 0, cnt = construction.hooks.length; i < cnt; ++i) {
|
|
if(construction.hooks[i].getCost(buildAt, construction, null, exCost, exIcon)) {
|
|
extraCosts.insertLast(exCost);
|
|
extraIcons.insertLast(exIcon);
|
|
}
|
|
}
|
|
}
|
|
|
|
int borrow = build - playerEmpire.RemainingBudget;
|
|
if(build > 0 && borrow > 0) {
|
|
if(ttText.length != 0)
|
|
ttText += "\n\n";
|
|
if(playerEmpire.canBorrow(borrow)) {
|
|
hasWarning = true;
|
|
double rate = playerEmpire.BorrowRate;
|
|
ttText += format(locale::NEED_BORROW,
|
|
formatMoney(borrow), formatMoney(ceil(double(borrow) * rate)));
|
|
}
|
|
else {
|
|
hasError = true;
|
|
ttText += "[color=#f00]"+locale::CANNOT_BORROW+"[/color]";
|
|
}
|
|
}
|
|
|
|
if(isSupport && !disp.isBuildingFlagship)
|
|
nameColor = Color(0x999999ff);
|
|
else if(hasError)
|
|
nameColor = Color(0xff0000ff);
|
|
else if(hasWarning)
|
|
nameColor = Color(0xfdff00ff);
|
|
else if(incomplete)
|
|
nameColor = Color(0x999999ff);
|
|
else
|
|
nameColor = Color(0xffffffff);
|
|
|
|
if(build != 0 || maintain != 0)
|
|
costText = formatMoney(build);
|
|
else
|
|
costText.resize(0);
|
|
if(maintain == 0)
|
|
maintainText.resize(0);
|
|
else
|
|
maintainText = formatMoney(maintain);
|
|
if(energy != 0)
|
|
energyText = formatRate(energy);
|
|
else
|
|
energyText.resize(0);
|
|
|
|
if(labor != 0) {
|
|
laborText = standardize(labor, true);
|
|
if(isSupport)
|
|
timeText = formatTimeRate(labor, buildAt.laborIncome*(float(buildAt.supportBuildSpeed)/100.f));
|
|
else
|
|
timeText = formatTimeRate(labor, buildAt.laborIncome);
|
|
}
|
|
else if(time != 0) {
|
|
laborText = "";
|
|
timeText = formatTime(time);
|
|
}
|
|
else {
|
|
laborText = "";
|
|
timeText = "";
|
|
}
|
|
}
|
|
|
|
void draw(GuiListbox@ ele, uint flags, const recti& pos) override {
|
|
const Font@ font = ele.skin.getFont(ele.TextFont);
|
|
const Font@ smallFont = ele.skin.getFont(FT_Small);
|
|
|
|
//Background element
|
|
ele.skin.draw(SS_BuildElement, flags, pos);
|
|
|
|
//Icon
|
|
int x = 4;
|
|
if(icon.valid) {
|
|
int isize = min(icon.size.width, 28);
|
|
recti ipos = recti_area(vec2i(x,(30-isize)/2)+pos.topLeft, vec2i(isize, isize));
|
|
if(dsg !is null && isSupport) {
|
|
spritesheet::ResourceIconsSmallMods.draw(0, ipos.padded(-1));
|
|
icon.draw(ipos.padded(3), iconColor);
|
|
}
|
|
else {
|
|
icon.draw(ipos.aspectAligned(icon.aspect), iconColor);
|
|
}
|
|
x += isize+8;
|
|
}
|
|
|
|
//Time
|
|
int tx = x;
|
|
x = pos.width-2;
|
|
|
|
//Labor
|
|
if(laborText.length != 0) {
|
|
if((costText.length == 0 && extraCosts.length < 4) || extraCosts.length == 0)
|
|
x -= 120;
|
|
else
|
|
x -= 60;
|
|
icons::Labor.draw(recti_area(vec2i(x, 3)+pos.topLeft, vec2i(24, 24)));
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(92, 30)),
|
|
horizAlign=0.0, vertAlign=0.5,
|
|
text=laborText, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
|
|
if(timeText.length != 0 && ((costText.length == 0 && extraCosts.length < 4) || extraCosts.length == 0)) {
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(92, 30)),
|
|
horizAlign=1.0, vertAlign=0.5,
|
|
text=timeText, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
}
|
|
}
|
|
else if(timeText.length != 0) {
|
|
x -= 120;
|
|
spritesheet::ContextIcons.draw(1, recti_area(vec2i(x, 3)+pos.topLeft, vec2i(24, 24)));
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(57, 30)),
|
|
horizAlign=0.0, vertAlign=0.5,
|
|
text=timeText, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
}
|
|
|
|
//Cost
|
|
if(energyText.length != 0) {
|
|
x -= 90;
|
|
icons::Energy.draw(recti_area(vec2i(x, 3)+pos.topLeft, vec2i(24, 24)));
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(62, 30)),
|
|
horizAlign=0.0, vertAlign=0.5,
|
|
text=energyText, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
}
|
|
|
|
if(costText.length != 0) {
|
|
if(maintainText.length != 0) {
|
|
x -= 135;
|
|
icons::Money.draw(recti_area(vec2i(x, 3)+pos.topLeft, vec2i(24, 24)));
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(92, 30)),
|
|
horizAlign=0.0, vertAlign=0.5,
|
|
text=costText, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(92, 30)),
|
|
horizAlign=0.5, vertAlign=0.5,
|
|
text=slashStr, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(92, 30)),
|
|
horizAlign=1.0, vertAlign=0.5,
|
|
text=maintainText, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
}
|
|
else {
|
|
x -= 90;
|
|
icons::Money.draw(recti_area(vec2i(x, 3)+pos.topLeft, vec2i(24, 24)));
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(62, 30)),
|
|
horizAlign=0.0, vertAlign=0.5,
|
|
text=costText, ellipsis=locale::ELLIPSIS, color=colors::White);
|
|
}
|
|
}
|
|
|
|
for(uint i = 0, cnt = extraCosts.length; i < cnt; ++i) {
|
|
x -= 60;
|
|
extraIcons[i].draw(recti_area(vec2i(x, 3)+pos.topLeft, vec2i(24, 24)));
|
|
font.draw(pos=recti_area(vec2i(x+28, 0)+pos.topLeft, vec2i(32, 32)),
|
|
horizAlign=0.0, vertAlign=0.5,
|
|
text=extraCosts[i], ellipsis=locale::SHORT_ELLIPSIS, color=colors::White);
|
|
}
|
|
|
|
//Requirements
|
|
if(orb !is null) {
|
|
x = pos.width - 246;
|
|
int cx = x;
|
|
for(uint i = 0; i < TR_COUNT; ++i) {
|
|
for(uint n = 0, ncnt = orb.affinities[i]; n < ncnt; ++n) {
|
|
getTileResourceSprite(i).draw(
|
|
recti_area(vec2i(cx, 3)+pos.topLeft, vec2i(24, 24)));
|
|
cx += 24;
|
|
}
|
|
}
|
|
for(uint i = 0, cnt = orb.requirements.length; i < cnt; ++i) {
|
|
orb.requirements[i].icon.draw(
|
|
recti_area(vec2i(cx, 3)+pos.topLeft, vec2i(24, 24)));
|
|
cx += 24;
|
|
}
|
|
}
|
|
|
|
//Name
|
|
font.draw(pos=pos.padded(tx, 0, ((pos.width-2) - x), 0), vertAlign=0.5,
|
|
text=nameText, ellipsis=locale::ELLIPSIS, color=nameColor);
|
|
}
|
|
};
|
|
|
|
class QueueItem : BaseGuiElement {
|
|
ConstructionDisplay@ display;
|
|
Object@ obj;
|
|
Constructible cons;
|
|
Color color;
|
|
string name;
|
|
string timeText;
|
|
float pct;
|
|
bool first = false;
|
|
SupportItem@[] supportItems;
|
|
GuiButton@ cancelButton;
|
|
|
|
vec2i dragStart;
|
|
vec2i dragOffset;
|
|
bool leftHeld = false;
|
|
|
|
QueueItem(BaseGuiElement@ parent, ConstructionDisplay@ disp) {
|
|
@display = disp;
|
|
super(parent, recti(0, 0, 100, 32));
|
|
updateAbsolutePosition();
|
|
|
|
@cancelButton = GuiButton(this, Alignment(Right-32, Top+6, Right-5, Top+32));
|
|
cancelButton.color = colors::Red;
|
|
cancelButton.style = SS_IconButton;
|
|
cancelButton.setIcon(icons::Remove);
|
|
cancelButton.tooltip = locale::CANCEL_CONSTRUCTION;
|
|
}
|
|
|
|
void remove() override {
|
|
BaseGuiElement::remove();
|
|
@display = null;
|
|
}
|
|
|
|
void update(Constructible@ Cons, Object@ Obj) {
|
|
@obj = Obj;
|
|
cons = Cons;
|
|
|
|
//Update construction data
|
|
name = cons.name;
|
|
if(cons.dsg !is null) {
|
|
name += " ("+toString(cons.dsg.size, 0)+")";
|
|
color = cons.dsg.dullColor;
|
|
}
|
|
|
|
double eta = cons.getETA(obj);
|
|
if(eta == INFINITY)
|
|
timeText = toString(cons.progress*100.f, 0.f)+"/"+toString(cons.percentage*100.f, 0.f)+"%";
|
|
else
|
|
timeText = formatTime(eta);
|
|
pct = cons.progress;
|
|
|
|
//Update support groups
|
|
uint oldCnt = supportItems.length;
|
|
uint newCnt = cons.groups.length;
|
|
|
|
for(uint i = newCnt; i < oldCnt; ++i)
|
|
supportItems[i].remove();
|
|
supportItems.length = newCnt;
|
|
|
|
int y = 32;
|
|
for(uint i = 0; i < newCnt; ++i) {
|
|
SupportItem@ ele = supportItems[i];
|
|
if(ele is null) {
|
|
@ele = SupportItem(this);
|
|
@supportItems[i] = ele;
|
|
}
|
|
|
|
ele.update(cons.groups[i]);
|
|
ele.position = vec2i(0, y);
|
|
y += ele.size.height;
|
|
}
|
|
|
|
size = vec2i(size.width, y);
|
|
}
|
|
|
|
void updateAbsolutePosition() override {
|
|
int w = parent.size.width;
|
|
GuiPanel@ pn = cast<GuiPanel>(parent);
|
|
if(pn !is null && pn.vert.visible)
|
|
w -= 20;
|
|
size = vec2i(w, size.height);
|
|
BaseGuiElement::updateAbsolutePosition();
|
|
}
|
|
|
|
bool onGuiEvent(const GuiEvent& evt) {
|
|
if(evt.type == GUI_Mouse_Entered && display !is null) {
|
|
@display.hoveredItem = this;
|
|
}
|
|
if(evt.type == GUI_Mouse_Left && display !is null) {
|
|
if(!isAncestorOf(evt.caller) && display.hoveredItem is this)
|
|
@display.hoveredItem = null;
|
|
}
|
|
if(evt.caller is cancelButton && evt.type == GUI_Clicked) {
|
|
obj.cancelConstruction(cons.id);
|
|
emitClicked();
|
|
return true;
|
|
}
|
|
return BaseGuiElement::onGuiEvent(evt);
|
|
}
|
|
|
|
bool onMouseEvent(const MouseEvent& event, IGuiElement@ caller) override {
|
|
if(display is null)
|
|
return true;
|
|
if(event.type == MET_Button_Down) {
|
|
if(event.button == 0) {
|
|
leftHeld = true;
|
|
dragStart = mousePos;
|
|
dragOffset = mousePos - absolutePosition.topLeft;
|
|
}
|
|
return true;
|
|
}
|
|
if(event.type == MET_Moved){
|
|
if(leftHeld) {
|
|
if(dragStart.distanceTo(mousePos) > 5) {
|
|
leftHeld = false;
|
|
@display.draggingItem = this;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
if(event.type == MET_Button_Up) {
|
|
if(event.button == 0) {
|
|
if(leftHeld) {
|
|
leftHeld = false;
|
|
if(cons.dsg !is null)
|
|
display.selectQueue(this);
|
|
else
|
|
display.selectQueue(null);
|
|
return true;
|
|
}
|
|
}
|
|
else if(event.button == 1) {
|
|
if(caller is this) {
|
|
obj.cancelConstruction(cons.id);
|
|
}
|
|
else {
|
|
SupportItem@ it = cast<SupportItem>(caller);
|
|
if(it !is null)
|
|
obj.removeSupportShipConstruction(cons.id, it.dat.dsg, shiftKey ? it.dat.totalSize : 10);
|
|
}
|
|
emitClicked();
|
|
return true;
|
|
}
|
|
}
|
|
return BaseGuiElement::onMouseEvent(event, caller);
|
|
}
|
|
|
|
void draw(const recti& absPos) {
|
|
const Font@ font = skin.getFont(FT_Normal);
|
|
|
|
//Left panel (icon/size)
|
|
skin.draw(SS_Field, SF_Normal, recti_area(
|
|
absPos.topLeft, vec2i(38, 29)));
|
|
|
|
if(cons.dsg !is null) {
|
|
cons.dsg.icon.draw(recti_area(
|
|
absPos.topLeft + vec2i(5, 0), vec2i(29, 29)), color);
|
|
}
|
|
else {
|
|
Sprite icon = cons.icon;
|
|
icon.draw(recti_area(
|
|
absPos.topLeft + vec2i(5, 0), vec2i(29, 29)).aspectAligned(icon.aspect));
|
|
}
|
|
|
|
//Middle panel (progress/name)
|
|
recti midBar = recti_area(absPos.topLeft + vec2i(40, 0), vec2i(absPos.width - 142, 29));
|
|
skin.draw(SS_Field, SF_Normal, midBar);
|
|
|
|
int w = (1.f - pct) * float(midBar.width - 4);
|
|
drawRectangle(midBar.padded(2, 2, 2+w, 2), Color(0x474545aa));
|
|
|
|
Color textColor(0xffffffff);
|
|
if(!cons.started) {
|
|
if(first)
|
|
textColor = Color(0xff0000ff);
|
|
else
|
|
textColor = Color(0x999999ff);
|
|
}
|
|
font.draw(pos=midBar.padded(7, 0), text=name, color=textColor,
|
|
horizAlign=0.0, vertAlign=0.5, stroke=colors::Black);
|
|
|
|
//vec2i nameSize = font.getDimension(name);
|
|
//drawResources(skin, absPos.topLeft + vec2i(80+nameSize.x, 3), cost, supplyCost);
|
|
|
|
//Right panel (eta)
|
|
skin.draw(SS_Field, SF_Normal, recti_area(
|
|
absPos.botRight - vec2i(100, 29), vec2i(100, 29)));
|
|
|
|
font.draw(pos=recti_area(absPos.botRight - vec2i(100, 29), vec2i(64, 29)),
|
|
text=timeText, horizAlign=1.0, vertAlign=0.5, stroke=colors::Black);
|
|
}
|
|
|
|
void draw() override {
|
|
//Selected box
|
|
if(display.selQueue is this)
|
|
drawRectangle(AbsolutePosition, Color(0x00808080));
|
|
|
|
recti absPos = recti_area(AbsolutePosition.topLeft+vec2i(4,3), vec2i(AbsolutePosition.size.width-8, 29));
|
|
draw(absPos);
|
|
|
|
BaseGuiElement::draw();
|
|
}
|
|
};
|
|
|
|
class SupportItem : BaseGuiElement {
|
|
QueueItem@ item;
|
|
GroupData dat;
|
|
|
|
SupportItem(QueueItem@ parent) {
|
|
@item = parent;
|
|
super(parent, recti(0, 0, 100, 20));
|
|
updateAbsolutePosition();
|
|
}
|
|
|
|
void updateAbsolutePosition() override {
|
|
size = vec2i(parent.size.width, 20);
|
|
BaseGuiElement::updateAbsolutePosition();
|
|
}
|
|
|
|
void update(GroupData@ Dat) {
|
|
dat = Dat;
|
|
}
|
|
|
|
void remove() override {
|
|
@item = null;
|
|
BaseGuiElement::remove();
|
|
}
|
|
|
|
void draw(const recti& absPos) {
|
|
skin.draw(SS_Field, SF_Normal, absPos);
|
|
|
|
recti ipos = recti_area(absPos.topLeft+vec2i(8, 2), vec2i(16, 16));
|
|
spritesheet::ResourceIconsSmallMods.draw(0, ipos);
|
|
dat.dsg.icon.draw(ipos.padded(2), dat.dsg.color);
|
|
|
|
skin.draw(FT_Normal, absPos.topLeft+vec2i(32, 3), dat.dsg.name);
|
|
skin.draw(FT_Normal, absPos.topLeft+vec2i(180, 3), toString(dat.amount)+"x");
|
|
skin.draw(FT_Normal, absPos.topLeft+vec2i(260, 3), format("(+$1x)", toString(dat.ordered)), Color(0x80ff80ff));
|
|
}
|
|
|
|
void draw() override {
|
|
recti absPos = AbsolutePosition.padded(72, 0, 76, 0);
|
|
draw(absPos);
|
|
|
|
BaseGuiElement::draw();
|
|
}
|
|
};
|
|
|
|
class FlagshipDrydock : GuiMarkupContextOption {
|
|
const Design@ dsg;
|
|
Empire@ forEmpire;
|
|
Object@ fromObj;
|
|
|
|
FlagshipDrydock(Object@ obj, const Design@ dsg, Empire@ emp = playerEmpire) {
|
|
@this.dsg = dsg;
|
|
@this.forEmpire = emp;
|
|
@this.fromObj = obj;
|
|
super(format("[offset=10][color=$5][b]$2[/b][/color] [offset=260]([loc=SIZE/] $3)[/offset][/offset]",
|
|
toString(dsg.color),
|
|
dsg.name,
|
|
standardize(dsg.size, true),
|
|
getSpriteDesc(dsg.icon),
|
|
toString(dsg.color.interpolate(colors::White, 0.5))),
|
|
FT_Subtitle);
|
|
icon = dsg.icon;
|
|
icon.color = dsg.color;
|
|
}
|
|
|
|
void call(GuiContextMenu@ menu) {
|
|
FinanceDryDock(dsg, fromObj);
|
|
}
|
|
|
|
int opCmp(const GuiListElement@ other) const {
|
|
auto@ cmp = cast<const FlagshipDrydock@>(other);
|
|
if(cmp.dsg.size < dsg.size)
|
|
return 1;
|
|
if(cmp.dsg.size > dsg.size)
|
|
return -1;
|
|
return 0;
|
|
}
|
|
};
|