Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,219 @@
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiOverlay;
|
||||
import elements.GuiBackgroundPanel;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiText;
|
||||
import elements.GuiListbox;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiPanel;
|
||||
import elements.GuiSprite;
|
||||
from dialogs.MessageDialog import message;
|
||||
import anomalies;
|
||||
import targeting.ObjectTarget;
|
||||
|
||||
class Option : GuiButton {
|
||||
const AnomalyOption@ option;
|
||||
GuiSprite@ icon;
|
||||
GuiMarkupText@ content;
|
||||
uint index;
|
||||
|
||||
Option(IGuiElement@ parent) {
|
||||
super(parent, recti());
|
||||
@icon = GuiSprite(this, Alignment(Left+4, Top+0.5f-30, Left+64, Top+0.5f+30));
|
||||
@content = GuiMarkupText(this, recti(68, 4, 100, 100));
|
||||
style = SS_ChoiceBox;
|
||||
}
|
||||
|
||||
int set(const AnomalyOption@ option, int y, uint index) {
|
||||
@this.option = option;
|
||||
this.index = index;
|
||||
position = vec2i(4, y);
|
||||
size = vec2i(parent.size.width-8, size.height);
|
||||
content.size = vec2i(size.width - 72, content.size.height);
|
||||
content.text = option.desc;
|
||||
icon.desc = option.icon;
|
||||
updateAbsolutePosition();
|
||||
size = vec2i(size.width, max(content.size.height + 12, 68));
|
||||
return size.height;
|
||||
}
|
||||
};
|
||||
|
||||
class AnomalyOverlay : GuiOverlay {
|
||||
Anomaly@ anomaly;
|
||||
GuiBackgroundPanel@ bg;
|
||||
GuiBackgroundPanel@ choices;
|
||||
|
||||
GuiPanel@ descPanel;
|
||||
GuiMarkupText@ description;
|
||||
GuiText@ progressLabel;
|
||||
Gui3DObject@ model;
|
||||
GuiProgressbar@ bar;
|
||||
|
||||
GuiPanel@ choicePanel;
|
||||
array<Option@> options;
|
||||
|
||||
AnomalyOverlay(IGuiElement@ parent, Anomaly@ obj) {
|
||||
@anomaly = obj;
|
||||
|
||||
super(parent);
|
||||
|
||||
uint left = 500;
|
||||
uint right = 500;
|
||||
uint total = left + 12 + right;
|
||||
|
||||
@bg = GuiBackgroundPanel(this, Alignment(Left+0.5f-total/2, Top+0.5f-200, Left+0.5f-total/2+left, Top+0.5f+200));
|
||||
bg.title = obj.name;
|
||||
bg.titleColor = Color(0x00ff00ff);
|
||||
|
||||
@choices = GuiBackgroundPanel(this, Alignment(Left+0.5f-total/2+left+12, Top+0.5f-300, Left+0.5f+total/2, Top+0.5f+300));
|
||||
choices.title = locale::ANOMALY_CHOICES;
|
||||
choices.titleColor = Color(0xff8000ff);
|
||||
|
||||
@progressLabel = GuiText(choices, Alignment(Left+8, Top+0.4f-30, Right-8, Top+0.4f-3), locale::SCAN_PROGRESS);
|
||||
progressLabel.horizAlign = 0.5;
|
||||
progressLabel.vertAlign = 1.0;
|
||||
@bar = GuiProgressbar(choices, Alignment(Left+12, Top+0.4f+3, Right-12, Top+0.4f+30));
|
||||
bar.frontColor = Color(0x6aadcbff);
|
||||
|
||||
@descPanel = GuiPanel(bg, Alignment(Left+4, Top+36, Left+left-4, Bottom-8));
|
||||
|
||||
@description = GuiMarkupText(descPanel, recti_area(4, 0, left-8, 100), obj.narrative);
|
||||
description.fitWidth = true;
|
||||
@model = Gui3DObject(descPanel, recti_area(7, 250, 100, 100), obj);
|
||||
|
||||
@choicePanel = GuiPanel(choices, Alignment(Left, Top+38, Right, Bottom));
|
||||
choicePanel.visible = true;
|
||||
|
||||
closeSelf = false;
|
||||
updateAbsolutePosition();
|
||||
update();
|
||||
bringToFront();
|
||||
|
||||
overlays.insertLast(this);
|
||||
}
|
||||
|
||||
void remove() {
|
||||
overlays.remove(this);
|
||||
GuiOverlay::remove();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
{
|
||||
Option@ opt = cast<Option>(evt.caller);
|
||||
if(opt !is null) {
|
||||
if(opt.option.targets.length == 0) {
|
||||
anomaly.choose(opt.index);
|
||||
}
|
||||
else {
|
||||
targetObject(AnomalyTargeting(anomaly, opt.option, opt.index, 0));
|
||||
GuiOverlay::close();
|
||||
}
|
||||
timer = 0.05f;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return GuiOverlay::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
float timer = 0.f;
|
||||
void update() {
|
||||
if(!anomaly.valid) {
|
||||
GuiOverlay::close();
|
||||
return;
|
||||
}
|
||||
|
||||
timer -= frameLength;
|
||||
if(timer <= 0.f) {
|
||||
float progress = anomaly.progress;
|
||||
if(progress >= 1.f) {
|
||||
description.text = anomaly.narrative;
|
||||
|
||||
const AnomalyType@ type = getAnomalyType(anomaly.anomalyType);
|
||||
uint optCount = anomaly.optionCount;
|
||||
|
||||
choicePanel.visible = true;
|
||||
progressLabel.visible = false;
|
||||
bar.visible = false;
|
||||
|
||||
uint oldCnt = options.length;
|
||||
for(uint i = optCount; i < oldCnt; ++i)
|
||||
options[i].remove();
|
||||
options.length = optCount;
|
||||
int y = 0;
|
||||
for(uint i = 0; i < optCount; ++i) {
|
||||
AnomalyOption@ option = type.options[anomaly.option[i]];
|
||||
|
||||
if(options[i] is null)
|
||||
@options[i] = Option(choicePanel);
|
||||
|
||||
y += options[i].set(option, y, i) + 8;
|
||||
}
|
||||
}
|
||||
else {
|
||||
choicePanel.visible = false;
|
||||
progressLabel.visible = true;
|
||||
bar.visible = true;
|
||||
}
|
||||
|
||||
if(bar.visible) {
|
||||
bar.progress = progress;
|
||||
bar.text = toString(floor(progress * 100.0),0) + "%";
|
||||
}
|
||||
|
||||
@model.drawMode = makeDrawMode(anomaly);
|
||||
updateAbsolutePosition();
|
||||
timer += 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
void updateAbsolutePosition() {
|
||||
GuiOverlay::updateAbsolutePosition();
|
||||
if(model !is null) {
|
||||
int mh = max(128, descPanel.size.height - description.size.height - 42);
|
||||
model.rect = recti_area(8, description.size.height+42, descPanel.size.width-16, mh);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class AnomalyTargeting : ObjectTargeting {
|
||||
Anomaly@ obj;
|
||||
const AnomalyOption@ option;
|
||||
Targets@ targets;
|
||||
uint optIndex;
|
||||
uint targIndex;
|
||||
|
||||
AnomalyTargeting(Anomaly@ obj, const AnomalyOption@ opt, uint optIndex, uint targIndex, Targets@ targets = null) {
|
||||
if(targets is null)
|
||||
@targets = Targets(opt.targets);
|
||||
@this.obj = obj;
|
||||
this.optIndex = optIndex;
|
||||
this.targIndex = targIndex;
|
||||
@this.option = opt;
|
||||
@this.targets = targets;
|
||||
}
|
||||
|
||||
bool valid(Object@ obj) override {
|
||||
@targets[targIndex].obj = obj;
|
||||
targets[targIndex].filled = true;
|
||||
return option.checkTargets(playerEmpire, targets);
|
||||
}
|
||||
|
||||
void call(Object@ targ) override {
|
||||
obj.choose(optIndex, targ);
|
||||
}
|
||||
|
||||
string message(Object@ obj, bool valid) override {
|
||||
return option.blurb;
|
||||
}
|
||||
};
|
||||
|
||||
array<AnomalyOverlay@> overlays;
|
||||
void tick(double time) {
|
||||
for(uint i = 0, cnt = overlays.length; i < cnt; ++i)
|
||||
overlays[i].update();
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import overlays.InfoBar;
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiResources;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.GuiGroupDisplay;
|
||||
import elements.GuiBlueprint;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.GuiMarkupText;
|
||||
import ship_groups;
|
||||
import util.formatting;
|
||||
import artifacts;
|
||||
from obj_selection import isSelected, selectObject, clearSelection, addToSelection;
|
||||
|
||||
class ArtifactInfoBar : InfoBar {
|
||||
Artifact@ obj;
|
||||
Gui3DObject@ objView;
|
||||
|
||||
GuiSkinElement@ nameBox;
|
||||
GuiText@ name;
|
||||
|
||||
GuiSkinElement@ descBox;
|
||||
GuiMarkupText@ desc;
|
||||
|
||||
ActionBar@ actions;
|
||||
|
||||
ArtifactInfoBar(IGuiElement@ parent) {
|
||||
super(parent);
|
||||
@alignment = Alignment(Left, Bottom-228, Left+395, Bottom);
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(
|
||||
Left-1.f, Top, Right, Bottom+3.f));
|
||||
objView.objectRotation = false;
|
||||
objView.internalRotation = quaterniond_fromAxisAngle(vec3d(0.0, 0.0, 1.0), -0.15*pi);
|
||||
|
||||
@actions = ActionBar(this, vec2i(315, 172));
|
||||
actions.noClip = true;
|
||||
|
||||
int y = 104;
|
||||
@nameBox = GuiSkinElement(this, Alignment(Left+4, Top+y, Left+266, Top+y+34), SS_PlainOverlay);
|
||||
@name = GuiText(nameBox, Alignment().padded(8, 0));
|
||||
name.font = FT_Medium;
|
||||
|
||||
y += 40;
|
||||
@descBox = GuiSkinElement(this, Alignment(Left+4, Top+y, Left+266, Top+y+80), SS_PlainOverlay);
|
||||
@desc = GuiMarkupText(descBox, Alignment().padded(3));
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void updateActions() {
|
||||
actions.clear();
|
||||
if(obj.owner is playerEmpire || obj.owner is null || !obj.owner.valid)
|
||||
actions.addAbilities(obj, expanded=true);
|
||||
actions.init(obj);
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) override {
|
||||
return obj.isArtifact;
|
||||
}
|
||||
|
||||
Object@ get() override {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void set(Object@ obj) override {
|
||||
@this.obj = cast<Artifact>(obj);
|
||||
@objView.object = obj;
|
||||
updateTimer = 0.0;
|
||||
updateActions();
|
||||
}
|
||||
|
||||
bool displays(Object@ obj) override {
|
||||
if(obj is this.obj)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool showManage(Object@ obj) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
double updateTimer = 1.0;
|
||||
void update(double time) override {
|
||||
updateTimer -= time;
|
||||
if(updateTimer <= 0) {
|
||||
updateTimer = randomd(0.1,0.9);
|
||||
Empire@ owner = obj.owner;
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(owner !is null)
|
||||
name.color = owner.color;
|
||||
|
||||
auto@ type = getArtifactType(obj.ArtifactType);
|
||||
if(type !is null)
|
||||
desc.text = type.description;
|
||||
|
||||
//Update action bar
|
||||
updateActions();
|
||||
}
|
||||
}
|
||||
|
||||
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 * 1.6)
|
||||
return null;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
if(actions.visible) {
|
||||
recti pos = actions.absolutePosition;
|
||||
skin.draw(SS_Panel, SF_Normal, recti(pos.topLeft - vec2i(80, 0), pos.botRight + vec2i(0, 20)));
|
||||
}
|
||||
InfoBar::draw();
|
||||
}
|
||||
};
|
||||
|
||||
InfoBar@ makeArtifactInfoBar(IGuiElement@ parent, Object@ obj) {
|
||||
ArtifactInfoBar bar(parent);
|
||||
bar.set(obj);
|
||||
return bar;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.Gui3DObject;
|
||||
import util.formatting;
|
||||
import artifacts;
|
||||
import abilities;
|
||||
import icons;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
|
||||
class ArtifactPopup : Popup {
|
||||
GuiText@ name;
|
||||
GuiMarkupText@ description;
|
||||
GuiText@ cost;
|
||||
GuiSprite@ icon;
|
||||
Gui3DObject@ objView;
|
||||
Artifact@ obj;
|
||||
double lastUpdate = -INFINITY;
|
||||
|
||||
ArtifactPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(210, 220);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+4, Right-4, Top+30));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+3+4, Top+30+4, Right-4-4, Top+110-4));
|
||||
|
||||
@description = GuiMarkupText(this, Alignment(Left+8, Top+110, Right-8, Bottom-4));
|
||||
|
||||
@icon = GuiSprite(this, Alignment(Left+8, Bottom-34, Left+35, Bottom-7));
|
||||
|
||||
@cost = GuiText(this, Alignment(Left+35, Bottom-34, Right, Bottom-4));
|
||||
cost.horizAlign = 0.5;
|
||||
cost.font = FT_Bold;
|
||||
cost.color = colors::Energy;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return Obj.isArtifact;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = cast<Artifact>(Obj);
|
||||
@objView.object = Obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
skin.draw(SS_Panel, SF_Normal, bgPos);
|
||||
skin.draw(SS_BG3D, SF_Normal, objView.absolutePosition.padded(-4));
|
||||
skin.draw(SS_SubTitle, SF_Normal, recti_area(bgPos.topLeft+vec2i(2,1), vec2i(bgPos.width-5, 30)));
|
||||
if(cost.visible)
|
||||
skin.draw(SS_SubTitle, SF_Normal, cost.absolutePosition.padded(-31,-3,4,-1), colors::Energy);
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(frameTime - 1.0 < lastUpdate)
|
||||
return;
|
||||
|
||||
lastUpdate = frameTime;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
const ArtifactType@ type = getArtifactType(obj.ArtifactType);
|
||||
if(type !is null)
|
||||
description.text = type.description;
|
||||
else
|
||||
description.text = "--";
|
||||
|
||||
//Update cost
|
||||
array<Ability> abilities;
|
||||
abilities.syncFrom(obj.getAbilities());
|
||||
|
||||
if(abilities.length != 0) {
|
||||
icon.visible = true;
|
||||
cost.visible = true;
|
||||
|
||||
double energyCost = abilities[0].getEnergyCost();
|
||||
|
||||
@abilities[0].emp = playerEmpire;
|
||||
icon.desc = abilities[0].type.icon;
|
||||
cost.text = format("$1 $2", energyCost, locale::RESOURCE_ENERGY);
|
||||
|
||||
cost.visible = energyCost != 0;
|
||||
icon.visible = cost.visible;
|
||||
}
|
||||
else {
|
||||
icon.visible = false;
|
||||
cost.visible = false;
|
||||
}
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,194 @@
|
||||
import overlays.InfoBar;
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiResources;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiText;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.GuiGroupDisplay;
|
||||
import elements.GuiBlueprint;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.GuiResources;
|
||||
import cargo;
|
||||
import resources;
|
||||
import ship_groups;
|
||||
import util.formatting;
|
||||
from obj_selection import isSelected, selectObject, clearSelection, addToSelection;
|
||||
|
||||
class AsteroidInfoBar : InfoBar {
|
||||
Asteroid@ obj;
|
||||
Gui3DObject@ objView;
|
||||
|
||||
GuiSkinElement@ nameBox;
|
||||
GuiText@ name;
|
||||
|
||||
GuiSkinElement@ resourceBox;
|
||||
GuiSprite@ cargoIcon;
|
||||
GuiText@ cargoName;
|
||||
GuiText@ cargoValue;
|
||||
GuiResourceGrid@ resources;
|
||||
|
||||
GuiSkinElement@ stateBox;
|
||||
GuiMarkupText@ state;
|
||||
|
||||
ActionBar@ actions;
|
||||
|
||||
AsteroidInfoBar(IGuiElement@ parent) {
|
||||
super(parent);
|
||||
@alignment = Alignment(Left, Bottom-228, Left+395, Bottom);
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(
|
||||
Left-1.f, Top, Right, Bottom+3.f));
|
||||
objView.objectRotation = false;
|
||||
objView.internalRotation = quaterniond_fromAxisAngle(vec3d(0.0, 0.0, 1.0), -0.15*pi);
|
||||
|
||||
@actions = ActionBar(this, vec2i(305, 172));
|
||||
actions.noClip = true;
|
||||
|
||||
int y = 56;
|
||||
@nameBox = GuiSkinElement(this, Alignment(Left+12, Top+y, Left+156, Top+y+34), SS_PlainOverlay);
|
||||
@name = GuiText(nameBox, Alignment().padded(8, 0));
|
||||
name.font = FT_Medium;
|
||||
|
||||
y += 40;
|
||||
@resourceBox = GuiSkinElement(this, Alignment(Left+12, Top+y, Left+226, Top+y+34), SS_PlainOverlay);
|
||||
@resources = GuiResourceGrid(resourceBox, Alignment(Left+8, Top+5, Right-8, Bottom-5));
|
||||
resources.visible = false;
|
||||
resources.spacing.x = 6;
|
||||
resources.horizAlign = 0.0;
|
||||
@cargoIcon = GuiSprite(resourceBox, Alignment(Left+2, Top+2, Left+31, Bottom-2));
|
||||
@cargoName = GuiText(resourceBox, Alignment(Left+38, Top+4, Right-4, Bottom-4));
|
||||
cargoName.font = FT_Bold;
|
||||
@cargoValue = GuiText(resourceBox, Alignment(Left+38, Top+4, Right-12, Bottom-4));
|
||||
cargoValue.horizAlign = 1.0;
|
||||
|
||||
y += 40;
|
||||
@stateBox = GuiSkinElement(this, Alignment(Left+12, Top+y, Left+236, Bottom-4), SS_PlainOverlay);
|
||||
@state = GuiMarkupText(stateBox, Alignment(Left+8, Top+4, Right-4, Bottom));
|
||||
state.memo = true;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void updateActions() {
|
||||
actions.clear();
|
||||
|
||||
if(obj.owner is playerEmpire) {
|
||||
actions.addBasic(obj);
|
||||
actions.addEmpireAbilities(playerEmpire, obj);
|
||||
}
|
||||
|
||||
actions.init(obj);
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) override {
|
||||
return obj.isAsteroid;
|
||||
}
|
||||
|
||||
Object@ get() override {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void set(Object@ obj) override {
|
||||
@this.obj = cast<Asteroid>(obj);
|
||||
@objView.object = obj;
|
||||
updateTimer = 0.0;
|
||||
updateActions();
|
||||
}
|
||||
|
||||
bool displays(Object@ obj) override {
|
||||
if(obj is this.obj)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool showManage(Object@ obj) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
double updateTimer = 1.0;
|
||||
void update(double time) override {
|
||||
updateTimer -= time;
|
||||
if(updateTimer <= 0) {
|
||||
updateTimer = randomd(0.1,0.9);
|
||||
Empire@ owner = obj.owner;
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(owner !is null)
|
||||
name.color = owner.color;
|
||||
|
||||
if(obj.cargoTypes != 0) {
|
||||
//Update cargo display
|
||||
auto@ cargo = getCargoType(obj.cargoType[0]);
|
||||
resourceBox.visible = cargo !is null;
|
||||
if(cargo !is null) {
|
||||
cargoIcon.desc = cargo.icon;
|
||||
cargoName.text = cargo.name+":";
|
||||
cargoValue.text = standardize(obj.getCargoStored(cargo.id), true);
|
||||
}
|
||||
|
||||
cargoIcon.visible = true;
|
||||
cargoName.visible = true;
|
||||
cargoValue.visible = true;
|
||||
resources.visible = false;
|
||||
|
||||
state.text = locale::ASTEROID_MINING;
|
||||
}
|
||||
else if(obj.nativeResourceCount != 0) {
|
||||
//Update resource display
|
||||
resources.resources.syncFrom(obj.getAllResources());
|
||||
resources.resources.sortDesc();
|
||||
resources.setSingleMode(align=0.0);
|
||||
|
||||
resourceBox.visible = resources.length != 0;
|
||||
|
||||
cargoIcon.visible = false;
|
||||
cargoName.visible = false;
|
||||
cargoValue.visible = false;
|
||||
resources.visible = true;
|
||||
|
||||
if(owner.valid && obj.visible)
|
||||
state.text = locale::ASTEROID_OWNED;
|
||||
else
|
||||
state.text = locale::ASTEROID_UNOWNED;
|
||||
}
|
||||
else {
|
||||
resourceBox.visible = false;
|
||||
}
|
||||
|
||||
//Update action bar
|
||||
updateActions();
|
||||
}
|
||||
}
|
||||
|
||||
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 * 1.6)
|
||||
return null;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
if(actions.visible) {
|
||||
recti pos = actions.absolutePosition;
|
||||
skin.draw(SS_Panel, SF_Normal, recti(pos.topLeft - vec2i(70, 0), pos.botRight + vec2i(0, 20)));
|
||||
}
|
||||
InfoBar::draw();
|
||||
}
|
||||
};
|
||||
|
||||
InfoBar@ makeAsteroidInfoBar(IGuiElement@ parent, Object@ obj) {
|
||||
AsteroidInfoBar bar(parent);
|
||||
bar.set(obj);
|
||||
return bar;
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.GuiResources;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
import resources;
|
||||
import cargo;
|
||||
|
||||
class AsteroidPopup : Popup {
|
||||
GuiText@ name;
|
||||
Gui3DObject@ objView;
|
||||
Asteroid@ obj;
|
||||
double lastUpdate = -INFINITY;
|
||||
|
||||
GuiSkinElement@ band;
|
||||
GuiSprite@ cargoIcon;
|
||||
GuiText@ cargoName;
|
||||
GuiText@ cargoValue;
|
||||
GuiResourceGrid@ resources;
|
||||
|
||||
AsteroidPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(150, 135);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+2, Right-4, Top+24));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+4, Top+25, Right-4, Top+95));
|
||||
|
||||
@band = GuiSkinElement(this, Alignment(Left+3, Bottom-35, Right-4, Bottom-2), SS_SubTitle);
|
||||
band.color = Color(0xaaaaaaff);
|
||||
|
||||
@resources = GuiResourceGrid(band, Alignment(Left+4, Top+4, Right-3, Bottom-4));
|
||||
resources.visible = false;
|
||||
|
||||
@cargoIcon = GuiSprite(band, Alignment(Left+2, Top+2, Left+31, Bottom-2));
|
||||
@cargoName = GuiText(band, Alignment(Left+38, Top+4, Right-4, Bottom-4));
|
||||
cargoName.font = FT_Bold;
|
||||
@cargoValue = GuiText(band, Alignment(Left+38, Top+4, Right-4, Bottom-4));
|
||||
cargoValue.horizAlign = 1.0;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return Obj.isAsteroid;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = cast<Asteroid>(Obj);
|
||||
@objView.object = Obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
SkinStyle style = SS_GenericPopupBG;
|
||||
if(isSelectable && Hovered)
|
||||
flags |= SF_Hovered;
|
||||
if(obj.owner !is null) {
|
||||
skin.draw(style, flags, bgPos, obj.owner.color);
|
||||
if(obj.owner.flag !is null)
|
||||
obj.owner.flag.draw(
|
||||
objView.absolutePosition.aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0),
|
||||
obj.owner.color * Color(0xffffff30));
|
||||
}
|
||||
else
|
||||
skin.draw(style, flags, bgPos, Color(0xffffffff));
|
||||
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(frameTime - 0.5 < lastUpdate)
|
||||
return;
|
||||
|
||||
lastUpdate = frameTime;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
if(obj.cargoTypes != 0) {
|
||||
auto@ cargo = getCargoType(obj.cargoType[0]);
|
||||
band.visible = cargo !is null;
|
||||
if(cargo !is null) {
|
||||
cargoIcon.desc = cargo.icon;
|
||||
cargoName.text = cargo.name+":";
|
||||
cargoValue.text = standardize(obj.getCargoStored(cargo.id), true);
|
||||
}
|
||||
cargoIcon.visible = true;
|
||||
cargoName.visible = true;
|
||||
cargoValue.visible = true;
|
||||
resources.visible = false;
|
||||
}
|
||||
else if(obj.nativeResourceCount != 0) {
|
||||
cargoIcon.visible = false;
|
||||
cargoName.visible = false;
|
||||
cargoValue.visible = false;
|
||||
resources.visible = true;
|
||||
|
||||
resources.resources.syncFrom(obj.getAllResources());
|
||||
resources.setSingleMode();
|
||||
|
||||
band.visible = resources.length != 0;
|
||||
}
|
||||
else {
|
||||
band.visible = false;
|
||||
}
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,176 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiResources;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.GuiSkinElement;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
import icons;
|
||||
import resources;
|
||||
import civilians;
|
||||
|
||||
class CivilianPopup : Popup {
|
||||
GuiText@ name;
|
||||
Gui3DObject@ objView;
|
||||
Civilian@ obj;
|
||||
double lastUpdate = -INFINITY;
|
||||
GuiResourceGrid@ resources;
|
||||
|
||||
GuiText@ cargoLabel;
|
||||
GuiMarkupText@ worth;
|
||||
GuiMarkupText@ cargoText;
|
||||
|
||||
GuiProgressbar@ health;
|
||||
|
||||
CivilianPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(190, 185);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+2, Right-4, Top+24));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+4, Top+25, Right-4, Top+95));
|
||||
|
||||
@health = GuiProgressbar(this, Alignment(Left+3, Bottom-89, Right-4, Bottom-63));
|
||||
health.tooltip = locale::HEALTH;
|
||||
GuiSprite healthIcon(health, Alignment(Left+2, Top+1, Width=24, Height=24), icons::Health);
|
||||
|
||||
GuiSkinElement band(this, Alignment(Left+3, Bottom-65, Right-4, Bottom-32), SS_SubTitle);
|
||||
band.color = Color(0xaaaaaaff);
|
||||
|
||||
@cargoLabel = GuiText(band, Alignment(Left+5, Top+3, Left+70, Bottom-2), locale::SHIP_CARGO);
|
||||
cargoLabel.font = FT_Bold;
|
||||
cargoLabel.stroke = colors::Black;
|
||||
@worth = GuiMarkupText(band, Alignment(Left+70, Top+6, Right-5, Bottom-2));
|
||||
|
||||
GuiSkinElement band2(this, Alignment(Left+3, Bottom-34, Right-4, Bottom-2), SS_SubTitle);
|
||||
|
||||
@cargoText = GuiMarkupText(band2, Alignment(Left+3, Top+4, Right-3, Bottom-2));
|
||||
cargoText.defaultFont = FT_Bold;
|
||||
@resources = GuiResourceGrid(band2, Alignment(Left+3, Top+3, Right-3, Bottom-2));
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return Obj.isCivilian;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = cast<Civilian>(Obj);
|
||||
@objView.object = Obj;
|
||||
@resources.drawFrom = obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
SkinStyle style = SS_GenericPopupBG;
|
||||
if(isSelectable && Hovered)
|
||||
flags |= SF_Hovered;
|
||||
if(obj.owner !is null) {
|
||||
skin.draw(style, flags, bgPos, obj.owner.color);
|
||||
if(obj.owner.flag !is null)
|
||||
obj.owner.flag.draw(
|
||||
objView.absolutePosition.aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0),
|
||||
obj.owner.color * Color(0xffffff30));
|
||||
}
|
||||
else
|
||||
skin.draw(style, flags, bgPos, Color(0xffffffff));
|
||||
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(frameTime - 0.5 < lastUpdate)
|
||||
return;
|
||||
|
||||
lastUpdate = frameTime;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
//Update hp display
|
||||
double curHP = obj.health;
|
||||
double maxHP = obj.maxHealth;
|
||||
|
||||
Color high(0x00ff00ff);
|
||||
Color low(0xff0000ff);
|
||||
|
||||
health.progress = curHP / maxHP;
|
||||
health.frontColor = low.interpolate(high, health.progress);
|
||||
health.text = standardize(curHP)+" / "+standardize(maxHP);
|
||||
|
||||
//Update resources
|
||||
uint type = obj.getCargoType();
|
||||
int value = obj.getCargoWorth();
|
||||
cargoLabel.color = obj.owner.color;
|
||||
if(type == CT_Goods) {
|
||||
resources.visible = false;
|
||||
cargoText.visible = true;
|
||||
cargoText.text = locale::CARGO_GOODS;
|
||||
}
|
||||
else if(type == CT_Resource) {
|
||||
const ResourceType@ res = getResource(obj.getCargoResource());
|
||||
resources.visible = true;
|
||||
cargoText.visible = false;
|
||||
if(res !is null) {
|
||||
resources.types.length = 1;
|
||||
@resources.types[0] = res;
|
||||
resources.typeMode = true;
|
||||
resources.setSingleMode();
|
||||
}
|
||||
else {
|
||||
resources.types.length = 0;
|
||||
}
|
||||
}
|
||||
|
||||
worth.text = format(locale::SHIP_CARGO_WORTH, formatMoney(value));
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,139 @@
|
||||
#priority init -100
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiText;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiContextMenu;
|
||||
import elements.MarkupTooltip;
|
||||
import tabs.Tab;
|
||||
import util.formatting;
|
||||
import timing;
|
||||
import influence;
|
||||
from tabs.tabbar import TAB_HEIGHT, GLOBAL_BAR_HEIGHT, ActiveTab;
|
||||
|
||||
class ClearEdictOption : GuiContextOption {
|
||||
ClearEdictOption() {
|
||||
super(locale::EDICT_CLEAR_OPTION);
|
||||
}
|
||||
|
||||
void call(GuiContextMenu@ menu) override {
|
||||
playerEmpire.clearEdict();
|
||||
}
|
||||
};
|
||||
|
||||
class EdictDisplay : BaseGuiElement {
|
||||
GuiMarkupText@ text;
|
||||
string prevText;
|
||||
bool clicking = false;
|
||||
|
||||
EdictDisplay() {
|
||||
super(null, recti_area(210,TAB_HEIGHT+GLOBAL_BAR_HEIGHT, 200, 30));
|
||||
@text = GuiMarkupText(this, recti_area(6,6, 120,24));
|
||||
text.expandWidth = true;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Mouse_Left:
|
||||
if(evt.caller is this)
|
||||
clicking = false;
|
||||
break;
|
||||
}
|
||||
return BaseGuiElement::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void showMenu() {
|
||||
if(playerEmpire.SubjugatedBy !is null)
|
||||
return;
|
||||
|
||||
GuiContextMenu menu(mousePos);
|
||||
menu.addOption(ClearEdictOption());
|
||||
menu.finalize();
|
||||
}
|
||||
|
||||
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
|
||||
if(source is this || source is text) {
|
||||
switch(event.type) {
|
||||
case MET_Button_Down:
|
||||
if(event.button == 1) {
|
||||
clicking = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MET_Button_Up:
|
||||
if(event.button == 1) {
|
||||
if(clicking)
|
||||
showMenu();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return BaseGuiElement::onMouseEvent(event, source);
|
||||
}
|
||||
|
||||
void tick(double time) {
|
||||
Empire@ master = playerEmpire.SubjugatedBy;
|
||||
uint type = 0;
|
||||
if(master is null)
|
||||
type = playerEmpire.getEdictType();
|
||||
else
|
||||
type = master.getEdictType();
|
||||
|
||||
visible = (ActiveTab.category == TC_Galaxy) && type != 0;
|
||||
|
||||
if(visible) {
|
||||
string txt;
|
||||
Empire@ emp; Object@ obj;
|
||||
if(master !is null) {
|
||||
@emp = master.getEdictEmpire();
|
||||
@obj = master.getEdictObject();
|
||||
txt = locale::EDICT_FROM_MASTER;
|
||||
}
|
||||
else {
|
||||
@emp = playerEmpire.getEdictEmpire();
|
||||
@obj = playerEmpire.getEdictObject();
|
||||
txt = locale::EDICT_TO_VASSALS;
|
||||
}
|
||||
|
||||
txt = format(txt, formatEmpireName(master),
|
||||
format(localize("#EDICT_"+toString(type)),
|
||||
formatEmpireName(emp), formatObject(obj)));
|
||||
|
||||
if(txt != prevText) {
|
||||
prevText = txt;
|
||||
text.text = txt;
|
||||
text.updateAbsolutePosition();
|
||||
size = vec2i(text.textWidth + 12, 30);
|
||||
|
||||
string tt;
|
||||
if(master !is null)
|
||||
tt = locale::TT_EDICT_FROM_MASTER;
|
||||
else
|
||||
tt = locale::TT_EDICT_TO_VASSALS;
|
||||
|
||||
tt = format(tt, formatEmpireName(master),
|
||||
format(localize("#TT_EDICT_"+toString(type)),
|
||||
formatEmpireName(emp), formatObject(obj)));
|
||||
setMarkupTooltip(this, tt, width=300);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
skin.draw(SS_TimeDisplay, SF_Normal, AbsolutePosition);
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
};
|
||||
|
||||
EdictDisplay@ disp;
|
||||
void init() {
|
||||
@disp = EdictDisplay();
|
||||
}
|
||||
|
||||
void tick(double time) {
|
||||
disp.tick(time);
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiOverlay;
|
||||
import elements.GuiBackgroundPanel;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiText;
|
||||
import elements.GuiListbox;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiPanel;
|
||||
import elements.GuiSprite;
|
||||
import random_events;
|
||||
import util.formatting;
|
||||
import tabs.tabbar;
|
||||
|
||||
class Option : GuiButton {
|
||||
CurrentEvent@ event;
|
||||
const EventOption@ option;
|
||||
|
||||
GuiSprite@ icon;
|
||||
GuiMarkupText@ content;
|
||||
uint index;
|
||||
|
||||
Option(IGuiElement@ parent) {
|
||||
super(parent, recti());
|
||||
@icon = GuiSprite(this, Alignment(Left+4, Top+0.5f-30, Left+64, Top+0.5f+30));
|
||||
@content = GuiMarkupText(this, recti(68, 4, 100, 100));
|
||||
style = SS_ChoiceBox;
|
||||
}
|
||||
|
||||
int set(CurrentEvent@ evt, const EventOption@ option, int y, uint index) {
|
||||
@this.event = evt;
|
||||
@this.option = option;
|
||||
this.index = index;
|
||||
position = vec2i(4, y);
|
||||
size = vec2i(parent.size.width-8, size.height);
|
||||
@content.targets = evt.targets;
|
||||
content.size = vec2i(size.width - 72, content.size.height);
|
||||
content.text = option.text;
|
||||
icon.desc = option.icon;
|
||||
updateAbsolutePosition();
|
||||
size = vec2i(size.width, max(content.size.height + 12, 68));
|
||||
return size.height;
|
||||
}
|
||||
};
|
||||
|
||||
class EventOverlay : GuiOverlay {
|
||||
CurrentEvent event;
|
||||
GuiBackgroundPanel@ bg;
|
||||
GuiBackgroundPanel@ choices;
|
||||
|
||||
GuiPanel@ descPanel;
|
||||
GuiMarkupText@ description;
|
||||
|
||||
GuiPanel@ choicePanel;
|
||||
array<Option@> options;
|
||||
GuiProgressbar@ bar;
|
||||
|
||||
EventOverlay(IGuiElement@ parent, int eventId) {
|
||||
super(parent);
|
||||
|
||||
uint left = 500;
|
||||
uint right = 500;
|
||||
uint total = left + 12 + right;
|
||||
|
||||
@bg = GuiBackgroundPanel(this, Alignment(Left+0.5f-total/2, Top+0.5f-200, Left+0.5f-total/2+left, Top+0.5f+200));
|
||||
bg.titleColor = Color(0x00ff00ff);
|
||||
|
||||
@choices = GuiBackgroundPanel(this, Alignment(Left+0.5f-total/2+left+12, Top+0.5f-300, Left+0.5f+total/2, Top+0.5f+300));
|
||||
choices.title = locale::ANOMALY_CHOICES;
|
||||
choices.titleColor = Color(0xff8000ff);
|
||||
|
||||
@descPanel = GuiPanel(bg, Alignment(Left+4, Top+36, Left+left-4, Bottom-8));
|
||||
|
||||
@description = GuiMarkupText(descPanel, recti_area(4, 0, left-8, 100));
|
||||
description.fitWidth = true;
|
||||
|
||||
@choicePanel = GuiPanel(choices, Alignment(Left, Top+38, Right, Bottom-34));
|
||||
|
||||
@bar = GuiProgressbar(choices, Alignment(Left+12, Bottom-34, Right-12, Bottom-4));
|
||||
bar.frontColor = Color(0xf4b62bff);
|
||||
|
||||
closeSelf = false;
|
||||
updateAbsolutePosition();
|
||||
update();
|
||||
bringToFront();
|
||||
|
||||
overlays.insertLast(this);
|
||||
|
||||
if(!receive(playerEmpire.getEvent(eventId), event))
|
||||
close();
|
||||
else
|
||||
update();
|
||||
}
|
||||
|
||||
void remove() {
|
||||
overlays.remove(this);
|
||||
GuiOverlay::remove();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
{
|
||||
Option@ opt = cast<Option>(evt.caller);
|
||||
if(opt !is null) {
|
||||
playerEmpire.chooseEventOption(event.id, opt.option.id);
|
||||
autoOpen = 1.0;
|
||||
timer = 0.05f;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return GuiOverlay::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
float timer = 0.f;
|
||||
void update() {
|
||||
if(event.type is null)
|
||||
return;
|
||||
timer -= frameLength;
|
||||
if(timer <= 0.f) {
|
||||
if(playerEmpire.currentEventID != event.id) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
if(!receive(playerEmpire.getEvent(event.id), event)) {
|
||||
close();
|
||||
return;
|
||||
}
|
||||
description.text = event.type.text;
|
||||
@description.targets = event.targets;
|
||||
bg.title = event.type.name;
|
||||
|
||||
bar.visible = event.timer > 0;
|
||||
if(bar.visible) {
|
||||
if(event.type.timer > 0)
|
||||
bar.progress = 1.f - (event.timer / event.type.timer);
|
||||
else
|
||||
bar.progress = 0.f;
|
||||
bar.text = formatTime(event.timer);
|
||||
}
|
||||
|
||||
uint optCount = event.options.length;
|
||||
uint oldCnt = options.length;
|
||||
for(uint i = optCount; i < oldCnt; ++i)
|
||||
options[i].remove();
|
||||
options.length = optCount;
|
||||
int y = 0;
|
||||
for(uint i = 0; i < optCount; ++i) {
|
||||
auto@ optType = event.options[i];
|
||||
|
||||
if(options[i] is null)
|
||||
@options[i] = Option(choicePanel);
|
||||
y += options[i].set(event, optType, y, i) + 8;
|
||||
}
|
||||
updateAbsolutePosition();
|
||||
timer += 1.f;
|
||||
}
|
||||
}
|
||||
|
||||
void updateAbsolutePosition() {
|
||||
GuiOverlay::updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void draw() {
|
||||
update();
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
};
|
||||
|
||||
GuiButton@ eventButton;
|
||||
|
||||
double autoOpen = 0.0;
|
||||
array<EventOverlay@> overlays;
|
||||
void tick(double time) {
|
||||
for(uint i = 0, cnt = overlays.length; i < cnt; ++i)
|
||||
overlays[i].update();
|
||||
|
||||
eventButton.visible = playerEmpire.hasCurrentEvents();
|
||||
eventButton.pressed = overlays.length != 0;
|
||||
|
||||
if(autoOpen > 0) {
|
||||
if(overlays.length == 0 && eventButton.visible)
|
||||
EventOverlay(ActiveTab, playerEmpire.currentEventID);
|
||||
autoOpen -= time;
|
||||
}
|
||||
}
|
||||
|
||||
class OpenEvent : onButtonClick {
|
||||
bool onClick(GuiButton@ btn) {
|
||||
for(uint i = 0, cnt = overlays.length; i < cnt; ++i) {
|
||||
if(overlays[i].isChildOf(ActiveTab)) {
|
||||
overlays[i].close();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
EventOverlay(ActiveTab, playerEmpire.currentEventID);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void init() {
|
||||
@eventButton = GuiButton(null, Alignment(Left+0.5f-120, Top+TAB_HEIGHT+GLOBAL_BAR_HEIGHT, Width=240, Height=40), "Event");
|
||||
@eventButton.onClick = OpenEvent();
|
||||
eventButton.font = FT_Bold;
|
||||
eventButton.visible = false;
|
||||
eventButton.toggleButton = true;
|
||||
}
|
||||
@@ -0,0 +1,659 @@
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiButton;
|
||||
import elements.MarkupTooltip;
|
||||
import elements.GuiContextMenu;
|
||||
import abilities;
|
||||
import tabs.Tab;
|
||||
import util.formatting;
|
||||
import targeting.ObjectTarget;
|
||||
import targeting.PointTarget;
|
||||
import ftl;
|
||||
import icons;
|
||||
import systems;
|
||||
import ship_groups;
|
||||
from obj_selection import selectObject, selectedObjects;
|
||||
from targeting.Hyperdrive import targetHyperdrive;
|
||||
from targeting.Jumpdrive import targetJumpdrive;
|
||||
from targeting.Fling import targetFling;
|
||||
from targeting.Slipstream import targetSlipstream;
|
||||
|
||||
import InfoBar@ makeOrbitalInfoBar(IGuiElement@ parent, Object@ obj) from "overlays.OrbitalInfoBar";
|
||||
import InfoBar@ makePlanetInfoBar(IGuiElement@ parent, Object@ obj) from "overlays.PlanetInfoBar";
|
||||
import InfoBar@ makeShipInfoBar(IGuiElement@ parent, Object@ obj) from "overlays.ShipInfoBar";
|
||||
import InfoBar@ makeAsteroidInfoBar(IGuiElement@ parent, Object@ obj) from "overlays.AsteroidInfoBar";
|
||||
import InfoBar@ makeArtifactInfoBar(IGuiElement@ parent, Object@ obj) from "overlays.ArtifactInfoBar";
|
||||
import void toggleSupportOverlay(Object@ obj) from "tabs.GalaxyTab";
|
||||
import void openOverlay(Object@ obj) from "tabs.GalaxyTab";
|
||||
|
||||
export InfoBar, createInfoBar;
|
||||
export BarAction, ActionBar;
|
||||
|
||||
class InfoBar : BaseGuiElement {
|
||||
InfoBar(IGuiElement@ parent) {
|
||||
super(parent, Alignment(Left+0.1f, Bottom-68, Right-0.1f, Bottom));
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void set(Object@ obj) {
|
||||
}
|
||||
|
||||
bool displays(Object@ obj) {
|
||||
return get() is obj;
|
||||
}
|
||||
|
||||
void update(double time) {
|
||||
}
|
||||
|
||||
bool showManage(Object@ obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool get_showingManage() {
|
||||
return false;
|
||||
}
|
||||
|
||||
IGuiElement@ findTab() {
|
||||
IGuiElement@ check = parent;
|
||||
while(check !is null && cast<Tab>(check) is null)
|
||||
@check = check.parent;
|
||||
return check;
|
||||
}
|
||||
};
|
||||
|
||||
const int BTN_SIZE = 40;
|
||||
const int BTN_PADD = 12;
|
||||
const int BTN_SPACE = 12;
|
||||
class BarAction : GuiButton {
|
||||
Object@ obj;
|
||||
Sprite icon;
|
||||
|
||||
BarAction() {
|
||||
super(NoParent=true);
|
||||
}
|
||||
|
||||
int get_needWidth() {
|
||||
return BTN_SIZE;
|
||||
}
|
||||
|
||||
void set_tooltip(const string& text) override {
|
||||
setMarkupTooltip(this, text, width=400, hoverStyle=true);
|
||||
}
|
||||
|
||||
void update(double time) {
|
||||
}
|
||||
|
||||
bool opEquals(const BarAction& other) const {
|
||||
return getClass(this) is getClass(other);
|
||||
}
|
||||
|
||||
void init() {
|
||||
}
|
||||
|
||||
void postInit() {
|
||||
}
|
||||
|
||||
void call() {
|
||||
}
|
||||
|
||||
void from(BarAction@ act) {
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override{
|
||||
if(evt.type == GUI_Clicked) {
|
||||
call();
|
||||
return true;
|
||||
}
|
||||
return GuiButton::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
GuiButton::draw();
|
||||
icon.draw(AbsolutePosition.padded(4));
|
||||
}
|
||||
};
|
||||
|
||||
funcdef void TrivialCB();
|
||||
class TrivialAction : BarAction {
|
||||
TrivialCB@ cb;
|
||||
bool doSelect;
|
||||
|
||||
TrivialAction(TrivialCB@ cb, const string& text, const Sprite& icon, bool doSelect = true) {
|
||||
super();
|
||||
@this.cb = cb;
|
||||
this.tooltip = text;
|
||||
this.icon = icon;
|
||||
this.doSelect = doSelect;
|
||||
}
|
||||
|
||||
void call() {
|
||||
if(doSelect && !obj.selected)
|
||||
selectObject(obj);
|
||||
cb();
|
||||
}
|
||||
}
|
||||
|
||||
class AbilityAction : BarAction {
|
||||
Ability@ abl;
|
||||
string str;
|
||||
Object@ objCast;
|
||||
bool expanded = false;
|
||||
bool independent = false;
|
||||
|
||||
int get_needWidth() {
|
||||
return expanded ? 160 : BTN_SIZE;
|
||||
}
|
||||
|
||||
AbilityAction(Ability@ abl, const string& opt, Object@ objCast = null, bool expanded = false) {
|
||||
@this.abl = abl;
|
||||
@this.objCast = objCast;
|
||||
this.expanded = expanded;
|
||||
@abl.emp = playerEmpire;
|
||||
str = opt;
|
||||
}
|
||||
|
||||
void init() override {
|
||||
tooltip = abl.formatTooltip();
|
||||
|
||||
bool usable = true;
|
||||
if(abl.cooldown > 0)
|
||||
usable = false;
|
||||
if(!abl.canActivate())
|
||||
usable = false;
|
||||
|
||||
if(usable) {
|
||||
double cost = abl.getEnergyCost();
|
||||
if(cost > 0) {
|
||||
double have = abl.emp.EnergyStored;
|
||||
if(cost > have)
|
||||
usable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(abl.obj !is null && abl.obj.isArtifact) {
|
||||
auto@ reg = abl.obj.region;
|
||||
if(reg is null)
|
||||
usable = false;
|
||||
else if(reg.PlanetsMask != 0)
|
||||
usable = usable ? (reg.PlanetsMask & playerEmpire.mask != 0) : false;
|
||||
else
|
||||
usable = usable ? hasTradeAdjacent(playerEmpire, reg) : false;
|
||||
}
|
||||
|
||||
disabled = !usable;
|
||||
|
||||
if(expanded || independent) {
|
||||
if(disabled)
|
||||
color = colors::Red;
|
||||
else
|
||||
color = colors::Energy;
|
||||
}
|
||||
else
|
||||
color = colors::White;
|
||||
}
|
||||
|
||||
void call() override {
|
||||
if(abl.type.targets.length == 0) {
|
||||
if(abl.obj !is null)
|
||||
abl.obj.activateAbility(abl.id);
|
||||
else
|
||||
abl.emp.activateAbility(abl.id);
|
||||
if(abl.type.activateSound !is null)
|
||||
abl.type.activateSound.play(priority=true);
|
||||
}
|
||||
else if(abl.type.targets[0].type == TT_Point) {
|
||||
targetPoint(AbilityTargetPoint(abl));
|
||||
}
|
||||
else if(abl.type.targets[0].type == TT_Object) {
|
||||
if(objCast !is null) {
|
||||
abl.emp.activateAbility(abl.id, objCast);
|
||||
}
|
||||
else {
|
||||
targetObject(AbilityTargetObject(abl));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void from(BarAction@ act) override {
|
||||
@abl = cast<AbilityAction>(act).abl;
|
||||
str = cast<AbilityAction>(act).str;
|
||||
expanded = cast<AbilityAction>(act).expanded;
|
||||
init();
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
GuiButton::draw();
|
||||
const Font@ ft = skin.getFont(FT_Small);
|
||||
recti pos = AbsolutePosition.padded(4);
|
||||
if(expanded || independent)
|
||||
pos = recti_area(vec2i(4,4) + AbsolutePosition.topLeft,
|
||||
vec2i(AbsolutePosition.height-4, AbsolutePosition.height-4));
|
||||
if(abl.cooldown <= 0) {
|
||||
abl.type.icon.draw(pos.aspectAligned(abl.type.icon.aspect));
|
||||
}
|
||||
else {
|
||||
if(abl.cooldown > 0) {
|
||||
if(abl.type.cooldown == 0)
|
||||
shader::PROGRESS = 1.f - clamp(abl.cooldown / abl.type.cooldown, 0.f, 1.f);
|
||||
else
|
||||
shader::PROGRESS = 0.f;
|
||||
shader::DIM_FACTOR = 0.2f;
|
||||
abl.type.icon.draw(pos.aspectAligned(abl.type.icon.aspect), color=colors::White, shader=shader::RadialDimmed);
|
||||
ft.draw(pos=pos, text=formatShortTime(abl.cooldown), color=colors::Red, horizAlign=0.5, vertAlign=1.0);
|
||||
}
|
||||
else if(abl.type.cooldown > 0) {
|
||||
abl.type.icon.draw(pos.aspectAligned(abl.type.icon.aspect));
|
||||
ft.draw(pos=pos, text=formatShortTime(abl.type.cooldown), color=colors::Green, horizAlign=0.5, vertAlign=1.0);
|
||||
}
|
||||
}
|
||||
|
||||
if(expanded) {
|
||||
pos = recti_area(vec2i(AbsolutePosition.topLeft.x+AbsolutePosition.height, AbsolutePosition.topLeft.y+2),
|
||||
vec2i(AbsolutePosition.width-AbsolutePosition.height, AbsolutePosition.height-4));
|
||||
|
||||
auto cost = abl.getEnergyCost();
|
||||
|
||||
if(cost <= 0) {
|
||||
@ft = skin.getFont(FT_Bold);
|
||||
ft.draw(pos=pos, horizAlign=0.0, vertAlign=0.1, text=abl.type.name);
|
||||
}
|
||||
else {
|
||||
@ft = skin.getFont(FT_Bold);
|
||||
ft.draw(pos=pos, horizAlign=0.0, vertAlign=0.1, text=locale::ACTIVATE);
|
||||
|
||||
string txt = format("$1 $2", toString(cost, 0), locale::RESOURCE_ENERGY);
|
||||
ft.draw(pos=pos, horizAlign=0.5, vertAlign=0.85, text=txt, color=colors::Energy);
|
||||
}
|
||||
}
|
||||
else if(independent) {
|
||||
pos = recti_area(vec2i(AbsolutePosition.topLeft.x+AbsolutePosition.height, AbsolutePosition.topLeft.y+2),
|
||||
vec2i(AbsolutePosition.width-AbsolutePosition.height, AbsolutePosition.height-4));
|
||||
|
||||
@ft = skin.getFont(FT_Bold);
|
||||
ft.draw(pos=pos, horizAlign=0.5, vertAlign=0.5, text=abl.type.name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class SupportsAction : BarAction {
|
||||
void init() override {
|
||||
icon = icons::ManageSupports;
|
||||
tooltip = locale::TT_MANAGE_PLANET_SUPPORTS;
|
||||
}
|
||||
|
||||
void call() override {
|
||||
selectObject(obj);
|
||||
toggleSupportOverlay(obj);
|
||||
}
|
||||
};
|
||||
|
||||
class ConstructionAction : BarAction {
|
||||
void init() override {
|
||||
icon = icons::Labor;
|
||||
tooltip = locale::TT_OPEN_CONSTRUCTION;
|
||||
}
|
||||
|
||||
void call() override {
|
||||
selectObject(obj);
|
||||
openOverlay(obj);
|
||||
}
|
||||
};
|
||||
|
||||
class ScoutAction : BarAction {
|
||||
bool useFTL = false;
|
||||
|
||||
ScoutAction(bool ftl) {
|
||||
useFTL = ftl;
|
||||
}
|
||||
|
||||
void init() override {
|
||||
if(useFTL) {
|
||||
icon = icons::HyperExplore;
|
||||
tooltip = locale::TT_EXPLORE_FTL;
|
||||
}
|
||||
else {
|
||||
icon = icons::Explore;
|
||||
tooltip = locale::TT_EXPLORE;
|
||||
}
|
||||
}
|
||||
|
||||
void call() override {
|
||||
obj.addAutoExploreOrder(useFTL, shiftKey);
|
||||
}
|
||||
};
|
||||
|
||||
class ModeAction : BarAction {
|
||||
uint mode = 0;
|
||||
array<string> names;
|
||||
array<string> descriptions;
|
||||
array<Sprite> icons;
|
||||
|
||||
void init() override {
|
||||
value = get();
|
||||
}
|
||||
|
||||
void set_value(uint v) {
|
||||
mode = min(v, names.length-1);
|
||||
icon = icons[mode];
|
||||
tooltip = format("[img=$1;42][font=Subtitle][b]$2[/b][/font]\n$3[/img]",
|
||||
getSpriteDesc(icons[mode]), names[mode], descriptions[mode]);
|
||||
}
|
||||
|
||||
void update(double time) {
|
||||
uint val = get();
|
||||
if(val != mode)
|
||||
this.value = val;
|
||||
}
|
||||
|
||||
void call() override {
|
||||
GuiContextMenu menu(mousePos, 400);
|
||||
menu.itemHeight = 64;
|
||||
|
||||
for(uint i = 0, cnt = names.length; i < cnt; ++i) {
|
||||
string text = format("[font=Subtitle][b]$1[/b][/font]\n$2",
|
||||
names[i], descriptions[i]);
|
||||
menu.addOption(ModeOption(this), text, icons[i], i);
|
||||
}
|
||||
|
||||
menu.finalize();
|
||||
}
|
||||
|
||||
void call(uint type) {
|
||||
value = type;
|
||||
set(type);
|
||||
for(uint i = 0, cnt = selectedObjects.length; i < cnt; ++i) {
|
||||
Object@ other = selectedObjects[i];
|
||||
if(other !is obj)
|
||||
setSecondary(other, type);
|
||||
}
|
||||
}
|
||||
|
||||
uint get() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set(uint type) {
|
||||
set(obj, type);
|
||||
}
|
||||
|
||||
void set(Object& obj, uint type) {
|
||||
}
|
||||
|
||||
void setSecondary(Object& obj, uint type) {
|
||||
set(obj, type);
|
||||
}
|
||||
};
|
||||
|
||||
class ModeOption : GuiMarkupContextOption {
|
||||
ModeAction@ act;
|
||||
ModeOption(ModeAction@ act) {
|
||||
@this.act = act;
|
||||
}
|
||||
|
||||
void call(GuiContextMenu@ menu) override {
|
||||
act.call(value);
|
||||
}
|
||||
};
|
||||
|
||||
class AutoModeAction : ModeAction {
|
||||
AutoModeAction() {
|
||||
names.insertLast(locale::HOLD_POSITION);
|
||||
descriptions.insertLast(locale::HOLD_POSITION_DESC);
|
||||
icons.insertLast(spritesheet::ActionBarIcons+12);
|
||||
|
||||
names.insertLast(locale::AREA_BOUND);
|
||||
descriptions.insertLast(locale::AREA_BOUND_DESC);
|
||||
icons.insertLast(spritesheet::ActionBarIcons+13);
|
||||
|
||||
names.insertLast(locale::REGION_BOUND);
|
||||
descriptions.insertLast(locale::REGION_BOUND_DESC);
|
||||
icons.insertLast((spritesheet::ActionBarIcons+13)*Color(0xff8080ff));
|
||||
|
||||
names.insertLast(locale::HOLD_FIRE);
|
||||
descriptions.insertLast(locale::HOLD_FIRE_DESC);
|
||||
icons.insertLast(Sprite(material::Minus));
|
||||
}
|
||||
|
||||
uint get() override {
|
||||
switch(obj.getAutoMode()) {
|
||||
case AM_HoldPosition: return 0;
|
||||
case AM_AreaBound: return 1;
|
||||
case AM_RegionBound: return 2;
|
||||
case AM_HoldFire: return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void set(Object& obj, uint type) override {
|
||||
uint autoMode = AM_AreaBound;
|
||||
switch(type) {
|
||||
case 0: autoMode = AM_HoldPosition; break;
|
||||
case 1: autoMode = AM_AreaBound; break;
|
||||
case 2: autoMode = AM_RegionBound; break;
|
||||
case 3: autoMode = AM_HoldFire; break;
|
||||
}
|
||||
obj.setAutoMode(autoMode);
|
||||
}
|
||||
};
|
||||
|
||||
class EngageBehaveAction : ModeAction {
|
||||
EngageBehaveAction() {
|
||||
names.insertLast(locale::BEH_CLOSE_IN);
|
||||
descriptions.insertLast(locale::BEH_CLOSE_IN_DESC);
|
||||
icons.insertLast(spritesheet::ActionBarIcons+16);
|
||||
|
||||
names.insertLast(locale::BEH_KEEP_DISTANCE);
|
||||
descriptions.insertLast(locale::BEH_KEEP_DISTANCE_DESC);
|
||||
icons.insertLast(spritesheet::ActionBarIcons+17);
|
||||
}
|
||||
|
||||
uint get() override {
|
||||
return obj.getEngageBehave();
|
||||
}
|
||||
|
||||
void set(Object& obj, uint type) override {
|
||||
obj.setEngageBehave(type);
|
||||
}
|
||||
};
|
||||
|
||||
class EngageTypeAction : ModeAction {
|
||||
EngageTypeAction() {
|
||||
names.insertLast(locale::ENG_FLAGSHIP_MIN);
|
||||
descriptions.insertLast(locale::ENG_FLAGSHIP_MIN_DESC);
|
||||
icons.insertLast(spritesheet::ActionBarIcons+20);
|
||||
|
||||
names.insertLast(locale::ENG_FLAGSHIP_MAX);
|
||||
descriptions.insertLast(locale::ENG_FLAGSHIP_MAX_DESC);
|
||||
icons.insertLast(spritesheet::ActionBarIcons+21);
|
||||
|
||||
names.insertLast(locale::ENG_SUPPORT_MIN);
|
||||
descriptions.insertLast(locale::ENG_SUPPORT_MIN_DESC);
|
||||
icons.insertLast(spritesheet::ActionBarIcons+22);
|
||||
|
||||
names.insertLast(locale::ENG_RAIDING_ONLY);
|
||||
descriptions.insertLast(locale::ENG_RAIDING_ONLY_DESC);
|
||||
icons.insertLast(Sprite(spritesheet::ActionBarIcons, 22, Color(0xff0000ff)));
|
||||
}
|
||||
|
||||
uint get() override {
|
||||
return obj.getEngageType();
|
||||
}
|
||||
|
||||
void set(Object& obj, uint type) override {
|
||||
obj.setEngageType(type);
|
||||
}
|
||||
};
|
||||
|
||||
class ActionBar : BaseGuiElement {
|
||||
array<BarAction@> actions;
|
||||
uint index = 0;
|
||||
|
||||
ActionBar(IGuiElement@ bar, vec2i pos) {
|
||||
super(bar, recti_area(pos, vec2i(100, BTN_SIZE+BTN_PADD*2)));
|
||||
}
|
||||
|
||||
void clear() {
|
||||
index = 0;
|
||||
}
|
||||
|
||||
void add(BarAction@ act) {
|
||||
if(index >= actions.length) {
|
||||
@act.parent = this;
|
||||
actions.insertLast(act);
|
||||
}
|
||||
else {
|
||||
if(act != actions[index]) {
|
||||
actions[index].remove();
|
||||
@act.parent = this;
|
||||
@actions[index] = act;
|
||||
}
|
||||
else {
|
||||
actions[index].from(act);
|
||||
}
|
||||
}
|
||||
++index;
|
||||
}
|
||||
|
||||
void init(Object@ obj) {
|
||||
for(uint i = index, cnt = actions.length; i < cnt; ++i)
|
||||
actions[i].remove();
|
||||
|
||||
actions.length = index;
|
||||
for(uint i = 0, cnt = actions.length; i < cnt; ++i) {
|
||||
@actions[i].obj = obj;
|
||||
actions[i].init();
|
||||
actions[i].postInit();
|
||||
}
|
||||
visible = actions.length != 0;
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void update(double time) {
|
||||
for(uint i = 0, cnt = actions.length; i < cnt; ++i)
|
||||
actions[i].update(time);
|
||||
}
|
||||
|
||||
void addBasic(Object@ obj) {
|
||||
if(obj.hasLeaderAI) {
|
||||
if(obj.SupplyCapacity > 0)
|
||||
add(SupportsAction());
|
||||
if(obj.hasMover && !obj.hasOrbit) {
|
||||
add(AutoModeAction());
|
||||
add(EngageBehaveAction());
|
||||
add(EngageTypeAction());
|
||||
}
|
||||
if(!obj.isPlanet && obj.hasConstruction)
|
||||
add(ConstructionAction());
|
||||
}
|
||||
}
|
||||
|
||||
void addScouting(Object@ obj) {
|
||||
if(obj.hasLeaderAI && obj.getFleetMaxStrength() < 5000.0 && obj.owner.ForbidDeepSpace == 0) {
|
||||
add(ScoutAction(ftl=false));
|
||||
if(obj.owner.hasFlingBeacons || canHyperdrive(obj) || canJumpdrive(obj))
|
||||
add(ScoutAction(ftl=true));
|
||||
}
|
||||
}
|
||||
|
||||
void addAbilities(Object@ obj, bool expanded = false) {
|
||||
if(!obj.hasAbilities)
|
||||
return;
|
||||
|
||||
array<Ability> abilities;
|
||||
abilities.syncFrom(obj.getAbilities());
|
||||
|
||||
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
|
||||
Ability@ abl = abilities[i];
|
||||
if(abl.disabled)
|
||||
continue;
|
||||
|
||||
string option = format(locale::ABILITY_TRIGGER, abl.type.name);
|
||||
double cost = abl.getEnergyCost();
|
||||
if(cost > 0)
|
||||
option += format(locale::ABILITY_ENERGY, standardize(cost));
|
||||
|
||||
add(AbilityAction(abl, option, expanded=expanded));
|
||||
}
|
||||
}
|
||||
|
||||
void addEmpireAbilities(Empire@ emp, Object@ obj) {
|
||||
array<Ability> abilities;
|
||||
abilities.syncFrom(emp.getAbilities());
|
||||
|
||||
Targets targs;
|
||||
@targs.add(TT_Object, fill=true).obj = obj;
|
||||
|
||||
for(uint i = 0, cnt = abilities.length; i < cnt; ++i) {
|
||||
Ability@ abl = abilities[i];
|
||||
if(abl.disabled)
|
||||
continue;
|
||||
if(obj is null) {
|
||||
if(abl.type.objectCast != -1)
|
||||
continue;
|
||||
if(abl.type.hideGlobal)
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
if(abl.type.objectCast == -1)
|
||||
continue;
|
||||
if(!abl.canActivate(targs, ignoreCost=true))
|
||||
continue;
|
||||
}
|
||||
|
||||
string option = format(locale::ABILITY_TRIGGER, abl.type.name);
|
||||
double cost = abl.getEnergyCost();
|
||||
if(cost > 0)
|
||||
option += format(locale::ABILITY_ENERGY, standardize(cost));
|
||||
|
||||
add(AbilityAction(abl, option, obj));
|
||||
}
|
||||
}
|
||||
|
||||
void addFTL(Object@ obj) {
|
||||
if(canHyperdrive(obj))
|
||||
add(TrivialAction(targetHyperdrive, locale::TT_HYPERDRIVE, icons::Hyperdrive));
|
||||
if(canJumpdrive(obj))
|
||||
add(TrivialAction(targetJumpdrive, locale::TT_JUMPDRIVE, icons::Hyperdrive));
|
||||
if(canFling(obj) && obj.owner.getFlingBeacon(obj.position) !is null)
|
||||
add(TrivialAction(targetFling, locale::TT_FLING, icons::Fling));
|
||||
if(canSlipstream(obj))
|
||||
add(TrivialAction(targetSlipstream, locale::TT_SLIPSTREAM, icons::Slipstream));
|
||||
}
|
||||
|
||||
void updateAbsolutePosition() override {
|
||||
int w = BTN_PADD;
|
||||
for(uint i = 0, cnt = actions.length; i < cnt; ++i) {
|
||||
if(i != 0)
|
||||
w += BTN_SPACE;
|
||||
int size = actions[i].needWidth;
|
||||
actions[i].size = vec2i(size, BTN_SIZE);
|
||||
actions[i].position = vec2i(w, 8);
|
||||
w += size;
|
||||
}
|
||||
|
||||
w += BTN_PADD;
|
||||
size = vec2i(w, BTN_SIZE + BTN_PADD*2);
|
||||
BaseGuiElement::updateAbsolutePosition();
|
||||
}
|
||||
};
|
||||
|
||||
InfoBar@ createInfoBar(IGuiElement@ parent, Object& obj) {
|
||||
if(obj.isPlanet)
|
||||
return makePlanetInfoBar(parent, obj);
|
||||
if(obj.isShip)
|
||||
return makeShipInfoBar(parent, obj);
|
||||
if(obj.isOrbital)
|
||||
return makeOrbitalInfoBar(parent, obj);
|
||||
if(obj.isAsteroid)
|
||||
return makeAsteroidInfoBar(parent, obj);
|
||||
if(obj.isArtifact)
|
||||
return makeArtifactInfoBar(parent, obj);
|
||||
return null;
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.Gui3DObject;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
|
||||
class ObjectPopup : Popup {
|
||||
GuiText@ name;
|
||||
Gui3DObject@ objView;
|
||||
Object@ obj;
|
||||
double lastUpdate = -INFINITY;
|
||||
|
||||
ObjectPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(150, 110);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+2, Right-4, Top+24));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+4, Top+25, Right-4, Top+95));
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return obj is Obj;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = Obj;
|
||||
@objView.object = Obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
SkinStyle style = SS_GenericPopupBG;
|
||||
if(isSelectable && Hovered)
|
||||
flags |= SF_Hovered;
|
||||
|
||||
Color col;
|
||||
if(obj.isStar) {
|
||||
Region@ reg = obj.region;
|
||||
if(reg !is null) {
|
||||
Empire@ other = reg.visiblePrimaryEmpire;
|
||||
if(other !is null)
|
||||
col = other.color;
|
||||
}
|
||||
}
|
||||
else if(obj.owner !is null)
|
||||
col = obj.owner.color;
|
||||
|
||||
skin.draw(style, flags, bgPos, col);
|
||||
if(obj.owner !is null && obj.owner.flag !is null) {
|
||||
obj.owner.flag.draw(
|
||||
objView.absolutePosition.aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0),
|
||||
obj.owner.color * Color(0xffffff30));
|
||||
}
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(frameTime - 0.2 < lastUpdate)
|
||||
return;
|
||||
|
||||
lastUpdate = frameTime;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,129 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.Gui3DObject;
|
||||
import util.formatting;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
|
||||
class OddityPopup : Popup {
|
||||
GuiText@ name;
|
||||
Gui3DObject@ objView;
|
||||
Object@ obj;
|
||||
double lastUpdate = -INFINITY;
|
||||
GuiText@ timerBox;
|
||||
|
||||
OddityPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(150, 110);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+2, Right-4, Top+24));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+4, Top+25, Right-4, Top+95));
|
||||
@timerBox = GuiText(this, Alignment(Left+10, Top+10, Right-10, Bottom-10));
|
||||
timerBox.horizAlign = 1.0;
|
||||
timerBox.vertAlign = 1.0;
|
||||
timerBox.visible = false;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return Obj.isOddity;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = Obj;
|
||||
@objView.object = Obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
SkinStyle style = SS_GenericPopupBG;
|
||||
if(isSelectable && Hovered)
|
||||
flags |= SF_Hovered;
|
||||
|
||||
Color col;
|
||||
if(obj.isStar) {
|
||||
Region@ reg = obj.region;
|
||||
if(reg !is null) {
|
||||
Empire@ other = reg.visiblePrimaryEmpire;
|
||||
if(other !is null)
|
||||
col = other.color;
|
||||
}
|
||||
}
|
||||
else if(obj.owner !is null)
|
||||
col = obj.owner.color;
|
||||
|
||||
skin.draw(style, flags, bgPos, col);
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(frameTime - 0.2 < lastUpdate)
|
||||
return;
|
||||
|
||||
lastUpdate = frameTime;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
//Update timer
|
||||
Oddity@ odd = cast<Oddity>(obj);
|
||||
if(odd !is null) {
|
||||
double timer = odd.getTimer();
|
||||
if(timer <= 0.0) {
|
||||
timerBox.visible = false;
|
||||
}
|
||||
else {
|
||||
timerBox.visible = true;
|
||||
timerBox.text = formatTime(timer);
|
||||
}
|
||||
}
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,265 @@
|
||||
import overlays.InfoBar;
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiResources;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiText;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.GuiGroupDisplay;
|
||||
import elements.GuiBlueprint;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.GuiIconGrid;
|
||||
import elements.MarkupTooltip;
|
||||
import ship_groups;
|
||||
import orbitals;
|
||||
import util.formatting;
|
||||
import icons;
|
||||
from overlays.ContextMenu import openContextMenu, FinanceDryDock;
|
||||
from overlays.Construction import ConstructionOverlay;
|
||||
from obj_selection import isSelected, selectObject, clearSelection, addToSelection, selectedObject;
|
||||
from tabs.GalaxyTab import zoomTabTo, openOverlay;
|
||||
|
||||
class ModuleGrid : GuiIconGrid {
|
||||
array<OrbitalSection> sections;
|
||||
|
||||
ModuleGrid(IGuiElement@ parent, Alignment@ align) {
|
||||
super(parent, align);
|
||||
}
|
||||
|
||||
uint get_length() override {
|
||||
return sections.length;
|
||||
}
|
||||
|
||||
void drawElement(uint index, const recti& pos) override {
|
||||
sections[index].type.icon.draw(pos);
|
||||
}
|
||||
|
||||
string get_tooltip() override {
|
||||
if(hovered < 0 || hovered >= int(length))
|
||||
return "";
|
||||
return sections[hovered].type.getTooltip();
|
||||
}
|
||||
};
|
||||
|
||||
class OrbitalInfoBar : InfoBar {
|
||||
Orbital@ obj;
|
||||
Gui3DObject@ objView;
|
||||
ConstructionOverlay@ overlay;
|
||||
|
||||
GuiSkinElement@ nameBox;
|
||||
GuiText@ name;
|
||||
|
||||
GuiSkinElement@ moduleBox;
|
||||
GuiMarkupText@ moduleText;
|
||||
GuiSprite@ moduleIcon;
|
||||
|
||||
ActionBar@ actions;
|
||||
|
||||
OrbitalInfoBar(IGuiElement@ parent) {
|
||||
super(parent);
|
||||
@alignment = Alignment(Left, Bottom-228, Left+395, Bottom);
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(
|
||||
Left-0.5f, Top, Right+0.15f, Bottom));
|
||||
|
||||
@actions = ActionBar(this, vec2i(305, 172));
|
||||
actions.noClip = true;
|
||||
|
||||
int y = 90;
|
||||
@nameBox = GuiSkinElement(this, Alignment(Left+12, Top+y, Left+256, Top+y+34), SS_PlainOverlay);
|
||||
@name = GuiText(nameBox, Alignment().padded(8, 0, 30, 0));
|
||||
name.font = FT_Medium;
|
||||
@moduleIcon = GuiSprite(nameBox, Alignment(Right-30, Top, Right, Bottom));
|
||||
|
||||
y += 40;
|
||||
@moduleBox = GuiSkinElement(this, Alignment(Left+12, Top+y, Left+256, Top+y+94), SS_PlainOverlay);
|
||||
@moduleText = GuiMarkupText(moduleBox, Alignment().padded(6, 3, 6, 0));
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void updateActions() {
|
||||
actions.clear();
|
||||
|
||||
if(obj.owner is playerEmpire) {
|
||||
auto@ core = getOrbitalModule(obj.coreModule);
|
||||
if(!core.isStandalone)
|
||||
actions.add(ManageAction());
|
||||
if(obj.getDesign(OV_PackUp) !is null)
|
||||
actions.add(PackUpAction());
|
||||
actions.addBasic(obj);
|
||||
actions.addFTL(obj);
|
||||
actions.addAbilities(obj);
|
||||
actions.addEmpireAbilities(playerEmpire, obj);
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
actions.init(obj);
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
if(actions.visible) {
|
||||
recti pos = actions.absolutePosition;
|
||||
skin.draw(SS_Panel, SF_Normal, recti(vec2i(-5, pos.topLeft.y), pos.botRight + vec2i(0, 20)));
|
||||
}
|
||||
InfoBar::draw();
|
||||
}
|
||||
|
||||
void remove() override {
|
||||
if(overlay !is null)
|
||||
overlay.remove();
|
||||
InfoBar::remove();
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) override {
|
||||
return obj.isOrbital;
|
||||
}
|
||||
|
||||
Object@ get() override {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void set(Object@ obj) override {
|
||||
@this.obj = cast<Orbital>(obj);
|
||||
@objView.object = obj;
|
||||
updateTimer = 0.0;
|
||||
updateActions();
|
||||
}
|
||||
|
||||
bool displays(Object@ obj) override {
|
||||
if(obj is this.obj)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool showManage(Object@ obj) override {
|
||||
if(overlay !is null)
|
||||
overlay.remove();
|
||||
if(cast<Orbital>(obj).getDesign(OV_DRY_Design) !is null) {
|
||||
FinanceDryDock(obj);
|
||||
return false;
|
||||
}
|
||||
if(obj.hasConstruction) {
|
||||
@overlay = ConstructionOverlay(findTab(), obj);
|
||||
visible = false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
double updateTimer = 1.0;
|
||||
void update(double time) override {
|
||||
if(overlay !is null) {
|
||||
if(overlay.parent is null) {
|
||||
@overlay = null;
|
||||
visible = true;
|
||||
}
|
||||
else
|
||||
overlay.update(time);
|
||||
}
|
||||
|
||||
updateTimer -= time;
|
||||
if(updateTimer <= 0) {
|
||||
updateTimer = randomd(0.1,0.9);
|
||||
Empire@ owner = obj.owner;
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(obj.isDisabled)
|
||||
name.color = colors::Red;
|
||||
else if(owner !is null)
|
||||
name.color = owner.color;
|
||||
else
|
||||
name.color = colors::White;
|
||||
|
||||
const Font@ ft = skin.getFont(FT_Medium);
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Bold;
|
||||
else
|
||||
name.font = FT_Medium;
|
||||
|
||||
//Update description
|
||||
auto@ core = getOrbitalModule(obj.coreModule);
|
||||
if(core !is null) {
|
||||
moduleText.text = core.blurb;
|
||||
moduleIcon.desc = core.icon;
|
||||
setMarkupTooltip(moduleBox, format("[font=Medium]$1[/font]\n$2", core.name, core.description), width=350);
|
||||
setMarkupTooltip(nameBox, format("[font=Medium]$1[/font]\n$2", core.name, core.description), width=350);
|
||||
}
|
||||
|
||||
updateActions();
|
||||
}
|
||||
}
|
||||
|
||||
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 * 1.6)
|
||||
return null;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
selectObject(obj, shiftKey);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
if(selectedObject is null)
|
||||
openContextMenu(obj, obj);
|
||||
else
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
zoomTabTo(obj);
|
||||
return true;
|
||||
case OA_DoubleClick:
|
||||
showManage(obj);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return InfoBar::onGuiEvent(evt);
|
||||
}
|
||||
};
|
||||
|
||||
class ManageAction : BarAction {
|
||||
void init() override {
|
||||
icon = icons::Manage;
|
||||
tooltip = locale::TT_MANAGE_ORBITAL;
|
||||
}
|
||||
|
||||
void call() override {
|
||||
selectObject(obj);
|
||||
openOverlay(obj);
|
||||
}
|
||||
};
|
||||
|
||||
class PackUpAction : BarAction {
|
||||
void init() override {
|
||||
icon = icons::Gate;
|
||||
tooltip = locale::TT_PACKUP_ORBITAL;
|
||||
}
|
||||
|
||||
void call() override {
|
||||
cast<Orbital>(obj).sendValue(OV_PackUp);
|
||||
}
|
||||
};
|
||||
|
||||
InfoBar@ makeOrbitalInfoBar(IGuiElement@ parent, Object@ obj) {
|
||||
OrbitalInfoBar bar(parent);
|
||||
bar.set(obj);
|
||||
return bar;
|
||||
}
|
||||
@@ -0,0 +1,428 @@
|
||||
import elements.BaseGuiElement;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiOverlay;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.GuiText;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiListbox;
|
||||
import elements.GuiPanel;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.MarkupTooltip;
|
||||
import elements.GuiResources;
|
||||
import elements.GuiIconGrid;
|
||||
import orbitals;
|
||||
import util.formatting;
|
||||
import icons;
|
||||
import buildings;
|
||||
import resources;
|
||||
import tile_resources;
|
||||
import overlays.Construction;
|
||||
from gui import animate_time;
|
||||
|
||||
const double ANIM1_TIME = 0.15;
|
||||
const double ANIM2_TIME = 0.001;
|
||||
const uint BORDER = 20;
|
||||
const uint WIDTH = 500;
|
||||
const uint R_HEIGHT = 256;
|
||||
|
||||
// {{{ Overlay
|
||||
class OrbitalOverlay : GuiOverlay, ConstructionParent {
|
||||
Gui3DObject@ objView;
|
||||
Orbital@ obj;
|
||||
bool closing = false;
|
||||
|
||||
ResourceDisplay@ resources;
|
||||
ModuleDisplay@ modules;
|
||||
ConstructionDisplay@ construction;
|
||||
|
||||
Alignment@ objTarget;
|
||||
|
||||
OrbitalRequirements reqs;
|
||||
array<OrbitalSection> sections;
|
||||
array<Resource> resList;
|
||||
|
||||
OrbitalOverlay(IGuiElement@ parent, Orbital@ Obj) {
|
||||
super(parent);
|
||||
fade.a = 0;
|
||||
@obj = Obj;
|
||||
|
||||
vec2i parSize = parent.size;
|
||||
@objView = Gui3DObject(this, recti_area(vec2i(-456, parSize.y-228), vec2i(912, 912)));
|
||||
objView.internalRotation = quaterniond_fromAxisAngle(vec3d(0.0, 0.0, 1.0), -0.25*pi);
|
||||
@objView.object = obj;
|
||||
|
||||
int plSize = parSize.x * 2;
|
||||
@objTarget = Alignment(Left+0.5f-plSize/2, Top+0.5f, Width=plSize, Height=plSize);
|
||||
recti targPos = objTarget.resolve(parSize);
|
||||
animate_time(objView, targPos, ANIM1_TIME);
|
||||
|
||||
updateAbsolutePosition();
|
||||
|
||||
vec2i origin = targPos.center;
|
||||
@resources = ResourceDisplay(this, origin, Alignment(Left+0.5f-BORDER/2-WIDTH,
|
||||
Top+BORDER, Left+0.5f-BORDER/2, Top+BORDER+R_HEIGHT));
|
||||
resources.visible = false;
|
||||
@modules = ModuleDisplay(this, origin, Alignment(Left+0.5f-BORDER/2-WIDTH,
|
||||
Top+BORDER, Left+0.5f-BORDER/2, Bottom-BORDER));
|
||||
|
||||
@construction = ConstructionDisplay(this, origin, Alignment(Right-0.5f+BORDER/2,
|
||||
Top+BORDER, Right-0.5f+BORDER/2+WIDTH, Bottom-BORDER));
|
||||
}
|
||||
|
||||
IGuiElement@ elementFromPosition(const vec2i& pos) override {
|
||||
IGuiElement@ elem = BaseGuiElement::elementFromPosition(pos);
|
||||
if(elem is objView)
|
||||
return this;
|
||||
return elem;
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override {
|
||||
switch(evt.type) {
|
||||
case GUI_Animation_Complete:
|
||||
if(evt.caller is objView) {
|
||||
if(closing) {
|
||||
GuiOverlay::close();
|
||||
return true;
|
||||
}
|
||||
|
||||
//Make sure the object view stays in the right position
|
||||
@objView.alignment = objTarget;
|
||||
|
||||
//Start showing all the data
|
||||
resources.animate();
|
||||
modules.animate();
|
||||
construction.animate();
|
||||
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case GUI_Confirmed:
|
||||
triggerUpdate();
|
||||
break;
|
||||
}
|
||||
return GuiOverlay::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
bool onMouseEvent(const MouseEvent& evt, IGuiElement@ source) override {
|
||||
return GuiOverlay::onMouseEvent(evt, source);
|
||||
}
|
||||
|
||||
void close() override {
|
||||
if(parent is null)
|
||||
return;
|
||||
closing = true;
|
||||
@objView.alignment = null;
|
||||
|
||||
modules.visible = false;
|
||||
resources.visible = false;
|
||||
construction.visible = false;
|
||||
|
||||
vec2i parSize = parent.size;
|
||||
recti targPos = recti_area(vec2i(-456, parSize.y-228), vec2i(912, 912));
|
||||
animate_time(objView, targPos, ANIM1_TIME);
|
||||
}
|
||||
|
||||
void startBuild(const BuildingType@ type) {
|
||||
}
|
||||
|
||||
Object@ get_object() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
Object@ get_slaved() {
|
||||
return null;
|
||||
}
|
||||
|
||||
void updateData() {
|
||||
resList.syncFrom(obj.getAllResources());
|
||||
sections.syncFrom(obj.getSections());
|
||||
|
||||
reqs.init(resList);
|
||||
for(uint i = 0, cnt = sections.length; i < cnt; ++i) {
|
||||
if(sections[i].enabled)
|
||||
reqs.add(sections[i].type);
|
||||
}
|
||||
|
||||
for(uint i = 0, cnt = construction.modulesList.items.length; i < cnt; ++i) {
|
||||
GuiListbox@ list = cast<GuiListbox>(construction.modulesList.items[i]);
|
||||
for(uint n = 0, ncnt = list.itemCount; n < ncnt; ++n) {
|
||||
auto@ item = cast<BuildElement>(list.getItemElement(n));
|
||||
if(item !is null && item.orb !is null) {
|
||||
item.incomplete = !reqs.check(item.orb);
|
||||
item.update(construction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double updateTimer = 0.0;
|
||||
void update(double time) {
|
||||
construction.update(time);
|
||||
|
||||
updateTimer -= time;
|
||||
if(updateTimer <= 0 || construction.longTimer == 5.0) {
|
||||
updateData();
|
||||
updateTimer = 1.0;
|
||||
}
|
||||
|
||||
resources.update(time);
|
||||
modules.update(time);
|
||||
}
|
||||
|
||||
void triggerUpdate() {
|
||||
updateData();
|
||||
updateTimer = 0.0;
|
||||
resources.updateTimer = 0.0;
|
||||
modules.updateTimer = 0.0;
|
||||
construction.updateTimer = 0.0;
|
||||
construction.longTimer = 0.0;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
if(!settings::bGalaxyBG && objView.Alignment !is null)
|
||||
material::Skybox.draw(AbsolutePosition);
|
||||
GuiOverlay::draw();
|
||||
}
|
||||
};
|
||||
|
||||
class DisplayBox : BaseGuiElement {
|
||||
OrbitalOverlay@ overlay;
|
||||
Alignment@ targetAlign;
|
||||
|
||||
DisplayBox(OrbitalOverlay@ ov, vec2i origin, Alignment@ target) {
|
||||
@overlay = ov;
|
||||
@targetAlign = target;
|
||||
super(overlay, recti_area(origin, vec2i(1,1)));
|
||||
visible = false;
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void animate() {
|
||||
visible = true;
|
||||
animate_time(this, targetAlign.resolve(overlay.size), ANIM2_TIME);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
//}}}
|
||||
|
||||
// {{{ Resources
|
||||
class ResourceDisplay : DisplayBox {
|
||||
GuiText@ resourceLabel;
|
||||
GuiSkinElement@ resourceBox;
|
||||
GuiResources@ resources;
|
||||
|
||||
GuiText@ affinityLabel;
|
||||
GuiSpriteGrid@ affinities;
|
||||
|
||||
ResourceDisplay(OrbitalOverlay@ ov, vec2i origin, Alignment@ target) {
|
||||
super(ov, origin, target);
|
||||
|
||||
@resourceLabel = GuiText(this, Alignment(Left+8, Top+8, Right-8, Top+30), locale::LAB_RESOURCES);
|
||||
resourceLabel.font = FT_Bold;
|
||||
|
||||
@resourceBox = GuiSkinElement(this, Alignment(Left+20, Top+34, Right-20, Top+78), SS_PlainOverlay);
|
||||
@resources = GuiResources(resourceBox, Alignment().fill());
|
||||
resources.horizAlign = 0.0;
|
||||
@resources.drawFrom = overlay.obj;
|
||||
|
||||
@affinityLabel = GuiText(this, Alignment(Left+8, Top+88, Right-8, Top+110), locale::LAB_AVAIL_AFFINITIES);
|
||||
affinityLabel.font = FT_Bold;
|
||||
|
||||
@affinities = GuiSpriteGrid(this, Alignment(Left+20, Top+114, Right-20, Bottom-8), vec2i(40, 40));
|
||||
affinities.horizAlign = 0.0;
|
||||
affinities.vertAlign = 0.0;
|
||||
}
|
||||
|
||||
void updateData() {
|
||||
resources.resources = overlay.resList;
|
||||
|
||||
affinities.clear();
|
||||
for(uint i = 0, cnt = overlay.reqs.available.length; i < cnt; ++i) {
|
||||
if(overlay.reqs.used[i])
|
||||
continue;
|
||||
auto@ res = overlay.reqs.available[i];
|
||||
for(uint n = 0, ncnt = res.affinities.length; n < ncnt; ++n)
|
||||
affinities.add(getAffinitySprite(res.affinities[n]));
|
||||
}
|
||||
affinityLabel.visible = affinities.sprites.length != 0;
|
||||
}
|
||||
|
||||
double updateTimer = 0.0;
|
||||
void update(double time) override {
|
||||
updateTimer -= time;
|
||||
if(updateTimer <= 0) {
|
||||
updateData();
|
||||
updateTimer = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
|
||||
// {{{ Modules
|
||||
class ModuleElement : BaseGuiElement {
|
||||
Orbital@ obj;
|
||||
OrbitalSection section;
|
||||
|
||||
GuiSprite@ icon;
|
||||
GuiText@ name;
|
||||
GuiMarkupText@ blurb;
|
||||
GuiMarkupText@ data;
|
||||
Color color;
|
||||
GuiButton@ removeButton;
|
||||
|
||||
ModuleElement(IGuiElement@ disp, OrbitalSection@ section, Orbital@ obj) {
|
||||
super(disp, Alignment(Left+8, Top, Right-8, Top+76));
|
||||
@icon = GuiSprite(this, recti_area(8, 13, 50, 50));
|
||||
@name = GuiText(this, Alignment(Left+70, Top+4, Right-4, Top+28));
|
||||
name.font = FT_Bold;
|
||||
@blurb = GuiMarkupText(this, Alignment(Left+68, Top+26, Right-4, Top+48));
|
||||
@data = GuiMarkupText(this, Alignment(Left+68, Top+48, Right-4, Bottom-4));
|
||||
data.defaultFont = FT_Italic;
|
||||
@removeButton = GuiButton(this, Alignment(Right-40, Bottom-26, Right, Bottom), "-");
|
||||
removeButton.color = colors::Red;
|
||||
|
||||
set(section, obj);
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
int prevY = 0;
|
||||
void setPos(int y) {
|
||||
alignment.top.pixels = y;
|
||||
alignment.bottom.pixels = y + 76;
|
||||
if(prevY != y)
|
||||
updateAbsolutePosition();
|
||||
prevY = y;
|
||||
}
|
||||
|
||||
void set(OrbitalSection@ section, Orbital@ obj) {
|
||||
@this.obj = obj;
|
||||
this.section = section;
|
||||
icon.desc = section.type.icon;
|
||||
name.text = section.type.name;
|
||||
blurb.text = section.type.blurb;
|
||||
data.text = section.getData(obj);
|
||||
if(section.enabled) {
|
||||
color = colors::White;
|
||||
name.color = colors::White;
|
||||
}
|
||||
else {
|
||||
color = colors::Red;
|
||||
name.color = colors::Red;
|
||||
}
|
||||
setMarkupTooltip(this, section.type.getTooltip());
|
||||
removeButton.visible = !section.type.isCore && !obj.isContested;
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override {
|
||||
if(evt.caller is removeButton) {
|
||||
if(evt.type == GUI_Clicked) {
|
||||
obj.destroyModule(section.id);
|
||||
emitConfirmed();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return BaseGuiElement::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
skin.draw(SS_PlainOverlay, SF_Normal, AbsolutePosition, color=color);
|
||||
BaseGuiElement::draw();
|
||||
|
||||
int x = AbsolutePosition.width - 32;
|
||||
for(uint i = 0; i < TR_COUNT; ++i) {
|
||||
for(uint n = 0, ncnt = section.type.affinities[i]; n < ncnt; ++n) {
|
||||
getTileResourceSprite(i).draw(
|
||||
recti_area(AbsolutePosition.topLeft + vec2i(x, 4), vec2i(26, 26)));
|
||||
x -= 30;
|
||||
}
|
||||
}
|
||||
for(uint i = 0, cnt = section.type.requirements.length; i < cnt; ++i) {
|
||||
section.type.requirements[i].icon.draw(
|
||||
recti_area(vec2i(x, 4)+AbsolutePosition.topLeft, vec2i(26, 26)));
|
||||
x -= 30;
|
||||
}
|
||||
if(section.type.maintenance != 0) {
|
||||
x -= 60;
|
||||
skin.getFont(FT_Bold).draw(
|
||||
pos=recti_area(vec2i(x, 4)+AbsolutePosition.topLeft, vec2i(86, 26)),
|
||||
text=formatMoney(section.type.maintenance),
|
||||
color=Color(0xd1cb6aff),
|
||||
horizAlign=1.0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class ModuleDisplay : DisplayBox {
|
||||
GuiPanel@ panel;
|
||||
array<ModuleElement@> modules;
|
||||
|
||||
ModuleDisplay(OrbitalOverlay@ ov, vec2i origin, Alignment@ target) {
|
||||
super(ov, origin, target);
|
||||
@panel = GuiPanel(this, Alignment().fill());
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void updateData() {
|
||||
uint oldCnt = modules.length;
|
||||
uint newCnt = overlay.sections.length;
|
||||
for(uint i = newCnt; i < oldCnt; ++i)
|
||||
modules[i].remove();
|
||||
modules.length = newCnt;
|
||||
int y = 8;
|
||||
for(uint i = 0; i < newCnt; ++i) {
|
||||
if(modules[i] is null)
|
||||
@modules[i] = ModuleElement(panel, overlay.sections[i], overlay.obj);
|
||||
else
|
||||
modules[i].set(overlay.sections[i], overlay.obj);
|
||||
modules[i].setPos(y);
|
||||
y += 80;
|
||||
}
|
||||
}
|
||||
|
||||
double updateTimer = 0.0;
|
||||
void update(double time) override {
|
||||
updateTimer -= time;
|
||||
if(updateTimer <= 0) {
|
||||
updateData();
|
||||
updateTimer = 1.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
// }}}
|
||||
@@ -0,0 +1,283 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.MarkupTooltip;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.GuiCargoDisplay;
|
||||
from elements.Gui3DObject import Gui3DObject, ObjectAction;
|
||||
import orbitals;
|
||||
from obj_selection import isSelected;
|
||||
import constructible;
|
||||
import statuses;
|
||||
import util.constructible_view;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
|
||||
class OrbitalPopup : Popup {
|
||||
Constructible cons;
|
||||
bool hasConstruction = false;
|
||||
|
||||
array<GuiSprite@> statusIcons;
|
||||
|
||||
GuiText@ name;
|
||||
Gui3DObject@ objView;
|
||||
|
||||
GuiProgressbar@ health;
|
||||
GuiProgressbar@ strength;
|
||||
|
||||
GuiCargoDisplay@ cargo;
|
||||
|
||||
GuiSprite@ defIcon;
|
||||
|
||||
Orbital@ obj;
|
||||
bool selected = false;
|
||||
double lastUpdate = -INFINITY;
|
||||
|
||||
OrbitalPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(190, 155);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+2, Right-4, Top+24));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@health = GuiProgressbar(this, Alignment(Left+3, Bottom-56, Right-4, Bottom-30));
|
||||
health.tooltip = locale::HEALTH;
|
||||
|
||||
GuiSprite healthIcon(health, Alignment(Left+2, Top+1, Width=24, Height=24), icons::Health);
|
||||
|
||||
@strength = GuiProgressbar(this, Alignment(Left+3, Bottom-30, Right-4, Bottom-4));
|
||||
strength.tooltip = locale::FLEET_STRENGTH;
|
||||
|
||||
GuiSprite strIcon(strength, Alignment(Left+2, Top+1, Width=24, Height=24), icons::Strength);
|
||||
|
||||
@objView = Gui3DObject(this, recti(34, 24, 156, 98));
|
||||
|
||||
@cargo = GuiCargoDisplay(objView, Alignment(Left, Top, Right, Top+25));
|
||||
|
||||
@defIcon = GuiSprite(this, Alignment(Right-44, Top+25, Width=40, Height=40));
|
||||
defIcon.desc = icons::Defense;
|
||||
setMarkupTooltip(defIcon, locale::TT_IS_DEFENDING);
|
||||
defIcon.visible = false;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return cast<Orbital>(Obj) !is null;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = cast<Orbital>(Obj);
|
||||
@objView.object = Obj;
|
||||
lastUpdate = -INFINITY;
|
||||
statusUpdate = 0.f;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
SkinStyle style = isSelectable ? SS_SelectablePopup : SS_GenericPopupBG;
|
||||
if(selected)
|
||||
flags |= SF_Active;
|
||||
if(isSelectable && Hovered)
|
||||
flags |= SF_Hovered;
|
||||
skin.draw(style, flags, bgPos, obj.owner.color);
|
||||
if(obj.owner.flag !is null)
|
||||
obj.owner.flag.draw(
|
||||
objView.absolutePosition.aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0),
|
||||
obj.owner.color * Color(0xffffff30));
|
||||
|
||||
objView.draw();
|
||||
|
||||
if(cargo.visible)
|
||||
drawRectangle(cargo.absolutePosition, Color(0x00000040));
|
||||
|
||||
//Construction display
|
||||
if(hasConstruction) {
|
||||
recti plPos = objView.absolutePosition;
|
||||
const Font@ ft = skin.getFont(FT_Small);
|
||||
int sz = ft.getLineHeight() * 2 + 6;
|
||||
Color nameCol(0xffffffff);
|
||||
if(!cons.started)
|
||||
nameCol = Color(0xff0000ff);
|
||||
ft.draw(plPos.resized(0, sz, 0.0, 1.0),
|
||||
cons.name, locale::ELLIPSIS, nameCol, 0.5, 0.0);
|
||||
|
||||
string prog = toString(cons.progress * 100.f, 0)+"%";
|
||||
if(cons.type == CT_DryDock)
|
||||
prog += " / "+toString(cons.pct * 100.f, 0)+"%";
|
||||
ft.draw(plPos.resized(0, sz - ft.getLineHeight(), 0.0, 1.0),
|
||||
prog, locale::ELLIPSIS, Color(0xffffffff), 0.5, 0.0);
|
||||
|
||||
drawConstructible(cons, plPos.resized(0, plPos.size.height - sz + 6));
|
||||
}
|
||||
|
||||
objView.visible = false;
|
||||
BaseGuiElement::draw();
|
||||
objView.visible = true;
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void updateStrengthBar() {
|
||||
double curStr = 0, totStr = 0;
|
||||
if(obj.hasLeaderAI) {
|
||||
curStr = obj.getFleetStrength() * 0.001;
|
||||
totStr = obj.getFleetMaxStrength() * 0.001;
|
||||
}
|
||||
else {
|
||||
curStr = obj.dps * obj.efficiency * (obj.health + obj.armor) * 0.001;
|
||||
totStr = obj.dps * (obj.maxHealth + obj.maxArmor) * 0.001;
|
||||
}
|
||||
|
||||
if(totStr == 0) {
|
||||
strength.progress = 0.f;
|
||||
strength.frontColor = Color(0xff6a00ff);
|
||||
strength.text = "--";
|
||||
}
|
||||
else {
|
||||
strength.progress = curStr / totStr;
|
||||
if(strength.progress > 1.001f) {
|
||||
strength.progress = 1.f;
|
||||
strength.font = FT_Bold;
|
||||
}
|
||||
else {
|
||||
strength.font = FT_Normal;
|
||||
}
|
||||
|
||||
strength.frontColor = Color(0xff6a00ff).interpolate(Color(0xffc600ff), strength.progress);
|
||||
strength.text = standardize(curStr);
|
||||
strength.tooltip = locale::FLEET_STRENGTH+": "+standardize(curStr)+"/"+standardize(totStr);
|
||||
}
|
||||
}
|
||||
|
||||
float statusUpdate = 0.f;
|
||||
void update() {
|
||||
if(frameTime - 0.2 < lastUpdate)
|
||||
return;
|
||||
lastUpdate = frameTime;
|
||||
|
||||
bool owned = obj.owner is playerEmpire;
|
||||
if(!isSelectable)
|
||||
selected = separated && isSelected(obj);
|
||||
|
||||
//Update static info
|
||||
name.text = obj.name;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
if(obj.isDisabled)
|
||||
name.color = colors::Red;
|
||||
else
|
||||
name.color = colors::White;
|
||||
|
||||
//Update hp display
|
||||
double curHP = obj.health + obj.armor;
|
||||
double maxHP = max(obj.maxHealth + obj.maxArmor, 0.001);
|
||||
|
||||
Color high(0x00ff00ff);
|
||||
Color low(0xff0000ff);
|
||||
|
||||
health.progress = curHP / maxHP;
|
||||
health.frontColor = low.interpolate(high, health.progress);
|
||||
health.text = standardize(curHP)+" / "+standardize(maxHP);
|
||||
|
||||
defIcon.visible = playerEmpire.isDefending(obj);
|
||||
|
||||
updateStrengthBar();
|
||||
|
||||
//Find master obj
|
||||
Object@ fromMaster = obj;
|
||||
if(obj.hasMaster())
|
||||
@fromMaster = obj.getMaster();
|
||||
|
||||
//Update cargo
|
||||
cargo.visible = fromMaster.hasCargo && fromMaster.cargoTypes > 0;
|
||||
if(cargo.visible)
|
||||
cargo.update(fromMaster);
|
||||
|
||||
//Update construction
|
||||
Object@ constructObj = fromMaster;
|
||||
if(owned && constructObj.hasConstruction) {
|
||||
DataList@ list = constructObj.getConstructionQueue(1);
|
||||
hasConstruction = receive(list, cons);
|
||||
}
|
||||
else {
|
||||
const Design@ dsg = obj.getDesign(OV_DRY_Design);
|
||||
if(dsg !is null) {
|
||||
cons.type = CT_DryDock;
|
||||
@cons.obj = obj;
|
||||
@cons.dsg = dsg;
|
||||
cons.prog = obj.getValue(OV_DRY_Progress);
|
||||
cons.pct = obj.getValue(OV_DRY_Financed);
|
||||
cons.started = true;
|
||||
cons.id = -1;
|
||||
hasConstruction = true;
|
||||
}
|
||||
else {
|
||||
hasConstruction = false;
|
||||
}
|
||||
}
|
||||
|
||||
//Update statuses
|
||||
statusUpdate -= frameLength;
|
||||
if(statusUpdate <= 0.f) {
|
||||
array<Status> statuses;
|
||||
if(obj.statusEffectCount > 0)
|
||||
statuses.syncFrom(obj.getStatusEffects());
|
||||
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 = GuiSprite(this, recti_area(6, 25+25*i, 25, 25));
|
||||
@statusIcons[i] = icon;
|
||||
}
|
||||
|
||||
auto@ status = statuses[i];
|
||||
icon.desc = status.type.icon;
|
||||
setMarkupTooltip(icon, format("[b]$1[/b]\n$2", status.type.name, status.type.description));
|
||||
}
|
||||
statusUpdate += 1.f;
|
||||
}
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,152 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.Gui3DObject;
|
||||
import util.formatting;
|
||||
import pickups;
|
||||
import icons;
|
||||
from overlays.ShipPopup import ShipPopup;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
|
||||
class PickupPopup : Popup {
|
||||
GuiText@ title;
|
||||
GuiText@ name;
|
||||
GuiMarkupText@ description;
|
||||
Gui3DObject@ objView;
|
||||
Pickup@ obj;
|
||||
double lastUpdate = -INFINITY;
|
||||
ShipPopup@ ship;
|
||||
|
||||
PickupPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(180, 220);
|
||||
|
||||
@title = GuiText(this, Alignment(Left+4, Top+4, Right-4, Top+24), locale::PICKUP_PROTECTING);
|
||||
title.font = FT_Bold;
|
||||
title.vertAlign = 0.0;
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+20, Right-4, Top+46));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+3, Top+46, Right-4, Top+130));
|
||||
|
||||
@description = GuiMarkupText(this, Alignment(Left+8, Top+134, Right-8, Bottom-4));
|
||||
|
||||
@ship = ShipPopup(null);
|
||||
ship.mouseLinked = true;
|
||||
ship.updateAbsolutePosition();
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void remove() {
|
||||
ship.remove();
|
||||
Popup::remove();
|
||||
}
|
||||
|
||||
void set_visible(bool v) {
|
||||
ship.visible = v;
|
||||
Popup::set_visible(v);
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return Obj.isPickup;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = cast<Pickup>(Obj);
|
||||
@objView.object = Obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
skin.draw(SS_Panel, SF_Normal, bgPos);
|
||||
skin.draw(SS_BG3D, SF_Normal, objView.absolutePosition);
|
||||
skin.draw(SS_SubTitle, SF_Normal, recti_area(bgPos.topLeft+vec2i(2,1), vec2i(bgPos.width-5, 45)));
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
Ship@ prot = cast<Ship>(obj.getProtector());
|
||||
if(!separated && prot !is null && prot.valid) {
|
||||
if(ship.get() !is prot)
|
||||
ship.set(prot);
|
||||
ship.update();
|
||||
ship.visible = true;
|
||||
|
||||
objOffset = vec2i(200, 0);
|
||||
mouseOffset = objOffset;
|
||||
|
||||
title.text = locale::PICKUP_PROTECTING;
|
||||
title.color = colors::Artifact;
|
||||
}
|
||||
else {
|
||||
ship.visible = false;
|
||||
|
||||
objOffset = vec2i();
|
||||
mouseOffset = objOffset;
|
||||
|
||||
title.text = locale::PICKUP_UNPROTECTED;
|
||||
title.color = colors::Green;
|
||||
}
|
||||
|
||||
if(frameTime - 0.2 < lastUpdate)
|
||||
return;
|
||||
|
||||
lastUpdate = frameTime;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
const PickupType@ type = getPickupType(obj.PickupType);
|
||||
if(type !is null)
|
||||
description.text = type.description;
|
||||
else
|
||||
description.text = "--";
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,557 @@
|
||||
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;
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,347 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.GuiImage;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.GuiResources;
|
||||
import elements.MarkupTooltip;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiStatusBox;
|
||||
import elements.GuiCargoDisplay;
|
||||
import biomes;
|
||||
import orbitals;
|
||||
from obj_selection import isSelected;
|
||||
import constructible;
|
||||
import util.constructible_view;
|
||||
import util.obj_locate;
|
||||
import util.icon_view;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
import statuses;
|
||||
|
||||
const uint CONSTRUCTION_SLIDESHOW_TIMER = 2.0;
|
||||
|
||||
class PlanetPopup : Popup {
|
||||
Constructible[] cons;
|
||||
uint consDisp = 0;
|
||||
double consDispTimer = 0;
|
||||
|
||||
GuiText@ name;
|
||||
GuiText@ ownerName;
|
||||
|
||||
array<GuiStatusBox@> statusIcons;
|
||||
Gui3DObject@ objView;
|
||||
GuiSprite@ defIcon;
|
||||
|
||||
BaseGuiElement@ popBox;
|
||||
GuiSprite@ popIcon;
|
||||
GuiText@ popValue;
|
||||
|
||||
BaseGuiElement@ loyBox;
|
||||
GuiSprite@ loyIcon;
|
||||
GuiText@ loyValue;
|
||||
|
||||
GuiResourceGrid@ resources;
|
||||
|
||||
GuiSkinElement@ statusBox;
|
||||
|
||||
GuiProgressbar@ health;
|
||||
|
||||
GuiCargoDisplay@ cargo;
|
||||
|
||||
Planet@ pl;
|
||||
bool selected = false;
|
||||
bool showOrbitalConstruction = true;
|
||||
double lastUpdate = -INFINITY;
|
||||
|
||||
PlanetPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(190, 160);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+50, Top+6, Right-4, Top+28));
|
||||
@ownerName = GuiText(this, Alignment(Left+48, Top+28, Right-6, Top+46));
|
||||
ownerName.horizAlign = 1.0;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+4, Top+50, Right-4, Top+120));
|
||||
|
||||
@cargo = GuiCargoDisplay(objView, Alignment(Left, Top, Right, Top+25));
|
||||
|
||||
@defIcon = GuiSprite(this, Alignment(Left+4, Top+50, Width=40, Height=40));
|
||||
defIcon.desc = icons::Defense;
|
||||
setMarkupTooltip(defIcon, locale::TT_IS_DEFENDING);
|
||||
defIcon.visible = false;
|
||||
|
||||
GuiSkinElement band(this, Alignment(Left+3, Bottom-35, Right-4, Bottom-2), SS_SubTitle);
|
||||
band.color = Color(0xaaaaaaff);
|
||||
|
||||
@popBox = BaseGuiElement(this, Alignment(Left+3, Bottom-67, Left+50, Bottom-35));
|
||||
@popIcon = GuiSprite(popBox, Alignment(Left-12, Top+2, Left+24, Bottom+6));
|
||||
popIcon.desc = icons::Population;
|
||||
@popValue = GuiText(popBox, Alignment(Left+26, Top+12, Right, Height=20));
|
||||
popIcon.tooltip = locale::POPULATION;
|
||||
popValue.tooltip = locale::POPULATION;
|
||||
|
||||
@loyBox = BaseGuiElement(this, Alignment(Right-50, Bottom-67, Right-5, Bottom-35));
|
||||
@loyIcon = GuiSprite(loyBox, Alignment(Right-24, Top+8, Right, Bottom-1));
|
||||
loyIcon.desc = icons::Loyalty;
|
||||
@loyValue = GuiText(loyBox, Alignment(Right-50, Top+12, Right-26, Height=20));
|
||||
loyValue.horizAlign = 1.0;
|
||||
loyIcon.tooltip = locale::LOYALTY;
|
||||
loyValue.tooltip = locale::LOYALTY;
|
||||
|
||||
@resources = GuiResourceGrid(band, Alignment(Left+4, Top+4, Right-3, Bottom-4));
|
||||
|
||||
@statusBox = GuiSkinElement(this, Alignment(Right-2, Top, Right+34, Bottom), SS_PlainBox);
|
||||
statusBox.noClip = true;
|
||||
statusBox.visible = false;
|
||||
|
||||
@health = GuiProgressbar(this, Alignment(Left+8, Top+53, Right-8, Top+75));
|
||||
health.visible = false;
|
||||
|
||||
auto@ healthIcon = GuiSprite(health, Alignment(Left-8, Top-9, Left+24, Bottom-8), icons::Health);
|
||||
healthIcon.noClip = true;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) {
|
||||
return cast<Planet>(obj) !is null;
|
||||
}
|
||||
|
||||
void set(Object@ obj) {
|
||||
@pl = cast<Planet>(obj);
|
||||
@objView.object = obj;
|
||||
@resources.drawFrom = obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return pl;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(pl);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
SkinStyle style = isSelectable ? SS_SelectablePopup : SS_PopupBG;
|
||||
if(selected)
|
||||
flags |= SF_Active;
|
||||
if(isSelectable && Hovered)
|
||||
flags |= SF_Hovered;
|
||||
|
||||
Empire@ owner = pl.visibleOwner;
|
||||
Color color;
|
||||
if(owner !is null) {
|
||||
color = owner.color;
|
||||
skin.draw(style, flags, bgPos, owner.color);
|
||||
if(owner.flag !is null) {
|
||||
vec2i s = objView.absolutePosition.size;
|
||||
owner.flag.draw(
|
||||
objView.absolutePosition
|
||||
.resized(s.x*0.5, s.y*0.5, 0.0, 0.0)
|
||||
.aspectAligned(1.0, horizAlign=0.0, vertAlign=0.0),
|
||||
owner.color * Color(0xffffff40));
|
||||
}
|
||||
}
|
||||
else {
|
||||
skin.draw(style, flags, bgPos, Color(0xffffffff));
|
||||
}
|
||||
|
||||
skin.draw(SS_SubTitle, SF_Normal, recti_area(bgPos.topLeft + vec2i(2,2), vec2i(bgPos.width-5, 50-4)), color);
|
||||
|
||||
if(resources.resources.length != 0)
|
||||
drawPlanetIcon(pl, recti_area(bgPos.topLeft+vec2i(2, 2), vec2i(46,46)), resources.resources[0]);
|
||||
else
|
||||
drawObjectIcon(pl, recti_area(bgPos.topLeft+vec2i(2, 2), vec2i(46,46)));
|
||||
|
||||
objView.draw();
|
||||
|
||||
if(cargo.visible)
|
||||
drawRectangle(cargo.absolutePosition, Color(0x00000040));
|
||||
|
||||
//Construction display
|
||||
if(cons.length != 0) {
|
||||
//Slide through different constructions
|
||||
if(consDisp >= cons.length)
|
||||
consDisp = 0;
|
||||
if(consDispTimer < frameTime) {
|
||||
consDispTimer = frameTime + CONSTRUCTION_SLIDESHOW_TIMER;
|
||||
consDisp = (consDisp + 1) % cons.length;
|
||||
}
|
||||
|
||||
//Draw the construction
|
||||
recti plPos = objView.absolutePosition;
|
||||
const Font@ ft = skin.getFont(FT_Small);
|
||||
drawConstructible(cons[consDisp], plPos, ft);
|
||||
}
|
||||
|
||||
objView.Visible = false;
|
||||
BaseGuiElement::draw();
|
||||
objView.Visible = true;
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(pl);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
emitClicked(PA_Zoom);
|
||||
return true;
|
||||
case OA_DoubleClick:
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(frameTime - 0.2 < lastUpdate)
|
||||
return;
|
||||
if(pl is null)
|
||||
return;
|
||||
lastUpdate = frameTime;
|
||||
|
||||
bool owned = pl.owner is playerEmpire;
|
||||
auto@ owner = pl.visibleOwner;
|
||||
bool colonized = owner !is null && owner.valid;
|
||||
if(!isSelectable)
|
||||
selected = separated && isSelected(pl);
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
defIcon.visible = playerEmpire.isDefending(pl);
|
||||
|
||||
//Update planet name
|
||||
name.text = pl.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
if(colonized && owner !is null) {
|
||||
ownerName.color = owner.color;
|
||||
ownerName.text = owner.name;
|
||||
|
||||
if(ft.getDimension(ownerName.text).x > ownerName.size.width)
|
||||
ownerName.font = FT_Detail;
|
||||
else
|
||||
ownerName.font = FT_Normal;
|
||||
}
|
||||
else {
|
||||
ownerName.color = Color(0xaaaaaaff);
|
||||
ownerName.text = locale::UNCOLONIZED;
|
||||
ownerName.font = FT_Normal;
|
||||
}
|
||||
|
||||
//Update statuses
|
||||
{
|
||||
array<Status> statuses;
|
||||
if(pl.statusEffectCount > 0)
|
||||
statuses.syncFrom(pl.getStatusEffects());
|
||||
if(!pl.visible) {
|
||||
for(uint i = 0, cnt = statuses.length; i < cnt; ++i) {
|
||||
if(statuses[i].type.conditionFrequency <= 0 && statuses[i].type.visibility != StV_Global) {
|
||||
statuses.removeAt(i);
|
||||
--i; --cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
uint prevCnt = statusIcons.length, cnt = statuses.length;
|
||||
for(uint i = cnt; i < prevCnt; ++i)
|
||||
statusIcons[i].remove();
|
||||
statusIcons.length = cnt;
|
||||
statusBox.visible = cnt != 0;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
auto@ icon = statusIcons[i];
|
||||
if(icon is null) {
|
||||
@icon = GuiStatusBox(statusBox, recti_area(2, 2+32*i, 30, 30));
|
||||
icon.noClip = true;
|
||||
@statusIcons[i] = icon;
|
||||
}
|
||||
icon.update(statuses[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//Update health
|
||||
if(pl.Health < pl.MaxHealth) {
|
||||
health.progress = pl.Health / pl.MaxHealth;
|
||||
health.frontColor = colors::Red.interpolate(colors::Green, health.progress);
|
||||
health.text = standardize(pl.Health)+" / "+standardize(pl.MaxHealth);
|
||||
health.visible = true;
|
||||
}
|
||||
else {
|
||||
health.visible = false;
|
||||
}
|
||||
|
||||
//Update resources
|
||||
resources.resources.syncFrom(pl.getAllResources());
|
||||
resources.resources.sortDesc();
|
||||
resources.setSingleMode();
|
||||
|
||||
//Update cargo
|
||||
cargo.visible = pl.cargoTypes > 0;
|
||||
if(cargo.visible)
|
||||
cargo.update(pl);
|
||||
|
||||
//Update population display
|
||||
if(colonized && owner.HasPopulation != 0) {
|
||||
double pop = pl.population, maxPop = pl.maxPopulation;
|
||||
if(pop < 1.0)
|
||||
popValue.text = toString(pl.population, 1);
|
||||
else if(maxPop >= 10.0 || pop >= 10.0)
|
||||
popValue.text = toString(pl.population, 0);
|
||||
else
|
||||
popValue.text = toString(floor(pl.population), 0) + "/" + toString(pl.maxPopulation, 0);
|
||||
popValue.color = Color(0xffffffff);
|
||||
popValue.visible = true;
|
||||
popIcon.visible = true;
|
||||
}
|
||||
else {
|
||||
popValue.visible = false;
|
||||
popIcon.visible = false;
|
||||
}
|
||||
|
||||
//Update loyalty display
|
||||
if(colonized) {
|
||||
loyValue.text = toString(pl.currentLoyalty);
|
||||
loyValue.visible = true;
|
||||
loyIcon.visible = true;
|
||||
}
|
||||
else {
|
||||
loyValue.visible = false;
|
||||
loyIcon.visible = false;
|
||||
}
|
||||
|
||||
//Update construction
|
||||
uint consIndex = 0;
|
||||
if(owned) {
|
||||
if(pl.constructionCount != 0) {
|
||||
if(cons.length <= consIndex)
|
||||
cons.length = consIndex + 1;
|
||||
DataList@ list = pl.getConstructionQueue(1);
|
||||
receive(list, cons[consIndex]);
|
||||
++consIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if(cons.length > consIndex)
|
||||
cons.length = consIndex;
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(pl);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,199 @@
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiButton;
|
||||
from input import activeCamera;
|
||||
|
||||
enum PopupAction {
|
||||
PA_Select,
|
||||
PA_Manage,
|
||||
PA_Zoom,
|
||||
};
|
||||
|
||||
class Popup : BaseGuiElement {
|
||||
GuiButton@ pinButton;
|
||||
GuiButton@ closeButton;
|
||||
|
||||
bool Hovered = false;
|
||||
bool separated = false;
|
||||
bool dragging = false;
|
||||
bool dragged = false;
|
||||
bool mouseLinked = false;
|
||||
bool objLinked = false;
|
||||
bool findPin = false;
|
||||
bool isSelectable = false;
|
||||
double zDist = 0;
|
||||
vec2i dragStart;
|
||||
vec2i objOffset;
|
||||
vec2i mouseOffset;
|
||||
|
||||
Popup(BaseGuiElement@ parent) {
|
||||
super(parent, recti(0, 0, 190, 115));
|
||||
|
||||
@closeButton = GuiButton(this, Alignment(Right-18, Top+6, Right-4, Top+20));
|
||||
closeButton.style = SS_GameTabClose;
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return null;
|
||||
}
|
||||
|
||||
bool onMouseEvent(const MouseEvent& evt, IGuiElement@ source) {
|
||||
//Make sure we can zoom over this
|
||||
switch(evt.type) {
|
||||
case MET_Button_Down:
|
||||
if(evt.button == 0) {
|
||||
if(isSelectable) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
dragging = true;
|
||||
dragged = false;
|
||||
dragStart = mousePos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MET_Button_Up:
|
||||
if(evt.button == 0) {
|
||||
if(dragging) {
|
||||
dragging = false;
|
||||
dragged = false;
|
||||
}
|
||||
else if(isSelectable) {
|
||||
emitClicked(PA_Select);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(evt.button == 1 && !isSelectable) {
|
||||
if(separated)
|
||||
remove();
|
||||
else
|
||||
visible = false;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MET_Scrolled:
|
||||
if(!isSelectable)
|
||||
activeCamera.zoom(evt.y);
|
||||
return true;
|
||||
case MET_Moved:
|
||||
if(dragging) {
|
||||
if(separated) {
|
||||
vec2i moved = mousePos - dragStart;
|
||||
if(!dragged) {
|
||||
if(moved.x < 2 && moved.y < 2)
|
||||
return false;
|
||||
}
|
||||
|
||||
dragged = true;
|
||||
objLinked = false;
|
||||
move(moved);
|
||||
dragStart = mousePos;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
vec2i moved = mousePos - dragStart;
|
||||
if(moved.x < 2 && moved.y < 2)
|
||||
return false;
|
||||
|
||||
separated = true;
|
||||
objLinked = false;
|
||||
mouseLinked = false;
|
||||
dragStart = mousePos;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return BaseGuiElement::onMouseEvent(evt, source);
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Mouse_Entered:
|
||||
if(evt.caller is this)
|
||||
Hovered = true;
|
||||
break;
|
||||
case GUI_Mouse_Left:
|
||||
if(evt.caller is this)
|
||||
Hovered = false;
|
||||
break;
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is closeButton) {
|
||||
remove();
|
||||
return true;
|
||||
}
|
||||
else if(evt.caller is pinButton) {
|
||||
separated = true;
|
||||
objLinked = shiftKey;
|
||||
mouseLinked = false;
|
||||
findPin = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return BaseGuiElement::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void set(Object@ obj) {
|
||||
}
|
||||
|
||||
bool displays(Object@ obj) {
|
||||
return get() is obj;
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(pinButton !is null)
|
||||
pinButton.visible = !mouseLinked && !separated;
|
||||
if(closeButton !is null)
|
||||
closeButton.visible = separated;
|
||||
}
|
||||
|
||||
vec2i objPos(Object@ obj) {
|
||||
if(parent is null)
|
||||
return vec2i();
|
||||
vec2i pos = activeCamera.camera.screenPos(obj.node_position);
|
||||
pos -= parent.absolutePosition.topLeft;
|
||||
pos.x += 16;
|
||||
return pos;
|
||||
}
|
||||
|
||||
void updatePosition(Object@ obj) {
|
||||
if(parent is null)
|
||||
return;
|
||||
zDist = 0;
|
||||
if(obj !is null)
|
||||
obj.focus();
|
||||
if(objLinked && obj !is null) {
|
||||
vec2i newPos = objPos(obj);
|
||||
newPos += objOffset;
|
||||
position = newPos;
|
||||
zDist = obj.node_position.distanceToSQ(activeCamera.camera.position);
|
||||
}
|
||||
else if(mouseLinked) {
|
||||
vec2i newPos = mousePos;
|
||||
newPos -= parent.absolutePosition.topLeft;
|
||||
newPos.x += 16;
|
||||
newPos += mouseOffset;
|
||||
position = newPos;
|
||||
}
|
||||
if(closeButton !is null)
|
||||
closeButton.bringToFront();
|
||||
}
|
||||
|
||||
int opCmp(const Popup@ other) const {
|
||||
if(other is null)
|
||||
return 0;
|
||||
double diff = zDist - other.zDist;
|
||||
if(diff < 0)
|
||||
return -1;
|
||||
else if(diff > 0)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,54 @@
|
||||
import overlays.Popup;
|
||||
from overlays.PlanetPopup import PlanetPopup;
|
||||
|
||||
class ObjectPopupTooltip : ITooltip {
|
||||
Popup@ pop;
|
||||
Object@ obj;
|
||||
|
||||
ObjectPopupTooltip() {
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = Obj;
|
||||
}
|
||||
|
||||
void show(const Skin@ skin, IGuiElement@ elem) {
|
||||
if(obj is null)
|
||||
return;
|
||||
if(pop is null) {
|
||||
@pop = PlanetPopup(null);
|
||||
pop.set(obj);
|
||||
pop.mouseLinked = true;
|
||||
pop.isSelectable = true;
|
||||
pop.update();
|
||||
}
|
||||
pop.visible = true;
|
||||
}
|
||||
|
||||
void hide(const Skin@ skin, IGuiElement@ elem) {
|
||||
if(pop !is null) {
|
||||
pop.remove();
|
||||
@pop = null;
|
||||
}
|
||||
}
|
||||
|
||||
float get_delay() {
|
||||
return 0.f;
|
||||
}
|
||||
|
||||
bool get_persist() {
|
||||
return true;
|
||||
}
|
||||
|
||||
void update(const Skin@ skin, IGuiElement@ elem) {
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(pop is null)
|
||||
return;
|
||||
pop.update();
|
||||
}
|
||||
|
||||
void draw(const Skin@ skin, IGuiElement@ elem) {
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,660 @@
|
||||
import overlays.InfoBar;
|
||||
import elements.BaseGuiElement;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.GuiGroupDisplay;
|
||||
import elements.GuiBlueprint;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.MarkupTooltip;
|
||||
import elements.GuiStatusBox;
|
||||
import ship_groups;
|
||||
import util.formatting;
|
||||
import statuses;
|
||||
from statuses import getStatusID;
|
||||
import icons;
|
||||
from overlays.Construction import ConstructionOverlay;
|
||||
from obj_selection import isSelected, selectObject, clearSelection, addToSelection;
|
||||
from tabs.GalaxyTab import zoomTabTo, openOverlay, toggleSupportOverlay;
|
||||
|
||||
bool SHIP_INFOBAR_EXPANDED = false;
|
||||
|
||||
class ShipInfoBar : InfoBar {
|
||||
Ship@ ship;
|
||||
ConstructionOverlay@ construction;
|
||||
|
||||
GuiBlueprint@ bpdisp;
|
||||
|
||||
GuiText@ name;
|
||||
GuiText@ subsystem;
|
||||
|
||||
GuiSprite@ healthIcon;
|
||||
GuiText@ healthLabel;
|
||||
GuiProgressbar@ health;
|
||||
|
||||
GuiSprite@ supplyIcon;
|
||||
GuiText@ supplyLabel;
|
||||
GuiProgressbar@ supply;
|
||||
|
||||
GuiSprite@ strengthIcon;
|
||||
GuiText@ strengthLabel;
|
||||
GuiProgressbar@ strength;
|
||||
|
||||
GuiSkinElement@ groupBox;
|
||||
GuiGroupDisplay@ groupdisp;
|
||||
|
||||
BaseGuiElement@ buttonBar;
|
||||
GuiButton@ supportsButton;
|
||||
GuiButton@ formationButton;
|
||||
GuiButton@ ordersButton;
|
||||
|
||||
GuiProgressbar@ shield;
|
||||
GuiSprite@ shieldIcon;
|
||||
|
||||
GuiProgressbar@ exp;
|
||||
|
||||
GuiButton@ expandButton;
|
||||
|
||||
ActionBar@ actions;
|
||||
bool expanded = false;
|
||||
|
||||
array<Status> statuses;
|
||||
array<GuiStatusBox@> statusBoxes;
|
||||
|
||||
ShipInfoBar(IGuiElement@ parent) {
|
||||
super(parent);
|
||||
@alignment = Alignment(Left, Bottom-263, Left+395, Bottom);
|
||||
|
||||
@actions = ActionBar(this, vec2i(385, 207));
|
||||
actions.noClip = true;
|
||||
|
||||
int y = -8;
|
||||
|
||||
y += 38;
|
||||
@bpdisp = GuiBlueprint(this, Alignment(Left+4, Top+y, Right-128, Bottom-70));
|
||||
bpdisp.noClip = true;
|
||||
bpdisp.popHover = true;
|
||||
bpdisp.popSize = vec2i(77, 40);
|
||||
bpdisp.horizAlign = 0.25;
|
||||
bpdisp.vertAlign = 1.0;
|
||||
bpdisp.hoverArcs = true;
|
||||
|
||||
@name = GuiText(bpdisp, Alignment(Left+12, Top+28, Right-12, Top+60));
|
||||
name.horizAlign = 0.0;
|
||||
name.vertAlign = 0.0;
|
||||
name.font = FT_Medium;
|
||||
name.stroke = colors::Black;
|
||||
name.visible = false;
|
||||
|
||||
@subsystem = GuiText(bpdisp, Alignment(Left+12, Top+28, Right-12, Top+60));
|
||||
subsystem.horizAlign = 1.0;
|
||||
subsystem.vertAlign = 0.0;
|
||||
subsystem.font = FT_Subtitle;
|
||||
subsystem.stroke = colors::Black;
|
||||
subsystem.visible = false;
|
||||
|
||||
@expandButton = GuiButton(bpdisp, Alignment(Right+75, Bottom-20, Width=20, Height=20), icons::Add);
|
||||
expandButton.noClip = true;
|
||||
expandButton.style = SS_IconButton;
|
||||
|
||||
@health = GuiProgressbar(this, Alignment(Left+8, Bottom-68, Left+200, Bottom-38));
|
||||
health.textHorizAlign = 0.9;
|
||||
|
||||
@healthIcon = GuiSprite(health, Alignment(Left-8, Top-9, Left+24, Bottom-8), icons::Health);
|
||||
healthIcon.noClip = true;
|
||||
@healthLabel = GuiText(health, Alignment(Left+23, Top, Left+100, Bottom));
|
||||
healthLabel.font = FT_Bold;
|
||||
healthLabel.text = locale::HEALTH;
|
||||
healthLabel.stroke = colors::Black;
|
||||
|
||||
@shield = GuiProgressbar(this, Alignment(Left+9, Bottom-48, Left+199, Bottom-38));
|
||||
shield.noClip = true;
|
||||
shield.textHorizAlign = 0.85;
|
||||
shield.textVertAlign = 1.65;
|
||||
shield.visible = false;
|
||||
shield.frontColor = Color(0x429cffff);
|
||||
shield.backColor = Color(0x59a8ff20);
|
||||
|
||||
@shieldIcon = GuiSprite(shield, Alignment(Right-25, Bottom-25, Width=30, Height=30), icons::Shield);
|
||||
shieldIcon.noClip = true;
|
||||
|
||||
@supply = GuiProgressbar(this, Alignment(Left+206, Bottom-68, Right-22, Bottom-38));
|
||||
supply.textHorizAlign = 0.9;
|
||||
|
||||
@supplyIcon = GuiSprite(supply, Alignment(Left-5, Top-6, Left+24, Bottom-8), icons::Supply);
|
||||
supplyIcon.noClip = true;
|
||||
@supplyLabel = GuiText(supply, Alignment(Left+23, Top, Left+100, Bottom));
|
||||
supplyLabel.font = FT_Bold;
|
||||
supplyLabel.text = locale::SUPPLY;
|
||||
supplyLabel.stroke = colors::Black;
|
||||
|
||||
@strength = GuiProgressbar(this, Alignment(Left+8, Bottom-34, Left+200, Bottom-4));
|
||||
strength.textHorizAlign = 0.9;
|
||||
|
||||
@exp = GuiProgressbar(strength, Alignment(Left, Bottom-6, Right, Bottom));
|
||||
exp.frontColor = Color(0xff009eff);
|
||||
exp.backColor = colors::Invisible;
|
||||
exp.visible = false;
|
||||
|
||||
@strengthIcon = GuiSprite(strength, Alignment(Left-5, Top-6, Left+24, Bottom-8), icons::Strength);
|
||||
strengthIcon.noClip = true;
|
||||
@strengthLabel = GuiText(strength, Alignment(Left+23, Top, Left+100, Bottom));
|
||||
strengthLabel.font = FT_Bold;
|
||||
strengthLabel.text = locale::STRENGTH;
|
||||
strengthLabel.stroke = colors::Black;
|
||||
|
||||
@groupBox = GuiSkinElement(this, Alignment(Left+206, Bottom-34, Right-22, Bottom-5), SS_PlainOverlay);
|
||||
@groupdisp = GuiGroupDisplay(groupBox, Alignment(Left+3, Top+4, Right-3, Bottom));
|
||||
groupdisp.horizAlign = 0.0;
|
||||
|
||||
updateAbsolutePosition();
|
||||
setExpanded(SHIP_INFOBAR_EXPANDED);
|
||||
}
|
||||
|
||||
void remove() override {
|
||||
if(construction !is null)
|
||||
construction.remove();
|
||||
InfoBar::remove();
|
||||
}
|
||||
|
||||
void setExpanded(bool value) {
|
||||
if(expanded == value)
|
||||
return;
|
||||
expanded = value;
|
||||
|
||||
bpdisp.alignment.set(Left+4, Top+30, Right-128, Bottom-70);
|
||||
if(expanded) {
|
||||
bpdisp.alignment.padded(0,
|
||||
-480.0/1920.0*double(screenSize.width),
|
||||
-610.0/1080.0*double(screenSize.height),
|
||||
0);
|
||||
bpdisp.horizAlign = 0.5;
|
||||
bpdisp.vertAlign = 0.5;
|
||||
expandButton.alignment.set(Right-34, Bottom-20, Right-34+28, Bottom-20+28);
|
||||
expandButton.setIcon(icons::Minus);
|
||||
}
|
||||
else {
|
||||
bpdisp.horizAlign = 0.25;
|
||||
bpdisp.vertAlign = 1.0;
|
||||
expandButton.alignment.set(Right+75, Bottom-20, Right+75+20, Bottom-20+20);
|
||||
expandButton.setIcon(icons::Add);
|
||||
}
|
||||
|
||||
bpdisp.updateAbsolutePosition();
|
||||
|
||||
bpdisp.popHover = !expanded;
|
||||
name.visible = expanded;
|
||||
subsystem.visible = expanded;
|
||||
}
|
||||
|
||||
void updateActions() {
|
||||
actions.clear();
|
||||
|
||||
if(ship.owner !is null && ship.owner.controlled) {
|
||||
actions.addBasic(ship);
|
||||
actions.addFTL(ship);
|
||||
actions.addAbilities(ship);
|
||||
actions.addEmpireAbilities(ship.owner, ship);
|
||||
actions.addScouting(ship);
|
||||
}
|
||||
|
||||
actions.init(ship);
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) override {
|
||||
return obj.isShip;
|
||||
}
|
||||
|
||||
Object@ get() override {
|
||||
return ship;
|
||||
}
|
||||
|
||||
void set(Object@ obj) override {
|
||||
@ship = cast<Ship>(obj);
|
||||
bpdisp.display(ship);
|
||||
|
||||
setExpanded(SHIP_INFOBAR_EXPANDED);
|
||||
updateActions();
|
||||
}
|
||||
|
||||
bool displays(Object@ obj) override {
|
||||
if(obj is ship)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool showManage(Object@ obj) override {
|
||||
if(construction !is null)
|
||||
construction.remove();
|
||||
if(obj.hasConstruction && obj.owner.controlled) {
|
||||
@construction = ConstructionOverlay(findTab(), obj);
|
||||
return false;
|
||||
}
|
||||
if(!expanded)
|
||||
setExpanded(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
void toggleExpanded() {
|
||||
if(expanded == SHIP_INFOBAR_EXPANDED) {
|
||||
SHIP_INFOBAR_EXPANDED = !SHIP_INFOBAR_EXPANDED;
|
||||
setExpanded(SHIP_INFOBAR_EXPANDED);
|
||||
}
|
||||
else {
|
||||
setExpanded(!expanded);
|
||||
}
|
||||
}
|
||||
|
||||
double lastClick = -INFINITY;
|
||||
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
|
||||
switch(event.type) {
|
||||
case MET_Button_Up: {
|
||||
if(event.button == 0) {
|
||||
if(lastClick > frameTime - double(settings::iDoubleClickMS) / 1000.0) {
|
||||
selectObject(ship);
|
||||
setExpanded(!expanded);
|
||||
if(SHIP_INFOBAR_EXPANDED && !expanded)
|
||||
SHIP_INFOBAR_EXPANDED = false;
|
||||
}
|
||||
else
|
||||
lastClick = frameTime;
|
||||
}
|
||||
else if(event.button == 2) {
|
||||
zoomTabTo(ship);
|
||||
return true;
|
||||
}
|
||||
else if(event.button == 1) {
|
||||
toggleExpanded();
|
||||
return true;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
return InfoBar::onMouseEvent(event, source);
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) override {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is groupdisp) {
|
||||
if(groupdisp.hovered == 0) {
|
||||
selectObject(groupdisp.leader);
|
||||
}
|
||||
else {
|
||||
if(!shiftKey)
|
||||
clearSelection();
|
||||
Object@ leader = groupdisp.leader;
|
||||
GroupData@ dat = groupdisp.groups[groupdisp.hovered-1];
|
||||
for(uint i = 0, cnt = leader.supportCount; i < cnt; ++i) {
|
||||
Ship@ supp = cast<Ship>(leader.supportShip[i]);
|
||||
if(supp !is null && supp.valid && supp.blueprint.design is dat.dsg)
|
||||
addToSelection(supp);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(evt.caller is expandButton) {
|
||||
toggleExpanded();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case GUI_Hover_Changed:
|
||||
if(evt.caller is bpdisp) {
|
||||
updateHealthBar();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return InfoBar::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void updateHealthBar() {
|
||||
if(ship is null)
|
||||
return;
|
||||
|
||||
const Blueprint@ bp = ship.blueprint;
|
||||
const Design@ design = bp.design;
|
||||
const Hull@ hull = design.hull;
|
||||
|
||||
Color high;
|
||||
Color low;
|
||||
|
||||
double curHP = 0, maxHP = 1;
|
||||
if(bpdisp.hexHovered.x < 0 || bpdisp.hexHovered.y < 0) {
|
||||
curHP = bp.currentHP * bp.hpFactor;
|
||||
maxHP = (design.totalHP - bp.removedHP) * bp.hpFactor;
|
||||
|
||||
high = Color(0x00ff00ff);
|
||||
low = Color(0xff0000ff);
|
||||
|
||||
subsystem.visible = false;
|
||||
}
|
||||
else {
|
||||
ObjectLock lock(ship, true);
|
||||
vec2u hex = vec2u(bpdisp.hexHovered);
|
||||
const HexStatus@ status = bp.getHexStatus(hex.x, hex.y);
|
||||
if(status !is null) {
|
||||
maxHP = design.variable(hex, HV_HP) * bp.hpFactor;
|
||||
curHP = maxHP * double(status.hp) / double(0xff);
|
||||
}
|
||||
|
||||
high = Color(0x9768ffff);
|
||||
low = Color(0xff689bff);
|
||||
|
||||
auto@ sys = design.subsystem(hex);
|
||||
auto@ mod = design.module(hex);
|
||||
if(sys !is null && expanded) {
|
||||
subsystem.visible = true;
|
||||
if(mod is null || mod is sys.type.coreModule || mod is sys.type.defaultModule) {
|
||||
if(mod is sys.type.coreModule)
|
||||
subsystem.text = format("$1 ($2)", sys.type.name, locale::SUBSYS_CORE);
|
||||
else
|
||||
subsystem.text = sys.type.name;
|
||||
subsystem.color = sys.type.color;
|
||||
}
|
||||
else {
|
||||
subsystem.text = mod.name;
|
||||
subsystem.color = mod.color;
|
||||
}
|
||||
}
|
||||
else {
|
||||
subsystem.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(!ship.visible)
|
||||
curHP = maxHP;
|
||||
|
||||
health.progress = curHP / maxHP;
|
||||
health.frontColor = low.interpolate(high, health.progress);
|
||||
health.text = standardize(curHP)+" / "+standardize(maxHP);
|
||||
|
||||
double repair = 0.0, combatMod = 1.0;
|
||||
if(design !is null) {
|
||||
repair = design.total(SV_Repair);
|
||||
combatMod *= min(bp.shipEffectiveness, 1.0);
|
||||
}
|
||||
|
||||
string tt = format(locale::TT_SHIP_HEALTH,
|
||||
standardize(curHP), standardize(maxHP),
|
||||
standardize(repair * 1.0/3.0 * combatMod), standardize(repair));
|
||||
|
||||
//Update shields
|
||||
double curShield = ship.Shield;
|
||||
double maxShield = ship.MaxShield;
|
||||
|
||||
if(maxShield != 0) {
|
||||
shield.visible = true;
|
||||
health.textHorizAlign = 0.25;
|
||||
healthLabel.visible = false;
|
||||
|
||||
shield.progress = min(curShield / max(maxShield, 0.01), 1.0);
|
||||
shield.text = standardize(curShield, true);
|
||||
|
||||
double shieldRegen = design.total(SV_ShieldRegen);
|
||||
tt += "\n\n";
|
||||
tt += format(locale::TT_SHIP_SHIELD,
|
||||
standardize(curShield), standardize(maxShield),
|
||||
standardize(shieldRegen));
|
||||
}
|
||||
else {
|
||||
shield.visible = false;
|
||||
health.textHorizAlign = 0.9;
|
||||
healthLabel.visible = true;
|
||||
}
|
||||
|
||||
setMarkupTooltip(health, tt, width = 350);
|
||||
@shield.tooltipObject = health.tooltipObject;
|
||||
}
|
||||
|
||||
void updateStrengthBar() {
|
||||
if(groupdisp.leader is null) {
|
||||
strength.progress = 0.f;
|
||||
strength.text = "-";
|
||||
setMarkupTooltip(strength, "");
|
||||
return;
|
||||
}
|
||||
|
||||
Ship@ leader = cast<Ship>(groupdisp.leader);
|
||||
const Design@ design;
|
||||
if(leader !is null)
|
||||
@design = leader.blueprint.design;
|
||||
|
||||
double curStr = groupdisp.leader.getFleetStrength() * 0.001;
|
||||
double totStr = groupdisp.leader.getFleetMaxStrength() * 0.001;
|
||||
|
||||
if(!ship.visible)
|
||||
curStr = totStr;
|
||||
|
||||
if(totStr == 0) {
|
||||
strength.progress = 0.f;
|
||||
strength.frontColor = Color(0xff6a00ff);
|
||||
strength.text = "-";
|
||||
}
|
||||
else {
|
||||
strength.progress = curStr / totStr;
|
||||
if(strength.progress > 1.001f) {
|
||||
strength.progress = 1.f;
|
||||
strength.font = FT_Bold;
|
||||
}
|
||||
else {
|
||||
strength.font = FT_Normal;
|
||||
}
|
||||
|
||||
strength.frontColor = Color(0xff6a00ff).interpolate(Color(0xffc600ff), strength.progress);
|
||||
strength.text = standardize(curStr);
|
||||
}
|
||||
|
||||
double dps = groupdisp.leader.getFleetDPS();
|
||||
double hp = groupdisp.leader.getFleetHP();
|
||||
|
||||
float curEff = groupdisp.leader.getFleetEffectiveness();
|
||||
float baseEff = groupdisp.leader.getBaseFleetEffectiveness();
|
||||
float eff = curEff / baseEff;
|
||||
|
||||
string tt = format(locale::TT_SHIP_STRENGTH,
|
||||
standardize(curStr), standardize(totStr),
|
||||
standardize(hp), standardize(dps),
|
||||
toString(eff*100.f, 0)+"%");
|
||||
if(baseEff != 1.f)
|
||||
tt += "\n"+format(locale::TT_SHIP_EFF_BONUS,
|
||||
toString((baseEff-1.f)*100.f, 0)+"%");
|
||||
|
||||
if(design !is null) {
|
||||
int curLevel = groupdisp.leader.getStatusStackCountAny(levelStatus);
|
||||
double needExp = groupdisp.leader.getRemainingExp();
|
||||
double totalExp = design.size * (config::EXPERIENCE_BASE_AMOUNT + config::EXPERIENCE_INCREASE_AMOUNT * curLevel);
|
||||
|
||||
if(config::EXPERIENCE_GAIN_FACTOR != 0 && totalExp != 0) {
|
||||
exp.progress = 1.0 - (needExp / totalExp);
|
||||
exp.visible = true;
|
||||
|
||||
tt += "\n\n";
|
||||
tt += format(locale::TT_SHIP_EXPERIENCE,
|
||||
toString(needExp, 0), toString(totalExp, 0),
|
||||
toString(curLevel, 0), toString(totalExp-needExp, 0));
|
||||
}
|
||||
else {
|
||||
exp.visible = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
exp.visible = false;
|
||||
}
|
||||
|
||||
setMarkupTooltip(strength, tt, width = 350);
|
||||
}
|
||||
|
||||
void updateSupplyBar() {
|
||||
double curSup = 0.0;
|
||||
double totSup = 0.0;
|
||||
|
||||
Ship@ leader = cast<Ship>(groupdisp.leader);
|
||||
const Design@ design;
|
||||
if(leader !is null) {
|
||||
curSup = leader.Supply;
|
||||
totSup = leader.MaxSupply;
|
||||
@design = leader.blueprint.design;
|
||||
}
|
||||
|
||||
if(!ship.visible)
|
||||
curSup = totSup;
|
||||
|
||||
if(totSup == 0) {
|
||||
supply.progress = 0.f;
|
||||
supply.frontColor = Color(0xff6a00ff);
|
||||
supply.text = "-";
|
||||
}
|
||||
else {
|
||||
supply.progress = curSup / totSup;
|
||||
if(supply.progress > 1.001f) {
|
||||
supply.progress = 1.f;
|
||||
supply.font = FT_Bold;
|
||||
}
|
||||
else {
|
||||
supply.font = FT_Normal;
|
||||
}
|
||||
|
||||
if(supply.progress < 0.4f)
|
||||
supply.frontColor = Color(0xd53f1eff).interpolate(Color(0xd5cc1eff), supply.progress/0.4f);
|
||||
else
|
||||
supply.frontColor = Color(0x4a9487ff);
|
||||
supply.text = standardize(curSup);
|
||||
}
|
||||
|
||||
double resupply = 0.0;
|
||||
if(design !is null)
|
||||
resupply = design.total(SV_SupplyRate);
|
||||
setMarkupTooltip(supply, format(locale::TT_SHIP_SUPPLY,
|
||||
standardize(curSup), standardize(totSup),
|
||||
standardize(resupply * 0.12f), standardize(resupply)),
|
||||
width = 350);
|
||||
}
|
||||
|
||||
IGuiElement@ elementFromPosition(const vec2i& pos) override {
|
||||
IGuiElement@ elem = BaseGuiElement::elementFromPosition(pos);
|
||||
if(!expanded && (elem is this || elem is bpdisp)) {
|
||||
vec2i relPos = pos - AbsolutePosition.topLeft;
|
||||
bool active = material::ShipInfoBar.isPixelActive(relPos);
|
||||
if(!active)
|
||||
return null;
|
||||
}
|
||||
return elem;
|
||||
}
|
||||
|
||||
double updateTimer = 1.0;
|
||||
void update(double time) override {
|
||||
Empire@ owner = ship.owner;
|
||||
bool owned = owner is playerEmpire;
|
||||
const Blueprint@ bp = ship.blueprint;
|
||||
const Design@ design = bp.design;
|
||||
const Hull@ hull = design.hull;
|
||||
|
||||
if(expanded && !SHIP_INFOBAR_EXPANDED && !ship.selected)
|
||||
setExpanded(false);
|
||||
|
||||
if(design !is bpdisp.design)
|
||||
set(ship);
|
||||
|
||||
if(ship.visible) {
|
||||
@bpdisp.bp = bp;
|
||||
groupdisp.visible = true;
|
||||
}
|
||||
else {
|
||||
@bpdisp.bp = null;
|
||||
groupdisp.visible = false;
|
||||
}
|
||||
|
||||
if(construction !is null) {
|
||||
if(construction.parent is null) {
|
||||
@construction = null;
|
||||
visible = true;
|
||||
}
|
||||
else
|
||||
construction.update(time);
|
||||
}
|
||||
|
||||
//Update ship data
|
||||
name.font = FT_Medium;
|
||||
name.text = formatShipName(ship);
|
||||
if(owner !is null)
|
||||
name.color = owner.color;
|
||||
vec2i dim = name.getTextDimension();
|
||||
int w = clamp(dim.x+26, 160, 1000);
|
||||
if(dim.x > name.size.width) {
|
||||
name.font = FT_Bold;
|
||||
dim = name.getTextDimension();
|
||||
if(dim.x > name.size.width)
|
||||
name.font = FT_Small;
|
||||
}
|
||||
|
||||
//Update statuses
|
||||
Object@ leader = groupdisp.leader;
|
||||
if(leader !is null && leader.statusEffectCount > 0)
|
||||
statuses.syncFrom(leader.getStatusEffects());
|
||||
else
|
||||
statuses.length = 0;
|
||||
|
||||
updateStatusBoxes(bpdisp, statuses, statusBoxes, fromObject=leader);
|
||||
int off = expanded ? -70 : 30;
|
||||
for(uint i = 0, cnt = statusBoxes.length; i < cnt; ++i) {
|
||||
statusBoxes[i].noClip = true;
|
||||
statusBoxes[i].rect = recti_area(bpdisp.size.width+off-36*i, bpdisp.size.height-26, 32,32);
|
||||
}
|
||||
|
||||
//Update whatever health is displayed
|
||||
updateHealthBar();
|
||||
updateSupplyBar();
|
||||
updateStrengthBar();
|
||||
|
||||
//Update group
|
||||
groupdisp.update(ship);
|
||||
|
||||
updateTimer -= time;
|
||||
if(updateTimer <= 0) {
|
||||
updateTimer = 1.0;
|
||||
updateActions();
|
||||
}
|
||||
|
||||
InfoBar::update(time);
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
Color col;
|
||||
Empire@ owner = ship.owner;
|
||||
if(owner !is null)
|
||||
col = owner.color;
|
||||
|
||||
if(!expanded)
|
||||
material::ShipInfoBar.draw(AbsolutePosition.padded(0,0,0,35), col);
|
||||
else {
|
||||
skin.draw(SS_Panel, SF_Normal, bpdisp.absolutePosition.padded(-20,0,0,-100), col);
|
||||
skin.draw(SS_BG3D, SF_Normal, bpdisp.absolutePosition.padded(0,4,4,-12), col);
|
||||
}
|
||||
|
||||
if(actions.visible) {
|
||||
recti pos = actions.absolutePosition;
|
||||
skin.draw(SS_Panel, SF_Normal, recti(pos.topLeft - vec2i(50, 0), pos.botRight + vec2i(0, 20)));
|
||||
}
|
||||
|
||||
skin.draw(SS_InfoBar, SF_Normal, recti_area(vec2i(AbsolutePosition.topLeft.x, AbsolutePosition.botRight.y-40), vec2i(AbsolutePosition.width-13, 45)));
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
};
|
||||
|
||||
InfoBar@ makeShipInfoBar(IGuiElement@ parent, Object@ obj) {
|
||||
ShipInfoBar bar(parent);
|
||||
bar.set(obj);
|
||||
return bar;
|
||||
}
|
||||
|
||||
import void resetGalaxyTabs() from "tabs.GalaxyTab";
|
||||
void postReload(Message& msg) {
|
||||
resetGalaxyTabs();
|
||||
}
|
||||
|
||||
int levelStatus = -1;
|
||||
void init() {
|
||||
levelStatus = getStatusID("ShipLevel");
|
||||
}
|
||||
@@ -0,0 +1,549 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiSkinElement;
|
||||
import elements.GuiBlueprint;
|
||||
import elements.GuiImage;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiGroupDisplay;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.MarkupTooltip;
|
||||
import elements.GuiStatusBox;
|
||||
import elements.GuiCargoDisplay;
|
||||
import constructible;
|
||||
import util.constructible_view;
|
||||
import ship_groups;
|
||||
import icons;
|
||||
import biomes;
|
||||
import statuses;
|
||||
from statuses import getStatusID;
|
||||
import util.icon_view;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
from obj_selection import isSelected, selectObject, clearSelection, addToSelection;
|
||||
|
||||
class ShipPopup : Popup {
|
||||
Object@ origObject;
|
||||
Ship@ ship;
|
||||
|
||||
Constructible cons;
|
||||
bool hasConstruction = false;
|
||||
|
||||
array<GuiStatusBox@> statusIcons;
|
||||
GuiBlueprint@ bpdisp;
|
||||
GuiText@ name;
|
||||
GuiText@ ownerName;
|
||||
|
||||
GuiSprite@ shieldIcon;
|
||||
GuiProgressbar@ health;
|
||||
GuiProgressbar@ strength;
|
||||
GuiProgressbar@ exp;
|
||||
GuiProgressbar@ shield;
|
||||
GuiProgressbar@ supply;
|
||||
|
||||
GuiCargoDisplay@ cargo;
|
||||
GuiGroupDisplay@ groupdisp;
|
||||
|
||||
bool selected = false;
|
||||
|
||||
ShipPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(190, 220);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+40, Top+6, Right-4, Top+28));
|
||||
@ownerName = GuiText(this, Alignment(Left+40, Top+28, Right-6, Top+46));
|
||||
ownerName.horizAlign = 1.0;
|
||||
|
||||
@bpdisp = GuiBlueprint(this, Alignment(Left+4, Top+50, Right-4, Bottom-80));
|
||||
bpdisp.popHover = true;
|
||||
bpdisp.popSize = vec2i(77, 40);
|
||||
|
||||
@cargo = GuiCargoDisplay(bpdisp, Alignment(Left, Top, Right, Top+25));
|
||||
|
||||
GuiSkinElement band(this, Alignment(Left+3, Bottom-80, Right-4, Bottom-50), SS_NULL);
|
||||
|
||||
@health = GuiProgressbar(band, Alignment(Left, Top, Right, Bottom));
|
||||
health.tooltip = locale::HEALTH;
|
||||
|
||||
@shield = GuiProgressbar(band, Alignment(Left+1, Top+20, Right-1, Bottom));
|
||||
shield.noClip = true;
|
||||
shield.tooltip = locale::SHIELD_STRENGTH;
|
||||
shield.textHorizAlign = 0.85;
|
||||
shield.textVertAlign = 1.65;
|
||||
shield.visible = false;
|
||||
shield.frontColor = Color(0x429cffff);
|
||||
shield.backColor = Color(0x59a8ff20);
|
||||
|
||||
GuiSprite healthIcon(band, Alignment(Left, Top, Width=30, Height=30), icons::Health);
|
||||
healthIcon.noClip = true;
|
||||
|
||||
@shieldIcon = GuiSprite(band, Alignment(Right-23, Bottom-23, Width=30, Height=30), icons::Shield);
|
||||
shieldIcon.visible = false;
|
||||
|
||||
GuiSkinElement strband(this, Alignment(Left+3, Bottom-50, Right-4, Bottom-30), SS_NULL);
|
||||
|
||||
@strength = GuiProgressbar(strband, Alignment(Left+0, Top, Right-0.5f, Bottom));
|
||||
strength.tooltip = locale::FLEET_STRENGTH;
|
||||
|
||||
@exp = GuiProgressbar(strength, Alignment(Left, Bottom-4, Right, Bottom));
|
||||
exp.frontColor = Color(0xff009eff);
|
||||
exp.backColor = colors::Invisible;
|
||||
exp.visible = false;
|
||||
|
||||
GuiSprite strIcon(strband, Alignment(Left, Top, Left+24, Bottom), icons::Strength);
|
||||
|
||||
@supply = GuiProgressbar(strband, Alignment(Left+0.5f, Top, Right-1, Bottom));
|
||||
supply.tooltip = locale::SUPPLY;
|
||||
|
||||
GuiSprite supIcon(strband, Alignment(Right-24, Top, Right, Bottom), icons::Supply);
|
||||
|
||||
@groupdisp = GuiGroupDisplay(this, Alignment(Left+8, Bottom-31, Right-8, Bottom-3));
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void remove() {
|
||||
Popup::remove();
|
||||
}
|
||||
|
||||
bool compatible(Object@ obj) {
|
||||
return cast<Ship>(obj) !is null;
|
||||
}
|
||||
|
||||
void set(Object@ obj) {
|
||||
if(!obj.valid)
|
||||
return;
|
||||
if(origObject is null)
|
||||
@origObject = obj;
|
||||
@ship = cast<Ship>(obj);
|
||||
bpdisp.display(ship);
|
||||
|
||||
if(ship.MaxShield > 0) {
|
||||
shield.visible = true;
|
||||
shieldIcon.visible = true;
|
||||
health.textHorizAlign = 0.3;
|
||||
health.textVertAlign = 0.25;
|
||||
}
|
||||
else {
|
||||
shield.visible = false;
|
||||
shieldIcon.visible = false;
|
||||
health.textHorizAlign = 0.5;
|
||||
health.textVertAlign = 0.5;
|
||||
}
|
||||
statusUpdate = 0.f;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return ship;
|
||||
}
|
||||
|
||||
bool displays(Object@ obj) {
|
||||
return obj is ship;
|
||||
}
|
||||
|
||||
double dblClick = 0;
|
||||
bool onMouseEvent(const MouseEvent& evt, IGuiElement@ source) {
|
||||
if(source is name) {
|
||||
switch(evt.type) {
|
||||
case MET_Button_Up:
|
||||
if(evt.button == 0 && !dragged) {
|
||||
dragging = false;
|
||||
if(size.width == 800)
|
||||
size = vec2i(1200, 910);
|
||||
else if(size.width == 380)
|
||||
size = vec2i(800, 640);
|
||||
else if(size.width == 190)
|
||||
size = vec2i(380, 360);
|
||||
else
|
||||
size = vec2i(190, 220);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if(source is bpdisp) {
|
||||
switch(evt.type) {
|
||||
case MET_Button_Up:
|
||||
if(!dragged) {
|
||||
dragging = false;
|
||||
if(evt.button == 0) {
|
||||
if(frameTime < dblClick) {
|
||||
emitClicked(PA_Manage);
|
||||
}
|
||||
else {
|
||||
emitClicked(PA_Select);
|
||||
dblClick = frameTime + 0.2;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else if(evt.button == 1) {
|
||||
openContextMenu(ship);
|
||||
return true;
|
||||
}
|
||||
else if(evt.button == 2) {
|
||||
emitClicked(PA_Zoom);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
return Popup::onMouseEvent(evt, source);
|
||||
}
|
||||
|
||||
vec2i objPos(Object@ obj) {
|
||||
return Popup::objPos(origObject);
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is groupdisp) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
if(groupdisp.hovered == 0) {
|
||||
if(cast<Ship>(groupdisp.leader) !is null) {
|
||||
selectObject(groupdisp.leader);
|
||||
set(groupdisp.leader);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(!shiftKey)
|
||||
clearSelection();
|
||||
Object@ leader = groupdisp.leader;
|
||||
GroupData@ dat = groupdisp.groups[groupdisp.hovered-1];
|
||||
bool found = false;
|
||||
for(uint i = 0, cnt = leader.supportCount; i < cnt; ++i) {
|
||||
Ship@ supp = cast<Ship>(leader.supportShip[i]);
|
||||
if(supp !is null && supp.blueprint.design is dat.dsg) {
|
||||
addToSelection(supp);
|
||||
if(!found) {
|
||||
set(supp);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GUI_Hover_Changed:
|
||||
if(evt.caller is bpdisp) {
|
||||
updateHealthBar();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(ship);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
if(selected)
|
||||
flags |= SF_Hovered;
|
||||
|
||||
Color col(0xffffffff);
|
||||
Empire@ owner;
|
||||
if(ship !is null) {
|
||||
@owner = ship.owner;
|
||||
if(owner !is null)
|
||||
col = owner.color;
|
||||
}
|
||||
|
||||
skin.draw(SS_ShipPopupBG, flags, bgPos, col);
|
||||
|
||||
if(owner !is null && owner.flag !is null) {
|
||||
vec2i s = bpdisp.absolutePosition.size;
|
||||
owner.flag.draw(
|
||||
bpdisp.absolutePosition
|
||||
.resized(s.x*0.5, s.y*0.5, 1.0, 0.0)
|
||||
.aspectAligned(1.0, horizAlign=1.0, vertAlign=0.0),
|
||||
owner.color * Color(0xffffff40));
|
||||
}
|
||||
|
||||
skin.draw(SS_SubTitle, SF_Normal, recti_area(bgPos.topLeft + vec2i(2,2), vec2i(bgPos.width-5, 50-4)), col);
|
||||
drawFleetIcon(ship, recti_area(bgPos.topLeft+vec2i(-2, 2), vec2i(46,46)), showStrength=false);
|
||||
|
||||
bpdisp.draw();
|
||||
|
||||
//Construction display
|
||||
if(hasConstruction) {
|
||||
recti plPos = bpdisp.absolutePosition;
|
||||
plPos.topLeft.y += plPos.height / 2;
|
||||
drawRectangle(plPos, Color(0x00000040));
|
||||
|
||||
const Font@ ft = skin.getFont(FT_Small);
|
||||
int sz = ft.getLineHeight() * 2 + 6;
|
||||
Color nameCol(0xffffffff);
|
||||
if(!cons.started)
|
||||
nameCol = Color(0xff0000ff);
|
||||
ft.draw(plPos.resized(0, sz, 0.0, 1.0),
|
||||
cons.name, locale::ELLIPSIS, nameCol, 0.5, 0.0);
|
||||
|
||||
string prog = toString(cons.progress * 100.f, 0)+"%";
|
||||
if(cons.type == CT_DryDock)
|
||||
prog += " / "+toString(cons.pct * 100.f, 0)+"%";
|
||||
ft.draw(plPos.resized(0, sz - ft.getLineHeight(), 0.0, 1.0),
|
||||
prog, locale::ELLIPSIS, Color(0xffffffff), 0.5, 0.0);
|
||||
|
||||
drawConstructible(cons, plPos.resized(0, plPos.size.height - sz + 6));
|
||||
}
|
||||
|
||||
if(cargo.visible)
|
||||
drawRectangle(cargo.absolutePosition, Color(0x00000040));
|
||||
|
||||
bpdisp.visible = false;
|
||||
Popup::draw();
|
||||
bpdisp.visible = true;
|
||||
}
|
||||
|
||||
void updateHealthBar() {
|
||||
if(ship is null)
|
||||
return;
|
||||
|
||||
const Blueprint@ bp = ship.blueprint;
|
||||
const Design@ design = bp.design;
|
||||
const Hull@ hull = design.hull;
|
||||
|
||||
Color high;
|
||||
Color low;
|
||||
|
||||
double curHP = 0, maxHP = 1;
|
||||
if(bpdisp.hexHovered.x < 0 || bpdisp.hexHovered.y < 0) {
|
||||
curHP = bp.currentHP * bp.hpFactor;
|
||||
maxHP = (design.totalHP - bp.removedHP) * bp.hpFactor;
|
||||
|
||||
high = Color(0x00ff00ff);
|
||||
low = Color(0xff0000ff);
|
||||
}
|
||||
else {
|
||||
vec2u hex = vec2u(bpdisp.hexHovered);
|
||||
const HexStatus@ status = bp.getHexStatus(hex.x, hex.y);
|
||||
if(status !is null) {
|
||||
maxHP = design.variable(hex, HV_HP) * bp.hpFactor;
|
||||
curHP = maxHP * double(status.hp) / double(0xff);
|
||||
}
|
||||
|
||||
high = Color(0x9768ffff);
|
||||
low = Color(0xff689bff);
|
||||
}
|
||||
|
||||
if(!ship.visible)
|
||||
curHP = maxHP;
|
||||
|
||||
health.progress = curHP / maxHP;
|
||||
health.frontColor = low.interpolate(high, health.progress);
|
||||
if(shield.visible)
|
||||
health.text = standardize(curHP);
|
||||
else
|
||||
health.text = standardize(curHP)+" / "+standardize(maxHP);
|
||||
}
|
||||
|
||||
void updateStrengthBar() {
|
||||
if(groupdisp.leader is null)
|
||||
return;
|
||||
|
||||
double curStr = groupdisp.leader.getFleetStrength() * 0.001;
|
||||
double totStr = groupdisp.leader.getFleetMaxStrength() * 0.001;
|
||||
|
||||
Ship@ leader = cast<Ship>(groupdisp.leader);
|
||||
const Design@ design;
|
||||
if(leader !is null)
|
||||
@design = leader.blueprint.design;
|
||||
|
||||
if(!ship.visible)
|
||||
curStr = totStr;
|
||||
|
||||
if(totStr == 0) {
|
||||
strength.progress = 0.f;
|
||||
strength.frontColor = Color(0xff6a00ff);
|
||||
strength.text = "--";
|
||||
}
|
||||
else {
|
||||
strength.progress = curStr / totStr;
|
||||
if(strength.progress > 1.001f) {
|
||||
strength.progress = 1.f;
|
||||
strength.font = FT_Bold;
|
||||
}
|
||||
else {
|
||||
strength.font = FT_Normal;
|
||||
}
|
||||
|
||||
strength.frontColor = Color(0xff6a00ff).interpolate(Color(0xffc600ff), strength.progress);
|
||||
strength.text = standardize(curStr);
|
||||
strength.tooltip = locale::FLEET_STRENGTH+": "+standardize(curStr)+"/"+standardize(totStr);
|
||||
}
|
||||
|
||||
if(design !is null) {
|
||||
int curLevel = groupdisp.leader.getStatusStackCountAny(levelStatus);
|
||||
double needExp = groupdisp.leader.getRemainingExp();
|
||||
double totalExp = design.size * (config::EXPERIENCE_BASE_AMOUNT + config::EXPERIENCE_INCREASE_AMOUNT * curLevel);
|
||||
|
||||
if(config::EXPERIENCE_GAIN_FACTOR != 0 && totalExp != 0) {
|
||||
exp.progress = 1.0 - (needExp / totalExp);
|
||||
exp.visible = true;
|
||||
}
|
||||
else {
|
||||
exp.visible = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
exp.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
void updateSupplyBar() {
|
||||
double curSup = 0.0;
|
||||
double totSup = 0.0;
|
||||
|
||||
Ship@ leader = cast<Ship>(groupdisp.leader);
|
||||
if(ship.MaxSupply >= 0) {
|
||||
curSup = ship.Supply;
|
||||
totSup = ship.MaxSupply;
|
||||
}
|
||||
else if(leader !is null) {
|
||||
curSup = leader.Supply;
|
||||
totSup = leader.MaxSupply;
|
||||
}
|
||||
|
||||
if(!ship.visible)
|
||||
curSup = totSup;
|
||||
|
||||
if(totSup == 0) {
|
||||
supply.progress = 0.f;
|
||||
supply.frontColor = Color(0xff6a00ff);
|
||||
supply.text = "--";
|
||||
}
|
||||
else {
|
||||
supply.progress = curSup / totSup;
|
||||
if(supply.progress > 1.001f) {
|
||||
supply.progress = 1.f;
|
||||
supply.font = FT_Bold;
|
||||
}
|
||||
else {
|
||||
supply.font = FT_Normal;
|
||||
}
|
||||
|
||||
if(supply.progress < 0.5f)
|
||||
supply.frontColor = Color(0xd53f1eff).interpolate(Color(0xd5cc1eff), supply.progress/0.5f);
|
||||
else
|
||||
supply.frontColor = Color(0x4a9487ff);
|
||||
supply.text = standardize(curSup);
|
||||
supply.tooltip = locale::SUPPLY+": "+standardize(curSup)+"/"+standardize(totSup);
|
||||
}
|
||||
}
|
||||
|
||||
float statusUpdate = 0.f;
|
||||
void update() {
|
||||
if(ship is null)
|
||||
return;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
Empire@ owner = ship.owner;
|
||||
bool owned = ship.owner is playerEmpire;
|
||||
const Blueprint@ bp = ship.blueprint;
|
||||
const Design@ design = bp.design;
|
||||
const Hull@ hull = design.hull;
|
||||
|
||||
if(design !is bpdisp.design)
|
||||
set(ship);
|
||||
|
||||
if(ship.visible) {
|
||||
@bpdisp.bp = bp;
|
||||
groupdisp.visible = true;
|
||||
}
|
||||
else {
|
||||
@bpdisp.bp = null;
|
||||
groupdisp.visible = false;
|
||||
}
|
||||
|
||||
//Update name
|
||||
name.text = formatShipName(ship);
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
//Update owner
|
||||
if(owner !is null) {
|
||||
ownerName.color = owner.color;
|
||||
ownerName.text = owner.name;
|
||||
|
||||
if(ft.getDimension(ownerName.text).x > ownerName.size.width)
|
||||
ownerName.font = FT_Detail;
|
||||
else
|
||||
ownerName.font = FT_Normal;
|
||||
}
|
||||
|
||||
//Update whatever health is displayed
|
||||
updateHealthBar();
|
||||
|
||||
//Update the strength display
|
||||
updateStrengthBar();
|
||||
|
||||
//Update the supply display
|
||||
updateSupplyBar();
|
||||
|
||||
//Update cargo
|
||||
cargo.visible = ship.hasCargo && ship.cargoTypes > 0;
|
||||
if(cargo.visible)
|
||||
cargo.update(ship);
|
||||
|
||||
//Update construction
|
||||
if(owned && ship.hasConstruction) {
|
||||
DataList@ list = ship.getConstructionQueue(1);
|
||||
hasConstruction = receive(list, cons);
|
||||
}
|
||||
else {
|
||||
hasConstruction = false;
|
||||
}
|
||||
|
||||
//Update statuses
|
||||
statusUpdate -= frameLength;
|
||||
if(statusUpdate <= 0.f) {
|
||||
array<Status> statuses;
|
||||
if(ship.visible && ship.statusEffectCount > 0)
|
||||
statuses.syncFrom(ship.getStatusEffects());
|
||||
uint prevCnt = statusIcons.length, cnt = statuses.length;
|
||||
for(uint i = cnt; i < prevCnt; ++i)
|
||||
statusIcons[i].remove();
|
||||
statusIcons.length = cnt;
|
||||
int y = 50;
|
||||
if(cargo.visible)
|
||||
y += 25;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
auto@ icon = statusIcons[i];
|
||||
if(icon is null) {
|
||||
@icon = GuiStatusBox(this, recti_area(6, y+25*i, 25, 25));
|
||||
icon.noClip = true;
|
||||
@statusIcons[i] = icon;
|
||||
}
|
||||
icon.update(statuses[i]);
|
||||
}
|
||||
statusUpdate += 1.f;
|
||||
}
|
||||
|
||||
//Update energy display
|
||||
if(shield.visible) {
|
||||
float curshield = ship.Shield;
|
||||
float maxshield = max(ship.MaxShield, 0.01);
|
||||
|
||||
shield.progress = min(curshield / maxshield, 1.0);
|
||||
shield.text = standardize(curshield, true);
|
||||
shield.tooltip = locale::SHIELD_STRENGTH+": "+standardize(curshield)+" / "+standardize(maxshield);
|
||||
}
|
||||
|
||||
//Update group
|
||||
groupdisp.update(ship);
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(ship);
|
||||
}
|
||||
};
|
||||
|
||||
int levelStatus = -1;
|
||||
void init() {
|
||||
levelStatus = getStatusID("ShipLevel");
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
import overlays.Popup;
|
||||
import elements.GuiText;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiSprite;
|
||||
import elements.Gui3DObject;
|
||||
import elements.GuiProgressbar;
|
||||
import elements.MarkupTooltip;
|
||||
import icons;
|
||||
from overlays.ContextMenu import openContextMenu;
|
||||
|
||||
class StarPopup : Popup {
|
||||
GuiText@ name;
|
||||
Gui3DObject@ objView;
|
||||
Star@ obj;
|
||||
double lastUpdate = -INFINITY;
|
||||
|
||||
GuiSprite@ defIcon;
|
||||
|
||||
GuiProgressbar@ health;
|
||||
|
||||
StarPopup(BaseGuiElement@ parent) {
|
||||
super(parent);
|
||||
size = vec2i(150, 110);
|
||||
|
||||
@name = GuiText(this, Alignment(Left+4, Top+2, Right-4, Top+24));
|
||||
name.horizAlign = 0.5;
|
||||
|
||||
@objView = Gui3DObject(this, Alignment(Left+4, Top+25, Right-4, Bottom-4));
|
||||
|
||||
@defIcon = GuiSprite(this, Alignment(Left+4, Top+25, Width=40, Height=40));
|
||||
defIcon.desc = icons::Defense;
|
||||
setMarkupTooltip(defIcon, locale::TT_IS_DEFENDING);
|
||||
defIcon.visible = false;
|
||||
|
||||
@health = GuiProgressbar(this, Alignment(Left+8, Top+28, Right-8, Top+50));
|
||||
health.visible = false;
|
||||
|
||||
auto@ healthIcon = GuiSprite(health, Alignment(Left-8, Top-9, Left+24, Bottom-8), icons::Health);
|
||||
healthIcon.noClip = true;
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
bool compatible(Object@ Obj) {
|
||||
return Obj.isStar;
|
||||
}
|
||||
|
||||
void set(Object@ Obj) {
|
||||
@obj = cast<Star>(Obj);
|
||||
@objView.object = Obj;
|
||||
lastUpdate = -INFINITY;
|
||||
}
|
||||
|
||||
Object@ get() {
|
||||
return obj;
|
||||
}
|
||||
|
||||
void draw() {
|
||||
Popup::updatePosition(obj);
|
||||
recti bgPos = AbsolutePosition;
|
||||
|
||||
uint flags = SF_Normal;
|
||||
SkinStyle style = SS_GenericPopupBG;
|
||||
if(isSelectable && Hovered)
|
||||
flags |= SF_Hovered;
|
||||
|
||||
Color col;
|
||||
Region@ reg = obj.region;
|
||||
if(reg !is null) {
|
||||
Empire@ other = reg.visiblePrimaryEmpire;
|
||||
if(other !is null)
|
||||
col = other.color;
|
||||
}
|
||||
|
||||
skin.draw(style, flags, bgPos, col);
|
||||
if(obj.owner !is null && obj.owner.flag !is null) {
|
||||
obj.owner.flag.draw(
|
||||
objView.absolutePosition.aspectAligned(1.0, horizAlign=1.0, vertAlign=1.0),
|
||||
obj.owner.color * Color(0xffffff30));
|
||||
}
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is objView) {
|
||||
dragging = false;
|
||||
if(!dragged) {
|
||||
switch(evt.value) {
|
||||
case OA_LeftClick:
|
||||
emitClicked(PA_Select);
|
||||
return true;
|
||||
case OA_RightClick:
|
||||
openContextMenu(obj);
|
||||
return true;
|
||||
case OA_MiddleClick:
|
||||
case OA_DoubleClick:
|
||||
if(isSelectable)
|
||||
emitClicked(PA_Select);
|
||||
else
|
||||
emitClicked(PA_Manage);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
return Popup::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
void update() {
|
||||
if(frameTime - 0.2 < lastUpdate)
|
||||
return;
|
||||
|
||||
lastUpdate = frameTime;
|
||||
const Font@ ft = skin.getFont(FT_Normal);
|
||||
|
||||
defIcon.visible = playerEmpire.isDefending(obj.region);
|
||||
|
||||
//Update name
|
||||
name.text = obj.name;
|
||||
if(ft.getDimension(name.text).x > name.size.width)
|
||||
name.font = FT_Detail;
|
||||
else
|
||||
name.font = FT_Normal;
|
||||
|
||||
//Update health
|
||||
if(obj.Health < obj.MaxHealth) {
|
||||
health.progress = obj.Health / obj.MaxHealth;
|
||||
health.frontColor = colors::Red.interpolate(colors::Green, health.progress);
|
||||
health.text = standardize(obj.Health)+" / "+standardize(obj.MaxHealth);
|
||||
health.visible = true;
|
||||
}
|
||||
else {
|
||||
health.visible = false;
|
||||
}
|
||||
|
||||
Popup::update();
|
||||
Popup::updatePosition(obj);
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,182 @@
|
||||
#priority init -100
|
||||
import elements.BaseGuiElement;
|
||||
import elements.GuiButton;
|
||||
import elements.GuiText;
|
||||
import elements.GuiSprite;
|
||||
import elements.GuiMarkupText;
|
||||
import elements.MarkupTooltip;
|
||||
import tabs.Tab;
|
||||
import util.formatting;
|
||||
import timing;
|
||||
from tabs.tabbar import TAB_HEIGHT, GLOBAL_BAR_HEIGHT, ActiveTab;
|
||||
|
||||
class TimeDisplay : BaseGuiElement {
|
||||
GuiSprite@ icon;
|
||||
GuiMarkupText@ text;
|
||||
bool showGameTime = true;
|
||||
bool clicking = false;
|
||||
bool wasAutoPause = false;
|
||||
string prevText;
|
||||
|
||||
GuiButton@ pauseButton;
|
||||
GuiButton@ slowButton;
|
||||
GuiButton@ fastButton;
|
||||
|
||||
TimeDisplay() {
|
||||
super(null, recti_area(-4,TAB_HEIGHT+GLOBAL_BAR_HEIGHT, 210, 30));
|
||||
@icon = GuiSprite(this, recti_area(9,3, 24,24));
|
||||
@text = GuiMarkupText(this, recti_area(35,6, 120,24));
|
||||
setMarkupTooltip(icon, locale::TT_GAMETIME);
|
||||
setMarkupTooltip(text, locale::TT_GAMETIME);
|
||||
|
||||
if(mpClient) {
|
||||
size = vec2i(128, 30);
|
||||
}
|
||||
else {
|
||||
@slowButton = GuiButton(this, Alignment(Right-69, Top+7, Width=18, Height=18));
|
||||
setMarkupTooltip(slowButton, locale::TT_SLOWER);
|
||||
slowButton.allowOtherButtons = true;
|
||||
slowButton.spriteStyle = Sprite(spritesheet::TimeSlow, 0);
|
||||
|
||||
@pauseButton = GuiButton(this, Alignment(Right-48, Top+7, Width=18, Height=18));
|
||||
setMarkupTooltip(pauseButton, locale::TT_PAUSE);
|
||||
pauseButton.allowOtherButtons = true;
|
||||
pauseButton.spriteStyle = Sprite(spritesheet::TimeStop, 0);
|
||||
|
||||
@fastButton = GuiButton(this, Alignment(Right-26, Top+7, Width=18, Height=18));
|
||||
setMarkupTooltip(fastButton, locale::TT_FASTER);
|
||||
fastButton.allowOtherButtons = true;
|
||||
fastButton.spriteStyle = Sprite(spritesheet::TimeHaste, 0);
|
||||
}
|
||||
|
||||
updateAbsolutePosition();
|
||||
}
|
||||
|
||||
void tick(double time) {
|
||||
visible = (ActiveTab.category == TC_Galaxy) && ShowTimeDisplay;
|
||||
|
||||
if(!mpClient && !mpServer && settings::bAutoPause) {
|
||||
bool shouldAutoPause = ActiveTab.category != TC_Galaxy && ActiveTab.category != TC_Diplomacy;
|
||||
if(gameSpeed != 0 && shouldAutoPause) {
|
||||
pause();
|
||||
wasAutoPause = true;
|
||||
}
|
||||
else if(!shouldAutoPause && wasAutoPause && gameSpeed == 0) {
|
||||
pause();
|
||||
wasAutoPause = false;
|
||||
}
|
||||
}
|
||||
string str;
|
||||
if(showGameTime) {
|
||||
double time = floor(gameTime / 60.0);
|
||||
int hrs = floor(time / 60.0);
|
||||
int mins = floor(time - (hrs * 60.0));
|
||||
str = format(locale::TIME_HM, toString(hrs), toString(mins));
|
||||
|
||||
icon.desc = Sprite(spritesheet::MenuIcons, 0);
|
||||
}
|
||||
else {
|
||||
icon.desc = Sprite(material::TimeReal);
|
||||
str = strftime("%H:%M", getSystemTime());
|
||||
}
|
||||
if(abs(gameSpeed - 1.0) > 0.05) {
|
||||
if(mpIsSerializing)
|
||||
str += format(" [color=#fff900]$1[/color]", locale::WAITING);
|
||||
else if(gameSpeed == 0.0)
|
||||
str += format(" [color=#f00]$1[/color]", locale::PAUSED);
|
||||
else if(gameSpeed > 1.0)
|
||||
str += format(" [color=#0f0]x$1[/color]", toString(gameSpeed, 1));
|
||||
else
|
||||
str += format(" [color=#f00]x$1[/color]", toString(gameSpeed, 1));
|
||||
}
|
||||
if(str != prevText) {
|
||||
text.text = str;
|
||||
text.updateAbsolutePosition();
|
||||
prevText = str;
|
||||
}
|
||||
if(pauseButton !is null) {
|
||||
if(gameSpeed == 0.0)
|
||||
pauseButton.spriteStyle = Sprite(spritesheet::TimeResume, 0);
|
||||
else
|
||||
pauseButton.spriteStyle = Sprite(spritesheet::TimeStop, 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool onGuiEvent(const GuiEvent& evt) {
|
||||
switch(evt.type) {
|
||||
case GUI_Mouse_Left:
|
||||
if(evt.caller is this)
|
||||
clicking = false;
|
||||
break;
|
||||
case GUI_Clicked:
|
||||
if(evt.caller is pauseButton) {
|
||||
if(evt.value == 1)
|
||||
speed_default();
|
||||
else
|
||||
pause();
|
||||
return true;
|
||||
}
|
||||
else if(evt.caller is slowButton) {
|
||||
if(evt.value == 1)
|
||||
speed_slowest();
|
||||
else
|
||||
speed_slower();
|
||||
return true;
|
||||
}
|
||||
else if(evt.caller is fastButton) {
|
||||
if(evt.value == 1)
|
||||
speed_fastest();
|
||||
else
|
||||
speed_faster();
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return BaseGuiElement::onGuiEvent(evt);
|
||||
}
|
||||
|
||||
bool onMouseEvent(const MouseEvent& event, IGuiElement@ source) {
|
||||
if(source is this || source is icon || source is text) {
|
||||
switch(event.type) {
|
||||
case MET_Button_Down:
|
||||
if(event.button == 0) {
|
||||
clicking = true;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case MET_Button_Up:
|
||||
if(event.button == 0) {
|
||||
if(clicking) {
|
||||
showGameTime = !showGameTime;
|
||||
if(showGameTime) {
|
||||
setMarkupTooltip(icon, locale::TT_GAMETIME);
|
||||
setMarkupTooltip(text, locale::TT_GAMETIME);
|
||||
}
|
||||
else {
|
||||
setMarkupTooltip(icon, locale::TT_REALTIME);
|
||||
setMarkupTooltip(text, locale::TT_REALTIME);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return BaseGuiElement::onMouseEvent(event, source);
|
||||
}
|
||||
|
||||
void draw() override {
|
||||
skin.draw(SS_TimeDisplay, SF_Normal, AbsolutePosition);
|
||||
BaseGuiElement::draw();
|
||||
}
|
||||
};
|
||||
|
||||
TimeDisplay@ disp;
|
||||
bool ShowTimeDisplay = true;
|
||||
void init() {
|
||||
@disp = TimeDisplay();
|
||||
}
|
||||
|
||||
void tick(double time) {
|
||||
disp.tick(time);
|
||||
}
|
||||
Reference in New Issue
Block a user