713 lines
22 KiB
ActionScript
713 lines
22 KiB
ActionScript
import settings.game_settings;
|
|
from empire_ai.EmpireAI import AIController;
|
|
|
|
import AIComponent@ createColonization() from "empire_ai.weasel.Colonization";
|
|
import AIComponent@ createResources() from "empire_ai.weasel.Resources";
|
|
import AIComponent@ createPlanets() from "empire_ai.weasel.Planets";
|
|
import AIComponent@ createSystems() from "empire_ai.weasel.Systems";
|
|
import AIComponent@ createFleets() from "empire_ai.weasel.Fleets";
|
|
import AIComponent@ createScouting() from "empire_ai.weasel.Scouting";
|
|
import AIComponent@ createDevelopment() from "empire_ai.weasel.Development";
|
|
import AIComponent@ createDesigns() from "empire_ai.weasel.Designs";
|
|
import AIComponent@ createBudget() from "empire_ai.weasel.Budget";
|
|
import AIComponent@ createConstruction() from "empire_ai.weasel.Construction";
|
|
import AIComponent@ createMilitary() from "empire_ai.weasel.Military";
|
|
import AIComponent@ createMovement() from "empire_ai.weasel.Movement";
|
|
import AIComponent@ createCreeping() from "empire_ai.weasel.Creeping";
|
|
import AIComponent@ createRelations() from "empire_ai.weasel.Relations";
|
|
import AIComponent@ createIntelligence() from "empire_ai.weasel.Intelligence";
|
|
import AIComponent@ createWar() from "empire_ai.weasel.War";
|
|
import AIComponent@ createResearch() from "empire_ai.weasel.Research";
|
|
import AIComponent@ createEnergy() from "empire_ai.weasel.Energy";
|
|
import IAIComponent@ createDiplomacy() from "empire_ai.weasel.Diplomacy";
|
|
import AIComponent@ createConsider() from "empire_ai.weasel.Consider";
|
|
import AIComponent@ createOrbitals() from "empire_ai.weasel.Orbitals";
|
|
|
|
import AIComponent@ createHyperdrive() from "empire_ai.weasel.ftl.Hyperdrive";
|
|
import AIComponent@ createGate() from "empire_ai.weasel.ftl.Gate";
|
|
import AIComponent@ createFling() from "empire_ai.weasel.ftl.Fling";
|
|
import AIComponent@ createSlipstream() from "empire_ai.weasel.ftl.Slipstream";
|
|
import AIComponent@ createJumpdrive() from "empire_ai.weasel.ftl.Jumpdrive";
|
|
|
|
import AIComponent@ createVerdant() from "empire_ai.weasel.race.Verdant";
|
|
import AIComponent@ createMechanoid() from "empire_ai.weasel.race.Mechanoid";
|
|
import AIComponent@ createStarChildren() from "empire_ai.weasel.race.StarChildren";
|
|
import AIComponent@ createExtragalactic() from "empire_ai.weasel.race.Extragalactic";
|
|
import AIComponent@ createLinked() from "empire_ai.weasel.race.Linked";
|
|
import AIComponent@ createDevout() from "empire_ai.weasel.race.Devout";
|
|
import AIComponent@ createAncient() from "empire_ai.weasel.race.Ancient";
|
|
|
|
import AIComponent@ createInvasion() from "empire_ai.weasel.misc.Invasion";
|
|
import bool hasInvasionMap() from "Invasion.InvasionMap";
|
|
|
|
from buildings import BuildingType;
|
|
from orbitals import OrbitalModule;
|
|
import util.formatting;
|
|
|
|
from empire import ai_full_speed;
|
|
|
|
from traits import getTraitID;
|
|
|
|
export IAIComponent, AIComponent, AI;
|
|
uint GUARD = 0xDEADBEEF;
|
|
|
|
enum AddedComponent {
|
|
AC_Invasion = 0x1,
|
|
};
|
|
|
|
interface IAIComponent : Savable {
|
|
void set(AI& ai);
|
|
void setLog();
|
|
void setLogCritical();
|
|
|
|
double getPrevFocus();
|
|
void setPrevFocus(double value);
|
|
|
|
void create();
|
|
void start();
|
|
|
|
void tick(double time);
|
|
void focusTick(double time);
|
|
void turn();
|
|
|
|
void save(SaveFile& file);
|
|
void load(SaveFile& file);
|
|
void postLoad(AI& ai);
|
|
void postSave(AI& ai);
|
|
void loadFinalize(AI& ai);
|
|
};
|
|
|
|
class AIComponent : IAIComponent, Savable {
|
|
AI@ ai;
|
|
double prevFocus = 0;
|
|
bool log = false;
|
|
bool logCritical = false;
|
|
bool logErrors = true;
|
|
|
|
double getPrevFocus() { return prevFocus; }
|
|
void setPrevFocus(double value) { prevFocus = value; }
|
|
|
|
void setLog() { log = true; }
|
|
void setLogCritical() { logCritical = true; }
|
|
|
|
void set(AI& ai) { @this.ai = ai; }
|
|
void create() {}
|
|
void start() {}
|
|
|
|
void tick(double time) {}
|
|
void focusTick(double time) {}
|
|
void turn() {}
|
|
|
|
void save(SaveFile& file) {}
|
|
void load(SaveFile& file) {}
|
|
void postLoad(AI& ai) {}
|
|
void postSave(AI& ai) {}
|
|
void loadFinalize(AI& ai) {}
|
|
};
|
|
|
|
class ProfileData {
|
|
double tickPeak = 0.0;
|
|
double tickAvg = 0.0;
|
|
double tickCount = 0.0;
|
|
|
|
double focusPeak = 0.0;
|
|
double focusAvg = 0.0;
|
|
double focusCount = 0.0;
|
|
};
|
|
|
|
final class AIBehavior {
|
|
//How many focuses we can manage in a tick
|
|
uint focusPerTick = 2;
|
|
|
|
//The maximum colonizations this AI can do in one turn
|
|
uint maxColonizations = UINT_MAX;
|
|
//How many colonizations we're guaranteed to be able to do in one turn regardless of finances
|
|
uint guaranteeColonizations = 2;
|
|
//How many colonizations at most we can be doing at once
|
|
uint maxConcurrentColonizations = UINT_MAX;
|
|
//Whether this AI will colonize planets in systems owned by someone else
|
|
// TODO: This should be partially ignored for border systems, so it can try to aggressively expand into the border
|
|
bool colonizeEnemySystems = false;
|
|
bool colonizeNeutralOwnedSystems = false;
|
|
bool colonizeAllySystems = false;
|
|
//How much this AI values claiming new systems instead of colonizing stuff in its existing ones
|
|
double weightOutwardExpand = 2.0;
|
|
//How much money this AI considers a colonization event to cost out of the budget
|
|
int colonizeBudgetCost = 80;
|
|
//Whether to do any generic expansion beyond any requests
|
|
bool colonizeGenericExpand = true;
|
|
//Latest percentage into a budget cycle that we still allow colonization
|
|
double colonizeMaxBudgetProgress = 0.66;
|
|
//Time after initial ownership change that an incomplete colonization is canceled
|
|
double colonizeFailGraceTime = 100.0;
|
|
//Time a planet that we failed to colonize is disregarded for colonization
|
|
double colonizePenalizeTime = 9.0 * 60.0;
|
|
|
|
//Maximum amount of scouting missions that can be performed simultaneously
|
|
uint maxScoutingMissions = UINT_MAX;
|
|
//Minimum time after losing vision over a system that we can scout it again
|
|
double minScoutingInterval = 3.0 * 60.0;
|
|
//Weight that it gives to exploring things near our empire instead of greedily exploring nearby things
|
|
double exploreBorderWeight = 2.0;
|
|
//How long we consider all fleets viable for scouting with
|
|
double scoutAllTimer = 3.0 * 60.0;
|
|
//How many scouts we want to have active
|
|
uint scoutsActive = 2;
|
|
//How many scanning missions we can do at once
|
|
uint maxScanningMissions = 1;
|
|
//Whether to prioritize scouting over scanning if we only have one scout
|
|
bool prioritizeScoutOverScan = true;
|
|
|
|
//Weights for what to do in generic planet development
|
|
// Leveling up an existing development focus
|
|
double focusDevelopWeight = 1.0;
|
|
// Colonizing a new scalable or high tier to focus on
|
|
double focusColonizeNewWeight = 4.0;
|
|
// Colonizing a new high tier resource to import to one of our focuses
|
|
double focusColonizeHighTierWeight = 1.0;
|
|
|
|
//How many potential designs are evaluated before choosing the best one
|
|
uint designEvaluateCount = 10;
|
|
//How long a fleet has to be fully idle before it returns to its stationed system
|
|
double fleetIdleReturnStationedTime = 60.0;
|
|
//How long we try to have a fleet be capable of firing before running out of supplies
|
|
double fleetAimSupplyDuration = 2.0 * 60.0;
|
|
|
|
//How long a potential construction can take at most before we consider it unreasonable
|
|
double constructionMaxTime = 10.0 * 60.0;
|
|
//How long a factory has to have been idle for us to consider constructing labor storage
|
|
double laborStoreIdleTimer = 60.0;
|
|
//Maximum amount of time worth of labor we want to store in our warehouses
|
|
double laborStoreMaxFillTime = 60.0 * 10.0;
|
|
//Whether to use labor to build asteroids in the background
|
|
bool backgroundBuildAsteroids = true;
|
|
//Whether to choose the best resource on an asteroid, instead of doing it randomly
|
|
bool chooseAsteroidResource = true;
|
|
//Whether to distribute labor to shipyards when planets are idle
|
|
bool distributeLaborExports = true;
|
|
//Whether to build a shipyard to consolidate multiple planets of labor where possible
|
|
bool consolidateLaborExports = true;
|
|
//Estimate amount of labor spent per point of support ship size
|
|
double estSizeSupportLabor = 0.25;
|
|
|
|
//Maximum combat fleets we can have in service at once (counts starting fleet(s))
|
|
uint maxActiveFleets = UINT_MAX;
|
|
//How much flagship size we try to make per available money
|
|
double shipSizePerMoney = 1.0 / 3.5;
|
|
//How much flagship size we try to make per available labor
|
|
double shipSizePerLabor = 1.0 / 0.33;
|
|
//How much maintenance we expect per ship size
|
|
double maintenancePerShipSize = 2.0;
|
|
//Minimum percentage increase in size before we decide to retrofit a flagship to be bigger
|
|
double shipRetrofitThreshold = 0.5;
|
|
//Whether to retrofit our free starting fleet if appropriate
|
|
bool retrofitFreeFleets = false;
|
|
//Minimum percentage of average current flagship size new fleets should be
|
|
double flagshipBuildMinAvgSize = 1.00;
|
|
//Minimum game time before we consider constructing new flagships
|
|
double flagshipBuildMinGameTime = 4.0 * 60.0;
|
|
//Whether to build factories when we need labor
|
|
bool buildFactoryForLabor = true;
|
|
//Whether to build warehouses when we're not using labor
|
|
bool buildLaborStorage = true;
|
|
//Whether factories can queue labor resource imports when needed
|
|
bool allowRequestLaborImports = true;
|
|
//Whether fleets with ghosted supports attempt to rebuild the ghosts or just clear them
|
|
bool fleetsRebuildGhosts = true;
|
|
//When trying to order supports on a fleet, wait for the planet to construct its supports so we can claim them
|
|
bool supportOrderWaitOnFactory = true;
|
|
|
|
//How much stronger we need to be than a remnant fleet to clear it
|
|
double remnantOverkillFactor = 1.5;
|
|
//Whether to allow idle fleets to be sent to clear remnants
|
|
// Modified by Relations
|
|
bool remnantAllowArbitraryClear = true;
|
|
|
|
//Whether we should aggressively try to take out enemies
|
|
bool aggressive = false;
|
|
//Whether to become aggressive after we get boxed in and can no longer expand anywhere
|
|
bool aggressiveWhenBoxedIn = false;
|
|
//Whether we should never declare war ourselves
|
|
bool passive = false;
|
|
//Whether to hate human players the most
|
|
bool biased = false;
|
|
//How much stronger we need to be than someone to declare war out of hatred
|
|
double hatredWarOverkill = 0.5;
|
|
//How much stronger we need to be than someone to try to take them out in an aggressive war
|
|
double aggressiveWarOverkill = 1.5;
|
|
//How much stronger we want to be before we attack a system
|
|
double attackStrengthOverkill = 1.5;
|
|
//How many battles we can be performing at once
|
|
uint maxBattles = UINT_MAX;
|
|
//How much we try to overkill while fighting
|
|
double battleStrengthOverkill = 1.5;
|
|
//How many fleets we don't commit to attacks when we're already currently fighting
|
|
uint battleReserveFleets = 1;
|
|
//How much extra supply we try to have before starting a capture, to make sure we can actually do it
|
|
double captureSupplyEstimate = 1.5;
|
|
//Maximum hop distance we use as staging areas for our attacks
|
|
int stagingMaxHops = 5;
|
|
//If our fleet fill is less than this, immediately move back to factory from staging
|
|
double stagingToFactoryFill = 0.6;
|
|
|
|
//How much ftl is reserved for critical applications
|
|
double ftlReservePctCritical = 0.25;
|
|
//How much ftl is reserved to not be used for background applications
|
|
double ftlReservePctNormal = 0.25;
|
|
|
|
//How many artifacts we consider where to use per focus turn
|
|
uint artifactFocusConsiderCount = 2;
|
|
|
|
//How long after trying to build a generically requested building we give up
|
|
double genericBuildExpire = 3.0 * 60.0;
|
|
|
|
//How much the hate in a relationship decays to every minute
|
|
double hateDecayRate = 0.9;
|
|
//How much weaker we need to be to even consider surrender
|
|
double surrenderMinStrength = 0.5;
|
|
//How many of our total war points have to be taken by an empire for us to surrender
|
|
double acceptSurrenderRatio = 0.75;
|
|
double offerSurrenderRatio = 0.5;
|
|
|
|
void setDifficulty(int diff, uint flags) {
|
|
//This changes the behavior values based on difficulty and flags
|
|
if(flags & AIF_Aggressive != 0)
|
|
aggressive = true;
|
|
if(flags & AIF_Passive != 0)
|
|
passive = true;
|
|
if(flags & AIF_Biased != 0)
|
|
biased = true;
|
|
|
|
//Low difficulties can't colonize as many things at once
|
|
if(diff <= 0) {
|
|
maxConcurrentColonizations = 1;
|
|
guaranteeColonizations = 1;
|
|
weightOutwardExpand = 0.5;
|
|
}
|
|
else if(diff <= 1) {
|
|
maxConcurrentColonizations = 2;
|
|
weightOutwardExpand = 1.0;
|
|
}
|
|
|
|
//Hard AI becomes aggressive when it gets boxed in
|
|
aggressiveWhenBoxedIn = diff >= 2;
|
|
|
|
//Easy difficulty can't attack and defend at the same time
|
|
if(diff <= 0)
|
|
maxBattles = 1;
|
|
|
|
//Low difficulties aren't as good at managing labor
|
|
if(diff <= 0) {
|
|
distributeLaborExports = false;
|
|
consolidateLaborExports = false;
|
|
buildLaborStorage = false;
|
|
}
|
|
else if(diff <= 1) {
|
|
consolidateLaborExports = false;
|
|
}
|
|
|
|
//Low difficulties aren't as good at managing fleets
|
|
if(diff <= 0) {
|
|
maxActiveFleets = 2;
|
|
retrofitFreeFleets = true;
|
|
}
|
|
|
|
//Low difficulties aren't as good at scouting
|
|
if(diff <= 1)
|
|
scoutAllTimer = 0.0;
|
|
|
|
//Low difficulties are worse at designing
|
|
if(diff <= 0)
|
|
designEvaluateCount = 3;
|
|
else if(diff <= 1)
|
|
designEvaluateCount = 8;
|
|
else
|
|
designEvaluateCount = 12;
|
|
|
|
//Easy is a bit slow
|
|
if(diff <= 0)
|
|
focusPerTick = 1;
|
|
else if(diff >= 2)
|
|
focusPerTick = 3;
|
|
}
|
|
};
|
|
|
|
final class AIDefs {
|
|
const BuildingType@ Factory;
|
|
const BuildingType@ LaborStorage;
|
|
const OrbitalModule@ Shipyard;
|
|
};
|
|
|
|
final class AI : AIController, Savable {
|
|
Empire@ empire;
|
|
AIBehavior behavior;
|
|
AIDefs defs;
|
|
|
|
int cycleId = -1;
|
|
uint componentCycle = 0;
|
|
uint addedComponents = 0;
|
|
|
|
uint majorMask = 0;
|
|
uint difficulty = 0;
|
|
uint flags = 0;
|
|
bool isLoading = false;
|
|
|
|
array<IAIComponent@> components;
|
|
array<ProfileData> profileData;
|
|
IAIComponent@ fleets;
|
|
IAIComponent@ budget;
|
|
IAIComponent@ colonization;
|
|
IAIComponent@ resources;
|
|
IAIComponent@ planets;
|
|
IAIComponent@ systems;
|
|
IAIComponent@ scouting;
|
|
IAIComponent@ development;
|
|
IAIComponent@ designs;
|
|
IAIComponent@ construction;
|
|
IAIComponent@ military;
|
|
IAIComponent@ movement;
|
|
IAIComponent@ creeping;
|
|
IAIComponent@ relations;
|
|
IAIComponent@ intelligence;
|
|
IAIComponent@ war;
|
|
IAIComponent@ research;
|
|
IAIComponent@ energy;
|
|
IAIComponent@ diplomacy;
|
|
IAIComponent@ consider;
|
|
IAIComponent@ orbitals;
|
|
|
|
IAIComponent@ ftl;
|
|
IAIComponent@ race;
|
|
|
|
IAIComponent@ invasion;
|
|
|
|
void createComponents() {
|
|
//NOTE: This is also save/load order, so
|
|
//make sure to add loading logic when changing this list
|
|
@budget = add(createBudget());
|
|
@planets = add(createPlanets());
|
|
@resources = add(createResources());
|
|
@colonization = add(createColonization());
|
|
@systems = add(createSystems());
|
|
@fleets = add(createFleets());
|
|
@scouting = add(createScouting());
|
|
@development = add(createDevelopment());
|
|
@designs = add(createDesigns());
|
|
@construction = add(createConstruction());
|
|
@military = add(createMilitary());
|
|
@movement = add(createMovement());
|
|
@creeping = add(createCreeping());
|
|
@relations = add(createRelations());
|
|
@intelligence = add(createIntelligence());
|
|
@war = add(createWar());
|
|
@research = add(createResearch());
|
|
@energy = add(createEnergy());
|
|
@diplomacy = add(createDiplomacy());
|
|
@consider = add(createConsider());
|
|
@orbitals = add(createOrbitals());
|
|
|
|
//Make FTL component
|
|
if(empire.hasTrait(getTraitID("Hyperdrive")))
|
|
@ftl = add(createHyperdrive());
|
|
else if(empire.hasTrait(getTraitID("Gate")))
|
|
@ftl = add(createGate());
|
|
else if(empire.hasTrait(getTraitID("Fling")))
|
|
@ftl = add(createFling());
|
|
else if(empire.hasTrait(getTraitID("Slipstream")))
|
|
@ftl = add(createSlipstream());
|
|
else if(empire.hasTrait(getTraitID("Jumpdrive")))
|
|
@ftl = add(createJumpdrive());
|
|
|
|
//Make racial component
|
|
if(empire.hasTrait(getTraitID("Verdant")))
|
|
@race = add(createVerdant());
|
|
else if(empire.hasTrait(getTraitID("Mechanoid")))
|
|
@race = add(createMechanoid());
|
|
else if(empire.hasTrait(getTraitID("StarChildren")))
|
|
@race = add(createStarChildren());
|
|
else if(empire.hasTrait(getTraitID("Extragalactic")))
|
|
@race = add(createExtragalactic());
|
|
else if(empire.hasTrait(getTraitID("Linked")))
|
|
@race = add(createLinked());
|
|
else if(empire.hasTrait(getTraitID("Devout")))
|
|
@race = add(createDevout());
|
|
else if(empire.hasTrait(getTraitID("Ancient")))
|
|
@race = add(createAncient());
|
|
|
|
//Misc components
|
|
if(hasInvasionMap() || addedComponents & AC_Invasion != 0) {
|
|
@invasion = add(createInvasion());
|
|
addedComponents |= AC_Invasion;
|
|
}
|
|
|
|
//if(empire is playerEmpire) {
|
|
//log(race);
|
|
// log(colonization);
|
|
// log(resources);
|
|
// log(construction);
|
|
//}
|
|
//log(intelligence);
|
|
//logAll();
|
|
logCritical();
|
|
|
|
profileData.length = components.length;
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i)
|
|
components[i].create();
|
|
}
|
|
|
|
void createGeneral() {
|
|
}
|
|
|
|
void init(Empire& emp, EmpireSettings& settings) {
|
|
@this.empire = emp;
|
|
flags = settings.aiFlags;
|
|
difficulty = settings.difficulty;
|
|
behavior.setDifficulty(difficulty, flags);
|
|
|
|
createComponents();
|
|
}
|
|
|
|
int getDifficultyLevel() {
|
|
return difficulty;
|
|
}
|
|
|
|
void load(SaveFile& file) {
|
|
file >> empire;
|
|
file >> cycleId;
|
|
file >> majorMask;
|
|
file >> difficulty;
|
|
file >> flags;
|
|
if(file >= SV_0153)
|
|
file >> addedComponents;
|
|
behavior.setDifficulty(difficulty, flags);
|
|
createComponents();
|
|
createGeneral();
|
|
|
|
uint loadCnt = 0;
|
|
file >> loadCnt;
|
|
loadCnt = loadCnt;
|
|
for(uint i = 0; i < loadCnt; ++i) {
|
|
double prevFocus = 0;
|
|
file >> prevFocus;
|
|
components[i].setPrevFocus(prevFocus);
|
|
file >> components[i];
|
|
|
|
uint check = 0;
|
|
file >> check;
|
|
if(check != GUARD)
|
|
error("ERROR: AI Load error detected in component "+addrstr(components[i])+" of "+empire.name);
|
|
}
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i)
|
|
components[i].postLoad(this);
|
|
isLoading = true;
|
|
}
|
|
|
|
void save(SaveFile& file) {
|
|
file << empire;
|
|
file << cycleId;
|
|
file << majorMask;
|
|
file << difficulty;
|
|
file << flags;
|
|
file << addedComponents;
|
|
uint saveCnt = components.length;
|
|
file << saveCnt;
|
|
for(uint i = 0; i < saveCnt; ++i) {
|
|
file << components[i].getPrevFocus();
|
|
file << components[i];
|
|
file << GUARD;
|
|
}
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i)
|
|
components[i].postSave(this);
|
|
}
|
|
|
|
void log(IAIComponent@ comp) {
|
|
if(comp is null)
|
|
return;
|
|
comp.setLog();
|
|
comp.setLogCritical();
|
|
}
|
|
|
|
void logCritical() {
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i)
|
|
components[i].setLogCritical();
|
|
}
|
|
|
|
void logAll() {
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i) {
|
|
components[i].setLog();
|
|
components[i].setLogCritical();
|
|
}
|
|
}
|
|
|
|
IAIComponent@ add(IAIComponent& component) {
|
|
component.set(this);
|
|
components.insertLast(component);
|
|
return component;
|
|
}
|
|
|
|
void init(Empire& emp) {
|
|
majorMask = 0;
|
|
for(uint i = 0, cnt = getEmpireCount(); i < cnt; ++i) {
|
|
Empire@ emp = getEmpire(i);
|
|
if(emp.major)
|
|
majorMask |= emp.mask;
|
|
}
|
|
|
|
createGeneral();
|
|
}
|
|
|
|
bool hasStarted = false;
|
|
void tick(Empire& emp, double time) {
|
|
if(isLoading) {
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i)
|
|
components[i].loadFinalize(this);
|
|
isLoading = false;
|
|
hasStarted = true;
|
|
}
|
|
else if(!hasStarted) {
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i)
|
|
components[i].start();
|
|
hasStarted = true;
|
|
}
|
|
else if(emp.Victory == -1) {
|
|
//Don't do anything when actually defeated
|
|
return;
|
|
}
|
|
|
|
//Manage gametime-specific behaviors
|
|
behavior.colonizeGenericExpand = gameTime >= 6.0 * 60.0;
|
|
|
|
//Find cycled turns
|
|
int curCycle = emp.BudgetCycleId;
|
|
if(curCycle != cycleId) {
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i)
|
|
components[i].turn();
|
|
cycleId = curCycle;
|
|
}
|
|
|
|
//Generic ticks
|
|
double startTime = getExactTime();
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i) {
|
|
auto@ comp = components[i];
|
|
comp.tick(time);
|
|
|
|
double endTime = getExactTime();
|
|
//double ms = 1000.0 * (endTime - startTime);
|
|
startTime = endTime;
|
|
|
|
//auto@ dat = profileData[i];
|
|
//dat.tickPeak = max(dat.tickPeak, ms);
|
|
//dat.tickAvg += ms;
|
|
//dat.tickCount += 1.0;
|
|
}
|
|
|
|
//Do focuseds tick on components
|
|
uint focusCount = behavior.focusPerTick;
|
|
if(ai_full_speed.value == 1.0)
|
|
focusCount = max(uint(round((time / 0.25) * behavior.focusPerTick)), behavior.focusPerTick);
|
|
double allocStart = startTime;
|
|
|
|
for(uint n = 0; n < focusCount; ++n) {
|
|
componentCycle = (componentCycle+1) % components.length;
|
|
auto@ focusComp = components[componentCycle];
|
|
focusComp.focusTick(gameTime - focusComp.getPrevFocus());
|
|
focusComp.setPrevFocus(gameTime);
|
|
|
|
double endTime = getExactTime();
|
|
//double ms = 1000.0 * (endTime - startTime);
|
|
startTime = endTime;
|
|
if(endTime - allocStart > 4000.0)
|
|
break;
|
|
|
|
//auto@ dat = profileData[componentCycle];
|
|
//dat.focusPeak = max(dat.focusPeak, ms);
|
|
//dat.focusAvg += ms;
|
|
//dat.focusCount += 1.0;
|
|
}
|
|
}
|
|
|
|
void dumpProfile() {
|
|
for(uint i = 0, cnt = components.length; i < cnt; ++i) {
|
|
auto@ c = profileData[i];
|
|
print(pad(addrstr(components[i]), 40)+" tick peak "+toString(c.tickPeak,2)+" tick avg "+toString(c.tickAvg/c.tickCount, 2)
|
|
+" focus peak "+toString(c.focusPeak,2)+" focus avg "+toString(c.focusAvg/c.focusCount, 2));
|
|
}
|
|
}
|
|
|
|
void resetProfile() {
|
|
for(uint i = 0, cnt = profileData.length; i < cnt; ++i) {
|
|
auto@ c = profileData[i];
|
|
c.tickPeak = 0.0;
|
|
c.tickAvg = 0.0;
|
|
c.tickCount = 0.0;
|
|
c.focusPeak = 0.0;
|
|
c.focusAvg = 0.0;
|
|
c.focusCount = 0.0;
|
|
}
|
|
}
|
|
|
|
uint get_mask() {
|
|
return empire.mask;
|
|
}
|
|
|
|
uint get_teamMask() {
|
|
//TODO
|
|
return empire.mask;
|
|
}
|
|
|
|
uint get_visionMask() {
|
|
return majorMask & empire.visionMask;
|
|
}
|
|
|
|
uint get_allyMask() {
|
|
return empire.mutualDefenseMask | empire.ForcedPeaceMask.value;
|
|
}
|
|
|
|
uint get_enemyMask() {
|
|
return empire.hostileMask & majorMask;
|
|
}
|
|
|
|
uint get_neutralMask() {
|
|
return majorMask & ~allyMask & ~mask & ~enemyMask;
|
|
}
|
|
|
|
uint get_otherMask() {
|
|
return majorMask & ~mask;
|
|
}
|
|
|
|
string pad(const string& input, uint width) {
|
|
string str = input;
|
|
while(str.length < width)
|
|
str += " ";
|
|
return str;
|
|
}
|
|
|
|
void print(const string& info, Object@ related = null, double value = INFINITY, bool flag = false, Empire@ emp = null) {
|
|
string str = info;
|
|
if(related !is null)
|
|
str = pad(related.name, 16)+" | "+str;
|
|
str = pad("["+empire.index+": "+empire.name+" AI] ", 20)+str;
|
|
str = formatGameTime(gameTime) + " " + str;
|
|
if(value != INFINITY)
|
|
str += " | Value = "+standardize(value, true);
|
|
if(flag)
|
|
str += " | FLAGGED On";
|
|
if(emp !is null)
|
|
str += " | Target = "+emp.name;
|
|
::print(str);
|
|
}
|
|
|
|
void debugAI() {}
|
|
void commandAI(string cmd) {}
|
|
void aiPing(Empire@ fromEmpire, vec3d position, uint type) {}
|
|
void pause(Empire& emp) {}
|
|
void resume(Empire& emp) {}
|
|
vec3d get_aiFocus() { return vec3d(); }
|
|
string getOpinionOf(Empire& emp, Empire@ other) { return ""; }
|
|
int getStandingTo(Empire& emp, Empire@ other) { return 0; }
|
|
};
|
|
|
|
AIController@ createWeaselAI() {
|
|
return AI();
|
|
}
|