Files
2018-07-17 14:15:37 +02:00

1007 lines
25 KiB
ActionScript

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<Ship>(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<const Design@> 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);
}
};