Files
starruler-linux/scripts/server/empire_ai/weasel/race/StarChildren.as
T
2018-07-17 14:15:37 +02:00

539 lines
13 KiB
ActionScript

import empire_ai.weasel.WeaselAI;
import empire_ai.weasel.race.Race;
import empire_ai.weasel.Colonization;
import empire_ai.weasel.Resources;
import empire_ai.weasel.Construction;
import empire_ai.weasel.Development;
import empire_ai.weasel.Fleets;
import empire_ai.weasel.Movement;
import empire_ai.weasel.Planets;
import empire_ai.weasel.Designs;
import oddity_navigation;
from abilities import getAbilityID;
from statuses import getStatusID;
class HabitatMission : Mission {
Planet@ target;
MoveOrder@ move;
double timer = 0.0;
void save(Fleets& fleets, SaveFile& file) override {
file << target;
file << timer;
fleets.movement.saveMoveOrder(file, move);
}
void load(Fleets& fleets, SaveFile& file) override {
file >> target;
file >> timer;
@move = fleets.movement.loadMoveOrder(file);
}
void start(AI& ai, FleetAI& fleet) override {
uint prior = MP_Normal;
if(gameTime < 30.0 * 60.0)
prior = MP_Critical;
@move = cast<Movement>(ai.movement).move(fleet.obj, target, prior);
}
void tick(AI& ai, FleetAI& fleet, double time) override {
if(move !is null) {
if(move.failed) {
canceled = true;
return;
}
if(move.completed) {
int ablId = cast<StarChildren>(ai.race).habitatAbl;
fleet.obj.activateAbilityTypeFor(ai.empire, ablId, target);
@move = null;
timer = gameTime + 60.0;
}
}
else {
if(target is null || !target.valid || target.quarantined
|| (target.owner !is ai.empire && target.owner.valid)
|| target.inCombat) {
canceled = true;
return;
}
double maxPop = max(double(target.maxPopulation), double(getPlanetLevel(target, target.primaryResourceLevel).population));
double curPop = target.population;
if(curPop >= maxPop) {
completed = true;
return;
}
if(gameTime >= timer) {
int popStatus = cast<StarChildren>(ai.race).popStatus;
if(target.getStatusStackCountAny(popStatus) >= 5) {
canceled = true;
return;
}
}
}
}
};
class LaborMission : Mission {
Planet@ target;
MoveOrder@ move;
double timer = 0.0;
void save(Fleets& fleets, SaveFile& file) override {
file << target;
file << timer;
fleets.movement.saveMoveOrder(file, move);
}
void load(Fleets& fleets, SaveFile& file) override {
file >> target;
file >> timer;
@move = fleets.movement.loadMoveOrder(file);
}
void start(AI& ai, FleetAI& fleet) override {
@move = cast<Movement>(ai.movement).move(fleet.obj, target);
}
void tick(AI& ai, FleetAI& fleet, double time) override {
if(move !is null) {
if(move.failed) {
canceled = true;
return;
}
if(move.completed) {
@move = null;
timer = gameTime + 10.0;
}
}
else {
if(target is null || !target.valid || target.quarantined
|| target.owner !is ai.empire) {
canceled = true;
return;
}
if(gameTime >= timer) {
int popStatus = cast<StarChildren>(ai.race).popStatus;
timer = gameTime + 10.0;
if(target.getStatusStackCountAny(popStatus) >= 10) {
completed = true;
return;
}
}
}
}
};
class StarChildren : Race {
Colonization@ colonization;
Construction@ construction;
Development@ development;
Movement@ movement;
Planets@ planets;
Fleets@ fleets;
Designs@ designs;
DesignTarget@ mothershipDesign;
double idleSince = 0;
array<FleetAI@> motherships;
int habitatAbl = -1;
int popStatus = -1;
array<Planet@> popRequests;
array<Planet@> laborPlanets;
BuildFlagship@ mcBuild;
BuildOrbital@ yardBuild;
void save(SaveFile& file) override {
designs.saveDesign(file, mothershipDesign);
file << idleSince;
construction.saveConstruction(file, mcBuild);
construction.saveConstruction(file, yardBuild);
uint cnt = motherships.length;
file << cnt;
for(uint i = 0; i < cnt; ++i)
fleets.saveAI(file, motherships[i]);
}
void load(SaveFile& file) override {
@mothershipDesign = designs.loadDesign(file);
file >> idleSince;
@mcBuild = cast<BuildFlagship>(construction.loadConstruction(file));
@yardBuild = cast<BuildOrbital>(construction.loadConstruction(file));
uint cnt = 0;
file >> cnt;
for(uint i = 0; i < cnt; ++i) {
auto@ flAI = fleets.loadAI(file);
if(flAI !is null)
motherships.insertLast(flAI);
}
}
void create() override {
@colonization = cast<Colonization>(ai.colonization);
colonization.performColonization = false;
@development = cast<Development>(ai.development);
development.managePlanetPressure = false;
development.buildBuildings = false;
@fleets = cast<Fleets>(ai.fleets);
@construction = cast<Construction>(ai.construction);
@planets = cast<Planets>(ai.planets);
@designs = cast<Designs>(ai.designs);
@movement = cast<Movement>(ai.movement);
@ai.defs.Factory = null;
@ai.defs.LaborStorage = null;
habitatAbl = getAbilityID("MothershipColonize");
popStatus = getAbilityID("MothershipPopulation");
}
void start() override {
//Get the Tier 1 in our home system
{
ResourceSpec spec;
spec.type = RST_Level_Specific;
spec.level = 1;
spec.isForImport = false;
spec.isLevelRequirement = false;
colonization.queueColonize(spec);
}
//Then find a Tier 2 to get
{
ResourceSpec spec;
spec.type = RST_Level_Specific;
spec.level = 2;
spec.isForImport = false;
spec.isLevelRequirement = false;
colonization.queueColonize(spec);
}
//Design a mothership
@mothershipDesign = designs.design(DP_Mothership, 500);
mothershipDesign.targetMaintenance = 300;
mothershipDesign.targetLaborCost = 110;
mothershipDesign.customName = "Mothership";
}
bool requiresPopulation(Planet& target) {
double maxPop = max(double(target.maxPopulation), double(getPlanetLevel(target, target.primaryResourceLevel).population));
double curPop = target.population;
return curPop < maxPop;
}
uint chkInd = 0;
void focusTick(double time) override {
//Detect motherships
for(uint i = 0, cnt = fleets.fleets.length; i < cnt; ++i) {
auto@ flAI = fleets.fleets[i];
if(flAI.fleetClass != FC_Mothership)
continue;
if(motherships.find(flAI) == -1) {
//Add to our tracking list
flAI.obj.autoFillSupports = false;
flAI.obj.allowFillFrom = true;
motherships.insertLast(flAI);
//Add as a factory
construction.registerFactory(flAI.obj);
}
}
for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {
Object@ obj = motherships[i].obj;
if(obj is null || !obj.valid || obj.owner !is ai.empire) {
motherships.removeAt(i);
--i; --cnt;
}
}
//Detect planets that require more population
for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) {
auto@ obj = popRequests[i];
if(obj is null || !obj.valid || obj.owner !is ai.empire) {
popRequests.removeAt(i);
--i; --cnt;
continue;
}
if(!requiresPopulation(obj)) {
popRequests.removeAt(i);
--i; --cnt;
continue;
}
}
for(uint i = 0, cnt = laborPlanets.length; i < cnt; ++i) {
auto@ obj = laborPlanets[i];
if(obj is null || !obj.valid || obj.owner !is ai.empire) {
laborPlanets.removeAt(i);
--i; --cnt;
continue;
}
if(obj.laborIncome < 3.0/60.0) {
laborPlanets.removeAt(i);
--i; --cnt;
continue;
}
}
uint plCnt = planets.planets.length;
for(uint n = 0, cnt = min(15, plCnt); n < cnt; ++n) {
chkInd = (chkInd+1) % plCnt;
auto@ plAI = planets.planets[chkInd];
//Find planets that need population
if(requiresPopulation(plAI.obj)) {
if(popRequests.find(plAI.obj) == -1)
popRequests.insertLast(plAI.obj);
}
//Find planets that have labor
if(plAI.obj.laborIncome >= 3.0/60.0) {
if(laborPlanets.find(plAI.obj) == -1)
laborPlanets.insertLast(plAI.obj);
}
}
//Send motherships to do colonization
uint totalCount = popRequests.length + colonization.awaitingSource.length;
uint motherCount = idleMothershipCount();
/*if(motherCount > totalCount) {*/
for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) {
Planet@ dest = popRequests[i];
if(isColonizing(dest))
continue;
if(dest.inCombat)
continue;
colonizeBest(dest);
}
for(uint i = 0, cnt = colonization.awaitingSource.length; i < cnt; ++i) {
Planet@ dest = colonization.awaitingSource[i].target;
if(isColonizing(dest))
continue;
colonizeBest(dest);
}
/*}*/
/*else {*/
/* for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {*/
/* auto@ flAI = motherships[i];*/
/* if(flAI.mission !is null)*/
/* continue;*/
/* if(isBuildingWithLabor(flAI))*/
/* continue;*/
/* colonizeBest(flAI);*/
/* }*/
/*}*/
if(totalCount != 0)
idleSince = gameTime;
//See if we should build new motherships
uint haveMC = motherships.length;
uint wantMC = 1;
if(gameTime > 20.0 * 60.0)
wantMC += 1;
wantMC = max(wantMC, min(laborPlanets.length, uint(gameTime/(30.0*60.0))));
if(mcBuild !is null && mcBuild.completed)
@mcBuild = null;
if(wantMC > haveMC && mcBuild is null)
@mcBuild = construction.buildFlagship(mothershipDesign, force=true);
if(yardBuild is null && haveMC > 0 && gameTime > 60 && gameTime < 180 && ai.defs.Shipyard !is null) {
Region@ reg = motherships[0].obj.region;
if(reg !is null) {
vec3d pos = reg.position;
vec2d offset = random2d(reg.radius * 0.4, reg.radius * 0.8);
pos.x += offset.x;
pos.z += offset.y;
@yardBuild = construction.buildOrbital(ai.defs.Shipyard, pos);
}
}
if(motherships.length == 1)
@colonization.colonizeWeightObj = motherships[0].obj;
else
@colonization.colonizeWeightObj = null;
//Idle motherships should be sent to go collect labor from labor planets
if(laborPlanets.length != 0) {
for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {
auto@ flAI = motherships[i];
if(flAI.mission !is null)
continue;
if(isAtLaborPlanet(flAI))
continue;
if(i == 0 && idleSince < gameTime-60.0)
continue;
double bestDist = INFINITY;
Planet@ best;
for(uint n = 0, ncnt = laborPlanets.length; n < ncnt; ++n) {
Planet@ check = laborPlanets[n];
if(hasMothershipAt(check))
continue;
double d = movement.getPathDistance(flAI.obj.position, check.position);
if(d < bestDist) {
@best = check;
bestDist = d;
}
}
if(best !is null) {
LaborMission miss;
@miss.target = best;
fleets.performMission(flAI, miss);
}
}
}
}
bool isAtLaborPlanet(FleetAI& flAI) {
auto@ miss = cast<LaborMission>(flAI);
if(miss !is null)
return true;
for(uint i = 0, cnt = laborPlanets.length; i < cnt; ++i) {
if(flAI.obj.isLockedOrbit(laborPlanets[i]))
return true;
}
return false;
}
bool isBuildingWithLabor(FleetAI& flAI) {
auto@ f = construction.get(flAI.obj);
if(f !is null && f.active !is null)
return false;
if(isAtLaborPlanet(flAI))
return true;
return false;
}
bool hasMothershipAt(Planet& pl) {
for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {
auto@ flAI = motherships[i];
auto@ miss = cast<LaborMission>(flAI);
if(miss !is null && miss.target is pl)
return true;
if(flAI.obj.isLockedOrbit(pl))
return true;
}
return false;
}
uint idleMothershipCount() {
uint count = 0;
for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {
if(motherships[i].mission is null)
count += 1;
}
return count;
}
bool isColonizing(Planet& dest) {
for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {
auto@ flAI = motherships[i];
auto@ miss = cast<HabitatMission>(flAI.mission);
if(miss !is null && miss.target is dest)
return true;
}
return false;
}
Planet@ colonizeBest(FleetAI& flAI) {
Planet@ best;
double bestDist = INFINITY;
for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) {
Planet@ dest = popRequests[i];
if(isColonizing(dest))
continue;
if(dest.inCombat)
continue;
double d = movement.getPathDistance(flAI.obj.position, dest.position);
if(d < bestDist) {
@best = dest;
bestDist = d;
}
}
if(best is null) {
for(uint i = 0, cnt = colonization.awaitingSource.length; i < cnt; ++i) {
Planet@ dest = colonization.awaitingSource[i].target;
if(isColonizing(dest))
continue;
double d = movement.getPathDistance(flAI.obj.position, dest.position);
if(d < bestDist) {
@best = dest;
bestDist = d;
}
}
}
if(best !is null) {
HabitatMission miss;
@miss.target = best;
fleets.performMission(flAI, miss);
}
return best;
}
FleetAI@ colonizeBest(Planet& dest) {
FleetAI@ best;
double bestDist = INFINITY;
for(uint i = 0, cnt = motherships.length; i < cnt; ++i) {
auto@ flAI = motherships[i];
if(flAI.mission !is null)
continue;
if(isBuildingWithLabor(flAI))
continue;
double d = movement.getPathDistance(flAI.obj.position, dest.position);
if(d < bestDist) {
@best = flAI;
bestDist = d;
}
}
if(best !is null) {
HabitatMission miss;
@miss.target = dest;
fleets.performMission(best, miss);
}
return best;
}
};
AIComponent@ createStarChildren() {
return StarChildren();
}