Open source Star Ruler 2 source code!
This commit is contained in:
@@ -0,0 +1,703 @@
|
||||
int64 colonyHash(Planet@ target) {
|
||||
return (int64(ACT_Colonize) << ACT_BIT_OFFSET) | int64(target.id);
|
||||
}
|
||||
|
||||
class Colonize : Action {
|
||||
bool requested = false;
|
||||
int64 Hash;
|
||||
Planet@ dest;
|
||||
Object@ source;
|
||||
float best = 0;
|
||||
SysSearch search;
|
||||
uint enroute = 0;
|
||||
bool colonyAttempted = false;
|
||||
|
||||
Colonize(BasicAI@ ai, Planet@ target) {
|
||||
@dest = target;
|
||||
Hash = colonyHash(target);
|
||||
}
|
||||
|
||||
Colonize(BasicAI@ ai, SaveFile& msg) {
|
||||
msg >> requested;
|
||||
msg >> Hash;
|
||||
msg >> source;
|
||||
msg >> dest;
|
||||
msg >> best;
|
||||
search.load(msg);
|
||||
}
|
||||
|
||||
void save(BasicAI@ ai, SaveFile& msg) {
|
||||
msg << requested;
|
||||
msg << Hash;
|
||||
msg << source;
|
||||
msg << dest;
|
||||
msg << best;
|
||||
search.save(msg);
|
||||
|
||||
if(source !is null)
|
||||
enroute = 1;
|
||||
}
|
||||
|
||||
void postLoad(BasicAI@ ai) {
|
||||
}
|
||||
|
||||
int64 get_hash() const {
|
||||
return Hash;
|
||||
}
|
||||
|
||||
void reset(BasicAI@ ai) {
|
||||
if(source !is null && source.isShip)
|
||||
ai.freeFleet(cast<Ship>(source), FT_Mothership);
|
||||
@source = null;
|
||||
requested = false;
|
||||
best = 0;
|
||||
enroute = 0;
|
||||
search.reset();
|
||||
}
|
||||
|
||||
ActionType get_actionType() const {
|
||||
return ACT_Colonize;
|
||||
}
|
||||
|
||||
string get_state() const {
|
||||
if(dest is null)
|
||||
return "Nothing";
|
||||
else if(source is null)
|
||||
return "Colonizing " + dest.name;
|
||||
else
|
||||
return "Colonizing " + dest.name + " from " + source.name;
|
||||
}
|
||||
|
||||
Planet@ findSourcePlanet(BasicAI@ ai) {
|
||||
const uint tries = 8;
|
||||
for(uint try = 0; try < tries; ++try) {
|
||||
PlanRegion@ reg = search.next(ai.ourSystems);
|
||||
|
||||
if(reg is null) {
|
||||
//The search is over, dispatch colony ships if possible
|
||||
if(source !is null) {
|
||||
return cast<Planet>(source);
|
||||
}
|
||||
else {
|
||||
reset(ai);
|
||||
}
|
||||
break;
|
||||
}
|
||||
else {
|
||||
for(uint i = 0, cnt = reg.planets.length; i < cnt; ++i) {
|
||||
Planet@ pl = reg.planets[i];
|
||||
if(pl is null || pl.owner !is ai.empire)
|
||||
continue;
|
||||
|
||||
double pop = pl.population;
|
||||
if(pop < 1.5 || pl.isSendingColonyShips || pl.quarantined)
|
||||
continue;
|
||||
|
||||
double maxPop = double(pl.maxPopulation);
|
||||
if(pop > maxPop * 0.5) {
|
||||
//Weight primarily by time (square root of distance due to newtonian motion)
|
||||
double weight = 1000.0 / pow(pl.position.distanceToSQ(dest.position), 0.25);
|
||||
|
||||
//Strongly prefer planets near max population
|
||||
if(pop >= maxPop - 1.0)
|
||||
weight *= 8.0;
|
||||
//Prefer full planets
|
||||
weight *= pop / maxPop;
|
||||
|
||||
if(weight > best) {
|
||||
@source = pl;
|
||||
best = float(weight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void sendColonizers(BasicAI@ ai, bool allowAuto = true) {
|
||||
if(ai.usesMotherships) {
|
||||
//Find an idle mothership and bring it over
|
||||
auto@ mother = ai.getAvailableFleet(FT_Mothership, build=false);
|
||||
if(mother !is null && !mother.hasOrders) {
|
||||
auto ablID = mother.findAbilityOfType(getAbilityID("MothershipColonize"));
|
||||
double range = mother.getAbilityRange(ablID, dest) * 0.99;
|
||||
moveToFastest(mother, dest.position + (mother.position - dest.position).normalize(range), ai.ftl);
|
||||
mother.addAbilityOrder(ablID, dest, range, append=true);
|
||||
@source = mother;
|
||||
ai.addIdle(this);
|
||||
}
|
||||
else if(mother !is null) {
|
||||
ai.freeFleet(mother, FT_Mothership);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//Easy AIs use auto-colonize
|
||||
if(ai.skillEconomy < DIFF_Medium || allowAuto || ai.isMachineRace) {
|
||||
ai.empire.autoColonize(dest);
|
||||
}
|
||||
else {
|
||||
//Multiple colony orders may try to request colonization from the same planet
|
||||
if(source !is null && source.isSendingColonyShips) {
|
||||
@source = null;
|
||||
best = 0;
|
||||
}
|
||||
|
||||
findSourcePlanet(ai);
|
||||
if(source !is null) {
|
||||
ai.focus = source.position;
|
||||
source.colonize(dest, 1.0);
|
||||
requested = true;
|
||||
enroute += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool perform(BasicAI@ ai) {
|
||||
//Keep trying until the planet can't be colonized
|
||||
if(dest is null || !dest.valid) {
|
||||
reset(ai);
|
||||
return true;
|
||||
}
|
||||
|
||||
Empire@ owner = dest.visibleOwnerToEmp(ai.empire);
|
||||
if(owner is null) {
|
||||
reset(ai);
|
||||
@dest = null;
|
||||
return true;
|
||||
}
|
||||
else if(owner.valid && owner !is ai.empire) {
|
||||
reset(ai);
|
||||
@dest = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(owner.valid && dest.Population >= 1.0) {
|
||||
ai.addPlanet(dest);
|
||||
ai.markAsColony(dest.region).scout(ai);
|
||||
@dest = null;
|
||||
reset(ai);
|
||||
return true;
|
||||
}
|
||||
|
||||
ai.focus = dest.position;
|
||||
|
||||
ai.addIdle(this);
|
||||
|
||||
if(requested) {
|
||||
if(source is null || !source.valid || source.owner !is ai.empire || source.level < 1 || (dest.IncomingPop + dest.Population) >= 1.0)
|
||||
reset(ai);
|
||||
else if(source !is null && source.isShip && !source.hasOrders)
|
||||
reset(ai);
|
||||
}
|
||||
else if(!dest.isEmpireColonizing(ai.empire)) {
|
||||
sendColonizers(ai, allowAuto=true);
|
||||
}
|
||||
else if(enroute < 2 && ai.skillEconomy >= DIFF_Medium) {
|
||||
if(dest.IncomingPop + dest.Population > 0.99)
|
||||
colonyAttempted = true;
|
||||
|
||||
//We may encounter some form of resistance during colonization
|
||||
// If so, brute force by sending ships from a second colony
|
||||
if(colonyAttempted && dest.IncomingPop + dest.Population < 1.0) {
|
||||
PlanRegion@ pr = ai.getPlanRegion(dest.region);
|
||||
if(pr !is null)
|
||||
sendColonizers(ai);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
int64 expandHash() {
|
||||
return (int64(ACT_Expand) << ACT_BIT_OFFSET);
|
||||
}
|
||||
|
||||
class Expand : Action {
|
||||
SysSearch search;
|
||||
Planet@ target;
|
||||
double started = 0.0;
|
||||
|
||||
Expand() {
|
||||
}
|
||||
|
||||
Expand(BasicAI@ ai, SaveFile& msg) {
|
||||
msg >> target;
|
||||
msg >> started;
|
||||
search.load(msg);
|
||||
}
|
||||
|
||||
void save(BasicAI@ ai, SaveFile& msg) {
|
||||
msg << target;
|
||||
msg << started;
|
||||
search.save(msg);
|
||||
}
|
||||
|
||||
void postLoad(BasicAI@ ai) {
|
||||
}
|
||||
|
||||
int64 get_hash() const {
|
||||
return expandHash();
|
||||
}
|
||||
|
||||
ActionType get_actionType() const {
|
||||
return ACT_Expand;
|
||||
}
|
||||
|
||||
string get_state() const {
|
||||
if(target is null)
|
||||
return "Expanding";
|
||||
else
|
||||
return "Expanding to " + target.name;
|
||||
}
|
||||
|
||||
bool expandable(BasicAI@ ai, Region@ region) {
|
||||
bool tradeable = false, bridge = false;
|
||||
uint mask = ai.empire.mask;
|
||||
const SystemDesc@ system = getSystem(region);
|
||||
Territory@ prevTerr;
|
||||
|
||||
for(uint i = 0, cnt = system.adjacent.length; i < cnt; ++i) {
|
||||
Region@ other = getSystem(system.adjacent[i]).object;
|
||||
auto@ terr = other.getTerritory(ai.empire);
|
||||
|
||||
if(other.TradeMask & mask != 0) {
|
||||
tradeable = true;
|
||||
if(prevTerr is null)
|
||||
@prevTerr = terr;
|
||||
else
|
||||
bridge = true;
|
||||
}
|
||||
else {
|
||||
bridge = true;
|
||||
}
|
||||
}
|
||||
|
||||
return tradeable && bridge;
|
||||
}
|
||||
|
||||
bool perform(BasicAI@ ai) {
|
||||
if(target !is null) {
|
||||
if(!target.valid)
|
||||
@target = null;
|
||||
else if(target.owner is ai.empire)
|
||||
return true;
|
||||
else if(target.visibleOwnerToEmp(ai.empire).valid)
|
||||
@target = null;
|
||||
else if(gameTime - started > 300.0)
|
||||
@target = null;
|
||||
}
|
||||
|
||||
PlanRegion@ reg = search.random(ai.exploredSystems);
|
||||
if(reg is null) {
|
||||
ai.requestExploration();
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!expandable(ai, reg.region))
|
||||
return false;
|
||||
|
||||
Planet@ bestExp;
|
||||
double expScore = 0;
|
||||
|
||||
for(uint i = 0, cnt = reg.planets.length; i < cnt; ++i) {
|
||||
Planet@ pl = reg.planets[i];
|
||||
auto@ owner = pl.visibleOwnerToEmp(ai.empire);
|
||||
if(owner !is null && !owner.valid && !pl.quarantined) {
|
||||
double score = 1.0;
|
||||
|
||||
const ResourceType@ res = getResource(pl.primaryResourceType);
|
||||
if(res is null) {
|
||||
score /= 1000.0;
|
||||
}
|
||||
else {
|
||||
uint resCls = ai.classifyResource(res);
|
||||
bool isFoodWater = resCls == RT_Food || resCls == RT_Water;
|
||||
if(isFoodWater) {
|
||||
if(ai.isMachineRace)
|
||||
score /= 10.0;
|
||||
else
|
||||
score *= 10.0;
|
||||
}
|
||||
score /= double(res.level+1);
|
||||
score *= double(res.rarity)+1.0;
|
||||
}
|
||||
|
||||
if(score > expScore) {
|
||||
expScore = score;
|
||||
@bestExp = pl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(bestExp !is null) {
|
||||
@target = bestExp;
|
||||
ai.requestColony(bestExp);
|
||||
started = gameTime;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
int64 colonyByResHash(array<int>@ res) {
|
||||
int64 Hash = (int64(ACT_ColonizeRes) << ACT_BIT_OFFSET) | int64(res.length);
|
||||
for(uint i = 0, cnt = res.length; i < cnt; ++i)
|
||||
Hash ^= int64(res[i]) << i;
|
||||
return Hash;
|
||||
}
|
||||
|
||||
class ColonizeByResource : Action {
|
||||
int64 Hash;
|
||||
Planet@ target;
|
||||
uint nextOurIndex = 0, nextOtherIndex = 0;
|
||||
ObjectReceiver@ dest;
|
||||
int64 reqHash = 0, waitHash = 0;
|
||||
array<int>@ resources;
|
||||
|
||||
PlanRegion@ expansion;
|
||||
Action@ waitingOn;
|
||||
|
||||
ColonizeByResource(array<int>@ Resources, ObjectReceiver@ requester) {
|
||||
@dest = requester;
|
||||
@resources = Resources;
|
||||
|
||||
Hash = colonyByResHash(Resources);
|
||||
}
|
||||
|
||||
ColonizeByResource(BasicAI@ ai, SaveFile& msg) {
|
||||
msg >> Hash;
|
||||
msg >> target;
|
||||
msg >> nextOurIndex;
|
||||
msg >> nextOtherIndex;
|
||||
msg >> reqHash;
|
||||
|
||||
uint cnt = 0;
|
||||
msg >> cnt;
|
||||
@resources = array<int>(cnt);
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
resources[i] = msg.readIdentifier(SI_Resource);
|
||||
|
||||
Region@ reg;
|
||||
msg >> reg;
|
||||
if(reg !is null)
|
||||
@expansion = ai.getPlanRegion(reg);
|
||||
msg >> waitHash;
|
||||
}
|
||||
|
||||
void postLoad(BasicAI@ ai) {
|
||||
if(reqHash != 0)
|
||||
@dest = cast<ObjectReceiver>(ai.locateAction(reqHash));
|
||||
if(waitHash != 0)
|
||||
@waitingOn = ai.locateAction(waitHash);
|
||||
}
|
||||
|
||||
void save(BasicAI@ ai, SaveFile& msg) {
|
||||
msg << Hash;
|
||||
msg << target;
|
||||
msg << nextOurIndex;
|
||||
msg << nextOtherIndex;
|
||||
|
||||
if(dest !is null) {
|
||||
msg << cast<Action>(dest).hash;
|
||||
}
|
||||
else {
|
||||
int64 tmp = 0;
|
||||
msg << tmp;
|
||||
}
|
||||
|
||||
uint cnt = resources.length;
|
||||
msg << cnt;
|
||||
for(uint i = 0; i < cnt; ++i)
|
||||
msg.writeIdentifier(SI_Resource, resources[i]);
|
||||
|
||||
msg << (expansion is null ? null : expansion.region);
|
||||
msg << (waitingOn !is null ? waitingOn.hash : int64(0));
|
||||
}
|
||||
|
||||
int64 get_hash() const {
|
||||
return Hash;
|
||||
}
|
||||
|
||||
ActionType get_actionType() const {
|
||||
return ACT_ColonizeRes;
|
||||
}
|
||||
|
||||
string get_state() const {
|
||||
if(target is null)
|
||||
return "Looking for source of "+getResource(resources[0]).name+" (+" + (resources.length-1) + " others) resources";
|
||||
else
|
||||
return "Awaiting ownership of " + target.name;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
nextOtherIndex = 0;
|
||||
nextOurIndex = 0;
|
||||
@target = null;
|
||||
}
|
||||
|
||||
//TODO: Determine trade access based on territories instead
|
||||
bool isTradeable(BasicAI@ ai, Region@ region) {
|
||||
uint mask = ai.empire.mask;
|
||||
if(region.TradeMask & mask != 0)
|
||||
return true;
|
||||
|
||||
const SystemDesc@ system = getSystem(region);
|
||||
for(uint i = 0, cnt = system.adjacent.length; i < cnt; ++i) {
|
||||
Region@ other = getSystem(system.adjacent[i]).object;
|
||||
if(other.TradeMask & mask != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
if(ai.usesMotherships && ai.ourSystems.length == 0)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
PlanRegion@ getBridgeSystem(BasicAI@ ai, PlanRegion@ to) {
|
||||
uint mask = ai.empire.mask;
|
||||
|
||||
const SystemDesc@ system = to.system;
|
||||
for(uint i = 0, cnt = system.adjacent.length; i < cnt; ++i) {
|
||||
Region@ other = getSystem(system.adjacent[i]).object;
|
||||
if(isTradeable(ai, other))
|
||||
return ai.getPlanRegion(other);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
bool searchForPlanets(BasicAI@ ai) {
|
||||
array<PlanRegion@>@ regions;
|
||||
uint index = 0;
|
||||
if(nextOurIndex < ai.ourSystems.length) {
|
||||
@regions = ai.ourSystems;
|
||||
index = nextOurIndex++;
|
||||
nextOtherIndex = 0;
|
||||
}
|
||||
else if(nextOtherIndex < ai.exploredSystems.length) {
|
||||
@regions = ai.exploredSystems;
|
||||
index = nextOtherIndex++;
|
||||
}
|
||||
else {
|
||||
nextOurIndex = 0;
|
||||
index = 0;
|
||||
}
|
||||
|
||||
if(regions !is null) {
|
||||
PlanRegion@ reg = regions[index];
|
||||
Planet@ wanted;
|
||||
for(uint i = 0, cnt = reg.planets.length; i < cnt; ++i) {
|
||||
ai.focus = reg.region.position;
|
||||
|
||||
Planet@ pl = reg.planets[i];
|
||||
if(!pl.valid || pl.visibleOwnerToEmp(ai.empire).valid || pl.isEmpireColonizing(ai.empire) || pl.quarantined)
|
||||
continue;
|
||||
|
||||
int native = reg.planetResources[i];
|
||||
if(resources.find(native) < 0)
|
||||
continue;
|
||||
|
||||
@wanted = pl;
|
||||
break;
|
||||
}
|
||||
|
||||
if(wanted !is null) {
|
||||
if(isTradeable(ai, reg.region)) {
|
||||
@target = wanted;
|
||||
@waitingOn = ai.requestColony(wanted);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
//Choose closer expansions that we can expand to
|
||||
PlanRegion@ bridge = getBridgeSystem(ai, reg);
|
||||
if(bridge !is null && (expansion is null || ai.homeworld is null || bridge.center.distanceToSQ(ai.homeworld.position) < expansion.center.distanceToSQ(ai.homeworld.position))) {
|
||||
for(uint i = 0, cnt = bridge.planets.length; i < cnt; ++i) {
|
||||
auto@ pl = bridge.planets[i];
|
||||
if(!pl.valid)
|
||||
continue;
|
||||
auto@ owner = pl.visibleOwnerToEmp(ai.empire);
|
||||
if(owner !is null && !owner.valid) {
|
||||
@expansion = bridge;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@expansion = bridge;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
else if(expansion !is null) {
|
||||
Planet@ bestExp;
|
||||
double expScore = 0;
|
||||
|
||||
for(uint i = 0, cnt = expansion.planets.length; i < cnt; ++i) {
|
||||
Planet@ pl = expansion.planets[i];
|
||||
if(!pl.visibleOwnerToEmp(ai.empire).valid) {
|
||||
double score = 1.0;
|
||||
|
||||
const ResourceType@ res = getResource(pl.primaryResourceType);
|
||||
if(res is null) {
|
||||
score /= 1000.0;
|
||||
}
|
||||
else {
|
||||
uint resCls = ai.classifyResource(res);
|
||||
bool isFoodWater = resCls == RT_Food || resCls == RT_Water;
|
||||
if(isFoodWater) {
|
||||
if(ai.isMachineRace)
|
||||
score /= 10.0;
|
||||
else
|
||||
score *= 10.0;
|
||||
}
|
||||
score /= double(res.level+1);
|
||||
score *= double(res.rarity)+1.0;
|
||||
}
|
||||
|
||||
if(score > expScore) {
|
||||
expScore = score;
|
||||
@bestExp = pl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(bestExp !is null) {
|
||||
@waitingOn = ai.requestColony(bestExp);
|
||||
@expansion = null;
|
||||
}
|
||||
|
||||
if(waitingOn is null) {
|
||||
@waitingOn = ai.requestOrbital(expansion.region, OT_TradeOutpost);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
//We can't find the resource we need anywhere, explore some more
|
||||
ai.requestExploration();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool perform(BasicAI@ ai) {
|
||||
if(waitingOn !is null) {
|
||||
if(ai.performAction(waitingOn))
|
||||
@waitingOn = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
if(target !is null) {
|
||||
if(target.owner is ai.empire) {
|
||||
if(dest is null || dest.giveObject(ai, target))
|
||||
return true;
|
||||
else
|
||||
reset();
|
||||
}
|
||||
else if(target.owner.valid) {
|
||||
reset();
|
||||
}
|
||||
}
|
||||
else {
|
||||
const uint tries = 8;
|
||||
for(uint i = 0; i < tries; ++i)
|
||||
if(searchForPlanets(ai))
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
int64 populateHash(Planet@ target) {
|
||||
return (int64(ACT_Populate) << ACT_BIT_OFFSET) | int64(target.id);
|
||||
}
|
||||
|
||||
class Populate : Action {
|
||||
int64 Hash;
|
||||
Planet@ dest;
|
||||
Object@ source;
|
||||
|
||||
Populate(BasicAI@ ai, Planet@ target) {
|
||||
@dest = target;
|
||||
Hash = populateHash(target);
|
||||
}
|
||||
|
||||
Populate(BasicAI@ ai, SaveFile& msg) {
|
||||
msg >> source;
|
||||
msg >> dest;
|
||||
Hash = populateHash(dest);
|
||||
}
|
||||
|
||||
void save(BasicAI@ ai, SaveFile& msg) {
|
||||
msg << source;
|
||||
msg << dest;
|
||||
}
|
||||
|
||||
void postLoad(BasicAI@ ai) {
|
||||
}
|
||||
|
||||
int64 get_hash() const {
|
||||
return Hash;
|
||||
}
|
||||
|
||||
void reset(BasicAI@ ai) {
|
||||
if(source !is null && source.isShip)
|
||||
ai.freeFleet(cast<Ship>(source), FT_Mothership);
|
||||
@source = null;
|
||||
ai.removeIdle(this);
|
||||
}
|
||||
|
||||
ActionType get_actionType() const {
|
||||
return ACT_Populate;
|
||||
}
|
||||
|
||||
string get_state() const {
|
||||
if(dest is null)
|
||||
return "Nothing";
|
||||
else if(source is null)
|
||||
return "Populating " + dest.name;
|
||||
else
|
||||
return "Populating " + dest.name + " from " + source.name;
|
||||
}
|
||||
|
||||
bool perform(BasicAI@ ai) {
|
||||
//Keep trying until the planet can't be colonized
|
||||
if(dest is null || !dest.valid) {
|
||||
reset(ai);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(dest.Population >= dest.maxPopulation - 0.001) {
|
||||
reset(ai);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(!ai.usesMotherships)
|
||||
return true;
|
||||
|
||||
if(source is null) {
|
||||
auto@ mother = ai.getAvailableFleet(FT_Mothership, build=false);
|
||||
if(mother !is null && !mother.hasOrders) {
|
||||
auto ablID = mother.findAbilityOfType(getAbilityID("MothershipColonize"));
|
||||
double range = mother.getAbilityRange(ablID, dest) * 0.99;
|
||||
moveToFastest(mother, dest.position + (mother.position - dest.position).normalize(range), ai.ftl);
|
||||
mother.addAbilityOrder(ablID, dest, range, append=true);
|
||||
@source = mother;
|
||||
ai.addIdle(this);
|
||||
}
|
||||
else if(mother !is null) {
|
||||
ai.freeFleet(mother, FT_Mothership);
|
||||
}
|
||||
}
|
||||
else if(!source.valid || source.owner !is ai.empire || !source.hasOrders) {
|
||||
@source = null;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,342 @@
|
||||
import empire_ai.DiplomacyAI;
|
||||
|
||||
class DiplomacyAction : DiplomacyAI, Action {
|
||||
BasicAI@ ai;
|
||||
|
||||
DiplomacyAction(BasicAI@ ai) {
|
||||
@this.ai = ai;
|
||||
}
|
||||
|
||||
DiplomacyAction(BasicAI@ ai, SaveFile& file) {
|
||||
@this.ai = ai;
|
||||
load(file);
|
||||
}
|
||||
|
||||
void save(BasicAI@, SaveFile& file) {
|
||||
save(file);
|
||||
}
|
||||
|
||||
string get_state() const {
|
||||
return "DiplomacyAI";
|
||||
}
|
||||
|
||||
Empire@ get_empire() {
|
||||
return ai.empire;
|
||||
}
|
||||
|
||||
uint get_allyMask() {
|
||||
return ai.allyMask;
|
||||
}
|
||||
|
||||
int64 get_hash() const {
|
||||
return int64(ACT_Diplomacy) << ACT_BIT_OFFSET;
|
||||
}
|
||||
|
||||
ActionType get_actionType() const {
|
||||
return ACT_Diplomacy;
|
||||
}
|
||||
|
||||
int getStanding(Empire@ emp) {
|
||||
auto@ rel = ai.getRelation(emp);
|
||||
return rel.standing;
|
||||
}
|
||||
|
||||
void print(const string& str) {
|
||||
::print(empire.name+": "+str);
|
||||
}
|
||||
|
||||
Object@ considerOwnedPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
Object@ hw = ai.empire.Homeworld;
|
||||
if(hw !is null && hw.valid && hw.owner is empire) {
|
||||
double w = hook.consider(this, targets, vote, card, hw);
|
||||
if(w != 0.0) {
|
||||
@best = hw;
|
||||
bestW = w;
|
||||
}
|
||||
}
|
||||
|
||||
auto@ reso = getRandomResource(randomi(0, 3));
|
||||
if(reso !is null) {
|
||||
auto@ list = ai.planetsByResource[reso.id];
|
||||
for(uint i = 0, cnt = list.length; i < cnt; ++i) {
|
||||
Object@ pl = list.planets[i];
|
||||
|
||||
double w = hook.consider(this, targets, vote, card, pl);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = pl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerImportantPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
Object@ hw = ai.empire.Homeworld;
|
||||
if(hw !is null && hw.valid && hw.owner is empire) {
|
||||
double w = hook.consider(this, targets, vote, card, hw);
|
||||
if(w != 0.0) {
|
||||
@best = hw;
|
||||
bestW = w;
|
||||
}
|
||||
}
|
||||
|
||||
auto@ reso = getRandomResource(randomi(2, 3));
|
||||
if(reso !is null) {
|
||||
auto@ list = ai.planetsByResource[reso.id];
|
||||
for(uint i = 0, cnt = list.length; i < cnt; ++i) {
|
||||
Object@ pl = list.planets[i];
|
||||
|
||||
double w = hook.consider(this, targets, vote, card, pl);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = pl;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerOwnedSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
Object@ hw = ai.empire.Homeworld;
|
||||
if(hw !is null && hw.valid && hw.owner is empire) {
|
||||
Region@ reg = hw.region;
|
||||
if(reg !is null) {
|
||||
double w = hook.consider(this, targets, vote, card, reg);
|
||||
if(w != 0.0) {
|
||||
@best = reg;
|
||||
bestW = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto@ reso = getRandomResource(randomi(0, 3));
|
||||
if(reso !is null) {
|
||||
auto@ list = ai.planetsByResource[reso.id];
|
||||
for(uint i = 0, cnt = list.length; i < cnt; ++i) {
|
||||
Object@ pl = list.planets[i];
|
||||
Region@ reg = pl.region;
|
||||
if(reg !is null) {
|
||||
double w = hook.consider(this, targets, vote, card, reg);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerImportantSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
Object@ hw = ai.empire.Homeworld;
|
||||
if(hw !is null && hw.valid && hw.owner is empire) {
|
||||
Region@ reg = hw.region;
|
||||
if(reg !is null) {
|
||||
double w = hook.consider(this, targets, vote, card, reg);
|
||||
if(w != 0.0) {
|
||||
@best = reg;
|
||||
bestW = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto@ reso = getRandomResource(randomi(2, 3));
|
||||
if(reso !is null) {
|
||||
auto@ list = ai.planetsByResource[reso.id];
|
||||
for(uint i = 0, cnt = list.length; i < cnt; ++i) {
|
||||
Object@ pl = list.planets[i];
|
||||
Region@ reg = pl.region;
|
||||
if(reg !is null) {
|
||||
double w = hook.consider(this, targets, vote, card, reg);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = reg;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
void markProtecting(Object@ obj) {
|
||||
@ai.protect = null;
|
||||
}
|
||||
|
||||
Object@ considerDefendingSystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
auto@ sys = ai.protect;
|
||||
if(sys is null)
|
||||
return null;
|
||||
|
||||
uint friendMask = empire.mask | allyMask;
|
||||
if((sys.planetMask & sys.region.ContestedMask) & empire.mask != 0) {
|
||||
double w = hook.consider(this, targets, vote, card, sys.region);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = sys.region;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerDefendingPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
auto@ sys = ai.protect;
|
||||
if(sys is null)
|
||||
return null;
|
||||
|
||||
uint friendMask = empire.mask | allyMask;
|
||||
if((sys.planetMask & sys.region.ContestedMask) & empire.mask != 0) {
|
||||
for(uint p = 0, pcnt = sys.planets.length; p < pcnt; ++p) {
|
||||
Object@ pl = sys.planets[p];
|
||||
double w = hook.consider(this, targets, vote, card, pl);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = sys.region;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerEnemySystems(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
uint enemies = enemiesMask();
|
||||
for(uint n = 0; n < 5; ++n) {
|
||||
auto@ fromSys = ai.ourSystems[randomi(0,ai.ourSystems.length-1)];
|
||||
for(uint adj = 0, adjCnt = fromSys.system.adjacent.length; adj < adjCnt; ++adj) {
|
||||
auto@ otherSys = getSystem(fromSys.system.adjacent[adj]);
|
||||
Object@ other = otherSys.object;
|
||||
if(otherSys.object.PlanetsMask & enemies == 0)
|
||||
continue;
|
||||
|
||||
double w = hook.consider(this, targets, vote, card, other);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = other;
|
||||
}
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerEnemyPlanets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
uint enemies = enemiesMask();
|
||||
for(uint n = 0; n < 5; ++n) {
|
||||
auto@ fromSys = ai.ourSystems[randomi(0,ai.ourSystems.length-1)];
|
||||
for(uint adj = 0, adjCnt = fromSys.system.adjacent.length; adj < adjCnt; ++adj) {
|
||||
auto@ otherSys = getSystem(fromSys.system.adjacent[adj]);
|
||||
Object@ other = otherSys.object;
|
||||
if(otherSys.object.PlanetsMask & enemies == 0)
|
||||
continue;
|
||||
|
||||
auto@ stored = ai.findSystem(otherSys.object);
|
||||
if(stored is null)
|
||||
continue;
|
||||
|
||||
uint off = randomi(0,stored.planets.length-1);
|
||||
for(uint i = 0, cnt = stored.planets.length; i < cnt; ++i) {
|
||||
Object@ pl = stored.planets[(i+off) % cnt];
|
||||
if(pl.owner.mask & enemies != 0) {
|
||||
double w = hook.consider(this, targets, vote, card, other);
|
||||
if(w > bestW) {
|
||||
bestW = w;
|
||||
@best = other;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerFleets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
Object@ flt = empire.getStrongestFleet();
|
||||
if(flt !is null) {
|
||||
double w = hook.consider(this, targets, vote, card, flt);
|
||||
if(w != 0.0) {
|
||||
@best = flt;
|
||||
bestW = w;
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
Object@ considerEnemyFleets(const CardAI& hook, Targets& targets, VoteState@ vote = null, const InfluenceCard@ card = null) {
|
||||
double bestW = 0.0;
|
||||
Object@ best;
|
||||
|
||||
Empire@ ofEmp = getEmpire(randomi(0, getEmpireCount()-1));
|
||||
if(empire.isHostile(ofEmp)) {
|
||||
Object@ flt = ai.empire.getStrongestFleet();
|
||||
if(flt !is null && flt.isVisibleTo(ai.empire)) {
|
||||
double w = hook.consider(this, targets, vote, card, flt);
|
||||
if(w != 0.0) {
|
||||
@best = flt;
|
||||
bestW = w;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return best;
|
||||
}
|
||||
|
||||
//Returns true if the action is finished
|
||||
uint nextStep = 0;
|
||||
bool perform(BasicAI@ ai) {
|
||||
summarize();
|
||||
|
||||
switch(nextStep++ % 3) {
|
||||
case 0:
|
||||
buyCards();
|
||||
break;
|
||||
case 1:
|
||||
considerActions();
|
||||
break;
|
||||
case 2:
|
||||
considerVotes();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void postLoad(BasicAI@ ai) {
|
||||
}
|
||||
};
|
||||
|
||||
Action@ loadDiplomacyAI(BasicAI@ ai, SaveFile& file) {
|
||||
return DiplomacyAction(ai, file);
|
||||
}
|
||||
|
||||
Action@ createDiplomacyAI(BasicAI@ ai) {
|
||||
return DiplomacyAction(ai);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,53 @@
|
||||
import influence;
|
||||
|
||||
//These strategies are flags regarding behaviors
|
||||
enum VoteStrategy {
|
||||
//Attempt to provide support (otherwise attempt to defeat)
|
||||
VS_Support = 1,
|
||||
|
||||
//Attempt to create the vote if it isn't already present
|
||||
VS_Create = 64
|
||||
};
|
||||
|
||||
int64 buildVoteHash(uint8 strat) {
|
||||
return (int64(ACT_Vote) << ACT_BIT_OFFSET) | (int64(strat) << 24);
|
||||
}
|
||||
|
||||
class Vote : Action {
|
||||
uint8 strat;
|
||||
int64 Hash;
|
||||
/*InfluenceVote@ vote;*/
|
||||
|
||||
Vote(uint8 Strat) {
|
||||
strat = Strat;
|
||||
Hash = buildVoteHash(Strat);
|
||||
}
|
||||
|
||||
Vote(BasicAI@ ai, SaveFile& msg) {
|
||||
msg >> strat;
|
||||
}
|
||||
|
||||
void postLoad(BasicAI@ ai) {
|
||||
}
|
||||
|
||||
void save(BasicAI@ ai, SaveFile& msg) {
|
||||
msg << strat;
|
||||
}
|
||||
|
||||
int64 get_hash() const {
|
||||
return Hash;
|
||||
}
|
||||
|
||||
ActionType get_actionType() const {
|
||||
return ACT_Vote;
|
||||
}
|
||||
|
||||
string get_state() const {
|
||||
return "Voting on proposition";
|
||||
}
|
||||
|
||||
bool perform(BasicAI@ ai) {
|
||||
return true;
|
||||
}
|
||||
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user