Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,349 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
|
||||
import empire_ai.weasel.Fleets;
|
||||
import empire_ai.weasel.Systems;
|
||||
import empire_ai.weasel.Movement;
|
||||
import empire_ai.weasel.searches;
|
||||
|
||||
import systems;
|
||||
from empire import Pirates;
|
||||
|
||||
class InvasionDefendMission : Mission {
|
||||
FleetAI@ fleet;
|
||||
Region@ targRegion;
|
||||
MoveOrder@ move;
|
||||
bool pending = false;
|
||||
|
||||
Object@ eliminate;
|
||||
|
||||
void save(Fleets& fleets, SaveFile& file) override {
|
||||
fleets.saveAI(file, fleet);
|
||||
file << targRegion;
|
||||
fleets.movement.saveMoveOrder(file, move);
|
||||
file << pending;
|
||||
}
|
||||
|
||||
void load(Fleets& fleets, SaveFile& file) override {
|
||||
@fleet = fleets.loadAI(file);
|
||||
file >> targRegion;
|
||||
@move = fleets.movement.loadMoveOrder(file);
|
||||
file >> pending;
|
||||
}
|
||||
|
||||
bool get_isActive() override {
|
||||
return targRegion !is null;
|
||||
}
|
||||
|
||||
void tick(AI& ai, FleetAI& fleet, double time) override {
|
||||
if(targRegion is null)
|
||||
return;
|
||||
if(move !is null)
|
||||
return;
|
||||
|
||||
//Find stuff to fight
|
||||
if(eliminate is null)
|
||||
@eliminate = findEnemy(targRegion, null, ai.empire.hostileMask);
|
||||
|
||||
if(eliminate !is null) {
|
||||
if(!eliminate.valid) {
|
||||
@eliminate = null;
|
||||
}
|
||||
else {
|
||||
if(!fleet.obj.hasOrders)
|
||||
fleet.obj.addAttackOrder(eliminate);
|
||||
if((fleet.filled < 0.3 || fleet.supplies < 0.3 || fleet.flagshipHealth < 0.5)
|
||||
&& eliminate.getFleetStrength() * 2.0 > fleet.strength
|
||||
&& !pending) {
|
||||
@targRegion = null;
|
||||
@eliminate = null;
|
||||
@move = cast<Fleets>(ai.fleets).returnToBase(fleet, MP_Critical);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void update(AI& ai, Invasion& invasion) {
|
||||
//Manage movement
|
||||
if(move !is null) {
|
||||
if(move.failed || move.completed)
|
||||
@move = null;
|
||||
}
|
||||
|
||||
//Find new regions to go to
|
||||
if(targRegion is null || (!pending && move is null && !invasion.isFighting(targRegion))) {
|
||||
bool ready = fleet.actionableState && move is null;
|
||||
|
||||
DefendSystem@ bestDef;
|
||||
double bestWeight = 0.0;
|
||||
|
||||
for(uint i = 0, cnt = invasion.defending.length; i < cnt; ++i) {
|
||||
auto@ def = invasion.defending[i];
|
||||
double w = randomd(0.9, 1.1);
|
||||
if(!def.fighting) {
|
||||
if(!ready)
|
||||
continue;
|
||||
else
|
||||
w *= 0.1;
|
||||
}
|
||||
|
||||
if(!def.winning) {
|
||||
w *= 10.0;
|
||||
}
|
||||
else {
|
||||
if(!ready)
|
||||
continue;
|
||||
}
|
||||
|
||||
if(def.obj is targRegion)
|
||||
w *= 1.5;
|
||||
|
||||
if(w > bestWeight) {
|
||||
bestWeight = w;
|
||||
@bestDef = def;
|
||||
}
|
||||
}
|
||||
|
||||
if(bestDef !is null && fleet.supplies >= 0.25 && fleet.filled >= 0.2 && fleet.fleetHealth >= 0.2) {
|
||||
@targRegion = bestDef.obj;
|
||||
invasion.pend(targRegion, fleet);
|
||||
pending = true;
|
||||
}
|
||||
}
|
||||
|
||||
//Move to the region we want to go to
|
||||
if(targRegion !is null) {
|
||||
if(move is null) {
|
||||
if(fleet.obj.region !is targRegion) {
|
||||
@eliminate = findEnemy(targRegion, null, ai.empire.hostileMask);
|
||||
if(eliminate is null) {
|
||||
vec3d targPos = targRegion.position;
|
||||
targPos += (targRegion.position - ai.empire.HomeSystem.position).normalized(targRegion.radius * 0.85);
|
||||
|
||||
@move = invasion.movement.move(fleet.obj, targPos, MP_Critical);
|
||||
}
|
||||
else {
|
||||
@move = invasion.movement.move(fleet.obj, eliminate, MP_Critical, nearOnly=true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
//Remove from pending list
|
||||
if(pending) {
|
||||
invasion.unpend(targRegion, fleet);
|
||||
pending = false;
|
||||
}
|
||||
|
||||
//See if we should return to base
|
||||
if(!invasion.isFighting(targRegion) && (fleet.supplies < 0.25 || fleet.filled < 0.5)) {
|
||||
@targRegion = null;
|
||||
@move = invasion.fleets.returnToBase(fleet, MP_Critical);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class DefendSystem {
|
||||
Region@ obj;
|
||||
array<FleetAI@> pending;
|
||||
|
||||
double enemyStrength = 0.0;
|
||||
double ourStrength = 0.0;
|
||||
double remnantStrength = 0.0;
|
||||
double pendingStrength = 0.0;
|
||||
|
||||
void save(Invasion& invasion, SaveFile& file) {
|
||||
file << obj;
|
||||
|
||||
uint cnt = pending.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
invasion.fleets.saveAI(file, pending[i]);
|
||||
|
||||
file << enemyStrength;
|
||||
file << ourStrength;
|
||||
file << remnantStrength;
|
||||
file << pendingStrength;
|
||||
}
|
||||
|
||||
void load(Invasion& invasion, SaveFile& file) {
|
||||
file >> obj;
|
||||
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
auto@ fleet = invasion.fleets.loadAI(file);
|
||||
if(fleet !is null && fleet.obj !is null)
|
||||
pending.insertLast(fleet);
|
||||
}
|
||||
|
||||
file >> enemyStrength;
|
||||
file >> ourStrength;
|
||||
file >> remnantStrength;
|
||||
file >> pendingStrength;
|
||||
}
|
||||
|
||||
void update(AI& ai, Invasion& invasion) {
|
||||
enemyStrength = getTotalFleetStrength(obj, ai.empire.hostileMask);
|
||||
|
||||
ourStrength = getTotalFleetStrength(obj, ai.mask);
|
||||
remnantStrength = getTotalFleetStrength(obj, Pirates.mask);
|
||||
if(gameTime < 10.0 * 60.0)
|
||||
ourStrength += remnantStrength;
|
||||
else if(gameTime < 30.0 * 60.0)
|
||||
ourStrength += remnantStrength * 0.5;
|
||||
|
||||
pendingStrength = 0.0;
|
||||
for(uint i = 0, cnt = pending.length; i < cnt; ++i)
|
||||
pendingStrength += sqrt(pending[i].strength);
|
||||
pendingStrength *= pendingStrength;
|
||||
|
||||
if(obj.PlanetsMask & ai.empire.mask != 0)
|
||||
ai.empire.setDefending(obj, true);
|
||||
}
|
||||
|
||||
bool get_fighting() {
|
||||
return enemyStrength > 0;
|
||||
}
|
||||
|
||||
bool get_winning() {
|
||||
return ourStrength + pendingStrength > enemyStrength;
|
||||
}
|
||||
};
|
||||
|
||||
class Invasion : AIComponent {
|
||||
Fleets@ fleets;
|
||||
Movement@ movement;
|
||||
|
||||
array<DefendSystem@> defending;
|
||||
array<InvasionDefendMission@> tracked;
|
||||
|
||||
void create() {
|
||||
@fleets = cast<Fleets>(ai.fleets);
|
||||
@movement = cast<Movement>(ai.movement);
|
||||
|
||||
ai.behavior.maintenancePerShipSize = 0.0;
|
||||
}
|
||||
|
||||
void save(SaveFile& file) {
|
||||
uint cnt = defending.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
defending[i].save(this, file);
|
||||
|
||||
cnt = tracked.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
fleets.saveMission(file, tracked[i]);
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
uint cnt = 0;
|
||||
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
DefendSystem def;
|
||||
def.load(this, file);
|
||||
defending.insertLast(def);
|
||||
}
|
||||
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
InvasionDefendMission@ miss = cast<InvasionDefendMission>(fleets.loadMission(file));
|
||||
if(miss !is null)
|
||||
tracked.insertLast(miss);
|
||||
}
|
||||
}
|
||||
|
||||
void start() {
|
||||
//Find systems to defend
|
||||
Region@ home = ai.empire.HomeSystem;
|
||||
const SystemDesc@ sys = getSystem(home);
|
||||
for(uint i = 0, cnt = sys.adjacent.length; i < cnt; ++i) {
|
||||
auto@ otherSys = getSystem(sys.adjacent[i]);
|
||||
if(findEnemy(otherSys.object, null, Pirates.mask, fleets=false, stations=true) !is null) {
|
||||
DefendSystem def;
|
||||
@def.obj = otherSys.object;
|
||||
defending.insertLast(def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool isManaging(FleetAI& fleet) {
|
||||
if(fleet.mission is null)
|
||||
return false;
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i) {
|
||||
if(tracked[i] is fleet.mission)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void manage(FleetAI& fleet) {
|
||||
InvasionDefendMission miss;
|
||||
@miss.fleet = fleet;
|
||||
|
||||
fleets.performMission(fleet, miss);
|
||||
tracked.insertLast(miss);
|
||||
}
|
||||
|
||||
void pend(Region@ region, FleetAI& fleet) {
|
||||
for(uint i = 0, cnt = defending.length; i < cnt; ++i ){
|
||||
if(defending[i].obj is region) {
|
||||
defending[i].pending.insertLast(fleet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void unpend(Region@ region, FleetAI& fleet) {
|
||||
for(uint i = 0, cnt = defending.length; i < cnt; ++i ){
|
||||
if(defending[i].obj is region) {
|
||||
defending[i].pending.remove(fleet);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DefendSystem@ getDefending(Region@ region) {
|
||||
for(uint i = 0, cnt = defending.length; i < cnt; ++i ){
|
||||
if(defending[i].obj is region)
|
||||
return defending[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool isFighting(Region@ region) {
|
||||
for(uint i = 0, cnt = defending.length; i < cnt; ++i ){
|
||||
if(defending[i].obj is region)
|
||||
return defending[i].fighting;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint sysUpd = 0;
|
||||
void focusTick(double time) {
|
||||
//All your fleets are belong to us
|
||||
for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) {
|
||||
auto@ flAI = fleets.fleets[i];
|
||||
if(flAI.fleetClass != FC_Combat)
|
||||
continue;
|
||||
if(!isManaging(flAI))
|
||||
manage(flAI);
|
||||
}
|
||||
|
||||
//Update systems we're defending
|
||||
if(defending.length != 0) {
|
||||
sysUpd = (sysUpd+1) % defending.length;
|
||||
defending[sysUpd].update(ai, this);
|
||||
}
|
||||
|
||||
//Make sure our fleets are in the right places
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i)
|
||||
tracked[i].update(ai, this);
|
||||
}
|
||||
};
|
||||
|
||||
AIComponent@ createInvasion() {
|
||||
return Invasion();
|
||||
}
|
||||
Reference in New Issue
Block a user