Ticket #4142: 4142_petra_regicide_support_v1.6.patch
File 4142_petra_regicide_support_v1.6.patch, 10.8 KB (added by , 7 years ago) |
---|
-
binaries/data/mods/public/simulation/ai/petra/attackPlan.js
677 677 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1 || 678 678 ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined) 679 679 return false; 680 // TODO if more than one hero in regicide, prevent only the "right one" from being affected 681 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8)) 680 if (gameState.ai.HQ.gameTypeManager.criticalEnts.has(ent.id()) && (this.overseas || ent.healthLevel() < 0.8)) 682 681 return false; 683 682 return true; 684 683 }; -
binaries/data/mods/public/simulation/ai/petra/defenseManager.js
388 388 return; 389 389 if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined) 390 390 return; 391 if (gameState. getGameType() === "regicide" && ent.hasClass("Hero"))391 if (gameState.ai.HQ.gameTypeManager.criticalEnts.has(ent.id())) 392 392 return; 393 393 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1) 394 394 { -
binaries/data/mods/public/simulation/ai/petra/gameTypeManager.js
4 4 /** 5 5 * Handle events that are important to specific gameTypes 6 6 * In regicide, train and manage healer guards for the hero 7 * TODO: Handle when there is more than one hero in regicide8 7 * TODO: Assign military units to guard the hero in regicide 8 * TODO: Assign guards to the wonder in a wonder game 9 9 */ 10 10 11 11 m.GameTypeManager = function(Config) 12 12 { 13 13 this.Config = Config; 14 this. heroGarrisonEmergency = false;15 this. healersAssignedToHero = 0; // Accounts for healers being trained as well16 this.he roGuards = []; // Holds id of ents currently guarding the hero14 this.criticalEnts = new Map(); 15 this.guardEnts = new Map(); // Holds guard ids and if the ent is currently guarding 16 this.healersPerCriticalEnt = 2 + Math.round(this.Config.personality.defensive * 2); 17 17 }; 18 18 19 19 /** 20 * Cache the ids of any inital gameType-critical entities 21 * In regicide, these are the inital heroes that the player starts with. 22 */ 23 m.GameTypeManager.prototype.init = function(gameState) 24 { 25 if (gameState.getGameType() !== "regicide") 26 return; 27 28 let heroEnts = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray(); 29 for (let hero of heroEnts) 30 this.criticalEnts.set(hero.id(), { 31 "garrisonEmergency": false, 32 "stance": hero.hasClass("Soldier") ? "aggressive" : "passive", 33 "healersAssigned": 0, 34 "guards": [] // ents who are currently guarding this hero 35 }); 36 }; 37 38 /** 20 39 * In regicide mode, if the hero has less than 70% health, try to garrison it in a healing structure 21 40 * If it is less than 40%, try to garrison in the closest possible structure 22 41 * If the hero cannot garrison, retreat it to the closest base … … 50 69 { 51 70 let target = gameState.getEntityById(evt.target); 52 71 if (!target || !gameState.isEntityOwn(target) || !target.position() || 53 !t arget.hasClass("Hero") || target.healthLevel() > 0.7)72 !this.criticalEnts.has(evt.target) || target.healthLevel() > 0.7) 54 73 continue; 55 74 56 75 let plan = target.getMetadata(PlayerID, "plan"); 76 let hero = this.criticalEnts.get(evt.target); 57 77 if (plan !== -2 && plan !== -3) 58 78 { 59 79 target.stopMoving(); … … 72 92 army.removeOwn(gameState, target.id()); 73 93 } 74 94 75 this.heroGarrisonEmergency = target.healthLevel() < 0.4;76 this.pickHeroRetreatLocation(gameState, target, this.heroGarrisonEmergency);95 hero.garrisonEmergency = target.healthLevel() < 0.4; 96 this.pickHeroRetreatLocation(gameState, target, hero.garrisonEmergency); 77 97 } 78 else if (target.healthLevel() < 0.4 && ! this.heroGarrisonEmergency)98 else if (target.healthLevel() < 0.4 && !hero.garrisonEmergency) 79 99 { 80 100 // the hero is severely wounded, try to retreat/garrison quicker 81 101 gameState.ai.HQ.garrisonManager.cancelGarrison(target); 82 102 this.pickHeroRetreatLocation(gameState, target, true); 83 this.heroGarrisonEmergency = true;103 hero.garrisonEmergency = true; 84 104 } 85 105 } 86 106 87 // check if new healers/guards need to be assigned to the hero107 // check if new healers/guards need to be assigned to an ent 88 108 for (let evt of events.Destroy) 89 109 { 90 if (!evt.entityObj || evt.entityObj.owner() !== PlayerID || 91 this.heroGuards.indexOf(evt.entityObj.id()) === -1) 110 if (!evt.entityObj || evt.entityObj.owner() !== PlayerID) 92 111 continue; 93 112 94 this.heroGuards.splice(this.heroGuards.indexOf(evt.entityObj.id()), 1); 95 if (evt.entityObj.hasClass("Healer")) 96 --this.healersAssignedToHero; 113 for (let data of this.criticalEnts.values()) 114 if (data.guards.indexOf(evt.entityObj.id()) !== -1) 115 { 116 let index = data.guards.indexOf(evt.entityObj.id()); 117 data.guards.splice(index, 1); 118 this.guardEnts.delete(evt.entityObj.id()); 119 120 if (evt.entityObj.hasClass("Healer")) 121 --data.healersAssigned; 122 } 97 123 } 98 124 99 125 for (let evt of events.TrainingFinished) … … 109 135 for (let evt of events.Garrison) 110 136 { 111 137 let ent = gameState.getEntityById(evt.entity); 112 if (!ent || !ent.isOwn(PlayerID) || ! ent.hasClass("Hero"))138 if (!ent || !ent.isOwn(PlayerID) || !this.criticalEnts.has(evt.entity)) 113 139 continue; 114 140 115 if (this.heroGarrisonEmergency) 116 this.heroGarrisonEmergency = false; 141 let hero = this.criticalEnts.get(evt.entity); 142 if (hero.garrisonEmergency) 143 hero.garrisonEmergency = false; 117 144 118 145 if (!gameState.getEntityById(evt.holder).hasClass("Ship")) 119 146 continue; 120 147 121 148 // If the hero is garrisoned on a ship, remove its guards 122 for (let guardId of this.heroGuards) 149 for (let guardId of hero.guards) 150 { 123 151 gameState.getEntityById(guardId).removeGuard(); 152 this.guardEnts.delete(guardId); 153 } 124 154 125 this.heroGuards = [];155 hero.guards = []; 126 156 } 127 157 128 158 for (let evt of events.UnGarrison) … … 132 162 continue; 133 163 134 164 // If this ent travelled to a hero's accessValue, try again to assign as a guard 135 if (ent.getMetadata(PlayerID, "role") === "regicideHealer" && 136 this.heroGuards.indexOf(evt.entity) === -1) 165 if (ent.getMetadata(PlayerID, "role") === "regicideHealer" && this.guardEnts.has(evt.entity)) 137 166 { 138 167 this.assignGuardToRegicideHero(gameState, ent); 139 168 continue; 140 169 } 141 170 142 if (! ent.hasClass("Hero"))171 if (!this.criticalEnts.has(evt.entity)) 143 172 continue; 144 173 145 174 // If this is the hero, try to assign ents that should be guarding it, but couldn't previously 146 let regicideHealers = gameState.getOwnUnits().filter(API3.Filters.byMetadata(PlayerID, "role", "regicideHealer")); 147 for (let healer of regicideHealers.values()) 148 if (this.heroGuards.indexOf(healer.id()) === -1) 149 this.assignGuardToRegicideHero(gameState, healer); 175 let criticalEnt = this.criticalEnts.get(ent.id()); 176 for (let [id, isGuarding] of this.guardEnts) 177 { 178 if (criticalEnt.guards.length >= this.healersPerCriticalEnt) 179 break; 180 181 if (!isGuarding) 182 this.assignGuardToRegicideHero(gameState, gameState.getEntityById(id)); 183 } 150 184 } 151 185 }; 152 186 … … 178 212 heroEnt.move(basePos.anchor.position()[0], basePos.anchor.position()[1]); 179 213 }; 180 214 181 m.GameTypeManager.prototype.trainRegicideHealer = function(gameState, queues) 215 /** 216 * The number of healers trained per regicide hero (dependant on the defensive trait) 217 * may not be the number of healers actually guarding a hero. 218 */ 219 m.GameTypeManager.prototype.trainRegicideHealer = function(gameState, queues, id) 182 220 { 183 221 if (gameState.ai.HQ.saveResources || !gameState.getOwnEntitiesByClass("Temple", true).hasEntities()) 184 222 return; … … 186 224 let template = gameState.applyCiv("units/{civ}_support_healer_b"); 187 225 188 226 queues.villager.addPlan(new m.TrainingPlan(gameState, template, { "role": "regicideHealer", "base": 0 }, 1, 1)); 189 ++this. healersAssignedToHero;227 ++this.criticalEnts.get(id).healersAssigned; 190 228 }; 191 229 192 230 /** … … 196 234 */ 197 235 m.GameTypeManager.prototype.assignGuardToRegicideHero = function(gameState, ent) 198 236 { 199 let heroEnt = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray()[0]; 237 // Assign to the ent with the fewest guards 238 let min = Math.min(); 239 let entId; 240 for (let [id, data] of this.criticalEnts) 241 { 242 if (data.guards.length < min) 243 { 244 entId = id; 245 min = data.guards.length; 246 } 247 } 248 249 let heroEnt = gameState.getEntityById(entId); 200 250 if (!heroEnt || !heroEnt.position() || 201 251 !ent.position() || ent.getMetadata(PlayerID, "transport") !== undefined || !ent.canGuard()) 202 252 return; … … 206 256 if (entAccess === heroAccess) 207 257 { 208 258 ent.guard(heroEnt); 209 this. heroGuards.push(ent.id());259 this.criticalEnts.get(entId).guards.push(ent.id()); 210 260 } 211 261 else 212 262 gameState.ai.HQ.navalManager.requireTransport(gameState, ent, entAccess, heroAccess, heroEnt.position()); 263 this.guardEnts.set(ent.id(), entAccess === heroAccess); 213 264 }; 214 265 215 266 m.GameTypeManager.prototype.update = function(gameState, events, queues) 216 267 { 268 // Wait a turn for trigger scripts to spawn any important ents (i.e. in regicide) 269 if (gameState.ai.playedTurn === 1) 270 this.init(gameState); 271 217 272 this.checkEvents(gameState, events); 218 273 219 274 if (gameState.getGameType() === "wonder") … … 224 279 225 280 if (gameState.ai.playedTurn % 50 === 0) 226 281 { 227 let heroEnt = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray()[0]; 228 if (heroEnt && heroEnt.healthLevel() > 0.7) 229 heroEnt.setStance("aggressive"); 282 for (let [id, data] of this.criticalEnts) 283 { 284 let ent = gameState.getEntityById(id); 285 if (ent && ent.healthLevel() > 0.7 && ent.hasClass("Soldier") && 286 data.stance !== "aggressive") 287 ent.setStance("aggressive"); 288 } 230 289 } 231 290 232 if (this.healersAssignedToHero < 2 + Math.round(this.Config.personality.defensive * 2)) 233 this.trainRegicideHealer(gameState, queues); 291 for (let [id, data] of this.criticalEnts) 292 if (data.healersAssigned < this.healersPerCriticalEnt) 293 this.trainRegicideHealer(gameState, queues, id); 234 294 }; 235 295 236 296 m.GameTypeManager.prototype.Serialize = function() 237 297 { 238 298 return { 239 " heroGarrisonEmergency": this.heroGarrisonEmergency,240 " healersAssignedToHero": this.healersAssignedToHero,241 "he roGuards": this.heroGuards299 "criticalEnts": this.criticalEnts, 300 "guardEnts": this.guardEnts, 301 "healersPerCriticalEnt": this.healersPerCriticalEnt 242 302 }; 243 303 }; 244 304