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

660 lines
16 KiB
ActionScript

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;
}