Files
starruler-linux/scripts/gui/overlays/PlanetInfoBar.as
T
2018-07-17 14:15:37 +02:00

558 lines
15 KiB
ActionScript

import overlays.InfoBar;
import elements.GuiResources;
import elements.GuiIconGrid;
import elements.GuiSprite;
import elements.GuiStatusBox;
import elements.GuiText;
import elements.GuiMarkupText;
import elements.Gui3DObject;
import elements.GuiSkinElement;
import elements.MarkupTooltip;
import constructible;
import resources;
import obj_selection;
import planet_levels;
import util.constructible_view;
import util.formatting;
import icons;
import targeting.ObjectTarget;
import statuses;
from elements.GuiResources import LEVEL_REQ;
from overlays.ContextMenu import openContextMenu;
from overlays.PlanetOverlay import PlanetOverlay;
from tabs.GalaxyTab import zoomTabTo, openOverlay, toggleSupportOverlay;
//Temporary to avoid allocations
Resources available;
Color boxColors(0xffffffff);
class PlanetInfoBar : InfoBar {
Constructible[] cons;
array<Status> statuses;
Planet@ pl;
PlanetOverlay@ overlay;
Gui3DObject@ objView;
GuiSkinElement@ nameBox;
GuiText@ name;
GuiSkinElement@ levelBox;
GuiText@ level;
GuiSkinElement@ requireBox;
GuiText@ reqLabel;
GuiText@ reqTimer;
GuiMarkupText@ popReq;
GuiResourceReqGrid@ reqDisplay;
GuiSkinElement@ resourceBox;
GuiResourceGrid@ resources;
GuiMarkupText@ resourceDesc;
GuiSkinElement@ popBox;
GuiSprite@ popIcon;
GuiText@ popValue;
GuiSkinElement@ consBox;
GuiSkinElement@ statusBox;
GuiMarkupText@ statusText;
GuiSkinElement@ actionBG;
ActionBar@ actions;
array<GuiStatusBox@> statusIcons;
GuiSkinElement@ statusList;
PlanetInfoBar(IGuiElement@ parent) {
super(parent);
@alignment = Alignment(Left, Bottom-228, Left+456, Bottom);
@actionBG = GuiSkinElement(this, recti_area(vec2i(335, 172), vec2i(100, 60)), SS_Panel);
actionBG.noClip = true;
@objView = Gui3DObject(this, Alignment(
Left-1.f, Top, Right, Bottom+3.f));
objView.internalRotation = quaterniond_fromAxisAngle(vec3d(0.0, 0.0, 1.0), -0.25*pi);
@actions = ActionBar(this, vec2i(385, 172));
actions.noClip = true;
int y = 40;
@nameBox = GuiSkinElement(this, Alignment(Left+5, Top+y, Left+0.4f-4, Top+y+34), SS_PlainOverlay);
nameBox.color = boxColors;
@name = GuiText(nameBox, Alignment().padded(10, 0));
name.font = FT_Medium;
name.stroke = colors::Black;
y += 40;
@levelBox = GuiSkinElement(this, Alignment(Left+5, Top+y, Left+0.22f-4, Top+y+34), SS_PlainOverlay);
levelBox.color = boxColors;
@level = GuiText(levelBox, Alignment().padded(8, 0));
level.stroke = colors::Black;
level.font = FT_Medium;
@requireBox = GuiSkinElement(this, Alignment(Left+0.22f+4, Top+y, Left+0.55f, Top+y+34), SS_PlainOverlay);
requireBox.color = boxColors;
@reqLabel = GuiText(requireBox, Alignment(Left-4, Top-6, Left+96, Top+6), locale::REQUIRED_RESOURCES);
reqLabel.noClip = true;
reqLabel.font = FT_Small;
reqLabel.stroke = colors::Black;
@reqTimer = GuiText(requireBox, Alignment(Right-96, Top-6, Right+4, Top+6));
reqTimer.color = Color(0xff0000fff);
reqTimer.noClip = true;
reqTimer.font = FT_Small;
reqTimer.horizAlign = 1.0;
reqTimer.visible = false;
reqTimer.stroke = colors::Black;
@reqDisplay = GuiResourceReqGrid(requireBox, Alignment(Left+8, Top+8, Right-8, Bottom));
reqDisplay.spacing.x = 6;
reqDisplay.horizAlign = 0.0;
reqDisplay.vertAlign = 0.0;
@popReq = GuiMarkupText(requireBox, Alignment(Left+4, Top+8, Right-4, Bottom));
popReq.visible = false;
y += 38;
@resourceBox = GuiSkinElement(this, Alignment(), SS_PlainOverlay);
resourceBox.color = boxColors;
@resources = GuiResourceGrid(resourceBox, Alignment(Left+8, Top+5, Right-8, Bottom-2));
resources.spacing.x = 6;
resources.horizAlign = 0.0;
resources.vertAlign = 0.0;
@resourceDesc = GuiMarkupText(resourceBox, Alignment(Left+8, Bottom-42, Right-8, Bottom));
resourceDesc.visible = false;
resourceDesc.defaultColor = Color(0xaaaaaaff);
@statusList = GuiSkinElement(this, Alignment(Left+295, Top+118, Left+295+34, Top+118+70), SS_PlainBox);
statusList.noClip = true;
statusList.visible = false;
@statusBox = GuiSkinElement(this, Alignment(Left+5, Top+153, Left+295, Bottom-2), SS_PlainOverlay);
statusBox.color = boxColors;
@statusText = GuiMarkupText(statusBox, Alignment().padded(2,0));
statusBox.visible = false;
int x = 5;
y += 72;
@popBox = GuiSkinElement(this, Alignment(Left, Bottom-32, Left+x+94, Bottom), SS_HorizBar);
popBox.color = Color(0x80ffffff);
popBox.padding = recti(0,4,4,4);
@popIcon = GuiSprite(popBox, Alignment(Left-16, Top-8, Width=50, Height=50));
popIcon.noClip = true;
popIcon.desc = icons::Population;
@popValue = GuiText(popBox, Alignment(Left+32, Top, Right-8, Bottom));
popValue.horizAlign = 0.5;
popValue.stroke = colors::Black;
MarkupTooltip popTT(locale::PLANET_POPULATION_TIP);
@popIcon.tooltipObject = popTT;
@popValue.tooltipObject = popTT;
@consBox = GuiSkinElement(this, Alignment(Left+112, Bottom-33, Left+336, Bottom-2), SS_PlainOverlay);
consBox.color = boxColors;
GuiSprite consIcon(consBox, Alignment(Left-16, Top-10, Left+35, Bottom+10), icons::Labor);
y += 30;
updateAbsolutePosition();
}
void updateActions() {
actions.clear();
if(pl.owner is playerEmpire) {
actions.add(ManageAction());
actions.addBasic(pl);
actions.addFTL(pl);
if(pl.population > 1.0 && playerEmpire.ForbidColonization == 0)
actions.add(ColonizeAction());
actions.addAbilities(pl);
actions.addEmpireAbilities(playerEmpire, pl);
}
else {
if((pl.owner is null || !pl.owner.valid) && !pl.quarantined && playerEmpire.ForbidColonization == 0)
actions.add(ColonizeThisAction());
}
actions.init(pl);
actionBG.size = vec2i(actions.size.width + 50, 60);
actionBG.visible = actions.visible;
}
bool compatible(Object@ obj) override {
return obj.isPlanet;
}
Object@ get() override {
return pl;
}
void remove() override {
if(overlay !is null)
overlay.remove();
InfoBar::remove();
}
void set(Object@ obj) override {
@pl = cast<Planet>(obj);
@objView.object = obj;
@resources.drawFrom = obj;
updateTimer = 0.0;
modID = 0;
updateActions();
}
bool displays(Object@ obj) override {
if(obj is pl)
return true;
return false;
}
bool get_showingManage() override {
return overlay !is null;
}
bool showManage(Object@ obj) override {
if(overlay !is null)
overlay.remove();
@overlay = PlanetOverlay(findTab(), cast<Planet>(obj));
visible = false;
return false;
}
bool onGuiEvent(const GuiEvent& evt) override {
switch(evt.type) {
case GUI_Clicked:
if(evt.caller is objView) {
switch(evt.value) {
case OA_LeftClick:
selectObject(pl, shiftKey);
return true;
case OA_RightClick:
if(selectedObject is null)
openContextMenu(pl, pl);
else
openContextMenu(pl);
return true;
case OA_MiddleClick:
zoomTabTo(pl);
return true;
case OA_DoubleClick:
showManage(pl);
return true;
}
}
break;
}
return InfoBar::onGuiEvent(evt);
}
bool onMouseEvent(const MouseEvent& evt, IGuiElement@ source) override {
if(source !is objView) {
switch(evt.type) {
case MET_Button_Up:
case MET_Button_Down:
return objView.onMouseEvent(evt, objView);
}
}
return BaseGuiElement::onMouseEvent(evt, source);
}
IGuiElement@ elementFromPosition(const vec2i& pos) override {
IGuiElement@ elem = BaseGuiElement::elementFromPosition(pos);
if(elem is this)
return null;
if(elem is objView) {
int height = AbsolutePosition.size.height;
vec2i origin(AbsolutePosition.topLeft.x, AbsolutePosition.botRight.y);
origin.y += height;
if(pos.distanceTo(origin) > height * 2)
return null;
}
return elem;
}
double updateTimer = 1.0;
uint modID = 0;
void update(double time) override {
if(overlay !is null) {
if(overlay.parent is null) {
@overlay = null;
visible = true;
}
else
overlay.update(time);
}
if(actions !is null)
actions.update(time);
updateTimer -= time;
if(updateTimer <= 0) {
updateTimer = randomd(0.1,0.9);
Empire@ owner = pl.visibleOwner;
bool owned = owner is playerEmpire;
bool colonized = owner !is null && owner.valid;
updateActions();
//Update name
name.text = pl.name;
if(owner !is null)
name.color = owner.color;
//Update level
uint lv = pl.level;
level.text = locale::LEVEL+" "+lv;
//Update resource display
resources.resources.syncFrom(pl.getAllResources());
if(!colonized)
resources.resources.length = min(resources.resources.length, 1);
else
resources.resources.sortDesc();
resources.setSingleMode(align=0.0);
auto@ primary = getResource(pl.primaryResourceType);
if(resources.resources.length == 1 || (primary !is null && primary.limitlessLevel)) {
resources.alignment.bottom.pixels = 40;
auto@ type = resources.resources[0].type;
string desc = format(type.blurb, toString(pl.level, 0));
if(desc.length == 0)
desc = format(type.description, toString(pl.level, 0));
if(desc.length == 0) {
if(type.level > 0) {
desc = format(locale::RESOURCE_TIER_DESC,
toString(type.level),
type.level < LEVEL_REQ.length ? getSpriteDesc(LEVEL_REQ[type.level]) : "ResourceClassIcons::5");
}
}
resourceDesc.text = desc;
resourceDesc.visible = true;
}
else {
resources.alignment.bottom.pixels = 2;
resourceDesc.visible = false;
}
//Update statuses
if(pl.statusEffectCount > 0)
statuses.syncFrom(pl.getStatusEffects());
else
statuses.length = 0;
//Update condition display
levelBox.visible = colonized;
popBox.visible = colonized && owner.HasPopulation != 0;
if(colonized || statuses.length == 0) {
if(statuses.length != 0) {
uint prevCnt = statusIcons.length, cnt = statuses.length;
for(uint i = cnt; i < prevCnt; ++i)
statusIcons[i].remove();
statusIcons.length = cnt;
for(uint i = 0; i < cnt; ++i) {
auto@ icon = statusIcons[i];
if(icon is null) {
@icon = GuiStatusBox(statusList, recti_area(2, 2+32*i, 30, 30));
@statusIcons[i] = icon;
}
@icon.fromObject = pl;
icon.update(statuses[i]);
}
statusList.visible = true;
}
else {
statusList.visible = false;
}
resourceBox.alignment = Alignment(Left+5, Top+118, Left+295, Top+118+70);
resourceBox.updateAbsolutePosition();
statusBox.visible = false;
resourceBox.visible = resources.length != 0;
}
else {
resourceBox.alignment = Alignment(Left+5, Top+80, Left+295, Top+80+70);
resourceBox.updateAbsolutePosition();
statusList.visible = false;
statusBox.visible = true;
resourceBox.visible = resources.length != 0;
auto@ status = statuses[0].type;
for(uint i = 0, cnt = statuses.length; i < cnt; ++i) {
if(statuses[i].type.conditionFrequency > 0) {
@status = statuses[i].type;
break;
}
}
statusText.text = format("[b][img=$3;22x22/] [color=$4][vspace=3]$1[/vspace][/color][/b]"
"\n[offset=6][vspace=4][color=#aaa]$2[/color][/vspace][/offset]",
status.name, status.description,
getSpriteDesc(status.icon), toString(status.color));
}
//Update population display
if(colonized) {
popValue.text = standardize(pl.population, true) + "/" + standardize(pl.maxPopulation, true);
popValue.color = Color(0xffffffff);
}
else {
popValue.text = "-";
}
//Update construction
if(owned)
cons.syncFrom(pl.getConstructionQueue(1));
else
cons.length = 0;
consBox.visible = cons.length != 0;
//Update level requirements
double decay = pl.decayTime;
uint maxLevel = getMaxPlanetLevel(pl);
if(!owned || (lv == maxLevel && decay <= 0) || (lv >= uint(pl.maxLevel) && decay <= 0)) {
requireBox.visible = false;
}
else if(decay > 0) {
requireBox.visible = true;
reqTimer.visible = true;
reqTimer.text = formatTime(decay);
reqLabel.color = Color(0xff0000ff);
reqLabel.tooltip = format(locale::REQ_STOP_DECAY, toString(lv-1), formatTime(decay));
reqLabel.text = format(locale::REQUIRED_RESOURCES, toString(lv));
uint newMod = pl.resourceModID;
if(modID != newMod) {
available.clear();
receive(pl.getResourceAmounts(), available);
const PlanetLevel@ lvl = getPlanetLevel(pl, lv);
reqDisplay.set(lvl.reqs, available);
reqDisplay.visible = true;
reqLabel.visible = true;
popReq.visible = reqDisplay.length == 0;
if(popReq.visible)
popReq.text = format(locale::POP_REQ, standardize(lvl.requiredPop, true));
reqLabel.tooltip = format(locale::REQ_FOR_LEVEL, toString(lv + 1));
modID = newMod;
}
}
else {
requireBox.visible = pl.nativeResourceDestination[0] is null || !pl.nativeResourceUsable[0];
reqTimer.visible = false;
reqLabel.color = Color(0xffffffff);
uint newMod = pl.resourceModID;
if(modID != newMod) {
available.clear();
receive(pl.getResourceAmounts(), available);
const PlanetLevel@ lvl = getPlanetLevel(pl, lv + 1);
reqDisplay.set(lvl.reqs, available);
reqDisplay.visible = true;
reqLabel.visible = true;
reqLabel.text = format(locale::REQUIRED_RESOURCES, toString(lv+1));
popReq.visible = reqDisplay.length == 0;
if(popReq.visible)
popReq.text = format(locale::POP_REQ, standardize(lvl.requiredPop, true));
reqLabel.tooltip = format(locale::REQ_FOR_LEVEL, toString(lv + 1));
modID = newMod;
}
}
}
InfoBar::update(time);
}
void draw() override {
BaseGuiElement::draw();
const Font@ ft = skin.getFont(FT_Normal);
//Construction display
if(cons.length != 0) {
recti pos = consBox.AbsolutePosition.padded(4);
drawConstructible(cons[0], consBox.AbsolutePosition, isBox=true);
ft.draw(pos=pos, text=formatTime(cons[0].getETA(pl)), horizAlign=1.0, vertAlign=1.0, stroke=colors::Black);
if(ft.bold !is null)
@ft = ft.bold;
ft.draw(pos=pos, text=cons[0].name, horizAlign=0.0, vertAlign=0.0, stroke=colors::Black);
}
}
};
class ManageAction : BarAction {
void init() override {
icon = icons::Manage;
tooltip = locale::TT_MANAGE_PLANET;
}
void call() override {
selectObject(obj);
openOverlay(obj);
}
};
class ColonizeAction : BarAction {
void init() override {
icon = icons::Colonize;
tooltip = locale::TT_COLONIZE;
}
void call() override {
targetObject(ColonizeTarget(obj));
}
};
class ColonizeTarget : ObjectTargeting {
Object@ obj;
ColonizeTarget(Object@ obj) {
@this.obj = obj;
}
void call(Object@ target) {
obj.colonize(target);
}
string message(Object@ obj, bool valid) {
return locale::COLONIZE;
}
bool valid(Object@ obj) {
if(!obj.isPlanet)
return false;
if(obj.quarantined)
return false;
return obj.owner is null || !obj.owner.valid;
}
};
class ColonizeThisAction : BarAction {
void init() override {
icon = icons::ColonizeThis;
tooltip = locale::TT_COLONIZE_THIS;
}
void call() override {
playerEmpire.autoColonize(obj);
}
};
InfoBar@ makePlanetInfoBar(IGuiElement@ parent, Object@ obj) {
PlanetInfoBar bar(parent);
bar.set(obj);
return bar;
}