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 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(Obj) !is null; } void set(Object@ Obj) { @obj = cast(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 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); } };