Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/chatHelper.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/chatHelper.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/chatHelper.js @@ -132,6 +132,15 @@ } }; +PETRA.emergencyMessages = { + "enter": [ + markForTranslation("My armies failed while defending my empire. Please honor our alliance and send help!") + ], + "exit": [ + markForTranslation("My empire regained its old strength, now it is time to seek revenge together!") + ] +}; + PETRA.chatLaunchAttack = function(gameState, player, type) { Engine.PostCommand(PlayerID, { @@ -234,3 +243,12 @@ "parameters": { "_player_": player } }); }; + +PETRA.chatEmergency = function(gameState, enable) +{ + Engine.PostCommand(PlayerID, { + "type": "aichat", + "message": "/allies " + pickRandom(this.emergencyMessages[enable ? "enter" : "exit"]), + "translateMessage": true + }); +}; Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/config.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/config.js +++ ps/trunk/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: ps/trunk/binaries/data/mods/public/simulation/ai/petra/emergencyManager.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/emergencyManager.js +++ ps/trunk/binaries/data/mods/public/simulation/ai/petra/emergencyManager.js @@ -0,0 +1,93 @@ +/** + * Checks for emergencies and acts accordingly + */ +PETRA.EmergencyManager = function(Config) +{ + this.Config = Config; + this.referencePopulation = 0; + this.referenceStructureCount = 0; + this.numRoots = 0; + this.hasEmergency = false; +}; + +PETRA.EmergencyManager.prototype.init = function(gameState) +{ + this.referencePopulation = gameState.getPopulation(); + this.referenceStructureCount = gameState.getOwnStructures().length; + this.numRoots = this.rootCount(gameState); +}; + +PETRA.EmergencyManager.prototype.update = function(gameState) +{ + if (this.hasEmergency) + { + this.emergencyUpdate(gameState); + return; + } + const pop = gameState.getPopulation(); + const nStructures = gameState.getOwnStructures().length; + const nRoots = this.rootCount(gameState); + const factors = this.Config.emergencyValues; + if (((pop / this.referencePopulation) < factors.population || pop == 0) && + ((nStructures / this.referenceStructureCount) < factors.structures || nStructures == 0)) + this.setEmergency(gameState, true); + else if ((nRoots / this.numRoots) <= factors.roots || (nRoots == 0 && this.numRoots != 0)) + this.setEmergency(gameState, true); + + if (pop > this.referencePopulation || this.hasEmergency) + this.referencePopulation = pop; + if (nStructures > this.referenceStructureCount || this.hasEmergency) + this.referenceStructureCount = nStructures; + if (nRoots > this.numRoots || this.hasEmergency) + this.numRoots = nRoots; +}; + +PETRA.EmergencyManager.prototype.emergencyUpdate = function(gameState) +{ + const pop = gameState.getPopulation(); + const nStructures = gameState.getOwnStructures().length; + const nRoots = this.rootCount(gameState); + const factors = this.Config.emergencyValues; + + if ((pop > this.referencePopulation * 1.2 && + nStructures > this.referenceStructureCount * 1.2) || + nRoots > this.numRoots) + { + this.setEmergency(gameState, false); + this.referencePopulation = pop; + this.referenceStructureCount = nStructures; + this.numRoots = nRoots; + } +}; + +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.setEmergency = function(gameState, enable) +{ + this.hasEmergency = enable; + PETRA.chatEmergency(gameState, enable); +}; + +PETRA.EmergencyManager.prototype.Serialize = function() +{ + return { + "referencePopulation": this.referencePopulation, + "referenceStructureCount": this.referenceStructureCount, + "numRoots": this.numRoots, + "hasEmergency": this.hasEmergency + }; +}; + +PETRA.EmergencyManager.prototype.Deserialize = function(data) +{ + for (const key in data) + this[key] = data[key]; +}; Index: ps/trunk/binaries/data/mods/public/simulation/ai/petra/headquarters.js =================================================================== --- ps/trunk/binaries/data/mods/public/simulation/ai/petra/headquarters.js +++ ps/trunk/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.init(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); };