Index: binaries/data/mods/public/simulation/ai/common-api/gamestate.js =================================================================== --- binaries/data/mods/public/simulation/ai/common-api/gamestate.js +++ binaries/data/mods/public/simulation/ai/common-api/gamestate.js @@ -23,6 +23,7 @@ this.alliedVictory = SharedScript.alliedVictory; this.ceasefireActive = SharedScript.ceasefireActive; this.ceasefireTimeRemaining = SharedScript.ceasefireTimeRemaining; + this.emergencyState = new Map(); // get the list of possible phases for this civ: // we assume all of them are researchable from the civil center Index: binaries/data/mods/public/simulation/ai/petra/chatHelper.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/chatHelper.js +++ binaries/data/mods/public/simulation/ai/petra/chatHelper.js @@ -132,6 +132,11 @@ } }; +PETRA.emergencyMessages = [ + /* Just show some message for now, this will be fleshed out in a more diverse way in upcoming patches */ + markForTranslation("My armies failed while defending my empire. Please honor our alliance and send help!") +]; + PETRA.chatLaunchAttack = function(gameState, player, type) { Engine.PostCommand(PlayerID, { @@ -234,3 +239,12 @@ "parameters": { "_player_": player } }); }; + +PETRA.chatEmergency = function(gameState) +{ + Engine.PostCommand(PlayerID, { + "type": "aichat", + "message": "/allies " + pickRandom(this.emergencyMessages), + "translateMessage": true + }); +}; Index: binaries/data/mods/public/simulation/ai/petra/config.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/config.js +++ binaries/data/mods/public/simulation/ai/petra/config.js @@ -175,6 +175,33 @@ "Market/InternationalBonus", "Player/sharedDropsites" ]; + + this.criticalPopulationFactors = [ + 0.8, + 0.8, + 0.7, + 0.6, + 0.5, + 0.35 + ]; + + this.criticalStructureFactors = [ + 0.8, + 0.8, + 0.7, + 0.6, + 0.5, + 0.35 + ]; + + this.criticalRootFactors = [ + 0.8, + 0.8, + 0.67, + 0.5, + 0.35, + 0.2 + ]; }; PETRA.Config.prototype.setConfig = function(gameState) @@ -287,6 +314,12 @@ if (this.difficulty < PETRA.DIFFICULTY_EASY) this.Economy.workPhase3 = Infinity; // prevent the phasing to city phase + this.emergencyValues = { + "population": this.criticalPopulationFactors[this.difficulty], + "structures": this.criticalStructureFactors[this.difficulty], + "roots": this.criticalRootFactors[this.difficulty], + }; + if (this.debug < 2) return; API3.warn(" >>> Petra bot: personality = " + uneval(this.personality)); Index: binaries/data/mods/public/simulation/ai/petra/emergencyManager.js =================================================================== --- /dev/null +++ binaries/data/mods/public/simulation/ai/petra/emergencyManager.js @@ -0,0 +1,68 @@ +/** + * Checks for emergencies and acts accordingly + */ +PETRA.EmergencyManager = function(Config) +{ + this.Config = Config; + // Initialized in initPhases + // Maximum population size during the game + this.referencePopulation = 0; + // Maximum number of structures during the game + this.referenceStructureCount = 0; + // Maximum number of territory roots + this.numRoots = 0; +}; + +PETRA.EmergencyManager.prototype.initPhases = function(gameState) +{ + this.referencePopulation = gameState.getPopulation(); + this.referenceStructureCount = gameState.getOwnStructures().length; + this.numRoots = this.rootCount(gameState); +}; + +PETRA.EmergencyManager.prototype.update = function(gameState) +{ + if (gameState.emergencyState[PlayerID]) + return; + const pop = gameState.getPopulation(); + const nStructures = gameState.getOwnStructures().length; + const nRoots = this.rootCount(gameState); + const factors = this.Config.emergencyValues; + if (((this.referencePopulation / pop) < factors.population || pop == 0) && + ((this.referenceStructureCount / nStructures) < factors.structures || nStructures == 0)) + gameState.emergencyState[PlayerID] = true; + else if ((nRoots / this.numRoots) <= factors.roots || (nRoots == 0 && this.numRoots != 0)) + gameState.emergencyState[PlayerID] = true; + + if (pop > this.referencePopulation) + this.referencePopulation = pop; + if (nStructures > this.referenceStructureCount) + this.referenceStructureCount = nStructures; + if (nRoots > this.numRoots) + this.numRoots = nRoots; + + if (gameState.emergencyState[PlayerID]) + PETRA.chatEmergency(gameState); +}; +PETRA.EmergencyManager.prototype.rootCount = function(gameState) +{ + let roots = 0; + gameState.getOwnStructures().toEntityArray().forEach(ent => { + if (ent?.get("TerritoryInfluence")?.Root === "true") + roots++; + }); + return roots; +}; +PETRA.EmergencyManager.prototype.Serialize = function() +{ + return { + "referencePopulation": this.referencePopulation, + "referenceStructureCount": this.referenceStructureCount, + "numRoots": this.numRoots, + }; +}; +PETRA.EmergencyManager.prototype.Deserialize = function(data) +{ + for (const key in data) + this[key] = data[key]; +}; Index: binaries/data/mods/public/simulation/ai/petra/headquarters.js =================================================================== --- binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ binaries/data/mods/public/simulation/ai/petra/headquarters.js @@ -44,6 +44,7 @@ this.diplomacyManager = new PETRA.DiplomacyManager(this.Config); this.garrisonManager = new PETRA.GarrisonManager(this.Config); this.victoryManager = new PETRA.VictoryManager(this.Config); + this.emergencyManager = new PETRA.EmergencyManager(this.Config); this.capturableTargets = new Map(); this.capturableTargetsTime = 0; @@ -66,6 +67,7 @@ this.treasures.registerUpdates(); this.currentPhase = gameState.currentPhase(); this.decayingStructures = new Set(); + this.emergencyManager.initPhases(gameState); }; /** @@ -2190,6 +2192,7 @@ PETRA.HQ.prototype.update = function(gameState, queues, events) { Engine.ProfileStart("Headquarters update"); + this.emergencyManager.update(gameState); this.turnCache = {}; this.territoryMap = PETRA.createTerritoryMap(gameState); this.canBarter = gameState.getOwnEntitiesByClass("Market", true).filter(API3.Filters.isBuilt()).hasEntities(); @@ -2337,6 +2340,7 @@ API3.warn(" diplomacyManager " + uneval(this.diplomacyManager.Serialize())); API3.warn(" garrisonManager " + uneval(this.garrisonManager.Serialize())); API3.warn(" victoryManager " + uneval(this.victoryManager.Serialize())); + API3.warn(" emergencyManager " + uneval(this.emergencyManager.Serialize())); } return { @@ -2352,6 +2356,7 @@ "diplomacyManager": this.diplomacyManager.Serialize(), "garrisonManager": this.garrisonManager.Serialize(), "victoryManager": this.victoryManager.Serialize(), + "emergencyManager": this.emergencyManager.Serialize(), }; }; @@ -2395,4 +2400,7 @@ this.victoryManager = new PETRA.VictoryManager(this.Config); this.victoryManager.Deserialize(data.victoryManager); + + this.emergencyManager = new PETRA.EmergencyManager(this.Config); + this.emergencyManager.Deserialize(data.emergencyManager); };