550 lines
13 KiB
ActionScript
550 lines
13 KiB
ActionScript
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");
|
|
}
|