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