Files
2018-07-17 14:15:37 +02:00

617 lines
14 KiB
ActionScript

import ship_groups;
import orders;
import resources;
//Factor of new design cost as minimum for retrofit
const double RETROFIT_MIN_PCT = 0.3;
tidy class LeaderAI : Component_LeaderAI {
OrderDesc[] orders;
Object@[] supports;
GroupData@[] groupData;
uint supplyCapacity = 0;
uint supplyUsed = 0;
double ghostHP = 0.0;
double ghostDPS = 0.0;
double orderedHP = 0.0;
double orderedDPS = 0.0;
double fleetHP = 0.0;
double fleetDPS = 0.0;
double fleetMaxHP = 0.0;
double fleetMaxDPS = 0.0;
double bonusDPS = 0.0;
float fleetEffectiveness = 1.f;
float permanentEffectiveness = 0.f;
float needExperience = 0.f;
bool autoFill = false;
bool autoBuy = false;
bool AllowFillFrom = false;
bool allowSatellites = false;
AutoMode autoMode = AM_AreaBound;
EngagementBehaviour engageBehave = EB_CloseIn;
EngagementRange engageType = ER_SupportMin;
FleetPlaneNode@ node;
float getFleetEffectiveness() const {
return fleetEffectiveness * getBaseFleetEffectiveness();
}
float getBaseFleetEffectiveness() const {
if(permanentEffectiveness < 0)
return pow(0.5, -2.0 * double(permanentEffectiveness));
return 1.0 + permanentEffectiveness;
}
void setFleetEffectiveness(float value) {
fleetEffectiveness = value;
}
uint getAutoMode() {
return uint(autoMode);
}
uint getEngageBehave() {
return uint(engageBehave);
}
uint getEngageType() {
return uint(engageType);
}
void leaderInit(Object& obj) {
if(obj.isShip) {
double formationRad = getFormationRadius(obj);
@node = FleetPlaneNode();
node.establish(obj, formationRad);
}
leaderChangeOwner(obj, null, obj.owner);
}
void leaderDestroy(Object& obj) {
if(obj.owner !is null && obj.owner.valid)
obj.owner.unregisterFleet(obj);
if(node !is null) {
cast<Node>(node).markForDeletion();
@node = null;
}
}
void leaderTick(Object& obj, double time) {
//Set plane visibility
if(node !is null) {
node.visible = obj.isVisibleTo(playerEmpire);
if(obj.region !is null)
node.hintParentObject(obj.region);
}
}
void leaderChangeOwner(Object& obj, Empire@ oldOwner, Empire@ newOwner) {
if(oldOwner !is null && oldOwner.valid)
oldOwner.unregisterFleet(obj);
if(newOwner !is null && newOwner.valid)
newOwner.registerFleet(obj);
}
int getRetrofitCost(const Object& obj) const {
int cost = 0;
bool have = false;
const Ship@ ship = cast<const Ship>(obj);
if(ship !is null) {
const Design@ from = ship.blueprint.design;
if(from !is null) {
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
int fromCost = getBuildCost(from);
int toCost = getBuildCost(to);
cost += max(toCost - fromCost, int(ceil(toCost * RETROFIT_MIN_PCT)));
have = true;
}
}
}
for(uint i = 0, cnt = groupData.length; i < cnt; ++i) {
GroupData@ dat = groupData[i];
const Design@ from = dat.dsg;
if(from is null)
continue;
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
int fromCost = getBuildCost(from);
int toCost = getBuildCost(to);
cost += max(toCost - fromCost, int(ceil(toCost * RETROFIT_MIN_PCT))) * dat.amount;
have = true;
}
}
if(!have)
return -1;
else
return cost;
}
double getRetrofitLabor(const Object& obj) const {
double cost = 0;
bool have = false;
const Ship@ ship = cast<const Ship>(obj);
if(ship !is null) {
const Design@ from = ship.blueprint.design;
if(from !is null) {
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
double fromCost = getLaborCost(from);
double toCost = getLaborCost(to);
cost += max(toCost - fromCost, toCost * RETROFIT_MIN_PCT);
have = true;
}
}
}
for(uint i = 0, cnt = groupData.length; i < cnt; ++i) {
GroupData@ dat = groupData[i];
const Design@ from = dat.dsg;
if(from is null)
continue;
@from = from.mostUpdated();
const Design@ to = from.newest().mostUpdated();
if(from !is to && from.hasTag(ST_Support) == to.hasTag(ST_Support) && from.hasTag(ST_Satellite) == to.hasTag(ST_Satellite)) {
double fromCost = getLaborCost(from);
double toCost = getLaborCost(to);
cost += max(toCost - fromCost, toCost * RETROFIT_MIN_PCT) * dat.amount;
have = true;
}
}
if(!have)
return -1;
else
return cost;
}
double get_GhostHP() const {
return ghostHP;
}
double get_GhostDPS() const {
return ghostDPS;
}
bool get_hasOrders() {
return orders.length != 0;
}
bool hasOrder(uint type, bool checkQueued = false) {
if(orders.length == 0)
return false;
if(!checkQueued)
return orders[0].type == type;
for(int i = orders.length - 1; i >= 0; --i) {
if(orders[i].type == type)
return true;
}
return false;
}
uint get_orderCount() {
return orders.length;
}
string get_orderName(uint num) {
return "(null)"; //TODO
}
uint get_orderType(uint num) {
if(num >= orders.length)
return 0;
return orders[num].type;
}
bool get_orderHasMovement(uint num) {
if(num >= orders.length)
return false;
return orders[num].hasMovement;
}
vec3d get_orderMoveDestination(uint num) {
if(num >= orders.length)
return vec3d();
return orders[num].moveDestination;
}
vec3d get_finalMoveDestination(const Object& obj) {
for(int i = orders.length - 1; i >= 0; --i) {
if(orders[i].hasMovement)
return orders[i].moveDestination;
}
return obj.position;
}
void getSupportGroups() const {
for(uint i = 0, cnt = groupData.length; i < cnt; ++i)
yield(groupData[i]);
}
double getFormationRadius(Object& obj) {
Planet@ pl = cast<Planet>(obj);
if(pl !is null)
return pl.OrbitSize;
return obj.radius * 10.0 + 20.0;
}
uint get_supportCount() {
return supports.length;
}
Object@ get_supportShip(uint index) {
if(index >= supports.length)
return null;
auto@ supp = supports[index];
if(!supp.valid || !supp.initialized)
return null;
return supp;
}
uint get_SupplyUsed() const {
return supplyUsed;
}
uint get_SupplyCapacity() const {
return supplyCapacity;
}
uint get_SupplyAvailable() const {
return supplyCapacity - supplyUsed;
}
void updateFleetStrength(Object& obj) {
double hp = 0.0, dps = 0.0, maxHP = 0.0, maxDPS = 0.0;
if(obj.isShip) {
Ship@ ship = cast<Ship>(obj);
auto@ bp = ship.blueprint;
hp = bp.currentHP * bp.hpFactor + ship.Shield;
dps = ship.DPS * bp.shipEffectiveness;
maxHP = bp.design.totalHP + ship.MaxShield;
maxDPS = ship.MaxDPS;
}
if(obj.isOrbital) {
Orbital@ orb = cast<Orbital>(obj);
hp = orb.health + orb.armor;
maxHP = orb.maxHealth + orb.maxArmor;
maxDPS = orb.dps;
dps = maxDPS * orb.efficiency;
}
for(uint i = 0, cnt = supports.length; i < cnt; ++i) {
Ship@ ship = cast<Ship>(supports[i]);
if(ship !is null) {
auto@ bp = ship.blueprint;
const Design@ dsg = bp.design;
if(dsg is null)
continue;
hp += bp.currentHP * bp.hpFactor + ship.Shield;
dps += ship.DPS * bp.shipEffectiveness;
maxHP += dsg.totalHP + ship.MaxShield;
maxDPS += ship.MaxDPS;
}
}
fleetHP = hp;
fleetDPS = dps;
fleetMaxHP = maxHP;
fleetMaxDPS = maxDPS;
}
void transferSupports(Object& obj, const Design@ ofDesign, uint amount, Object@ transferTo) {
if(!transferTo.hasLeaderAI || ofDesign is null || amount == 0)
return;
int ind = getGroupDataIndex(ofDesign, false);
if(ind == -1)
return;
//Don't try to transfer over our supply cap
amount = min(amount, transferTo.SupplyAvailable / uint(ofDesign.size));
if(amount == 0)
return;
ind = getGroupDataIndex(ofDesign, false);
if(ind == -1)
return;
GroupData@ dat = groupData[ind];
//Transfer real ships over first
uint take = min(dat.amount, amount);
if(take != 0) {
amount -= take;
dat.amount -= take;
supplyUsed -= take * ofDesign.size;
transferTo.addFakeSupports(ofDesign, take);
}
if(dat.totalSize <= 0)
groupData.removeAt(ind);
//Transfer ordered
if(amount == 0)
return;
take = min(dat.ordered, amount);
if(take != 0) {
amount -= take;
dat.ordered -= take;
supplyUsed -= take * ofDesign.size;
transferTo.addSupportOrdered(ofDesign, take);
}
if(dat.totalSize <= 0)
groupData.removeAt(ind);
//Transfer ghosts
if(amount == 0)
return;
take = min(dat.ghost, amount);
if(take != 0) {
dat.ghost -= take;
amount -= take;
supplyUsed -= take * ofDesign.size;
transferTo.addSupportGhosts(ofDesign, take);
}
if(dat.totalSize <= 0)
groupData.removeAt(ind);
}
void orderSupports(Object& obj, const Design@ ofDesign, uint amount) {
if(!obj.owner.canPay(getBuildCost(ofDesign, amount)))
return;
int index = getGroupDataIndex(ofDesign, true);
groupData[index].ordered += amount;
}
void addSupportGhosts(Object& obj, const Design@ ofDesign, uint amount) {
int ind = getGroupDataIndex(ofDesign, true);
GroupData@ dat = groupData[ind];
dat.ghost += amount;
supplyUsed += amount * ofDesign.size;
}
void addSupportOrdered(Object& obj, const Design@ ofDesign, uint amount) {
int ind = getGroupDataIndex(ofDesign, true);
GroupData@ dat = groupData[ind];
dat.ordered += amount;
supplyUsed += amount * ofDesign.size;
}
void addFakeSupports(Object& obj, const Design@ ofDesign, uint amount) {
int ind = getGroupDataIndex(ofDesign, true);
GroupData@ dat = groupData[ind];
dat.amount += amount;
supplyUsed += amount * ofDesign.size;
}
double getRemainingExp() const {
return needExperience;
}
double getFleetHP() const {
return fleetHP;
}
double getFleetDPS() const {
return fleetDPS + bonusDPS;
}
double getFleetStrength(const Object& obj) const {
return fleetHP * (fleetDPS + bonusDPS);
}
double getFleetMaxStrength(const Object& obj) const {
return (fleetMaxHP + ghostHP + orderedHP) * (fleetMaxDPS + bonusDPS + ghostDPS + orderedDPS) * getBaseFleetEffectiveness();
}
bool get_canHaveSatellites() const {
return allowSatellites;
}
int getGroupDataIndex(const Design@ dsg, bool create = false) {
@dsg = dsg.mostUpdated();
for(uint i = 0, cnt = groupData.length; i < cnt; ++i) {
GroupData@ dat = groupData[i];
const Design@ oldDesign = dat.dsg;
const Design@ newDesign = dat.dsg.mostUpdated();
if(newDesign is dsg.mostUpdated()) {
if(oldDesign !is newDesign)
@dat.dsg = newDesign;
return i;
}
}
if(create) {
GroupData dat;
@dat.dsg = dsg.mostUpdated();
groupData.insertLast(dat);
return groupData.length - 1;
}
return -1;
}
uint getGhostCount(const Design@ dsg) const {
int ind = getGroupDataIndex(dsg);
if(ind == -1)
return 0;
return groupData[ind].ghost;
}
void readOrders(Message& msg) {
msg.readAlign();
uint cnt = msg.read_uint();
orders.length = cnt;
for(uint i = 0; i < cnt; ++i)
orders[i].read(msg);
autoMode = AutoMode(msg.readSmall());
engageType = EngagementRange(msg.readSmall());
engageBehave = EngagementBehaviour(msg.readSmall());
msg >> autoFill >> autoBuy >> AllowFillFrom;
}
bool get_autoBuySupports() {
return autoBuy;
}
bool get_autoFillSupports() {
return autoFill;
}
void set_autoBuySupports(bool value) {
autoBuy = value;
}
void set_autoFillSupports(bool value) {
autoFill = value;
}
bool get_allowFillFrom() {
return AllowFillFrom;
}
void set_allowFillFrom(bool value) {
AllowFillFrom = value;
}
void readLeaderData(Message& msg) {
uint cnt = msg.readSmall();
groupData.length = cnt;
double gHP = 0, gDPS = 0;
double oHP = 0, oDPS = 0;
for(uint i = 0; i < cnt; ++i) {
GroupData@ dat = groupData[i];
if(dat is null) {
@dat = GroupData();
@groupData[i] = dat;
}
msg >> dat;
if(dat.dsg !is null) {
double dps = dat.dsg.total(SV_DPS);
gHP += double(dat.ghost) * dat.dsg.totalHP;
gDPS += double(dat.ghost) * dps;
oHP += double(dat.ordered) * dat.dsg.totalHP;
oDPS += double(dat.ordered) * dps;
}
}
ghostHP = gHP;
ghostDPS = gDPS;
orderedHP = oHP;
orderedDPS = oDPS;
bool hadSupply = supplyCapacity > 0;
supplyCapacity = msg.readSmall();
supplyUsed = msg.readSmall();
if(msg.readBit())
fleetEffectiveness = msg.readFixed(0.f, 50.f, 16);
else
fleetEffectiveness = 1.f;
if(msg.readBit())
permanentEffectiveness = msg.readFixed(0.f, 50.f, 16);
else
permanentEffectiveness = 0.f;
if(msg.readBit())
bonusDPS = msg.read_float();
else
bonusDPS = 0.0;
if(msg.readBit())
needExperience = msg.read_float();
else
needExperience = 0.0;
if(node !is null) {
if(hadSupply != (supplyCapacity > 0))
node.hasSupply = supplyCapacity > 0;
}
}
void readGroup(Message& msg) {
bool hadSupports = supports.length > 0;
uint cnt = msg.readSmall();
supports.length = cnt;
for(uint i = 0; i < cnt; ++i)
msg >> supports[i];
readLeaderData(msg);
if(node !is null) {
if(hadSupports != (supports.length > 0))
node.hasFleet = supports.length > 0;
}
}
void readGroupDelta(Message& msg) {
bool hadSupports = supports.length > 0;
//Added
if(msg.readBit()) {
uint cnt = msg.readSmall();
for(uint i = 0; i < cnt; ++i) {
Object@ ship;
msg >> ship;
supports.insertLast(ship);
}
}
//Removed
if(msg.readBit()) {
uint cnt = msg.readSmall();
for(uint i = 0; i < cnt; ++i) {
Object@ ship;
msg >> ship;
supports.remove(ship);
}
}
readLeaderData(msg);
if(node !is null) {
if(hadSupports != (supports.length > 0))
node.hasFleet = supports.length > 0;
}
}
void readLeaderAI(Object& obj, Message& msg) {
readGroup(msg);
readOrders(msg);
msg >> allowSatellites;
}
void readLeaderAIDelta(Message& msg) {
if(msg.readBit())
readGroupDelta(msg);
if(msg.readBit())
readOrders(msg);
}
};