Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,518 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
import empire_ai.weasel.race.Race;
|
||||
|
||||
import empire_ai.weasel.Colonization;
|
||||
import empire_ai.weasel.Construction;
|
||||
import empire_ai.weasel.Resources;
|
||||
import empire_ai.weasel.Development;
|
||||
import empire_ai.weasel.Movement;
|
||||
import empire_ai.weasel.Planets;
|
||||
import empire_ai.weasel.Orbitals;
|
||||
|
||||
from orbitals import getOrbitalModule, OrbitalModule;
|
||||
from buildings import getBuildingType, BuildingType;
|
||||
from resources import ResourceType, getResource, getResourceID;
|
||||
from statuses import getStatusID;
|
||||
from biomes import getBiomeID;
|
||||
|
||||
enum PlanetClass {
|
||||
PC_Empty,
|
||||
PC_Core,
|
||||
PC_Mine,
|
||||
PC_Transmute,
|
||||
}
|
||||
|
||||
class TrackReplicator {
|
||||
Object@ obj;
|
||||
Planet@ target;
|
||||
bool arrived = false;
|
||||
MoveOrder@ move;
|
||||
BuildingRequest@ build;
|
||||
uint intention = PC_Empty;
|
||||
|
||||
bool get_busy() {
|
||||
if(target is null)
|
||||
return false;
|
||||
if(!arrived || move !is null || build !is null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void save(Ancient& ancient, SaveFile& file) {
|
||||
file << obj;
|
||||
file << target;
|
||||
file << arrived;
|
||||
ancient.movement.saveMoveOrder(file, move);
|
||||
ancient.planets.saveBuildingRequest(file, build);
|
||||
file << intention;
|
||||
}
|
||||
|
||||
void load(Ancient& ancient, SaveFile& file) {
|
||||
file >> obj;
|
||||
file >> target;
|
||||
file >> arrived;
|
||||
@move = ancient.movement.loadMoveOrder(file);
|
||||
@build = ancient.planets.loadBuildingRequest(file);
|
||||
file >> intention;
|
||||
}
|
||||
};
|
||||
|
||||
class Ancient : Race, RaceResources, RaceColonization {
|
||||
Colonization@ colonization;
|
||||
Construction@ construction;
|
||||
Resources@ resources;
|
||||
Planets@ planets;
|
||||
Development@ development;
|
||||
Movement@ movement;
|
||||
Orbitals@ orbitals;
|
||||
|
||||
array<TrackReplicator@> replicators;
|
||||
|
||||
const OrbitalModule@ replicatorMod;
|
||||
|
||||
const BuildingType@ core;
|
||||
const BuildingType@ miner;
|
||||
const BuildingType@ transmuter;
|
||||
|
||||
const BuildingType@ foundry;
|
||||
|
||||
const BuildingType@ depot;
|
||||
const BuildingType@ refinery;
|
||||
const BuildingType@ reinforcer;
|
||||
const BuildingType@ developer;
|
||||
const BuildingType@ compressor;
|
||||
|
||||
int claimStatus = -1;
|
||||
int replicatorStatus = -1;
|
||||
|
||||
int mountainsBiome = -1;
|
||||
|
||||
int oreResource = -1;
|
||||
int baseMatResource = -1;
|
||||
|
||||
bool foundFirstT2 = false;
|
||||
|
||||
void create() {
|
||||
@colonization = cast<Colonization>(ai.colonization);
|
||||
colonization.performColonization = false;
|
||||
|
||||
@resources = cast<Resources>(ai.resources);
|
||||
@construction = cast<Construction>(ai.construction);
|
||||
@movement = cast<Movement>(ai.movement);
|
||||
@planets = cast<Planets>(ai.planets);
|
||||
@orbitals = cast<Orbitals>(ai.orbitals);
|
||||
@planets = cast<Planets>(ai.planets);
|
||||
|
||||
@development = cast<Development>(ai.development);
|
||||
development.managePlanetPressure = false;
|
||||
development.buildBuildings = false;
|
||||
development.colonizeResources = false;
|
||||
|
||||
@replicatorMod = getOrbitalModule("AncientReplicator");
|
||||
|
||||
@transmuter = getBuildingType("AncientTransmuter");
|
||||
@miner = getBuildingType("AncientMiner");
|
||||
@core = getBuildingType("AncientCore");
|
||||
|
||||
@foundry = getBuildingType("AncientFoundry");
|
||||
|
||||
@depot = getBuildingType("AncientDepot");
|
||||
@refinery = getBuildingType("AncientRefinery");
|
||||
@reinforcer = getBuildingType("AncientReinforcer");
|
||||
@developer = getBuildingType("AncientDeveloper");
|
||||
@compressor = getBuildingType("Compressor");
|
||||
|
||||
claimStatus = getStatusID("AncientClaim");
|
||||
replicatorStatus = getStatusID("AncientReplicator");
|
||||
|
||||
mountainsBiome = getBiomeID("Mountains");
|
||||
|
||||
oreResource = getResourceID("OreRate");
|
||||
baseMatResource = getResourceID("BaseMaterial");
|
||||
|
||||
@ai.defs.Factory = null;
|
||||
@ai.defs.LaborStorage = null;
|
||||
}
|
||||
|
||||
void save(SaveFile& file) override {
|
||||
file << foundFirstT2;
|
||||
uint cnt = replicators.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
replicators[i].save(this, file);
|
||||
}
|
||||
|
||||
void load(SaveFile& file) override {
|
||||
file >> foundFirstT2;
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
TrackReplicator t;
|
||||
t.load(this, file);
|
||||
if(t.obj !is null)
|
||||
replicators.insertLast(t);
|
||||
}
|
||||
}
|
||||
|
||||
void levelRequirements(Object& obj, int targetLevel, array<ResourceSpec@>& specs) {
|
||||
//YOLO
|
||||
specs.length = 0;
|
||||
}
|
||||
|
||||
bool orderColonization(ColonizeData& data, Planet@ sourcePlanet) {
|
||||
return true;
|
||||
}
|
||||
|
||||
double getGenericUsefulness(const ResourceType@ type) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
bool hasReplicator(Planet& pl) {
|
||||
for(uint i = 0, cnt = replicators.length; i < cnt; ++i) {
|
||||
if(replicators[i].target is pl)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool isTracking(Object& obj) {
|
||||
for(uint i = 0, cnt = replicators.length; i < cnt; ++i) {
|
||||
if(replicators[i].obj is obj)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void trackReplicator(Object& obj) {
|
||||
TrackReplicator t;
|
||||
@t.obj = obj;
|
||||
|
||||
replicators.insertLast(t);
|
||||
}
|
||||
|
||||
void updateRequests(Planet& pl) {
|
||||
//Handle requests for base materials
|
||||
uint baseMatReqs = 0;
|
||||
baseMatReqs += pl.getBuildingCount(depot.id);
|
||||
baseMatReqs += pl.getBuildingCount(refinery.id);
|
||||
baseMatReqs += pl.getBuildingCount(reinforcer.id);
|
||||
baseMatReqs += pl.getBuildingCount(developer.id);
|
||||
baseMatReqs += pl.getBuildingCount(compressor.id);
|
||||
|
||||
array<ImportData@> curBaseMat;
|
||||
resources.getImportsOf(curBaseMat, baseMatResource, pl);
|
||||
|
||||
if(curBaseMat.length < baseMatReqs) {
|
||||
for(uint i = curBaseMat.length, cnt = baseMatReqs; i < cnt; ++i) {
|
||||
ResourceSpec spec;
|
||||
spec.type = RST_Specific;
|
||||
@spec.resource = getResource(baseMatResource);
|
||||
|
||||
resources.requestResource(pl, spec);
|
||||
}
|
||||
}
|
||||
else if(curBaseMat.length > baseMatReqs) {
|
||||
for(uint i = baseMatReqs, cnt = curBaseMat.length; i < cnt; ++i)
|
||||
resources.cancelRequest(curBaseMat[i]);
|
||||
}
|
||||
|
||||
//Handle requests for ore
|
||||
uint oreReqs = 0;
|
||||
oreReqs += pl.getBuildingCount(foundry.id);
|
||||
|
||||
array<ImportData@> curOre;
|
||||
resources.getImportsOf(curOre, oreResource, pl);
|
||||
|
||||
if(curOre.length < oreReqs) {
|
||||
for(uint i = curOre.length, cnt = oreReqs; i < cnt; ++i) {
|
||||
ResourceSpec spec;
|
||||
spec.type = RST_Specific;
|
||||
@spec.resource = getResource(oreResource);
|
||||
|
||||
resources.requestResource(pl, spec);
|
||||
}
|
||||
}
|
||||
else if(curOre.length > oreReqs) {
|
||||
for(uint i = oreReqs, cnt = curOre.length; i < cnt; ++i)
|
||||
resources.cancelRequest(curOre[i]);
|
||||
}
|
||||
}
|
||||
|
||||
uint plInd = 0;
|
||||
void focusTick(double time) {
|
||||
//Find new replicators
|
||||
for(uint i = 0, cnt = orbitals.orbitals.length; i < cnt; ++i) {
|
||||
auto@ orb = cast<Orbital>(orbitals.orbitals[i].obj);
|
||||
if(orb.coreModule == replicatorMod.id) {
|
||||
if(!isTracking(orb))
|
||||
trackReplicator(orb);
|
||||
}
|
||||
}
|
||||
|
||||
//Update requests for planets
|
||||
if(planets.planets.length != 0) {
|
||||
for(uint n = 0, ncnt = min(planets.planets.length, 10); n < ncnt; ++n) {
|
||||
plInd = (plInd+1) % planets.planets.length;
|
||||
Planet@ pl = planets.planets[plInd].obj;
|
||||
|
||||
if(classify(pl) == PC_Core)
|
||||
updateRequests(pl);
|
||||
}
|
||||
}
|
||||
|
||||
//Manage existing replicators
|
||||
for(uint i = 0, cnt = replicators.length; i < cnt; ++i) {
|
||||
auto@ t = replicators[i];
|
||||
if(t.obj is null || !t.obj.valid || t.obj.owner !is ai.empire) {
|
||||
replicators.removeAt(i);
|
||||
--i; --cnt;
|
||||
continue;
|
||||
}
|
||||
|
||||
if(t.target !is null) {
|
||||
if(!t.target.valid) {
|
||||
@t.target = null;
|
||||
if(!t.arrived)
|
||||
t.obj.stopMoving();
|
||||
t.arrived = false;
|
||||
}
|
||||
else if(t.target.owner !is ai.empire && t.target.owner.valid) {
|
||||
@t.target = null;
|
||||
if(!t.arrived)
|
||||
t.obj.stopMoving();
|
||||
t.arrived = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(t.move !is null) {
|
||||
if(t.move.failed) {
|
||||
@t.move = null;
|
||||
t.arrived = false;
|
||||
}
|
||||
else if(t.move.completed) {
|
||||
if(t.obj.isOrbitingAround(t.target)) {
|
||||
@t.move = null;
|
||||
t.arrived = true;
|
||||
}
|
||||
else if(t.obj.inOrbit) {
|
||||
@t.move = null;
|
||||
t.arrived = false;
|
||||
@t.target = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(t.target !is null && !t.arrived) {
|
||||
@t.move = movement.move(t.obj, t.target);
|
||||
}
|
||||
|
||||
if(t.build !is null) {
|
||||
if(t.build.canceled) {
|
||||
//A build failed, give up on this planet
|
||||
if(log)
|
||||
ai.print("Failed building build", t.target);
|
||||
@t.target = null;
|
||||
@t.build = null;
|
||||
t.arrived = false;
|
||||
}
|
||||
else if(t.build.built) {
|
||||
float progress = t.build.getProgress();
|
||||
if(progress >= 1.f) {
|
||||
if(log)
|
||||
ai.print("Completed building build", t.target);
|
||||
@t.build = null;
|
||||
}
|
||||
else if(progress < -0.5f) {
|
||||
if(log)
|
||||
ai.print("Failed building build location "+t.build.builtAt, t.target);
|
||||
@t.build = null;
|
||||
@t.target = null;
|
||||
t.arrived = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(t.arrived || t.target is null) {
|
||||
if(!t.busy)
|
||||
useReplicator(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint classify(Planet& pl) {
|
||||
int resType = pl.primaryResourceType;
|
||||
if(resType == oreResource)
|
||||
return PC_Mine;
|
||||
if(resType == baseMatResource)
|
||||
return PC_Transmute;
|
||||
uint claims = pl.getStatusStackCountAny(claimStatus);
|
||||
if(claims <= 1)
|
||||
return PC_Empty;
|
||||
if(pl.getBuildingCount(core.id) >= 1)
|
||||
return PC_Core;
|
||||
if(pl.getBuildingCount(transmuter.id) >= 1)
|
||||
return PC_Transmute;
|
||||
if(pl.getBuildingCount(miner.id) >= 1)
|
||||
return PC_Mine;
|
||||
return PC_Empty;
|
||||
}
|
||||
|
||||
bool shouldBeCore(const ResourceType@ type) {
|
||||
if(type.level >= 1)
|
||||
return true;
|
||||
if(type.totalPressure >= 8)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
int openOreRequests(TrackReplicator@ discount = null) {
|
||||
int reqs = 0;
|
||||
for(uint i = 0, cnt = resources.requested.length; i < cnt; ++i) {
|
||||
auto@ req = resources.requested[i];
|
||||
if(req.beingMet)
|
||||
continue;
|
||||
if(req.spec.type != RST_Specific)
|
||||
continue;
|
||||
if(req.spec.resource.id != uint(oreResource))
|
||||
continue;
|
||||
reqs += 1;
|
||||
}
|
||||
for(uint i = 0, cnt = replicators.length; i < cnt; ++i) {
|
||||
auto@ t = replicators[i];
|
||||
if(t is discount)
|
||||
continue;
|
||||
if(t.target is null)
|
||||
continue;
|
||||
if(t.intention == PC_Mine && (t.build is null || t.build.type is miner))
|
||||
reqs -= 1;
|
||||
}
|
||||
return reqs;
|
||||
}
|
||||
|
||||
int openBaseMatRequests(TrackReplicator@ discount = null) {
|
||||
int reqs = 0;
|
||||
for(uint i = 0, cnt = resources.requested.length; i < cnt; ++i) {
|
||||
auto@ req = resources.requested[i];
|
||||
if(req.beingMet)
|
||||
continue;
|
||||
if(req.spec.type != RST_Specific)
|
||||
continue;
|
||||
if(req.spec.resource.id != uint(baseMatResource))
|
||||
continue;
|
||||
reqs += 1;
|
||||
}
|
||||
for(uint i = 0, cnt = replicators.length; i < cnt; ++i) {
|
||||
auto@ t = replicators[i];
|
||||
if(t is discount)
|
||||
continue;
|
||||
if(t.target is null)
|
||||
continue;
|
||||
if(t.intention == PC_Transmute && (t.build is null || t.build.type is transmuter))
|
||||
reqs -= 1;
|
||||
}
|
||||
return reqs;
|
||||
}
|
||||
|
||||
void build(TrackReplicator& t, const BuildingType@ building) {
|
||||
auto@ plAI = planets.getAI(t.target);
|
||||
if(plAI is null)
|
||||
return;
|
||||
if(!t.target.hasStatusEffect(replicatorStatus))
|
||||
return;
|
||||
|
||||
//bool scatter = building is miner || building is transmuter;
|
||||
bool scatter = false;
|
||||
@t.build = planets.requestBuilding(plAI, building, scatter=scatter, moneyType=BT_Colonization);
|
||||
|
||||
if(log)
|
||||
ai.print("Build "+building.name, t.target);
|
||||
}
|
||||
|
||||
void useReplicator(TrackReplicator& t) {
|
||||
if(t.target !is null) {
|
||||
uint type = classify(t.target);
|
||||
switch(type) {
|
||||
case PC_Empty: {
|
||||
const ResourceType@ res = getResource(t.target.primaryResourceType);
|
||||
if(res is null) {
|
||||
@t.target = null;
|
||||
t.arrived = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(shouldBeCore(res)) {
|
||||
build(t, core);
|
||||
}
|
||||
else if(openBaseMatRequests(t) >= openOreRequests(t) || gameTime < 6.0 * 60.0 || !t.target.hasBiome(mountainsBiome)) {
|
||||
build(t, transmuter);
|
||||
}
|
||||
else {
|
||||
build(t, miner);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case PC_Transmute:
|
||||
@t.target = null;
|
||||
t.arrived = false;
|
||||
break;
|
||||
case PC_Mine:
|
||||
@t.target = null;
|
||||
t.arrived = false;
|
||||
break;
|
||||
case PC_Core:
|
||||
build(t, refinery);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Find a new planet to colonize
|
||||
PotentialColonize@ best;
|
||||
double bestWeight = 0.0;
|
||||
|
||||
uint getType = PC_Core;
|
||||
if(openBaseMatRequests() >= 1)
|
||||
getType = PC_Transmute;
|
||||
else if(openOreRequests() >= 1 && gameTime > 6.0 * 60.0)
|
||||
getType = PC_Mine;
|
||||
|
||||
auto@ potentials = colonization.getPotentialColonize();
|
||||
for(uint i = 0, cnt = potentials.length; i < cnt; ++i) {
|
||||
PotentialColonize@ p = potentials[i];
|
||||
if(hasReplicator(p.pl))
|
||||
continue;
|
||||
|
||||
double w = p.weight;
|
||||
if(!foundFirstT2 && p.resource.level >= 2)
|
||||
w *= 100.0;
|
||||
else if((getType == PC_Core) != shouldBeCore(p.resource))
|
||||
w *= 0.6;
|
||||
if(getType == PC_Core && p.resource.level >= 2)
|
||||
w *= 4.0;
|
||||
if(getType == PC_Core && p.resource.level >= 3)
|
||||
w *= 6.0;
|
||||
if(getType == PC_Mine && !p.pl.hasBiome(mountainsBiome))
|
||||
w *= 0.1;
|
||||
if(getType == PC_Core)
|
||||
w *= double(p.pl.totalSurfaceTiles) / 100.0;
|
||||
w /= p.pl.position.distanceTo(t.obj.position)/1000.0;
|
||||
|
||||
if(w > bestWeight) {
|
||||
bestWeight = w;
|
||||
@best = p;
|
||||
}
|
||||
}
|
||||
|
||||
if(best !is null) {
|
||||
@t.target = best.pl;
|
||||
t.intention = shouldBeCore(best.resource) ? uint(PC_Core) : getType;
|
||||
t.arrived = false;
|
||||
if(!foundFirstT2) {
|
||||
if(best.resource.level == 2)
|
||||
foundFirstT2 = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AIComponent@ createAncient() {
|
||||
return Ancient();
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
import empire_ai.weasel.race.Race;
|
||||
|
||||
import empire_ai.weasel.Development;
|
||||
import empire_ai.weasel.Planets;
|
||||
import empire_ai.weasel.Budget;
|
||||
|
||||
import resources;
|
||||
import buildings;
|
||||
import attributes;
|
||||
|
||||
class Devout : Race, RaceDevelopment {
|
||||
Development@ development;
|
||||
Planets@ planets;
|
||||
Budget@ budget;
|
||||
|
||||
const ResourceType@ altarResource;
|
||||
const BuildingType@ altar;
|
||||
|
||||
int coverAttrib = -1;
|
||||
|
||||
BuildingRequest@ altarBuild;
|
||||
Planet@ focusAltar;
|
||||
|
||||
double considerTimer = 0.0;
|
||||
|
||||
void save(SaveFile& file) {
|
||||
planets.saveBuildingRequest(file, altarBuild);
|
||||
file << focusAltar;
|
||||
file << considerTimer;
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
@altarBuild = planets.loadBuildingRequest(file);
|
||||
file >> focusAltar;
|
||||
file >> considerTimer;
|
||||
}
|
||||
|
||||
void create() {
|
||||
@planets = cast<Planets>(ai.planets);
|
||||
@development = cast<Development>(ai.development);
|
||||
@budget = cast<Budget>(ai.budget);
|
||||
|
||||
@altarResource = getResource("Altar");
|
||||
|
||||
@altar = getBuildingType("Altar");
|
||||
|
||||
coverAttrib = getEmpAttribute("AltarSupportedPopulation");
|
||||
}
|
||||
|
||||
void start() {
|
||||
auto@ data = ai.empire.getPlanets();
|
||||
Object@ obj;
|
||||
while(receive(data, obj)) {
|
||||
Planet@ pl = cast<Planet>(obj);
|
||||
if(pl !is null){
|
||||
if(pl.primaryResourceType == altarResource.id) {
|
||||
@focusAltar = pl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool shouldBeFocus(Planet& pl, const ResourceType@ resource) override {
|
||||
if(resource is altarResource)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void focusTick(double time) override {
|
||||
//Handle our current altar build
|
||||
if(altarBuild !is null) {
|
||||
if(altarBuild.built) {
|
||||
@focusAltar = altarBuild.plAI.obj;
|
||||
@altarBuild = null;
|
||||
}
|
||||
else if(altarBuild.canceled) {
|
||||
@altarBuild = null;
|
||||
}
|
||||
}
|
||||
|
||||
//Handle our focused altar
|
||||
if(focusAltar !is null) {
|
||||
if(!focusAltar.valid || focusAltar.owner !is ai.empire || focusAltar.primaryResourceType != altarResource.id) {
|
||||
@focusAltar = null;
|
||||
}
|
||||
}
|
||||
|
||||
//If we aren't covering our entire population, find new planets to make into altars
|
||||
double coverage = ai.empire.getAttribute(coverAttrib);
|
||||
double population = ai.empire.TotalPopulation;
|
||||
|
||||
if(coverage >= population || altarBuild !is null)
|
||||
return;
|
||||
|
||||
bool makeNewAltar = true;
|
||||
if(focusAltar !is null) {
|
||||
auto@ foc = development.getFocus(focusAltar);
|
||||
if(foc !is null && int(foc.obj.level) >= foc.targetLevel) {
|
||||
foc.targetLevel += 1;
|
||||
considerTimer = gameTime + 180.0;
|
||||
makeNewAltar = false;
|
||||
}
|
||||
else {
|
||||
makeNewAltar = gameTime > considerTimer;
|
||||
}
|
||||
}
|
||||
|
||||
if(makeNewAltar) {
|
||||
if(budget.canSpend(BT_Development, 300)) {
|
||||
//Turn our most suitable planet into an altar
|
||||
PlanetAI@ bestBuild;
|
||||
double bestWeight = 0.0;
|
||||
|
||||
for(uint i = 0, cnt = planets.planets.length; i < cnt; ++i) {
|
||||
auto@ plAI = planets.planets[i];
|
||||
double w = randomd(0.9, 1.1);
|
||||
|
||||
if(plAI.resources !is null && plAI.resources.length != 0) {
|
||||
auto@ res = plAI.resources[0].resource;
|
||||
if(res.level == 0 && !res.limitlessLevel)
|
||||
w *= 5.0;
|
||||
if(res.cls !is null)
|
||||
w *= 0.5;
|
||||
if(res.level > 0)
|
||||
w /= pow(2.0, res.level);
|
||||
}
|
||||
else {
|
||||
w *= 100.0;
|
||||
}
|
||||
|
||||
if(w > bestWeight) {
|
||||
bestWeight = w;
|
||||
@bestBuild = plAI;
|
||||
}
|
||||
}
|
||||
|
||||
if(bestBuild !is null) {
|
||||
@altarBuild = planets.requestBuilding(bestBuild, altar, expire=60.0);
|
||||
considerTimer = gameTime + 120.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AIComponent@ createDevout() {
|
||||
return Devout();
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
import empire_ai.weasel.race.Race;
|
||||
|
||||
import empire_ai.weasel.Colonization;
|
||||
import empire_ai.weasel.Construction;
|
||||
import empire_ai.weasel.Resources;
|
||||
import empire_ai.weasel.Scouting;
|
||||
import empire_ai.weasel.Orbitals;
|
||||
import empire_ai.weasel.Budget;
|
||||
|
||||
from orbitals import getOrbitalModuleID;
|
||||
from constructions import ConstructionType, getConstructionType;
|
||||
|
||||
class Extragalactic : Race {
|
||||
Colonization@ colonization;
|
||||
Construction@ construction;
|
||||
Scouting@ scouting;
|
||||
Orbitals@ orbitals;
|
||||
Resources@ resources;
|
||||
Budget@ budget;
|
||||
|
||||
array<OrbitalAI@> beacons;
|
||||
OrbitalAI@ masterBeacon;
|
||||
|
||||
int beaconMod = -1;
|
||||
|
||||
array<ImportData@> imports;
|
||||
array<const ConstructionType@> beaconBuilds;
|
||||
|
||||
void create() {
|
||||
@colonization = cast<Colonization>(ai.colonization);
|
||||
colonization.performColonization = false;
|
||||
colonization.queueColonization = false;
|
||||
|
||||
@scouting = cast<Scouting>(ai.scouting);
|
||||
scouting.buildScouts = false;
|
||||
|
||||
@orbitals = cast<Orbitals>(ai.orbitals);
|
||||
beaconMod = getOrbitalModuleID("Beacon");
|
||||
|
||||
@construction = cast<Construction>(ai.construction);
|
||||
@resources = cast<Resources>(ai.resources);
|
||||
@budget = cast<Budget>(ai.budget);
|
||||
|
||||
beaconBuilds.insertLast(getConstructionType("BeaconHealth"));
|
||||
beaconBuilds.insertLast(getConstructionType("BeaconWeapons"));
|
||||
beaconBuilds.insertLast(getConstructionType("BeaconLabor"));
|
||||
}
|
||||
|
||||
void save(SaveFile& file) override {
|
||||
uint cnt = beacons.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
orbitals.saveAI(file, beacons[i]);
|
||||
orbitals.saveAI(file, masterBeacon);
|
||||
|
||||
cnt = imports.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
resources.saveImport(file, imports[i]);
|
||||
}
|
||||
|
||||
void load(SaveFile& file) override {
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
auto@ b = orbitals.loadAI(file);
|
||||
if(b !is null && b.obj !is null)
|
||||
beacons.insertLast(b);
|
||||
}
|
||||
@masterBeacon = orbitals.loadAI(file);
|
||||
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
auto@ imp = resources.loadImport(file);
|
||||
if(imp !is null)
|
||||
imports.insertLast(imp);
|
||||
}
|
||||
}
|
||||
|
||||
uint prevBeacons = 0;
|
||||
void focusTick(double time) {
|
||||
//Find our beacons
|
||||
for(uint i = 0, cnt = beacons.length; i < cnt; ++i) {
|
||||
auto@ b = beacons[i];
|
||||
if(b is null || b.obj is null || !b.obj.valid || b.obj.owner !is ai.empire) {
|
||||
if(b.obj !is null)
|
||||
resources.killImportsTo(b.obj);
|
||||
beacons.removeAt(i);
|
||||
--i; --cnt;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint i = 0, cnt = orbitals.orbitals.length; i < cnt; ++i) {
|
||||
auto@ orb = orbitals.orbitals[i];
|
||||
Orbital@ obj = cast<Orbital>(orb.obj);
|
||||
if(obj !is null && obj.coreModule == uint(beaconMod)) {
|
||||
if(beacons.find(orb) == -1)
|
||||
beacons.insertLast(orb);
|
||||
}
|
||||
}
|
||||
|
||||
//Find our master beacon
|
||||
if(masterBeacon !is null) {
|
||||
Orbital@ obj = cast<Orbital>(masterBeacon.obj);
|
||||
if(obj is null || !obj.valid || obj.owner !is ai.empire || obj.hasMaster())
|
||||
@masterBeacon = null;
|
||||
}
|
||||
else {
|
||||
for(uint i = 0, cnt = beacons.length; i < cnt; ++i) {
|
||||
auto@ b = beacons[i];
|
||||
Orbital@ obj = cast<Orbital>(b.obj);
|
||||
if(!obj.hasMaster()) {
|
||||
@masterBeacon = b;
|
||||
ai.empire.setDefending(obj, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
scouting.buildScouts = gameTime > 5.0 * 60.0;
|
||||
if(prevBeacons < beacons.length && masterBeacon !is null && gameTime > 10.0) {
|
||||
for(int i = beacons.length-1; i >= int(prevBeacons); --i) {
|
||||
//Make sure we order a scout at each beacon
|
||||
if(!scouting.buildScouts) {
|
||||
BuildFlagshipSourced build(scouting.scoutDesign);
|
||||
build.moneyType = BT_Military;
|
||||
@build.buildAt = masterBeacon.obj;
|
||||
if(beacons[i] !is masterBeacon)
|
||||
@build.buildFrom = beacons[i].obj;
|
||||
|
||||
construction.build(build, force=true);
|
||||
}
|
||||
|
||||
//Set the beacon to fill up other stuff
|
||||
beacons[i].obj.allowFillFrom = true;
|
||||
}
|
||||
prevBeacons = beacons.length;
|
||||
}
|
||||
|
||||
//Handle with importing labor and defense to our master beacon
|
||||
if(masterBeacon !is null) {
|
||||
if(imports.length == 0) {
|
||||
//Request labor and defense at our beacon
|
||||
{
|
||||
ResourceSpec spec;
|
||||
spec.type = RST_Pressure_Type;
|
||||
spec.pressureType = TR_Labor;
|
||||
|
||||
imports.insertLast(resources.requestResource(masterBeacon.obj, spec));
|
||||
}
|
||||
{
|
||||
ResourceSpec spec;
|
||||
spec.type = RST_Pressure_Type;
|
||||
spec.pressureType = TR_Defense;
|
||||
|
||||
imports.insertLast(resources.requestResource(masterBeacon.obj, spec));
|
||||
}
|
||||
{
|
||||
ResourceSpec spec;
|
||||
spec.type = RST_Pressure_Level0;
|
||||
spec.pressureType = TR_Research;
|
||||
|
||||
imports.insertLast(resources.requestResource(masterBeacon.obj, spec));
|
||||
}
|
||||
}
|
||||
else {
|
||||
//When our requests are met, make more requests!
|
||||
for(uint i = 0, cnt = imports.length; i < cnt; ++i) {
|
||||
if(imports[i].beingMet || imports[i].obj !is masterBeacon.obj) {
|
||||
ResourceSpec spec;
|
||||
spec = imports[i].spec;
|
||||
@imports[i] = resources.requestResource(masterBeacon.obj, spec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Build stuff on our beacon if we have enough stuff
|
||||
if(budget.canSpend(BT_Development, 300)) {
|
||||
uint offset = randomi(0, beaconBuilds.length-1);
|
||||
for(uint i = 0, cnt = beaconBuilds.length; i < cnt; ++i) {
|
||||
uint ind = (i+offset) % cnt;
|
||||
auto@ type = beaconBuilds[ind];
|
||||
if(type is null)
|
||||
continue;
|
||||
|
||||
if(type.canBuild(masterBeacon.obj, ignoreCost=false)) {
|
||||
masterBeacon.obj.buildConstruction(type.id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AIComponent@ createExtragalactic() {
|
||||
return Extragalactic();
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
import empire_ai.weasel.race.Race;
|
||||
|
||||
import empire_ai.weasel.Movement;
|
||||
import empire_ai.weasel.Military;
|
||||
import empire_ai.weasel.Construction;
|
||||
import empire_ai.weasel.Designs;
|
||||
import empire_ai.weasel.Development;
|
||||
import empire_ai.weasel.Systems;
|
||||
import empire_ai.weasel.Budget;
|
||||
|
||||
from orbitals import getOrbitalModuleID;
|
||||
|
||||
const double MAINFRAME_MIN_DISTANCE_STAGE = 15000;
|
||||
const double MAINFRAME_MIN_DISTANCE_DEVELOP = 20000;
|
||||
const double MAINFRAME_MIN_TIMER = 3.0 * 60.0;
|
||||
const int MAINFRAME_BUILD_MOVE_HOPS = 5;
|
||||
|
||||
class LinkRegion : Savable {
|
||||
Region@ region;
|
||||
Object@ obj;
|
||||
bool arrived = false;
|
||||
vec3d destination;
|
||||
|
||||
void save(SaveFile& file) {
|
||||
file << region;
|
||||
file << obj;
|
||||
file << arrived;
|
||||
file << destination;
|
||||
}
|
||||
|
||||
void load(SaveFile& file) {
|
||||
file >> region;
|
||||
file >> obj;
|
||||
file >> arrived;
|
||||
file >> destination;
|
||||
}
|
||||
};
|
||||
|
||||
class Linked : Race {
|
||||
Military@ military;
|
||||
Designs@ designs;
|
||||
Construction@ construction;
|
||||
Development@ development;
|
||||
Systems@ systems;
|
||||
Budget@ budget;
|
||||
|
||||
array<LinkRegion@> tracked;
|
||||
array<Object@> unassigned;
|
||||
|
||||
BuildOrbital@ buildMainframe;
|
||||
int mainframeId = -1;
|
||||
|
||||
double nextBuildTry = 15.0 * 60.0;
|
||||
|
||||
void create() override {
|
||||
@military = cast<Military>(ai.military);
|
||||
@designs = cast<Designs>(ai.designs);
|
||||
@construction = cast<Construction>(ai.construction);
|
||||
@development = cast<Development>(ai.development);
|
||||
@systems = cast<Systems>(ai.systems);
|
||||
@budget = cast<Budget>(ai.budget);
|
||||
|
||||
mainframeId = getOrbitalModuleID("Mainframe");
|
||||
}
|
||||
|
||||
void save(SaveFile& file) override {
|
||||
construction.saveConstruction(file, buildMainframe);
|
||||
file << nextBuildTry;
|
||||
|
||||
uint cnt = tracked.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << tracked[i];
|
||||
|
||||
cnt = unassigned.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
file << unassigned[i];
|
||||
}
|
||||
|
||||
void load(SaveFile& file) override {
|
||||
@buildMainframe = cast<BuildOrbital>(construction.loadConstruction(file));
|
||||
file >> nextBuildTry;
|
||||
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
LinkRegion gt;
|
||||
file >> gt;
|
||||
tracked.insertLast(gt);
|
||||
}
|
||||
|
||||
file >> cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
Object@ obj;
|
||||
file >> obj;
|
||||
if(obj !is null)
|
||||
unassigned.insertLast(obj);
|
||||
}
|
||||
}
|
||||
|
||||
LinkRegion@ get(Region@ reg) {
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i) {
|
||||
if(tracked[i].region is reg)
|
||||
return tracked[i];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void remove(LinkRegion@ gt) {
|
||||
if(gt.obj !is null && gt.obj.valid && gt.obj.owner is ai.empire)
|
||||
unassigned.insertLast(gt.obj);
|
||||
tracked.remove(gt);
|
||||
}
|
||||
|
||||
Object@ getClosestMainframe(const vec3d& position) {
|
||||
Object@ closest;
|
||||
double minDist = INFINITY;
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i) {
|
||||
Object@ obj = tracked[i].obj;
|
||||
if(obj is null)
|
||||
continue;
|
||||
if(!tracked[i].arrived)
|
||||
continue;
|
||||
double d = obj.position.distanceTo(position);
|
||||
if(d < minDist) {
|
||||
minDist = d;
|
||||
@closest = obj;
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
LinkRegion@ getClosestLinkRegion(const vec3d& position) {
|
||||
LinkRegion@ closest;
|
||||
double minDist = INFINITY;
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i) {
|
||||
double d = tracked[i].region.position.distanceTo(position);
|
||||
if(d < minDist) {
|
||||
minDist = d;
|
||||
@closest = tracked[i];
|
||||
}
|
||||
}
|
||||
return closest;
|
||||
}
|
||||
|
||||
void assignTo(LinkRegion@ gt, Object@ closest) {
|
||||
unassigned.remove(closest);
|
||||
@gt.obj = closest;
|
||||
gt.arrived = false;
|
||||
|
||||
if(closest.region is gt.region)
|
||||
gt.arrived = true;
|
||||
if(!gt.arrived) {
|
||||
gt.destination = military.getStationPosition(gt.region);
|
||||
closest.addMoveOrder(gt.destination);
|
||||
}
|
||||
}
|
||||
|
||||
bool trackingMainframe(Object@ obj) {
|
||||
for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) {
|
||||
if(unassigned[i] is obj)
|
||||
return true;
|
||||
}
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i) {
|
||||
if(tracked[i].obj is obj)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool shouldHaveMainframe(Region@ reg, bool always = false) {
|
||||
if(military.getBase(reg) !is null)
|
||||
return true;
|
||||
if(development.isDevelopingIn(reg))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void focusTick(double time) override {
|
||||
//Manage unassigned mainframes list
|
||||
for(uint i = 0, cnt = unassigned.length; i < cnt; ++i) {
|
||||
Object@ obj = unassigned[i];
|
||||
if(obj is null || !obj.valid || obj.owner !is ai.empire) {
|
||||
unassigned.removeAt(i);
|
||||
--i; --cnt;
|
||||
}
|
||||
}
|
||||
|
||||
//Detect new gates
|
||||
auto@ data = ai.empire.getOrbitals();
|
||||
Object@ obj;
|
||||
while(receive(data, obj)) {
|
||||
if(obj is null)
|
||||
continue;
|
||||
Orbital@ orb = cast<Orbital>(obj);
|
||||
if(orb is null || orb.coreModule != uint(mainframeId))
|
||||
continue;
|
||||
if(!trackingMainframe(obj))
|
||||
unassigned.insertLast(obj);
|
||||
}
|
||||
|
||||
//Update existing gates for staging bases
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i) {
|
||||
auto@ gt = tracked[i];
|
||||
bool checkAlways = false;
|
||||
if(gt.obj !is null) {
|
||||
if(!gt.obj.valid || gt.obj.owner !is ai.empire || (gt.arrived && gt.obj.region !is gt.region)) {
|
||||
@gt.obj = null;
|
||||
gt.arrived = false;
|
||||
checkAlways = true;
|
||||
}
|
||||
else if(!gt.arrived && !gt.obj.hasOrders) {
|
||||
if(gt.destination.distanceTo(gt.obj.position) < 10.0)
|
||||
gt.arrived = true;
|
||||
else
|
||||
gt.obj.addMoveOrder(gt.destination);
|
||||
}
|
||||
}
|
||||
if(!shouldHaveMainframe(gt.region, checkAlways)) {
|
||||
remove(tracked[i]);
|
||||
--i; --cnt;
|
||||
}
|
||||
}
|
||||
|
||||
//Detect new staging bases to build mainframes at
|
||||
for(uint i = 0, cnt = military.stagingBases.length; i < cnt; ++i) {
|
||||
auto@ base = military.stagingBases[i];
|
||||
if(base.occupiedTime < MAINFRAME_MIN_TIMER)
|
||||
continue;
|
||||
|
||||
if(get(base.region) is null) {
|
||||
LinkRegion@ closest = getClosestLinkRegion(base.region.position);
|
||||
if(closest !is null && closest.region.position.distanceTo(base.region.position) < MAINFRAME_MIN_DISTANCE_STAGE)
|
||||
continue;
|
||||
|
||||
LinkRegion gt;
|
||||
@gt.region = base.region;
|
||||
tracked.insertLast(gt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Detect new important planets to build mainframes at
|
||||
for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i) {
|
||||
auto@ focus = development.focuses[i];
|
||||
Region@ reg = focus.obj.region;
|
||||
if(reg is null)
|
||||
continue;
|
||||
|
||||
if(get(reg) is null) {
|
||||
LinkRegion@ closest = getClosestLinkRegion(reg.position);
|
||||
if(closest !is null && closest.region.position.distanceTo(reg.position) < MAINFRAME_MIN_DISTANCE_DEVELOP)
|
||||
continue;
|
||||
|
||||
LinkRegion gt;
|
||||
@gt.region = reg;
|
||||
tracked.insertLast(gt);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//See if we should build a new mainframe
|
||||
if(buildMainframe !is null) {
|
||||
if(buildMainframe.completed) {
|
||||
@buildMainframe = null;
|
||||
nextBuildTry = gameTime + 60.0;
|
||||
}
|
||||
}
|
||||
for(uint i = 0, cnt = tracked.length; i < cnt; ++i) {
|
||||
auto@ gt = tracked[i];
|
||||
if(gt.obj is null) {
|
||||
Object@ closest;
|
||||
double closestDist = INFINITY;
|
||||
for(uint n = 0, ncnt = unassigned.length; n < ncnt; ++n) {
|
||||
Object@ obj = unassigned[n];
|
||||
if(obj.region is gt.region) {
|
||||
@closest = obj;
|
||||
break;
|
||||
}
|
||||
if(!obj.hasMover)
|
||||
continue;
|
||||
if(buildMainframe is null && gameTime > nextBuildTry) {
|
||||
double d = obj.position.distanceTo(gt.region.position);
|
||||
if(d < closestDist) {
|
||||
closestDist = d;
|
||||
@closest = obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(closest !is null) {
|
||||
if(log)
|
||||
ai.print("Assign mainframe to => "+gt.region.name, closest.region);
|
||||
assignTo(gt, closest);
|
||||
} else if(buildMainframe is null && gameTime > nextBuildTry) {
|
||||
if(log)
|
||||
ai.print("Build mainframe for this system", gt.region);
|
||||
|
||||
bool buildLocal = true;
|
||||
auto@ factory = construction.primaryFactory;
|
||||
if(factory !is null) {
|
||||
Region@ factRegion = factory.obj.region;
|
||||
if(factRegion !is null && systems.hopDistance(gt.region, factRegion) < MAINFRAME_BUILD_MOVE_HOPS)
|
||||
buildLocal = false;
|
||||
}
|
||||
|
||||
if(buildLocal)
|
||||
@buildMainframe = construction.buildLocalOrbital(getOrbitalModule(mainframeId));
|
||||
else
|
||||
@buildMainframe = construction.buildOrbital(getOrbitalModule(mainframeId), military.getStationPosition(gt.region));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
AIComponent@ createLinked() {
|
||||
return Linked();
|
||||
}
|
||||
@@ -0,0 +1,348 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
import empire_ai.weasel.race.Race;
|
||||
|
||||
import empire_ai.weasel.Resources;
|
||||
import empire_ai.weasel.Colonization;
|
||||
import empire_ai.weasel.Construction;
|
||||
import empire_ai.weasel.Movement;
|
||||
import empire_ai.weasel.Planets;
|
||||
import empire_ai.weasel.Budget;
|
||||
|
||||
import resources;
|
||||
import abilities;
|
||||
import planet_levels;
|
||||
from constructions import getConstructionType, ConstructionType;
|
||||
from abilities import getAbilityID;
|
||||
import oddity_navigation;
|
||||
|
||||
const double MAX_POP_BUILDTIME = 3.0 * 60.0;
|
||||
|
||||
class Mechanoid : Race, RaceResources, RaceColonization {
|
||||
Colonization@ colonization;
|
||||
Construction@ construction;
|
||||
Movement@ movement;
|
||||
Budget@ budget;
|
||||
Planets@ planets;
|
||||
|
||||
const ResourceType@ unobtanium;
|
||||
const ResourceType@ crystals;
|
||||
int unobtaniumAbl = -1;
|
||||
|
||||
const ResourceClass@ foodClass;
|
||||
const ResourceClass@ waterClass;
|
||||
const ResourceClass@ scalableClass;
|
||||
const ConstructionType@ buildPop;
|
||||
|
||||
int colonizeAbl = -1;
|
||||
|
||||
array<Planet@> popRequests;
|
||||
array<Planet@> popSources;
|
||||
array<Planet@> popFactories;
|
||||
|
||||
void create() {
|
||||
@colonization = cast<Colonization>(ai.colonization);
|
||||
@construction = cast<Construction>(ai.construction);
|
||||
@movement = cast<Movement>(ai.movement);
|
||||
@planets = cast<Planets>(ai.planets);
|
||||
@budget = cast<Budget>(ai.budget);
|
||||
|
||||
@ai.defs.Shipyard = null;
|
||||
|
||||
@crystals = getResource("FTL");
|
||||
@unobtanium = getResource("Unobtanium");
|
||||
unobtaniumAbl = getAbilityID("UnobtaniumMorph");
|
||||
|
||||
@foodClass = getResourceClass("Food");
|
||||
@waterClass = getResourceClass("WaterType");
|
||||
@scalableClass = getResourceClass("Scalable");
|
||||
|
||||
colonizeAbl = getAbilityID("MechanoidColonize");
|
||||
colonization.performColonization = false;
|
||||
|
||||
@buildPop = getConstructionType("MechanoidPopulation");
|
||||
}
|
||||
|
||||
void start() {
|
||||
//Oh yes please can we have some ftl crystals sir
|
||||
if(crystals !is null) {
|
||||
ResourceSpec spec;
|
||||
spec.type = RST_Specific;
|
||||
@spec.resource = crystals;
|
||||
spec.isLevelRequirement = false;
|
||||
spec.isForImport = false;
|
||||
|
||||
colonization.queueColonize(spec);
|
||||
}
|
||||
}
|
||||
|
||||
void levelRequirements(Object& obj, int targetLevel, array<ResourceSpec@>& specs) override {
|
||||
//Remove all food and water resources
|
||||
if(obj.levelChain != baseLevelChain.id)
|
||||
return;
|
||||
for(int i = specs.length-1; i >= 0; --i) {
|
||||
auto@ spec = specs[i];
|
||||
if(spec.type == RST_Class && (spec.cls is foodClass || spec.cls is waterClass))
|
||||
specs.removeAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
double transferCost(double dist) {
|
||||
return 20 + dist * 0.002;
|
||||
}
|
||||
|
||||
bool orderColonization(ColonizeData& data, Planet@ sourcePlanet) {
|
||||
return false;
|
||||
}
|
||||
|
||||
double getGenericUsefulness(const ResourceType@ type) override {
|
||||
if(type.cls is foodClass || type.cls is waterClass)
|
||||
return 0.00001;
|
||||
if(type.level == 1)
|
||||
return 100.0;
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
bool canBuildPopulation(Planet& pl, double factor=1.0) {
|
||||
if(buildPop is null)
|
||||
return false;
|
||||
if(!buildPop.canBuild(pl, ignoreCost=true))
|
||||
return false;
|
||||
auto@ primFact = construction.primaryFactory;
|
||||
if(primFact !is null && pl is primFact.obj)
|
||||
return true;
|
||||
|
||||
double laborCost = buildPop.getLaborCost(pl);
|
||||
double laborIncome = pl.laborIncome;
|
||||
return laborCost < laborIncome * MAX_POP_BUILDTIME * factor;
|
||||
}
|
||||
|
||||
bool requiresPopulation(Planet& pl, double mod = 0.0) {
|
||||
double curPop = pl.population + mod;
|
||||
double maxPop = pl.maxPopulation;
|
||||
return curPop < maxPop;
|
||||
}
|
||||
|
||||
bool canSendPopulation(Planet& pl, double mod = 0.0) {
|
||||
double curPop = pl.population + mod;
|
||||
double maxPop = pl.maxPopulation;
|
||||
if(curPop >= maxPop + 1)
|
||||
return true;
|
||||
//auto@ primFact = construction.primaryFactory;
|
||||
//if(primFact !is null && pl is primFact.obj) {
|
||||
// uint minFacts = 2;
|
||||
// if(popFactories.find(pl) == -1)
|
||||
// minFacts -= 1;
|
||||
// if(popFactories.length >= minFacts)
|
||||
// return false;
|
||||
//}
|
||||
//if(canBuildPopulation(pl)) {
|
||||
// if(curPop >= maxPop)
|
||||
// return true;
|
||||
//}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint chkInd = 0;
|
||||
array<Planet@> availSources;
|
||||
void focusTick(double time) override {
|
||||
//Check existing lists
|
||||
for(uint i = 0, cnt = popFactories.length; i < cnt; ++i) {
|
||||
auto@ obj = popFactories[i];
|
||||
if(obj is null || !obj.valid || obj.owner !is ai.empire) {
|
||||
popFactories.removeAt(i);
|
||||
--i; --cnt;
|
||||
continue;
|
||||
}
|
||||
if(!canBuildPopulation(popFactories[i])) {
|
||||
popFactories.removeAt(i);
|
||||
--i; --cnt;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for(uint i = 0, cnt = popSources.length; i < cnt; ++i) {
|
||||
auto@ obj = popSources[i];
|
||||
if(obj is null || !obj.valid || obj.owner !is ai.empire) {
|
||||
popSources.removeAt(i);
|
||||
--i; --cnt;
|
||||
continue;
|
||||
}
|
||||
if(!canSendPopulation(popSources[i])) {
|
||||
popSources.removeAt(i);
|
||||
--i; --cnt;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
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(popRequests[i])) {
|
||||
popRequests.removeAt(i);
|
||||
--i; --cnt;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Find new planets to add to our lists
|
||||
bool checkMorph = false;
|
||||
Planet@ hw = ai.empire.Homeworld;
|
||||
if(hw !is null && hw.valid && hw.owner is ai.empire && unobtanium !is null) {
|
||||
if(hw.primaryResourceType == unobtanium.id)
|
||||
checkMorph = true;
|
||||
}
|
||||
|
||||
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 can build population reliably
|
||||
if(canBuildPopulation(plAI.obj)) {
|
||||
if(popFactories.find(plAI.obj) == -1)
|
||||
popFactories.insertLast(plAI.obj);
|
||||
}
|
||||
|
||||
//Find planets that need population
|
||||
if(requiresPopulation(plAI.obj)) {
|
||||
if(popRequests.find(plAI.obj) == -1)
|
||||
popRequests.insertLast(plAI.obj);
|
||||
}
|
||||
|
||||
//Find planets that have extra population
|
||||
if(canSendPopulation(plAI.obj)) {
|
||||
if(popSources.find(plAI.obj) == -1)
|
||||
popSources.insertLast(plAI.obj);
|
||||
}
|
||||
|
||||
if(plAI.resources !is null && plAI.resources.length != 0) {
|
||||
auto@ res = plAI.resources[0];
|
||||
|
||||
//Get rid of food and water we don't need
|
||||
if(res.resource.cls is foodClass || res.resource.cls is waterClass) {
|
||||
if(res.request is null) {
|
||||
Region@ reg = res.obj.region;
|
||||
if(reg !is null && reg.getPlanetCount(ai.empire) >= 2) {
|
||||
plAI.obj.abandon();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//See if we have anything useful to morph our homeworld too
|
||||
if(checkMorph) {
|
||||
bool morph = false;
|
||||
if(res.resource is crystals)
|
||||
morph = true;
|
||||
else if(res.resource.level >= 2 && res.resource.tilePressure[TR_Labor] >= 5)
|
||||
morph = true;
|
||||
else if(res.resource.level >= 3 && res.resource.totalPressure > 10)
|
||||
morph = true;
|
||||
else if(res.resource.cls is scalableClass && gameTime > 30.0 * 60.0)
|
||||
morph = true;
|
||||
else if(res.resource.level >= 2 && res.resource.totalPressure >= 5 && gameTime > 60.0 * 60.0)
|
||||
morph = true;
|
||||
|
||||
if(morph) {
|
||||
if(log)
|
||||
ai.print("Morph homeworld to "+res.resource.name+" from "+res.obj.name, hw);
|
||||
hw.activateAbilityTypeFor(ai.empire, unobtaniumAbl, plAI.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//See if we can find something to send population to
|
||||
availSources = popSources;
|
||||
|
||||
for(uint i = 0, cnt = popRequests.length; i < cnt; ++i) {
|
||||
Planet@ dest = popRequests[i];
|
||||
if(canBuildPopulation(dest, factor=(availSources.length == 0 ? 2.5 : 1.5))) {
|
||||
Factory@ f = construction.get(dest);
|
||||
if(f !is null) {
|
||||
if(f.active is null) {
|
||||
auto@ build = construction.buildConstruction(buildPop);
|
||||
construction.buildNow(build, f);
|
||||
if(log)
|
||||
ai.print("Build population", f.obj);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
auto@ cons = cast<BuildConstruction>(f.active);
|
||||
if(cons !is null && cons.consType is buildPop) {
|
||||
if(double(dest.maxPopulation) <= dest.population + 0.0)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
transferBest(dest, availSources);
|
||||
}
|
||||
|
||||
if(availSources.length != 0) {
|
||||
//If we have any population left, do stuff from our colonization queue
|
||||
for(uint i = 0, cnt = colonization.awaitingSource.length; i < cnt && availSources.length != 0; ++i) {
|
||||
Planet@ dest = colonization.awaitingSource[i].target;
|
||||
Planet@ source = transferBest(dest, availSources);
|
||||
if(source !is null) {
|
||||
@colonization.awaitingSource[i].colonizeFrom = source;
|
||||
colonization.awaitingSource.removeAt(i);
|
||||
--i; --cnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Build population on idle planets
|
||||
if(budget.canSpend(BT_Development, 100)) {
|
||||
for(int i = popFactories.length-1; i >= 0; --i) {
|
||||
Planet@ dest = popFactories[i];
|
||||
Factory@ f = construction.get(dest);
|
||||
if(f is null || f.active !is null)
|
||||
continue;
|
||||
if(dest.population >= double(dest.maxPopulation) + 1.0)
|
||||
continue;
|
||||
|
||||
auto@ build = construction.buildConstruction(buildPop);
|
||||
construction.buildNow(build, f);
|
||||
if(log)
|
||||
ai.print("Build population for idle", f.obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Planet@ transferBest(Planet& dest, array<Planet@>& availSources) {
|
||||
//Find closest source
|
||||
Planet@ bestSource;
|
||||
double bestDist = INFINITY;
|
||||
for(uint j = 0, jcnt = availSources.length; j < jcnt; ++j) {
|
||||
double d = movement.getPathDistance(availSources[j].position, dest.position);
|
||||
if(d < bestDist) {
|
||||
bestDist = d;
|
||||
@bestSource = availSources[j];
|
||||
}
|
||||
}
|
||||
|
||||
if(bestSource !is null) {
|
||||
double cost = transferCost(bestDist);
|
||||
if(cost <= ai.empire.FTLStored) {
|
||||
if(log)
|
||||
ai.print("Transfering population to "+dest.name, bestSource);
|
||||
availSources.remove(bestSource);
|
||||
bestSource.activateAbilityTypeFor(ai.empire, colonizeAbl, dest);
|
||||
return bestSource;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void tick(double time) override {
|
||||
}
|
||||
};
|
||||
|
||||
AIComponent@ createMechanoid() {
|
||||
return Mechanoid();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
|
||||
class Race : AIComponent {
|
||||
};
|
||||
@@ -0,0 +1,538 @@
|
||||
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();
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import empire_ai.weasel.WeaselAI;
|
||||
import empire_ai.weasel.race.Race;
|
||||
|
||||
import empire_ai.weasel.Designs;
|
||||
import empire_ai.weasel.Development;
|
||||
import empire_ai.weasel.Planets;
|
||||
|
||||
import buildings;
|
||||
|
||||
class Verdant : Race, RaceDesigns {
|
||||
Designs@ designs;
|
||||
Development@ development;
|
||||
Planets@ planets;
|
||||
|
||||
array<const Design@> defaultDesigns;
|
||||
array<uint> defaultGoals;
|
||||
|
||||
const SubsystemDef@ sinewSubsystem;
|
||||
const SubsystemDef@ supportSinewSubsystem;
|
||||
|
||||
const BuildingType@ stalk;
|
||||
|
||||
void create() override {
|
||||
@designs = cast<Designs>(ai.designs);
|
||||
@development = cast<Development>(ai.development);
|
||||
@planets = cast<Planets>(ai.planets);
|
||||
|
||||
@sinewSubsystem = getSubsystemDef("VerdantSinew");
|
||||
@supportSinewSubsystem = getSubsystemDef("VerdantSupportSinew");
|
||||
@stalk = getBuildingType("Stalk");
|
||||
}
|
||||
|
||||
void start() override {
|
||||
ReadLock lock(ai.empire.designMutex);
|
||||
for(uint i = 0, cnt = ai.empire.designCount; i < cnt; ++i) {
|
||||
const Design@ dsg = ai.empire.getDesign(i);
|
||||
if(dsg.newer !is null)
|
||||
continue;
|
||||
if(dsg.updated !is null)
|
||||
continue;
|
||||
|
||||
uint goal = designs.classify(dsg, DP_Unknown);
|
||||
if(goal == DP_Unknown)
|
||||
continue;
|
||||
|
||||
defaultDesigns.insertLast(dsg);
|
||||
defaultGoals.insertLast(goal);
|
||||
}
|
||||
}
|
||||
|
||||
void save(SaveFile& file) override {
|
||||
uint cnt = defaultDesigns.length;
|
||||
file << cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
file << defaultDesigns[i];
|
||||
file << defaultGoals[i];
|
||||
}
|
||||
}
|
||||
|
||||
void load(SaveFile& file) override {
|
||||
uint cnt = 0;
|
||||
file >> cnt;
|
||||
defaultDesigns.length = cnt;
|
||||
defaultGoals.length = cnt;
|
||||
for(uint i = 0; i < cnt; ++i) {
|
||||
file >> defaultDesigns[i];
|
||||
file >> defaultGoals[i];
|
||||
}
|
||||
}
|
||||
|
||||
uint plCheck = 0;
|
||||
void focusTick(double time) override {
|
||||
//Check if we need to build stalks anywhere
|
||||
for(uint i = 0, cnt = development.focuses.length; i < cnt; ++i)
|
||||
checkForStalk(development.focuses[i].plAI);
|
||||
|
||||
uint plCnt = planets.planets.length;
|
||||
if(plCnt != 0) {
|
||||
for(uint n = 0; n < min(plCnt, 5); ++n) {
|
||||
plCheck = (plCheck+1) % plCnt;
|
||||
auto@ plAI = planets.planets[plCheck];
|
||||
checkForStalk(plAI);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void checkForStalk(PlanetAI@ plAI) {
|
||||
if(plAI is null)
|
||||
return;
|
||||
Planet@ pl = plAI.obj;
|
||||
if(pl.pressureCap <= 0 && pl.totalPressure >= 1) {
|
||||
if(planets.isBuilding(plAI.obj, stalk))
|
||||
return;
|
||||
if(pl.getBuildingCount(stalk.id) != 0)
|
||||
return;
|
||||
|
||||
planets.requestBuilding(plAI, stalk, expire=180.0);
|
||||
}
|
||||
}
|
||||
|
||||
bool preCompose(DesignTarget@ target) override {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool postCompose(DesignTarget@ target) override {
|
||||
// auto@ d = target.designer;
|
||||
|
||||
// //Add an extra engine
|
||||
// if(target.purpose == DP_Combat)
|
||||
// d.composition.insertAt(0, Exhaust(tag("Engine") & tag("GivesThrust"), 0.25, 0.35));
|
||||
|
||||
// //Remove armor layers we don't need
|
||||
// for(uint i = 0, cnt = d.composition.length; i < cnt; ++i) {
|
||||
// if(cast<ArmorLayer>(d.composition[i]) !is null) {
|
||||
// d.composition.removeAt(i);
|
||||
// --i; --cnt;
|
||||
// }
|
||||
// }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool design(DesignTarget@ target, int size, const Design@& output) {
|
||||
//All designs are rescales of default designs
|
||||
const Design@ baseDesign;
|
||||
uint possible = 0;
|
||||
for(uint i = 0, cnt = defaultDesigns.length; i < cnt; ++i) {
|
||||
if(defaultGoals[i] == target.purpose) {
|
||||
possible += 1;
|
||||
if(randomd() < 1.0 / double(possible))
|
||||
@baseDesign = defaultDesigns[i];
|
||||
}
|
||||
}
|
||||
|
||||
if(baseDesign is null)
|
||||
return false;
|
||||
|
||||
//if(target.designer !is null) {
|
||||
// @target.designer.baseOffDesign = baseDesign;
|
||||
// if(target.purpose != DP_Support)
|
||||
// @target.designer.baseOffSubsystem = sinewSubsystem;
|
||||
// else
|
||||
// @target.designer.baseOffSubsystem = supportSinewSubsystem;
|
||||
// @output = target.designer.design();
|
||||
//}
|
||||
|
||||
if(output is null)
|
||||
@output = scaleDesign(baseDesign, size);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
AIComponent@ createVerdant() {
|
||||
return Verdant();
|
||||
}
|
||||
Reference in New Issue
Block a user