765 lines
19 KiB
ActionScript
765 lines
19 KiB
ActionScript
import empire_ai.weasel.WeaselAI;
|
|
import empire_ai.weasel.Planets;
|
|
import empire_ai.weasel.Resources;
|
|
import empire_ai.weasel.Colonization;
|
|
import empire_ai.weasel.Systems;
|
|
|
|
import planet_levels;
|
|
import buildings;
|
|
|
|
import ai.consider;
|
|
from ai.buildings import Buildings, BuildingAI, BuildingUse;
|
|
from ai.resources import AIResources, ResourceAI;
|
|
|
|
interface RaceDevelopment {
|
|
bool shouldBeFocus(Planet& pl, const ResourceType@ resource);
|
|
};
|
|
|
|
class DevelopmentFocus {
|
|
Object@ obj;
|
|
PlanetAI@ plAI;
|
|
int targetLevel = 0;
|
|
int requestedLevel = 0;
|
|
int maximumLevel = INT_MAX;
|
|
array<ExportData@> managedPressure;
|
|
double weight = 1.0;
|
|
|
|
void tick(AI& ai, Development& dev, double time) {
|
|
if(targetLevel != requestedLevel) {
|
|
if(targetLevel > requestedLevel) {
|
|
int nextLevel = min(targetLevel, min(obj.resourceLevel, requestedLevel)+1);
|
|
if(nextLevel != requestedLevel) {
|
|
for(int i = requestedLevel+1; i <= nextLevel; ++i)
|
|
dev.resources.organizeImports(obj, i);
|
|
requestedLevel = nextLevel;
|
|
}
|
|
}
|
|
else {
|
|
dev.resources.organizeImports(obj, targetLevel);
|
|
requestedLevel = targetLevel;
|
|
}
|
|
}
|
|
|
|
//Remove managed pressure resources that are no longer valid
|
|
for(uint i = 0, cnt = managedPressure.length; i < cnt; ++i) {
|
|
ExportData@ res = managedPressure[i];
|
|
if(res.request !is null || res.obj is null || !res.obj.valid || res.obj.owner !is ai.empire || !res.usable || res.developUse !is obj) {
|
|
if(res.developUse is obj)
|
|
@res.developUse = null;
|
|
managedPressure.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
}
|
|
|
|
//Make sure we're not exporting our resource
|
|
if(plAI !is null && plAI.resources !is null && plAI.resources.length != 0) {
|
|
auto@ res = plAI.resources[0];
|
|
res.localOnly = true;
|
|
if(res.request !is null && res.request.obj !is res.obj)
|
|
dev.resources.breakImport(res);
|
|
}
|
|
|
|
//TODO: We should be able to bump managed pressure resources back to Development for
|
|
//redistribution if we run out of pressure capacity.
|
|
}
|
|
|
|
void save(Development& development, SaveFile& file) {
|
|
file << obj;
|
|
development.planets.saveAI(file, plAI);
|
|
file << targetLevel;
|
|
file << requestedLevel;
|
|
file << maximumLevel;
|
|
file << weight;
|
|
|
|
uint cnt = managedPressure.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
development.resources.saveExport(file, managedPressure[i]);
|
|
}
|
|
|
|
void load(Development& development, SaveFile& file) {
|
|
file >> obj;
|
|
@plAI = development.planets.loadAI(file);
|
|
file >> targetLevel;
|
|
file >> requestedLevel;
|
|
file >> maximumLevel;
|
|
file >> weight;
|
|
|
|
uint cnt = 0;
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ data = development.resources.loadExport(file);
|
|
managedPressure.insertLast(data);
|
|
}
|
|
}
|
|
};
|
|
|
|
class Development : AIComponent, Buildings, ConsiderFilter, AIResources {
|
|
RaceDevelopment@ race;
|
|
Planets@ planets;
|
|
Resources@ resources;
|
|
Colonization@ colonization;
|
|
Systems@ systems;
|
|
|
|
array<DevelopmentFocus@> focuses;
|
|
array<ExportData@> managedPressure;
|
|
|
|
array<ColonizeData@> pendingFocuses;
|
|
array<ColonizeData@> pendingResources;
|
|
|
|
array<BuildingRequest@> genericBuilds;
|
|
array<ExportData@> aiResources;
|
|
|
|
double aimFTLStorage = 0.0;
|
|
|
|
bool managePlanetPressure = true;
|
|
bool manageAsteroidPressure = true;
|
|
bool buildBuildings = true;
|
|
bool colonizeResources = true;
|
|
|
|
void create() {
|
|
@planets = cast<Planets>(ai.planets);
|
|
@resources = cast<Resources>(ai.resources);
|
|
@colonization = cast<Colonization>(ai.colonization);
|
|
@systems = cast<Systems>(ai.systems);
|
|
@race = cast<RaceDevelopment>(ai.race);
|
|
|
|
//Register specialized building types
|
|
for(uint i = 0, cnt = getBuildingTypeCount(); i < cnt; ++i) {
|
|
auto@ type = getBuildingType(i);
|
|
for(uint n = 0, ncnt = type.ai.length; n < ncnt; ++n) {
|
|
auto@ hook = cast<BuildingAI>(type.ai[n]);
|
|
if(hook !is null)
|
|
hook.register(this, type);
|
|
}
|
|
}
|
|
}
|
|
|
|
Empire@ get_empire() {
|
|
return ai.empire;
|
|
}
|
|
|
|
Considerer@ get_consider() {
|
|
return cast<Considerer>(ai.consider);
|
|
}
|
|
|
|
void registerUse(BuildingUse use, const BuildingType& type) {
|
|
switch(use) {
|
|
case BU_Factory:
|
|
@ai.defs.Factory = type;
|
|
break;
|
|
case BU_LaborStorage:
|
|
@ai.defs.LaborStorage = type;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void save(SaveFile& file) {
|
|
file << aimFTLStorage;
|
|
|
|
uint cnt = focuses.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ focus = focuses[i];
|
|
focus.save(this, file);
|
|
}
|
|
|
|
cnt = managedPressure.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
resources.saveExport(file, managedPressure[i]);
|
|
|
|
cnt = pendingFocuses.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
colonization.saveColonize(file, pendingFocuses[i]);
|
|
|
|
cnt = pendingResources.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
colonization.saveColonize(file, pendingResources[i]);
|
|
|
|
cnt = genericBuilds.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
planets.saveBuildingRequest(file, genericBuilds[i]);
|
|
|
|
cnt = aiResources.length;
|
|
file << cnt;
|
|
for(uint i = 0; i < cnt; ++i)
|
|
resources.saveExport(file, aiResources[i]);
|
|
}
|
|
|
|
void load(SaveFile& file) {
|
|
file >> aimFTLStorage;
|
|
|
|
uint cnt = 0;
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ focus = DevelopmentFocus();
|
|
focus.load(this, file);
|
|
|
|
if(focus.obj !is null)
|
|
focuses.insertLast(focus);
|
|
}
|
|
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ data = resources.loadExport(file);
|
|
if(data !is null)
|
|
managedPressure.insertLast(data);
|
|
}
|
|
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ data = colonization.loadColonize(file);
|
|
if(data !is null)
|
|
pendingFocuses.insertLast(data);
|
|
}
|
|
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ data = colonization.loadColonize(file);
|
|
if(data !is null)
|
|
pendingResources.insertLast(data);
|
|
}
|
|
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ data = planets.loadBuildingRequest(file);
|
|
if(data !is null)
|
|
genericBuilds.insertLast(data);
|
|
}
|
|
|
|
file >> cnt;
|
|
for(uint i = 0; i < cnt; ++i) {
|
|
auto@ data = resources.loadExport(file);
|
|
if(data !is null)
|
|
aiResources.insertLast(data);
|
|
}
|
|
}
|
|
|
|
bool requestsFTLStorage() {
|
|
double capacity = ai.empire.FTLCapacity;
|
|
if(aimFTLStorage <= capacity)
|
|
return false;
|
|
if(ai.empire.FTLStored < capacity * 0.5)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool isBuilding(const BuildingType& type) {
|
|
for(uint i = 0, cnt = genericBuilds.length; i < cnt; ++i) {
|
|
if(genericBuilds[i].type is type)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isLeveling() {
|
|
for(uint i = 0, cnt = focuses.length; i < cnt; ++i) {
|
|
if(focuses[i].obj.resourceLevel < uint(focuses[i].targetLevel)) {
|
|
auto@ focus = focuses[i].obj;
|
|
|
|
//If all our requirements are resolved, then we can safely assume it will be leveled up
|
|
bool allResolved = true;
|
|
for(uint n = 0, ncnt = resources.requested.length; n < ncnt; ++n) {
|
|
auto@ req = resources.requested[n];
|
|
if(req.obj !is focus)
|
|
continue;
|
|
if(req.beingMet)
|
|
continue;
|
|
|
|
if(!req.isColonizing) {
|
|
allResolved = false;
|
|
break;
|
|
}
|
|
|
|
if(!colonization.isResolved(req)) {
|
|
allResolved = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(!allResolved)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isBusy() {
|
|
if(pendingFocuses.length != 0)
|
|
return true;
|
|
if(pendingResources.length != 0)
|
|
return true;
|
|
if(isLeveling())
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool isFocus(Object@ obj) {
|
|
for(uint i = 0, cnt = focuses.length; i < cnt; ++i) {
|
|
if(focuses[i].obj is obj)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isManaging(ExportData@ res) {
|
|
for(uint i = 0, cnt = managedPressure.length; i < cnt; ++i) {
|
|
if(managedPressure[i] is res)
|
|
return true;
|
|
}
|
|
for(uint i = 0, cnt = aiResources.length; i < cnt; ++i) {
|
|
if(aiResources[i] is res)
|
|
return true;
|
|
}
|
|
for(uint n = 0, ncnt = focuses.length; n < ncnt; ++n) {
|
|
auto@ f = focuses[n];
|
|
if(f.obj is res.obj)
|
|
return true;
|
|
for(uint i = 0, cnt = f.managedPressure.length; i < cnt; ++i) {
|
|
if(f.managedPressure[i] is res)
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool isDevelopingIn(Region@ reg) {
|
|
if(reg is null)
|
|
return false;
|
|
for(uint i = 0, cnt = focuses.length; i < cnt; ++i) {
|
|
if(focuses[i].obj.region is reg)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void start() {
|
|
//Level up the homeworld to level 3 to start with
|
|
for(uint i = 0, cnt = ai.empire.planetCount; i < cnt; ++i) {
|
|
Planet@ homeworld = ai.empire.planetList[i];
|
|
if(homeworld !is null && homeworld.valid) {
|
|
auto@ hwFocus = addFocus(planets.register(homeworld));
|
|
if(homeworld.nativeResourceCount >= 2 || homeworld.primaryResourceLimitLevel >= 3 || cnt == 1)
|
|
hwFocus.targetLevel = 3;
|
|
}
|
|
}
|
|
}
|
|
|
|
double idlePenalty = 0;
|
|
void findSomethingToDo() {
|
|
if(idlePenalty > gameTime)
|
|
return;
|
|
|
|
double totalChance =
|
|
ai.behavior.focusDevelopWeight
|
|
+ ai.behavior.focusColonizeNewWeight * sqr(1.0 / double(focuses.length))
|
|
+ ai.behavior.focusColonizeHighTierWeight;
|
|
double roll = randomd(0.0, totalChance);
|
|
|
|
//Level up one of our existing focuses
|
|
roll -= ai.behavior.focusDevelopWeight;
|
|
if(roll <= 0) {
|
|
DevelopmentFocus@ levelup;
|
|
double totalWeight = 0.0;
|
|
for(uint i = 0, cnt = focuses.length; i < cnt; ++i) {
|
|
auto@ f = focuses[i];
|
|
if(f.weight == 0)
|
|
continue;
|
|
if(f.targetLevel >= f.maximumLevel)
|
|
continue;
|
|
totalWeight += f.weight;
|
|
if(randomd() < f.weight / totalWeight)
|
|
@levelup = f;
|
|
}
|
|
|
|
if(levelup !is null) {
|
|
levelup.targetLevel += 1;
|
|
if(log)
|
|
ai.print("Develop chose to level this up to "+levelup.targetLevel, levelup.obj);
|
|
return;
|
|
}
|
|
else {
|
|
if(log)
|
|
ai.print("Develop ran out of things to level up.");
|
|
}
|
|
}
|
|
|
|
if(!colonizeResources)
|
|
return;
|
|
|
|
//Find a scalable or high tier resource to colonize and turn into a focus
|
|
roll -= ai.behavior.focusColonizeNewWeight * sqr(1.0 / double(focuses.length));
|
|
if(roll <= 0) {
|
|
Planet@ newFocus;
|
|
double totalWeight = 0.0;
|
|
|
|
for(uint i = 0, cnt = colonization.potentials.length; i < cnt; ++i) {
|
|
auto@ p = colonization.potentials[i];
|
|
|
|
if(p.resource.level < 3 && p.resource.cls !is colonization.scalableClass)
|
|
continue;
|
|
|
|
Region@ reg = p.pl.region;
|
|
if(reg is null)
|
|
continue;
|
|
|
|
if(colonization.isColonizing(p.pl))
|
|
continue;
|
|
|
|
vec2i surfaceSize = p.pl.surfaceGridSize;
|
|
int tiles = surfaceSize.width * surfaceSize.height;
|
|
if(tiles < 144)
|
|
continue;
|
|
|
|
auto@ sys = systems.getAI(reg);
|
|
|
|
double w = 1.0;
|
|
if(sys.border)
|
|
w *= 0.25;
|
|
if(sys.obj.PlanetsMask & ~ai.mask != 0)
|
|
w *= 0.25;
|
|
if(p.resource.cls is colonization.scalableClass)
|
|
w *= 10.0;
|
|
|
|
totalWeight += w;
|
|
if(randomd() < w / totalWeight)
|
|
@newFocus = p.pl;
|
|
}
|
|
|
|
if(newFocus !is null) {
|
|
auto@ data = colonization.colonize(newFocus);
|
|
if(data !is null)
|
|
pendingFocuses.insertLast(data);
|
|
if(log)
|
|
ai.print("Colonize to become develop focus", data.target);
|
|
return;
|
|
}
|
|
else {
|
|
if(log)
|
|
ai.print("Develop could not find a scalable or high tier resource to make a focus.");
|
|
}
|
|
}
|
|
|
|
if(focuses.length == 0)
|
|
return;
|
|
|
|
//Find a high tier resource to import to one of our focuses
|
|
roll -= ai.behavior.focusColonizeHighTierWeight;
|
|
if(roll <= 0) {
|
|
ResourceSpec spec;
|
|
spec.type = RST_Level_Minimum;
|
|
spec.level = 3;
|
|
spec.isLevelRequirement = false;
|
|
|
|
auto@ data = colonization.colonize(spec);
|
|
if(data !is null) {
|
|
if(log)
|
|
ai.print("Colonize as free resource", data.target);
|
|
pendingResources.insertLast(data);
|
|
return;
|
|
}
|
|
else {
|
|
if(log)
|
|
ai.print("Develop could not find a high tier resource to colonize as free resource.");
|
|
}
|
|
}
|
|
|
|
//Try to find a level 2 resource if everything else failed
|
|
{
|
|
ResourceSpec spec;
|
|
spec.type = RST_Level_Minimum;
|
|
spec.level = 2;
|
|
spec.isLevelRequirement = false;
|
|
|
|
if(colonization.shouldQueueFor(spec)) {
|
|
auto@ data = colonization.colonize(spec);
|
|
if(data !is null) {
|
|
if(log)
|
|
ai.print("Colonize as free resource", data.target);
|
|
pendingResources.insertLast(data);
|
|
return;
|
|
}
|
|
else {
|
|
if(log)
|
|
ai.print("Develop could not find a level 2 resource to colonize as free resource.");
|
|
}
|
|
}
|
|
}
|
|
|
|
idlePenalty = gameTime + randomd(10.0, 40.0);
|
|
}
|
|
|
|
uint bldIndex = 0;
|
|
uint aiInd = 0;
|
|
uint presInd = 0;
|
|
uint chkInd = 0;
|
|
void focusTick(double time) override {
|
|
//Remove any resources we're managing that got used
|
|
for(uint i = 0, cnt = managedPressure.length; i < cnt; ++i) {
|
|
ExportData@ res = managedPressure[i];
|
|
if(res.request !is null || res.obj is null || !res.obj.valid || res.obj.owner !is ai.empire || !res.usable) {
|
|
managedPressure.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
}
|
|
|
|
//Find new resources that we can put in our pressure manager
|
|
uint avCnt = resources.available.length;
|
|
if(avCnt != 0) {
|
|
uint index = randomi(0, avCnt-1);
|
|
for(uint i = 0, cnt = min(avCnt, 3); i < cnt; ++i) {
|
|
uint resInd = (index+i) % avCnt;
|
|
ExportData@ res = resources.available[resInd];
|
|
if(res.usable && res.request is null && res.obj !is null && res.obj.valid && res.obj.owner is ai.empire && res.developUse is null) {
|
|
if(res.resource.ai.length != 0) {
|
|
if(!isManaging(res))
|
|
aiResources.insertLast(res);
|
|
}
|
|
else if(res.resource.totalPressure > 0 && res.resource.exportable) {
|
|
if(!managePlanetPressure && res.obj.isPlanet)
|
|
continue;
|
|
if(!manageAsteroidPressure && res.obj.isAsteroid)
|
|
continue;
|
|
if(!isManaging(res))
|
|
managedPressure.insertLast(res);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Distribute managed pressure resources
|
|
if(managedPressure.length != 0) {
|
|
presInd = (presInd+1) % managedPressure.length;
|
|
ExportData@ res = managedPressure[presInd];
|
|
|
|
int pressure = res.resource.totalPressure;
|
|
|
|
DevelopmentFocus@ onFocus;
|
|
double bestWeight = 0;
|
|
bool havePressure = ai.empire.HasPressure != 0.0;
|
|
|
|
for(uint i = 0, cnt = focuses.length; i < cnt; ++i) {
|
|
auto@ f = focuses[i];
|
|
|
|
int cap = f.obj.pressureCap;
|
|
if(!havePressure)
|
|
cap = 10000;
|
|
int cur = f.obj.totalPressure;
|
|
|
|
if(cur + pressure > 2 * cap)
|
|
continue;
|
|
|
|
double w = 1.0;
|
|
if(cur + pressure > cap)
|
|
w *= 0.1;
|
|
|
|
if(w > bestWeight) {
|
|
bestWeight = w;
|
|
@onFocus = f;
|
|
}
|
|
}
|
|
|
|
if(onFocus !is null) {
|
|
if(res.obj !is onFocus.obj)
|
|
res.obj.exportResourceByID(res.resourceId, onFocus.obj);
|
|
else
|
|
res.obj.exportResourceByID(res.resourceId, null);
|
|
@res.developUse = onFocus.obj;
|
|
|
|
onFocus.managedPressure.insertLast(res);
|
|
managedPressure.removeAt(presInd);
|
|
|
|
if(log)
|
|
ai.print("Take "+res.resource.name+" from "+res.obj.name+" for pressure", onFocus.obj);
|
|
}
|
|
}
|
|
|
|
//Use generic AI distribution hooks
|
|
if(aiResources.length != 0) {
|
|
aiInd = (aiInd+1) % aiResources.length;
|
|
ExportData@ res = aiResources[aiInd];
|
|
if(res.request !is null || res.obj is null || !res.obj.valid || res.obj.owner !is ai.empire || !res.usable) {
|
|
aiResources.removeAt(aiInd);
|
|
}
|
|
else {
|
|
Object@ newTarget = res.developUse;
|
|
if(newTarget !is null) {
|
|
if(!newTarget.valid || newTarget.owner !is ai.empire)
|
|
@newTarget = null;
|
|
}
|
|
|
|
for(uint i = 0, cnt = res.resource.ai.length; i < cnt; ++i) {
|
|
auto@ hook = cast<ResourceAI>(res.resource.ai[i]);
|
|
if(hook !is null)
|
|
@newTarget = hook.distribute(this, res.resource, newTarget);
|
|
}
|
|
|
|
if(newTarget !is res.developUse) {
|
|
if(res.obj !is newTarget)
|
|
res.obj.exportResourceByID(res.resourceId, newTarget);
|
|
else
|
|
res.obj.exportResourceByID(res.resourceId, null);
|
|
@res.developUse = newTarget;
|
|
}
|
|
}
|
|
}
|
|
|
|
//Deal with focuses we're colonizing
|
|
for(uint i = 0, cnt = pendingFocuses.length; i < cnt; ++i) {
|
|
auto@ data = pendingFocuses[i];
|
|
if(data.completed) {
|
|
auto@ focus = addFocus(planets.register(data.target));
|
|
focus.targetLevel = 3;
|
|
|
|
pendingFocuses.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
else if(data.canceled) {
|
|
pendingFocuses.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
}
|
|
|
|
for(uint i = 0, cnt = pendingResources.length; i < cnt; ++i) {
|
|
auto@ data = pendingResources[i];
|
|
if(data.completed) {
|
|
planets.requestLevel(planets.register(data.target), data.target.primaryResourceLevel);
|
|
pendingResources.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
else if(data.canceled) {
|
|
pendingResources.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
}
|
|
|
|
//If we're not currently leveling something up, find something else to do
|
|
if(!isBusy())
|
|
findSomethingToDo();
|
|
|
|
//Deal with building AI hints
|
|
for(uint i = 0, cnt = genericBuilds.length; i < cnt; ++i) {
|
|
auto@ build = genericBuilds[i];
|
|
if(build.canceled) {
|
|
genericBuilds.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
else if(build.built) {
|
|
if(build.getProgress() >= 1.f) {
|
|
if(build.expires < gameTime) {
|
|
genericBuilds.removeAt(i);
|
|
--i; --cnt;
|
|
}
|
|
}
|
|
else
|
|
build.expires = gameTime + 60.0;
|
|
}
|
|
}
|
|
if(buildBuildings) {
|
|
for(uint i = 0, cnt = getBuildingTypeCount(); i < cnt; ++i) {
|
|
bldIndex = (bldIndex+1) % cnt;
|
|
|
|
auto@ type = getBuildingType(bldIndex);
|
|
if(type.ai.length == 0)
|
|
continue;
|
|
|
|
//If we're already generically building something of this type, wait
|
|
bool existing = false;
|
|
for(uint n = 0, ncnt = genericBuilds.length; n < ncnt; ++n) {
|
|
auto@ build = genericBuilds[n];
|
|
if(build.type is type && !build.built) {
|
|
existing = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(existing)
|
|
break;
|
|
|
|
@filterType = type;
|
|
@consider.filter = this;
|
|
|
|
//See if we should generically build something of this type
|
|
for(uint n = 0, ncnt = type.ai.length; n < ncnt; ++n) {
|
|
auto@ hook = cast<BuildingAI>(type.ai[n]);
|
|
if(hook !is null) {
|
|
Object@ buildOn = hook.considerBuild(this, type);
|
|
if(buildOn !is null && buildOn.isPlanet) {
|
|
auto@ plAI = planets.getAI(cast<Planet>(buildOn));
|
|
if(plAI !is null) {
|
|
if(log)
|
|
ai.print("AI hook generically requested building of type "+type.name, buildOn);
|
|
|
|
auto@ req = planets.requestBuilding(plAI, type, expire=ai.behavior.genericBuildExpire);
|
|
if(req !is null)
|
|
genericBuilds.insertLast(req);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
//Find planets we've acquired 'somehow' that have scalable resources and should be development focuses
|
|
if(planets.planets.length != 0) {
|
|
chkInd = (chkInd+1) % planets.planets.length;
|
|
auto@ plAI = planets.planets[chkInd];
|
|
|
|
if(plAI.resources.length != 0) {
|
|
auto@ res = plAI.resources[0];
|
|
if(res.resource.cls is colonization.scalableClass
|
|
|| focuses.length == 0 && res.resource.level >= 2
|
|
|| (race !is null && race.shouldBeFocus(plAI.obj, res.resource))) {
|
|
if(!isFocus(plAI.obj)) {
|
|
auto@ focus = addFocus(plAI);
|
|
focus.targetLevel = max(1, res.resource.level);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DevelopmentFocus@ addFocus(PlanetAI@ plAI) {
|
|
DevelopmentFocus focus;
|
|
@focus.obj = plAI.obj;
|
|
@focus.plAI = plAI;
|
|
focus.maximumLevel = getMaxPlanetLevel(plAI.obj);
|
|
|
|
focuses.insertLast(focus);
|
|
return focus;
|
|
}
|
|
|
|
DevelopmentFocus@ getFocus(Planet& pl) {
|
|
for(uint i = 0, cnt = focuses.length; i < cnt; ++i) {
|
|
if(focuses[i].obj is pl)
|
|
return focuses[i];
|
|
}
|
|
return null;
|
|
}
|
|
|
|
void tick(double time) override {
|
|
for(uint i = 0, cnt = focuses.length; i < cnt; ++i)
|
|
focuses[i].tick(ai, this, time);
|
|
}
|
|
|
|
const BuildingType@ filterType;
|
|
bool filter(Object@ obj) {
|
|
for(uint i = 0, cnt = genericBuilds.length; i < cnt; ++i) {
|
|
auto@ build = genericBuilds[i];
|
|
if(build.type is filterType && build.plAI.obj is obj)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
};
|
|
|
|
AIComponent@ createDevelopment() {
|
|
return Development();
|
|
}
|