import dialogs.InputDialog; import elements.BaseGuiElement; import elements.GuiPanel; import elements.GuiText; import elements.GuiButton; import elements.GuiSprite; import elements.GuiCheckbox; import elements.GuiSkinElement; import elements.GuiContextMenu; import elements.MarkupTooltip; import resources, ship_groups; import elements.Gui3DObject; #include "dialogs/include/UniqueDialogs.as" import obj_selection; import icons; from gui import animate_time; from targeting.targeting import cancelTargeting; from targeting.MoveTarget import isExtendedMoveTarget; const int GROUP_WIDTH = 256; const int GROUP_SPACING = 32; const int SUPPORT_PADDING = 12; const int SUPPORT_HEIGHT = 56; const int SUPPORT_WIDTH = GROUP_WIDTH - SUPPORT_PADDING * 2; const int SUPPORT_SPACING = 8; const int BAR_WIDTH = SUPPORT_WIDTH + SUPPORT_PADDING * 2 + 20; const vec2i addButtonSize = vec2i(180,32); const double ANIM_TIME = 0.15; from tabs.GalaxyTab import GalaxyOverlay; GalaxyOverlay@ createSupportOverlay(IGuiElement@ tab, Object@ obj, Object@ to, bool animate = true) { return SupportOverlay(tab, obj, to, animate); } class SupportOverlay : GalaxyOverlay, BaseGuiElement { Object@ leader; GroupDisplay@ main; GroupDisplay@ secondary; SupportClass@[] selected; vec2i dragStart; bool leftDown = false; bool rightDown = false; bool dragging = false; bool closing = false; SupportClass@ clsHover; GroupDisplay@ grpHover; SupportOverlay(IGuiElement@ parent, Object@ Leader, Object@ To, bool animate) { @leader = Leader; super(parent, Alignment(Left, Top, Right, Bottom)); if(To !is null) makeSecondary(To); @main = GroupDisplay(leader, this, 255); Alignment align(Left+4, Top+34, Left+4+BAR_WIDTH, Bottom); if(animate) main.animate(align); else @main.alignment = align; updateAbsolutePosition(); bringToFront(); setGuiFocus(this); } void makeSecondary(Object@ obj) { if(main !is null && obj is main.leader) @obj = null; if(secondary !is null) { if(obj is null) { secondary.remove(); @secondary = null; } else { secondary.set(obj); } } else if(obj !is null) { @secondary = GroupDisplay(obj, this); @secondary.alignment = Alignment(Left+2+BAR_WIDTH, Top+94, Left+2+BAR_WIDTH*2, Bottom-220); secondary.sendToBack(); } } GroupDisplay@ getHoveredGroup(vec2i absPos) { if(main.absolutePosition.isWithin(absPos)) return main; if(secondary !is null && secondary.absolutePosition.isWithin(absPos)) return secondary; return null; } SupportClass@ getHoveredClass(vec2i absPos) { GroupDisplay@ grp = getHoveredGroup(absPos); if(grp !is null) return grp.getHoveredClass(absPos); return null; } bool isOpen() { return parent !is null; } bool objectInteraction(Object& object, uint mouseButton, bool doubleClicked) { return false; } void close() { if(parent is null || closing) return; close(main); } void close(GroupDisplay@ disp) { if(disp is main) { main.animateClose(); closing = true; } else if(disp is secondary) { secondary.remove(); @secondary = null; deselect(); } } IGuiElement@ elementFromPosition(const vec2i& pos) { if(dragging) { //Cannot access inner elements while dragging, //we handle all the drag and drop stuff ourselves if(AbsoluteClipRect.isWithin(pos)) return this; return null; } else { return BaseGuiElement::elementFromPosition(pos); } } void updateHover() { @grpHover = getHoveredGroup(mousePos); if(grpHover !is null) @clsHover = grpHover.getHoveredClass(mousePos); else @clsHover = null; } void dropGroups() { updateTimer = 0.1; Object@ transferTo; if(grpHover !is null) @transferTo = grpHover.leader; else @transferTo = hoveredObject; if(transferTo !is null && transferTo.owner.controlled && transferTo.hasLeaderAI) { for(uint i = 0, cnt = selected.length; i < cnt; ++i) { SupportClass@ cls = selected[i]; if(cls.disp.leader is transferTo || cls.disp is null) continue; uint amt = cls.dat.totalSize - cls.leaveAmount; cls.disp.leader.transferSupports(cls.dat.dsg, amt, transferTo); } } main.update(); if(secondary !is null) secondary.update(); deselect(); } void deselect() { for(uint i = 0, cnt = selected.length; i < cnt; ++i) { selected[i].selected = false; selected[i].leaveAmount = 0; } selected.length = 0; } void select(SupportClass@ cls) { cls.selected = true; cls.leaveAmount = 0; if(selected.find(cls) == -1) selected.insertLast(cls); } void deselect(SupportClass@ cls) { cls.selected = false; cls.leaveAmount = 0; selected.remove(cls); } void pingUpdate() { main.update(); if(secondary !is null) secondary.update(); updateTimer = 0.15; } double updateTimer = 0.0; bool update(double time) { if(closing) return true; if(selectedObject !is main.leader) { if(selectedObject is null || !selectedObject.hasLeaderAI) return false; main.set(selectedObject); if(secondary !is null) { secondary.remove(); @secondary = null; } } updateTimer -= time; if(updateTimer <= 0) { main.update(); if(secondary !is null) { secondary.update(); secondary.visible = !main.animating; } updateTimer += 0.5; } return true; } bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) { if(event.type == MET_Button_Down) { if(event.button == 0) { dragging = false; leftDown = true; dragStart = mousePos; if(clsHover !is null) { if(ctrlKey) { if(clsHover.selected) deselect(clsHover); else select(clsHover); leftDown = false; } else { if(!shiftKey) deselect(); select(clsHover); } } else { deselect(); } } else if(event.button == 1) { if(!leftDown) dragStart = mousePos; rightDown = true; return false; } } else if(event.type == MET_Moved) { updateHover(); if(!dragging && leftDown && mousePos.distanceTo(dragStart) > 3 && selected.length != 0) { bool selSats = false; for(uint i = 0, cnt = selected.length; i < cnt; ++i) { if(selected[i].dat.dsg.hasTag(ST_Satellite)) { selSats = true; break; } } if(!selSats) { dragging = true; for(uint i = 0, cnt = selected.length; i < cnt; ++i) { if(shiftKey) selected[i].leaveAmount = selected[i].dat.totalSize - selected[i].dat.amount; else selected[i].leaveAmount = 0; } } } if(dragging) { } } else if(event.type == MET_Scrolled) { for(uint i = 0, cnt = selected.length; i < cnt; ++i) { SupportClass@ cls = selected[i]; int y = event.y; if(shiftKey) y *= 10; cls.leaveAmount = clamp(cls.leaveAmount - y, 0, cls.dat.totalSize); } return true; } else if(event.type == MET_Button_Up) { if(event.button == 0) { leftDown = false; if(dragging) { dropGroups(); dragging = false; return true; } } else if(event.button == 1) { rightDown = false; if(mousePos.distanceTo(dragStart) < 5 && !isExtendedMoveTarget() && hoveredObject is null) { close(main); cancelTargeting(); return true; } } } return BaseGuiElement::onMouseEvent(event, source); } bool onKeyEvent(const KeyboardEvent& event, IGuiElement@ source) override { switch(event.type) { case KET_Key_Down: if(event.key == KEY_ESC) return true; break; case KET_Key_Up: if(event.key == KEY_ESC) { close(main); return true; } break; } return BaseGuiElement::onKeyEvent(event, source); } bool get_isRoot() const override { return getHoveredGroup(mousePos) is null; } void draw() override { BaseGuiElement::draw(); if(dragging) { vec2i dragDiff = mousePos - dragStart; for(uint i = 0, cnt = selected.length; i < cnt; ++i) selected[i].drawAt(selected[i].absolutePosition.topLeft + dragDiff, true); } } }; class GroupDisplay : BaseGuiElement { SupportOverlay@ overlay; Object@ leader; Alignment@ targPos; bool animating = false; bool closing = false; //Group leader display GuiSkinElement@ capBG; GuiText@ capText; GuiText@ groupSize; //List of support groups GuiPanel@ listPanel; SupportClass@[] classes; GroupData[] groupData; //Action buttons GuiButton@ addButton; GuiButton@ rebuyButton; GuiButton@ clearButton; GuiCheckbox@ autoBuy; GuiCheckbox@ autoFill; GuiCheckbox@ allowFillFrom; GroupDisplay(Object@ obj, SupportOverlay@ Overlay, int offset = 0) { @leader = obj; @overlay = Overlay; super(overlay, recti()); @capBG = GuiSkinElement(this, Alignment(Left, Top, Right-22, Top+50), SS_FullTitle); @capText = GuiText(this, Alignment(Left+12, Top+8, Right-12, Top+24), locale::SUPPORT_CAPACITY); capText.font = FT_Bold; @groupSize = GuiText(this, Alignment(Left+4, Top+20, Right-32, Top+48)); groupSize.horizAlign = 1.0; @listPanel = GuiPanel(this, Alignment(Left+SUPPORT_PADDING, Top+55, Right-SUPPORT_PADDING, Bottom-offset-108)); @addButton = GuiButton(this, recti(0, 0, 180, 32), locale::ADD_SUPPORTS); addButton.tooltip = locale::CREATE_SUPPORT_SHIPS; addButton.buttonIcon = icons::Add; @rebuyButton = GuiButton(this, Alignment(Left+4, Bottom-offset-60, Left+0.5f-15, Bottom-offset-30), locale::REBUY_GHOSTS); setMarkupTooltip(rebuyButton, locale::TT_REBUY_GHOSTS); rebuyButton.color = colors::Money; rebuyButton.setIcon(icons::Money); @clearButton = GuiButton(this, Alignment(Left+0.5f-6, Bottom-offset-60, Right-25, Bottom-offset-30), locale::CLEAR_GHOSTS); clearButton.color = colors::Red; clearButton.setIcon(icons::Remove); setMarkupTooltip(clearButton, locale::TT_CLEAR_GHOSTS); @autoFill = GuiCheckbox(this, Alignment(Left+8, Bottom-offset-29, Left+0.5f-15, Bottom-offset-3), locale::AUTO_FILL_SUPPORTS); setMarkupTooltip(autoFill, locale::TT_AUTO_FILL_SUPPORTS); @autoBuy = GuiCheckbox(this, Alignment(Left+0.5f-5+3, Bottom-offset-29, Right-25, Bottom-offset-3), locale::AUTO_BUY_SUPPORTS); setMarkupTooltip(autoBuy, locale::TT_AUTO_BUY_SUPPORTS); @allowFillFrom = GuiCheckbox(this, Alignment(Left+8, Bottom-offset-29, Left+0.5f-15, Bottom-offset-3), locale::ALLOW_FILL_FROM_SUPPORTS); setMarkupTooltip(allowFillFrom, locale::TT_ALLOW_FILL_FROM_SUPPORTS); update(); } void remove() { @overlay = null; BaseGuiElement::remove(); } void set(Object@ obj) { @leader = obj; update(); } SupportClass@ getHoveredClass(vec2i absPos) { for(uint i = 0, cnt = classes.length; i < cnt; ++i) { if(classes[i].absolutePosition.isWithin(absPos)) return classes[i]; } return null; } void update() { groupData.syncFrom(leader.getSupportGroups()); //Remove old buttons uint newCnt = groupData.length; uint oldCnt = classes.length; for(uint i = newCnt; i < oldCnt; ++i) { classes[i].remove(); @classes[i] = null; } //Update current buttons classes.length = newCnt; int y = SUPPORT_SPACING; for(uint i = 0; i < newCnt; ++i) { if(classes[i] is null) @classes[i] = SupportClass(this); SupportClass@ cls = classes[i]; cls.set(groupData[i]); cls.position = vec2i(0, y); y += SUPPORT_HEIGHT + SUPPORT_SPACING; } //Update action button position if(y + 36 > listPanel.size.height) y = listPanel.rect.botRight.y + 14; else y += 55; addButton.position = vec2i((GROUP_WIDTH - addButton.size.width) / 2, y); //Update group size uint size = 0; Ship@ leaderShip = cast(leader); if(leaderShip !is null) size = leaderShip.blueprint.design.size; int supUsed = leader.SupplyUsed; int supCap = leader.SupplyCapacity; groupSize.text = toString(supUsed) + " / " + toString(supCap); if(supUsed >= supCap) capBG.color = colors::Red; else if(float(supUsed) >= float(supCap) * 0.9f) capBG.color = Color(0xff8000ff); else capBG.color = colors::White; addButton.disabled = supUsed >= supCap; //Update controls autoFill.visible = !leader.isPlanet && !leader.isOrbital; allowFillFrom.visible = !autoFill.visible; autoBuy.visible = true; rebuyButton.visible = !leader.isPlanet; clearButton.visible = !leader.isPlanet; autoBuy.checked = leader.autoBuySupports; if(autoFill.visible) { autoFill.checked = leader.autoFillSupports; int cost = 0; for(uint i = 0; i < newCnt; ++i) { auto@ dat = groupData[i]; if(dat.ghost > 0) cost += getBuildCost(dat.dsg) * dat.ghost; } rebuyButton.disabled = cost == 0 || !playerEmpire.canPay(cost); clearButton.disabled = cost == 0; rebuyButton.text = format(locale::REBUY_GHOSTS, formatMoney(cost)); } else { allowFillFrom.checked = leader.allowFillFrom; } } void draw() { uint flags = SF_Normal; recti pos = AbsolutePosition; if(!listPanel.vert.visible) pos.botRight.x -= 20; skin.draw(SS_GroupPanel, flags, pos); BaseGuiElement::draw(); } void animate(Alignment@ pos) { @targPos = pos; @alignment = null; recti endPos = targPos.resolve(parent.size); rect = endPos + vec2i(0, endPos.height); animate_time(this, endPos, ANIM_TIME); animating = true; if(overlay.secondary !is null) overlay.secondary.visible = false; } void animateClose() { if(parent is null || overlay is null) return; @alignment = null; recti endPos = rect + vec2i(0, rect.height); animate_time(this, endPos, ANIM_TIME); animating = true; closing = true; if(overlay.secondary !is null) overlay.secondary.visible = false; } bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) override { if(event.type == MET_Button_Down) { if(event.button == 1) { return true; } } else if(event.type == MET_Button_Up) { if(event.button == 1) { overlay.close(this); return true; } } return BaseGuiElement::onMouseEvent(event, source); } bool onGuiEvent(const GuiEvent& evt) { if(evt.type == GUI_Clicked) { if(evt.caller is addButton) { CreateSupportDialog dlg(overlay, leader, null, 10); addDialog(D_SupportAmount, dlg); return true; } if(evt.caller is clearButton) { leader.clearAllGhosts(); overlay.pingUpdate(); return true; } if(evt.caller is rebuyButton) { leader.rebuildAllGhosts(); overlay.pingUpdate(); return true; } } else if(evt.type == GUI_Changed) { if(evt.caller is autoFill) { leader.autoFillSupports = autoFill.checked; return true; } if(evt.caller is autoBuy) { leader.autoBuySupports = autoBuy.checked; return true; } if(evt.caller is allowFillFrom) { leader.allowFillFrom = allowFillFrom.checked; return true; } } else if(evt.type == GUI_Animation_Complete) { animating = false; if(closing) { if(overlay !is null) overlay.remove(); } else { @alignment = targPos; if(overlay.secondary !is null) overlay.secondary.visible = true; } return true; } return BaseGuiElement::onGuiEvent(evt); } }; class SupportClass : BaseGuiElement { GroupDisplay@ disp; GroupData@ dat; bool selected = false; uint leaveAmount = 0; GuiButton@ addButton; GuiButton@ removeButton; SupportClass(GroupDisplay@ d) { @disp = d; super(disp.listPanel, recti(0, 0, SUPPORT_WIDTH, SUPPORT_HEIGHT)); @addButton = GuiButton(this, Alignment(Right-40, Top, Right, Top+0.5f)); addButton.tooltip = locale::CREATE_SUPPORT_SHIPS; addButton.color = colors::Green; addButton.visible = false; addButton.setIcon(icons::Add); @removeButton = GuiButton(this, Alignment(Right-40, Top+0.5f, Right, Bottom)); removeButton.tooltip = locale::SCUTTLE_SUPPORT_SHIPS; removeButton.color = colors::Red; removeButton.setIcon(icons::Minus); removeButton.visible = false; } void remove() { @disp = null; BaseGuiElement::remove(); } void set(GroupData@ data) { @dat = data; } void drawAt(const vec2i& absPos, bool dragging) { recti pos = recti_area(absPos, size); uint flags = SF_Normal; if(disp !is null && disp.overlay.clsHover is this) flags |= SF_Hovered; if(selected) flags |= SF_Active; uint tot = dat.totalSize; uint amount = dat.amount; uint ghost = dat.ghost; uint ordered = dat.ordered; if(dragging) { tot = dat.totalSize - leaveAmount; //Calculate proportions to take uint lv = leaveAmount; uint take = min(ordered, lv); lv -= take; ordered -= take; take = min(ghost, lv); lv -= take; ghost -= take; take = min(amount, lv); lv -= take; amount -= take; } else if(disp.overlay.dragging && selected) { tot = leaveAmount; //Calculate proportions to leave uint lv = leaveAmount; uint take = min(ghost, lv); lv -= take; ghost = take; take = min(ordered, lv); lv -= take; ordered = take; take = min(amount, lv); lv -= take; amount= take; } skin.draw(SS_PatternBox, flags, pos, dat.dsg.color); if(disp !is null && disp.overlay.clsHover is this && !dragging) skin.draw(SS_SubtleGlow, SF_Normal, pos, dat.dsg.color); Color col; col = dat.dsg.color; col.a = 0x80; dat.dsg.icon.draw(recti_area(pos.topLeft+vec2i(4,0), vec2i(pos.height, pos.height)), col); const Font@ normal = skin.getFont(FT_Normal); normal.draw(pos=recti_area(pos.topLeft + vec2i(pos.height+6, 6), vec2i(pos.width-pos.height-12, 22)), text=formatShipName(dat.dsg), stroke=colors::Black); const Font@ bold = skin.getFont(FT_Bold); bold.draw(pos=recti_area(pos.topLeft + vec2i(pos.height+12, 28), vec2i(60, 22)), text=toString(amount)+"x", stroke=colors::Black); if(ordered > 0) normal.draw(pos=recti_area(pos.topLeft + vec2i(pos.height+82, 28), vec2i(60, 22)), text="(+"+toString(ordered)+"x)", stroke=colors::Black, color=Color(0x80ff80ff)); if(ghost > 0) normal.draw(pos=recti_area(pos.topLeft + vec2i(pos.height+132, 28), vec2i(60, 22)), text="(-"+toString(ghost)+"x)", stroke=colors::Black, color=Color(0xff8080ff)); } bool onGuiEvent(const GuiEvent& evt) { if(evt.type == GUI_Clicked) { if(evt.caller is addButton) { GuiContextMenu menu(mousePos); uint canBuild = uint(double(disp.leader.SupplyAvailable) / dat.dsg.size); uint max = canBuild + dat.ghost; if(max > 0) menu.addOption(OrderSupports(disp.leader, dat.dsg, max)); if(dat.ghost > 0 && dat.ghost < max) menu.addOption(OrderSupports(disp.leader, dat.dsg, dat.ghost)); uint maxMoney = floor(double(playerEmpire.RemainingBudget) / double(getBuildCost(dat.dsg))); if(maxMoney < max && maxMoney != dat.ghost) menu.addOption(OrderSupports(disp.leader, dat.dsg, maxMoney)); if(max > 10 && maxMoney > 10) menu.addOption(OrderSupports(disp.leader, dat.dsg, 10)); if(max > 1 && maxMoney > 1) menu.addOption(OrderSupports(disp.leader, dat.dsg, 1)); uint orderAmt = 10; if(dat.ghost != 0) orderAmt = dat.ghost; if(orderAmt > max) orderAmt = max; menu.addOption(CustomSupportOrder(disp.overlay, disp.leader, dat.dsg, orderAmt)); menu.finalize(); return true; } else if(evt.caller is removeButton) { InputDialog@ dialog = InputDialog(ScuttleSupports(this), disp.overlay); dialog.addTitle(locale::SCUTTLE_SUPPORT_SHIPS); dialog.accept.text = locale::REMOVE; uint defAmount = 0; if(dat.ghost > 0) defAmount = dat.ghost; if(dat.ordered > 0) defAmount = dat.ordered; dialog.addSpinboxInput(locale::AMOUNT, defAmount, 10.0, 1.0, dat.totalSize, 0); addDialog(D_SupportAmount, dialog); dialog.focusInput(); } } return BaseGuiElement::onGuiEvent(evt); } void draw() { drawAt(AbsolutePosition.topLeft, false); bool hover = disp !is null && !disp.overlay.dragging && disp.overlay.clsHover is this; addButton.visible = hover && (disp.leader.SupplyAvailable >= uint(dat.dsg.size) || dat.ghost > 0); removeButton.visible = hover; BaseGuiElement::draw(); } }; class OrderSupports : GuiContextOption { Object@ forObject; const Design@ dsg; uint amount; int build = 0; int maintain = 0; OrderSupports(Object@ forObject, const Design@ dsg, uint amount) { double labor = 0; getBuildCost(dsg, this.build, maintain, labor, amount); this.amount = amount; @this.forObject = forObject; @this.dsg = dsg; string text = format(locale::ORDER_SUPPORT_COUNT, toString(amount), dsg.name); text += " ("+formatMoney(this.build, maintain)+")"; super(text); } void call(GuiContextMenu@ menu) { if(dsg !is null && amount > 0) forObject.orderSupports(dsg, amount); } void draw(GuiListbox@ ele, uint flags, const recti& absPos) override { if(!playerEmpire.canPay(this.build)) drawRectangle(absPos, Color(0xff900040)); GuiContextOption::draw(ele, flags, absPos); } }; class CustomSupportOrder : GuiContextOption { SupportOverlay@ overlay; Object@ forObject; const Design@ dsg; uint amount; CustomSupportOrder(SupportOverlay@ overlay, Object@ forObject, const Design@ dsg, uint amount) { this.amount = amount; @this.forObject = forObject; @this.dsg = dsg; @this.overlay = overlay; super(locale::ORDER_SUPPORT_CUSTOM); } void call(GuiContextMenu@ menu) { CreateSupportDialog dlg(overlay, forObject, dsg, amount); addDialog(D_SupportAmount, dlg); } }; enum Dialogs { D_SupportAmount, }; class ScuttleSupports : InputDialogCallback { Object@ at; const Design@ dsg; ScuttleSupports(SupportClass@ cls) { @at = cls.disp.leader; @dsg = cls.dat.dsg; } void inputCallback(InputDialog@ dialog, bool accepted) { if(accepted) { double amt = dialog.getSpinboxInput(0); at.scuttleSupports(dsg, round(amt)); } } }; class CreateSupportDialog : Dialog { array designs; const Design@ dsg; Object@ forObject; GuiText@ designLabel; GuiListbox@ designList; GuiText@ amountLabel; GuiSpinbox@ amountBox; GuiText@ costLabel; GuiText@ costText; GuiButton@ accept; GuiButton@ cancel; CreateSupportDialog(SupportOverlay@ overlay, Object@ obj, const Design@ design = null, int defaultNum = 10) { @dsg = design; @forObject = obj; super(overlay); addTitle(locale::ADD_SUPPORTS); width = 700; int y = 32; //Show list of designs if(dsg is null) { @designLabel = GuiText(bg, recti(12, y, width / 3 - 6, y+22), locale::ORDER_SUPPORT_DESIGN); designLabel.font = FT_Bold; @designList = GuiListbox(bg, recti(width / 3 + 6, y, width - 12, y+222)); designList.required = true; designList.itemHeight = 40; designList.tabIndex = 0; designList.style = SS_PlainBox; { ReadLock lck(playerEmpire.designMutex); uint cnt = playerEmpire.designCount; designs.reserve(cnt); designs.length = 0; for(uint i = 0; i < cnt; ++i) { const Design@ other = playerEmpire.designs[i]; if(other.obsolete || other.newest() !is other) continue; if(other.hasTag(ST_Support) || (obj.canHaveSatellites && other.hasTag(ST_Satellite))) { designList.addItem(GuiListText(formatShipName(other), other.icon * other.color)); designs.insertLast(other); } } } y += 232; height += 232; } //Show spinbox @amountLabel = GuiText(bg, recti(12, y, width / 3 - 6, y+22), locale::ORDER_SUPPORT_AMOUNT); amountLabel.font = FT_Bold; @amountBox = GuiSpinbox(bg, recti(width / 3 + 6, y, width - 12, y+22), double(defaultNum)); amountBox.tabIndex = 1; amountBox.min = 0; amountBox.decimals = 0; amountBox.step = 10.0; y += 32; height += 28; //Show cost @costLabel = GuiText(bg, recti(12, y, width / 3 - 6, y+22), locale::ORDER_SUPPORT_COST); costLabel.font = FT_Bold; @costText = GuiText(bg, recti(width / 3 + 6, y, width - 12, y+22)); costText.font = FT_Medium; height += 32; @accept = GuiButton(bg, recti()); accept.text = locale::BUILD; accept.tabIndex = 2; @accept.callback = this; @cancel = GuiButton(bg, recti()); cancel.text = locale::CANCEL; cancel.tabIndex = 3; @cancel.callback = this; alignAcceptButtons(accept, cancel); updateCost(); } const Design@ get_currentDesign() { if(dsg is null && designList.selected != -1) return designs[designList.selected]; return dsg; } void updateCost() { const Design@ cur = currentDesign; if(cur !is null) { double canBuild = double(forObject.SupplyAvailable) / cur.size; canBuild += forObject.getGhostCount(cur); amountBox.maximum = floor(canBuild); int build = 0, maintain = 0; double time = 0.0; getBuildCost(cur, build, maintain, time, amountBox.value); costText.text = formatMoney(build)+" / "+formatMoney(maintain); if(playerEmpire.RemainingBudget >= build) { costText.color = Color(0xffffffff); accept.disabled = false; } else if(playerEmpire.canPay(build)) { costText.color = Color(0xfdff00ff); accept.disabled = false; } else { costText.color = Color(0xff0000ff); accept.disabled = true; } } else costText.text = ""; } void close() { close(false); } void close(bool accepted) { if(accepted) { @dsg = currentDesign; int amount = ceil(amountBox.value); if(dsg !is null && amount > 0) forObject.orderSupports(dsg, amount); } Dialog::close(); } void confirmDialog() { close(true); } bool onGuiEvent(const GuiEvent& event) { if(Closed) return false; if(event.type == GUI_Clicked && (event.caller is accept || event.caller is cancel)) { close(event.caller is accept); return true; } else if(event.type == GUI_Changed && (event.caller is amountBox || event.caller is designList)) { updateCost(); return true; } else if(event.type == GUI_Confirmed) { close(true); return true; } return Dialog::onGuiEvent(event); } };