Open source Star Ruler 2 source code!

This commit is contained in:
Lucas de Vries
2018-07-17 14:15:37 +02:00
commit cc307720ff
4342 changed files with 2365070 additions and 0 deletions
+219
View File
@@ -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();
}
+133
View File
@@ -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;
}
+142
View File
@@ -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);
}
};
+194
View File
@@ -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;
}
+156
View File
@@ -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);
}
};
+176
View File
@@ -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
+139
View File
@@ -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);
}
+209
View File
@@ -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;
}
+659
View File
@@ -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;
}
+115
View File
@@ -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);
}
};
+129
View File
@@ -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);
}
};
+265
View File
@@ -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;
}
+428
View File
@@ -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;
}
}
}
// }}}
+283
View File
@@ -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);
}
};
+152
View File
@@ -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);
}
};
+557
View File
@@ -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
+347
View File
@@ -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);
}
};
+199
View File
@@ -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;
}
};
+54
View File
@@ -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
+660
View File
@@ -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");
}
+549
View File
@@ -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");
}
+142
View File
@@ -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
+182
View File
@@ -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);
}