Ticket #4142: 4142_petra_regicide_support_v1.7.patch
File 4142_petra_regicide_support_v1.7.patch, 12.9 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 well 16 this.heroGuards = []; // Holds id of ents currently guarding the hero 14 this.criticalEnts = new Map(); 15 // Holds ids of all ents who are (or can be) guarding and if the ent is currently guarding 16 this.guardEnts = new Map(); 17 this.healersPerCriticalEnt = 2 + Math.round(this.Config.personality.defensive * 2); 17 18 }; 18 19 19 20 /** 21 * Cache the ids of any inital gameType-critical entities. 22 * In regicide, these are the inital heroes that the player starts with. 23 */ 24 m.GameTypeManager.prototype.init = function(gameState) 25 { 26 if (gameState.getGameType() !== "regicide") 27 return; 28 29 let heroEnts = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray(); 30 for (let hero of heroEnts) 31 { 32 hero.setStance(hero.hasClass("Soldier") ? "aggressive" : "passive"); 33 this.criticalEnts.set(hero.id(), { 34 "garrisonEmergency": false, 35 "stance": hero.hasClass("Soldier") ? "aggressive" : "passive", 36 "healersAssigned": 0, 37 "guards": new Set() // ids of ents who are currently guarding this hero 38 }); 39 } 40 }; 41 42 /** 20 43 * In regicide mode, if the hero has less than 70% health, try to garrison it in a healing structure 21 44 * If it is less than 40%, try to garrison in the closest possible structure 22 45 * If the hero cannot garrison, retreat it to the closest base … … 48 71 49 72 for (let evt of events.Attacked) 50 73 { 74 if (!evt.target || !this.criticalEnts.has(evt.target)) 75 continue; 76 51 77 let target = gameState.getEntityById(evt.target); 52 if (!target || !gameState.isEntityOwn(target) || !target.position() || 53 !target.hasClass("Hero") || target.healthLevel() > 0.7) 78 if (!target.position() || target.healthLevel() > 0.7) 54 79 continue; 55 80 56 81 let plan = target.getMetadata(PlayerID, "plan"); 82 let hero = this.criticalEnts.get(evt.target); 57 83 if (plan !== -2 && plan !== -3) 58 84 { 59 85 target.stopMoving(); … … 72 98 army.removeOwn(gameState, target.id()); 73 99 } 74 100 75 this.heroGarrisonEmergency = target.healthLevel() < 0.4;76 this.pickHeroRetreatLocation(gameState, target, this.heroGarrisonEmergency);101 hero.garrisonEmergency = target.healthLevel() < 0.4; 102 this.pickHeroRetreatLocation(gameState, target, hero.garrisonEmergency); 77 103 } 78 else if (target.healthLevel() < 0.4 && ! this.heroGarrisonEmergency)104 else if (target.healthLevel() < 0.4 && !hero.garrisonEmergency) 79 105 { 80 106 // the hero is severely wounded, try to retreat/garrison quicker 81 107 gameState.ai.HQ.garrisonManager.cancelGarrison(target); 82 108 this.pickHeroRetreatLocation(gameState, target, true); 83 this.heroGarrisonEmergency = true;109 hero.garrisonEmergency = true; 84 110 } 85 111 } 86 112 87 // check if new healers/guards need to be assigned to the hero113 // check if new healers/guards need to be assigned to an ent 88 114 for (let evt of events.Destroy) 89 115 { 90 if (!evt.entityObj || evt.entityObj.owner() !== PlayerID || 91 this.heroGuards.indexOf(evt.entityObj.id()) === -1) 116 if (!evt.entityObj || evt.entityObj.owner() !== PlayerID) 92 117 continue; 93 118 94 this.heroGuards.splice(this.heroGuards.indexOf(evt.entityObj.id()), 1); 95 if (evt.entityObj.hasClass("Healer")) 96 --this.healersAssignedToHero; 119 let entId = evt.entityObj.id(); 120 if (this.criticalEnts.has(entId)) 121 { 122 for (let guardId of this.criticalEnts.get(entId).guards) 123 this.guardEnts.set(guardId, false); 124 125 this.criticalEnts.delete(entId); 126 continue; 127 } 128 129 if (!this.guardEnts.has(entId)) 130 continue; 131 132 for (let data of this.criticalEnts.values()) 133 if (data.guards.has(entId)) 134 { 135 data.guards.delete(entId); 136 if (evt.entityObj.hasClass("Healer")) 137 --data.healersAssigned; 138 } 139 140 this.guardEnts.delete(entId); 97 141 } 98 142 99 143 for (let evt of events.TrainingFinished) … … 103 147 if (!ent || !ent.isOwn(PlayerID) || ent.getMetadata(PlayerID, "role") !== "regicideHealer") 104 148 continue; 105 149 106 this.assignGuardTo RegicideHero(gameState, ent);150 this.assignGuardToCriticalEnt(gameState, ent); 107 151 } 108 152 109 153 for (let evt of events.Garrison) 110 154 { 111 let ent = gameState.getEntityById(evt.entity); 112 if (!ent || !ent.isOwn(PlayerID) || !ent.hasClass("Hero")) 155 if (!this.criticalEnts.has(evt.entity)) 113 156 continue; 114 157 115 if (this.heroGarrisonEmergency) 116 this.heroGarrisonEmergency = false; 158 let hero = this.criticalEnts.get(evt.entity); 159 if (hero.garrisonEmergency) 160 hero.garrisonEmergency = false; 117 161 118 162 if (!gameState.getEntityById(evt.holder).hasClass("Ship")) 119 163 continue; 120 164 121 165 // If the hero is garrisoned on a ship, remove its guards 122 for (let guardId of this.heroGuards) 166 for (let guardId of hero.guards) 167 { 123 168 gameState.getEntityById(guardId).removeGuard(); 124 125 this.heroGuards = []; 169 this.guardEnts.set(guardId, false); 170 } 171 hero.guards.clear(); 126 172 } 127 173 128 174 for (let evt of events.UnGarrison) 129 175 { 130 let ent = gameState.getEntityById(evt.entity); 131 if (!ent || !ent.isOwn(PlayerID)) 176 if (!this.guardEnts.has(evt.entity) && !this.criticalEnts.has(evt.entity)) 132 177 continue; 133 178 179 let ent = gameState.getEntityById(evt.entity); 134 180 // 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) 181 if (ent.getMetadata(PlayerID, "role") === "regicideHealer" && !this.guardEnts.get(evt.entity)) 137 182 { 138 this.assignGuardTo RegicideHero(gameState, ent);183 this.assignGuardToCriticalEnt(gameState, ent); 139 184 continue; 140 185 } 141 186 142 if (! ent.hasClass("Hero"))187 if (!this.criticalEnts.has(evt.entity)) 143 188 continue; 144 189 145 // 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); 190 // If this is a hero, try to assign ents that should be guarding it, but couldn't previously 191 let criticalEnt = this.criticalEnts.get(ent.id()); 192 for (let [id, isGuarding] of this.guardEnts) 193 { 194 if (criticalEnt.guards.size >= this.healersPerCriticalEnt) 195 break; 196 197 if (!isGuarding) 198 this.assignGuardToCriticalEnt(gameState, gameState.getEntityById(id)); 199 } 150 200 } 151 201 }; 152 202 … … 178 228 heroEnt.move(basePos.anchor.position()[0], basePos.anchor.position()[1]); 179 229 }; 180 230 181 m.GameTypeManager.prototype.trainRegicideHealer = function(gameState, queues) 231 /** 232 * The number of healers trained per regicide hero (dependent on the defensive trait) 233 * may not be the number of healers actually guarding a hero at any one time. 234 */ 235 m.GameTypeManager.prototype.trainRegicideHealer = function(gameState, queues, id) 182 236 { 183 237 if (gameState.ai.HQ.saveResources || !gameState.getOwnEntitiesByClass("Temple", true).hasEntities()) 184 238 return; … … 186 240 let template = gameState.applyCiv("units/{civ}_support_healer_b"); 187 241 188 242 queues.villager.addPlan(new m.TrainingPlan(gameState, template, { "role": "regicideHealer", "base": 0 }, 1, 1)); 189 ++this. healersAssignedToHero;243 ++this.criticalEnts.get(id).healersAssigned; 190 244 }; 191 245 192 246 /** 193 * Only send the guard command if the guard's accessIndex is the same as the hero194 * and the herohas a position (i.e. not garrisoned)247 * Only send the guard command if the guard's accessIndex is the same as the critical ent 248 * and the critical ent has a position (i.e. not garrisoned) 195 249 * request a transport if the accessIndex value is different 196 250 */ 197 m.GameTypeManager.prototype.assignGuardTo RegicideHero = function(gameState, ent)251 m.GameTypeManager.prototype.assignGuardToCriticalEnt = function(gameState, guardEnt) 198 252 { 199 let heroEnt = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray()[0]; 200 if (!heroEnt || !heroEnt.position() || 201 !ent.position() || ent.getMetadata(PlayerID, "transport") !== undefined || !ent.canGuard()) 253 // Assign to the critical ent with the fewest guards 254 let min = Math.min(); 255 let criticalEntId; 256 for (let [id, data] of this.criticalEnts) 257 { 258 if (data.guards.size > min) 259 continue; 260 261 criticalEntId = id; 262 min = data.guards.size; 263 } 264 265 if (guardEnt.getMetadata(PlayerID, "transport") !== undefined || !guardEnt.canGuard()) 202 266 return; 203 267 204 let entAccess = gameState.ai.accessibility.getAccessValue(ent.position()); 205 let heroAccess = gameState.ai.accessibility.getAccessValue(heroEnt.position()); 268 let criticalEnt = gameState.getEntityById(criticalEntId); 269 if (!criticalEnt || !criticalEnt.position() || !guardEnt.position()) 270 { 271 this.guardEnts.set(guardEnt.id(), false); 272 return; 273 } 274 275 let entAccess = gameState.ai.accessibility.getAccessValue(guardEnt.position()); 276 let heroAccess = gameState.ai.accessibility.getAccessValue(criticalEnt.position()); 206 277 if (entAccess === heroAccess) 207 278 { 208 ent.guard(heroEnt);209 this. heroGuards.push(ent.id());279 guardEnt.guard(criticalEnt); 280 this.criticalEnts.get(criticalEntId).guards.add(guardEnt.id()); 210 281 } 211 282 else 212 gameState.ai.HQ.navalManager.requireTransport(gameState, ent, entAccess, heroAccess, heroEnt.position()); 283 gameState.ai.HQ.navalManager.requireTransport(gameState, guardEnt, entAccess, heroAccess, criticalEnt.position()); 284 this.guardEnts.set(guardEnt.id(), entAccess === heroAccess); 213 285 }; 214 286 215 287 m.GameTypeManager.prototype.update = function(gameState, events, queues) 216 288 { 289 // Wait a turn for trigger scripts to spawn any critical ents (i.e. in regicide) 290 if (gameState.ai.playedTurn === 1) 291 this.init(gameState); 292 217 293 this.checkEvents(gameState, events); 218 294 219 295 if (gameState.getGameType() === "wonder") … … 223 299 return; 224 300 225 301 if (gameState.ai.playedTurn % 50 === 0) 226 { 227 let heroEnt = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray()[0]; 228 if (heroEnt && heroEnt.healthLevel() > 0.7) 229 heroEnt.setStance("aggressive"); 230 } 302 for (let [id, data] of this.criticalEnts) 303 { 304 let ent = gameState.getEntityById(id); 305 if (ent && ent.healthLevel() > 0.7 && ent.hasClass("Soldier") && 306 data.stance !== "aggressive") 307 ent.setStance("aggressive"); 308 } 231 309 232 if (this.healersAssignedToHero < 2 + Math.round(this.Config.personality.defensive * 2)) 233 this.trainRegicideHealer(gameState, queues); 310 for (let [id, data] of this.criticalEnts) 311 if (data.healersAssigned < this.healersPerCriticalEnt && 312 this.guardEnts.size < gameState.getPopulationMax() / 10) 313 this.trainRegicideHealer(gameState, queues, id); 234 314 }; 235 315 236 316 m.GameTypeManager.prototype.Serialize = function() 237 317 { 238 318 return { 239 " heroGarrisonEmergency": this.heroGarrisonEmergency,240 " healersAssignedToHero": this.healersAssignedToHero,241 "he roGuards": this.heroGuards319 "criticalEnts": this.criticalEnts, 320 "guardEnts": this.guardEnts, 321 "healersPerCriticalEnt": this.healersPerCriticalEnt 242 322 }; 243 323 }; 244 324