Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,616 @@
|
||||
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);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
tidy class SupportAI : Component_SupportAI {
|
||||
void readSupportAI(Object& obj, Message& msg) {
|
||||
Ship@ ship = cast<Ship>(obj);
|
||||
@ship.Leader = msg.readObject();
|
||||
}
|
||||
|
||||
void readSupportAIDelta(Object& obj, Message& msg) {
|
||||
Ship@ ship = cast<Ship>(obj);
|
||||
@ship.Leader = msg.readObject();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user