Ticket #4142: 4142_petra_regicide_support_v1.3.patch
File 4142_petra_regicide_support_v1.3.patch, 22.1 KB (added by , 7 years ago) |
---|
-
binaries/data/mods/public/simulation/ai/common-api/entity.js
929 929 if (item.progress < percentToStopAt) 930 930 Engine.PostCommand(PlayerID,{ "type": "stop-production", "entity": this.id(), "id": item.id }); 931 931 return this; 932 }, 933 934 "canGuard": function() { return this.get("UnitAI/CanGuard") === "true"; }, 935 936 "guard": function(target, queued = false) { 937 Engine.PostCommand(PlayerID, { "type": "guard", "entities": [this.id()], "target": target.id(), "queued": queued }); 938 return this; 939 }, 940 941 "removeGuard": function() { 942 Engine.PostCommand(PlayerID, { "type": "remove-guard", "entities": [this.id()] }); 943 return this; 932 944 } 933 945 }); 934 946 -
binaries/data/mods/public/simulation/ai/petra/attackManager.js
358 358 return enemyPlayer; 359 359 } 360 360 } 361 else if (gameState.getGameType() === "regicide") 362 { 363 // Check for the player with the closest hero 364 let heroes = gameState.getEnemyUnits().filter(API3.Filters.byClass("Hero")); 365 enemyPlayer = this.getMostAccessiblePlayer(gameState, attack, heroes); 361 366 367 if (enemyPlayer) 368 return enemyPlayer; 369 } 370 362 371 let veto = {}; 363 372 for (let i in this.defeated) 364 373 veto[i] = true; … … 390 399 gameState.getEntities(this.currentEnemyPlayer).hasEntities()) 391 400 return this.currentEnemyPlayer; 392 401 393 let distmin; 394 let ccmin; 395 let ccEnts = gameState.updatingGlobalCollection("allCCs", API3.Filters.byClass("CivCentre")); 396 for (let ourcc of ccEnts.values()) 397 { 398 if (ourcc.owner() !== PlayerID) 399 continue; 400 let ourPos = ourcc.position(); 401 let access = gameState.ai.accessibility.getAccessValue(ourPos); 402 for (let enemycc of ccEnts.values()) 403 { 404 if (veto[enemycc.owner()]) 405 continue; 406 if (!gameState.isPlayerEnemy(enemycc.owner())) 407 continue; 408 let enemyPos = enemycc.position(); 409 if (access !== gameState.ai.accessibility.getAccessValue(enemyPos)) 410 continue; 411 let dist = API3.SquareVectorDistance(ourPos, enemyPos); 412 if (distmin && dist > distmin) 413 continue; 414 ccmin = enemycc; 415 distmin = dist; 416 } 417 } 418 if (ccmin) 419 { 420 enemyPlayer = ccmin.owner(); 421 if (attack.targetPlayer === undefined) 422 this.currentEnemyPlayer = enemyPlayer; 402 let enemyccEnts = gameState.getEnemyStructures().filter(API3.Filters.byClass("CivCentre")); 403 enemyPlayer = this.getMostAccessiblePlayer(gameState, attack, enemyccEnts, veto); 404 405 if (enemyPlayer) 423 406 return enemyPlayer; 424 }425 407 } 426 408 427 409 // then let's target our strongest enemy (basically counting enemies units) … … 453 435 return enemyPlayer; 454 436 }; 455 437 438 m.AttackManager.prototype.getMostAccessiblePlayer = function(gameState, attack, targets, veto) 439 { 440 let distmin; 441 let choice; 442 let ourccs = gameState.getOwnStructures().filter(API3.Filters.byClass("CivCentre")); 443 for (let ourcc of ourccs.values()) 444 { 445 let ourPos = ourcc.position(); 446 let access = gameState.ai.accessibility.getAccessValue(ourPos); 447 for (let target of targets.values()) 448 { 449 if (veto && veto[target.owner()]) 450 continue; 451 let enemyPos = target.position(); 452 // TODO target might be garrisoned, check holders? 453 if (!enemyPos || access !== gameState.ai.accessibility.getAccessValue(enemyPos)) 454 continue; 455 let dist = API3.SquareVectorDistance(ourPos, enemyPos); 456 if (distmin && dist > distmin) 457 continue; 458 choice = target.owner(); 459 distmin = dist; 460 } 461 } 462 if (choice) 463 { 464 if (attack.targetPlayer === undefined) 465 this.currentEnemyPlayer = choice; 466 return choice; 467 } 468 }; 469 456 470 m.AttackManager.prototype.Serialize = function() 457 471 { 458 472 let properties = { -
binaries/data/mods/public/simulation/ai/petra/attackPlan.js
600 600 continue; 601 601 if (ent.getMetadata(PlayerID, "allied")) 602 602 continue; 603 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero")) 604 continue; 603 605 ent.setMetadata(PlayerID, "plan", plan); 604 606 this.unitCollection.updateEnt(ent); 605 607 added = true; … … 621 623 continue; 622 624 if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined) 623 625 continue; 626 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8)) 627 continue; 624 628 if (num++ < 2) 625 629 continue; 626 630 ent.setMetadata(PlayerID, "plan", plan); … … 656 660 continue; 657 661 if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined) 658 662 continue; 663 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8)) 664 continue; 659 665 ent.setMetadata(PlayerID, "plan", plan); 660 666 this.unitCollection.updateEnt(ent); 661 667 added = true; … … 707 713 continue; 708 714 if (!ent.hasClass("Cavalry") || !ent.hasClass("CitizenSoldier")) 709 715 continue; 716 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8)) 717 continue; 710 718 found = ent; 711 719 break; 712 720 } … … 854 862 if (targets.hasEntities()) 855 863 return targets; 856 864 } 865 else if (gameState.getGameType() === "regicide") 866 { 867 targets = gameState.getEnemyUnits(playerEnemy).filter(API3.Filters.byClass("Hero")); 868 if (targets.hasEntities()) 869 return targets; 870 } 857 871 858 872 targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("CivCentre")); 859 873 if (!targets.hasEntities()) -
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") && ent.healthLevel() < 0.8)391 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero")) 392 392 return; 393 393 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1) 394 394 { … … 457 457 * If our defense structures are attacked, garrison soldiers inside when possible 458 458 * and if a support unit is attacked and has less than 55% health, garrison it inside the nearest healing structure 459 459 * and if a ranged siege unit (not used for defense) is attacked, garrison it in the nearest fortress 460 * If our hero is attacked in regicide game mode, the gameTypeManager will handle it 460 461 */ 461 462 m.DefenseManager.prototype.checkEvents = function(gameState, events) 462 463 { … … 477 478 if (target.hasClass("Ship")) // TODO integrate ships later need to be sure it is accessible 478 479 continue; 479 480 481 // If inside a started attack plan, let the plan deal with this unit 480 482 let plan = target.getMetadata(PlayerID, "plan"); 481 482 // Retreat the hero in regicide mode if wounded483 if (gameState.getGameType() == "regicide" && target.hasClass("Hero") && target.healthLevel() < 0.7)484 {485 target.stopMoving();486 487 if (plan >= 0)488 {489 let attackPlan = gameState.ai.HQ.attackManager.getPlan(target.getMetadata(PlayerID, "plan"));490 if (attackPlan)491 attackPlan.removeUnit(target, true);492 }493 494 if (target.getMetadata(PlayerID, "PartOfArmy"))495 {496 let army = gameState.ai.HQ.defenseManager.getArmy(target.getMetadata(PlayerID, "PartOfArmy"));497 if (army)498 army.removeOwn(gameState, target.id());499 }500 501 this.garrisonUnitForHealing(gameState, target);502 503 if (plan >= 0) // couldn't find a place to garrison, so the hero will flee from attacks504 {505 target.setStance("passive");506 let accessIndex = gameState.ai.accessibility.getAccessValue(target.position());507 let basePos = m.getBestBase(gameState, target);508 if (basePos && basePos.accessIndex == accessIndex)509 target.move(basePos.anchor.position()[0], basePos.anchor.position()[1]);510 }511 continue;512 }513 514 // If inside a started attack plan, let the plan deal with this unit515 483 if (plan !== undefined && plan >= 0) 516 484 { 517 485 let attack = gameState.ai.HQ.attackManager.getPlan(plan); … … 562 530 if (target.hasClass("Support") && target.healthLevel() < 0.55 && 563 531 !target.getMetadata(PlayerID, "transport") && plan !== -2 && plan !== -3) 564 532 { 565 this.garrison UnitForHealing(gameState, target);533 this.garrisonAttackedUnit(gameState, target, false); 566 534 continue; 567 535 } 568 536 … … 658 626 garrisonManager.garrison(gameState, unit, nearest, "protection"); 659 627 }; 660 628 661 /** garrison a hurt unit inside the nearest healing structure */ 662 m.DefenseManager.prototype.garrisonUnitForHealing = function(gameState, unit) 629 /** 630 * Garrison a hurt unit inside a player-owned or allied structure 631 * If emergency is true, the unit will be garrisoned in the closest possible structure 632 * Otherwise, it will garrison in the closest healing structure 633 */ 634 m.DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit, emergency) 663 635 { 664 636 let distmin = Math.min(); 665 637 let nearest; 666 638 let unitAccess = gameState.ai.accessibility.getAccessValue(unit.position()); 667 639 let garrisonManager = gameState.ai.HQ.garrisonManager; 668 gameState.getAllyStructures().forEach(function(ent) { 669 if (!ent.buffHeal()) 670 return; 640 for (let ent of gameState.getAllyStructures().values()) 641 { 642 if (!emergency && !ent.buffHeal()) 643 continue; 671 644 if (!MatchesClassList(unit.classes(), ent.garrisonableClasses())) 672 return;673 if ( garrisonManager.numberOfGarrisonedUnits(ent) >= ent.garrisonMax())674 return;645 continue; 646 if (!emergency && garrisonManager.numberOfGarrisonedUnits(ent) >= ent.garrisonMax()) 647 continue; 675 648 if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints()) 676 return;649 continue; 677 650 let entAccess = ent.getMetadata(PlayerID, "access"); 678 651 if (!entAccess) 679 652 { … … 681 654 ent.setMetadata(PlayerID, "access", entAccess); 682 655 } 683 656 if (entAccess !== unitAccess) 684 return;657 continue; 685 658 let dist = API3.SquareVectorDistance(ent.position(), unit.position()); 686 659 if (dist > distmin) 687 return;660 continue; 688 661 distmin = dist; 689 662 nearest = ent; 690 }); 691 if (nearest) 663 } 664 if (!nearest) 665 return; 666 667 if (!emergency) 668 { 692 669 garrisonManager.garrison(gameState, unit, nearest, "protection"); 670 return; 671 } 672 else if (garrisonManager.numberOfGarrisonedUnits(nearest) >= nearest.garrisonMax()) // make room for this ent 673 for (let entId of nearest.garrisoned()) 674 { 675 nearest.unload(entId); 676 if (garrisonManager.numberOfGarrisonedUnits(nearest) < nearest.garrisonMax()) 677 break; 678 } 679 garrisonManager.garrison(gameState, unit, nearest, "emergency"); 693 680 }; 694 681 695 682 /** -
binaries/data/mods/public/simulation/ai/petra/gameTypeManager.js
1 var PETRA = function(m) 2 { 3 4 /** 5 * Handle events that are important to specific gameTypes 6 * TODO: assign military units to guard the hero in regicide 7 */ 8 9 m.GameTypeManager = function(Config) 10 { 11 this.Config = Config; 12 this.heroGarrisonEmergency = false; 13 this.healersAssignedToHero = 0; 14 this.heroGuards = []; 15 }; 16 17 /** 18 * In regicide mode, if the hero has less than 70% health, try to garrison it in a healing structure 19 * If it is less than 40%, try to garrison in the closest possible structure 20 * If the hero cannot garrison, retreat it to the closest base 21 */ 22 m.GameTypeManager.prototype.checkEvents = function(gameState, events) 23 { 24 for (let evt of events.Create) 25 { 26 let ent = gameState.getEntityById(evt.entity); 27 if (!ent || !ent.isOwn(PlayerID) || ent.foundationProgress() === undefined || 28 !ent.hasClass("Wonder") || gameState.getGameType() !== "wonder") 29 continue; 30 31 // Let's get a few units from other bases to build the wonder. 32 let base = gameState.ai.HQ.getBaseByID(ent.getMetadata(PlayerID, "base")); 33 let builders = gameState.ai.HQ.bulkPickWorkers(gameState, base, 10); 34 if (builders !== false) 35 for (let worker of builders.values()) 36 { 37 worker.setMetadata(PlayerID, "base", base.ID); 38 worker.setMetadata(PlayerID, "subrole", "builder"); 39 worker.setMetadata(PlayerID, "target-foundation", ent.id()); 40 } 41 } 42 43 if (gameState.getGameType() !== "regicide") 44 return; 45 46 for (let evt of events.Attacked) 47 { 48 let target = gameState.getEntityById(evt.target); 49 if (!target || !gameState.isEntityOwn(target) || !target.position() || 50 !target.hasClass("Hero") || target.healthLevel() > 0.7) 51 continue; 52 53 let plan = target.getMetadata(PlayerID, "plan"); 54 if (plan !== -2 && plan !== -3) 55 { 56 target.stopMoving(); 57 58 if (plan >= 0) 59 { 60 let attackPlan = gameState.ai.HQ.attackManager.getPlan(plan); 61 if (attackPlan) 62 attackPlan.removeUnit(target, true); 63 } 64 65 if (target.getMetadata(PlayerID, "PartOfArmy")) 66 { 67 let army = gameState.ai.HQ.defenseManager.getArmy(target.getMetadata(PlayerID, "PartOfArmy")); 68 if (army) 69 army.removeOwn(gameState, target.id()); 70 } 71 72 let emergency = target.healthLevel() < 0.4; 73 this.pickHeroRetreatLocation(gameState, target, emergency); 74 } 75 else if ((plan === -2 || plan === -3) && target.healthLevel() < 0.4 && !this.heroGarrisonEmergency) 76 { 77 // the hero is severely wounded, try to retreat/garrison quicker 78 gameState.ai.HQ.garrisonManager.leaveGarrison(target); 79 target.stopMoving(); 80 this.pickHeroRetreatLocation(gameState, target, true); 81 this.heroGarrisonEmergency = true; 82 } 83 } 84 85 // check if new healers need to be assigned to the hero 86 for (let evt of events.Destroy) 87 { 88 if (!evt.entityObj || evt.entityObj.owner() !== PlayerID || 89 this.heroGuards.indexOf(evt.entityObj) === -1) 90 continue; 91 92 this.heroGuards.splice(this.heroGuards.indexOf(evt.entityObj), 1); 93 if (evt.entityObj.hasClass("Healer")) 94 --this.healersAssignedToHero; 95 } 96 97 if (this.heroGarrisonEmergency) 98 for (let evt of events.Garrison) 99 if (gameState.getEntityById(evt.entity).hasClass("Hero")) 100 this.heroGarrisonEmergency = false; 101 102 for (let evt of events.TrainingFinished) 103 for (let entId of evt.entities) 104 { 105 let ent = gameState.getEntityById(entId); 106 if (!ent || !ent.isOwn(PlayerID) || ent.getMetadata(PlayerID, "role") !== "regicideHealer") 107 continue; 108 109 let heroEnt = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray()[0]; 110 let access = gameState.ai.accessibility.getAccessValue(ent.position()); 111 112 if (heroEnt && heroEnt.position() && ent.canGuard() && 113 access === gameState.ai.accessibility.getAccessValue(heroEnt.position())) 114 { 115 ent.guard(heroEnt); 116 this.heroGuards.push(ent); 117 } 118 } 119 120 // If the hero is garrisoned on a ship (shouldn't happen), remove guards 121 for (let evt of events.Garrison) 122 { 123 let ent = gameState.getEntityById(evt.entity) 124 if (!ent || !ent.isOwn(PlayerID) || !ent.hasClass("Hero") || 125 !gameState.getEntityById(evt.holder).hasClass("Ship")) 126 continue; 127 128 for (let guardEnt of this.heroGuards) 129 if (guardEnt) 130 guardEnt.removeGuard(); 131 } 132 }; 133 134 m.GameTypeManager.prototype.buildWonder = function(gameState, queues) 135 { 136 if (queues.wonder && queues.wonder.hasQueuedUnits() || gameState.getOwnEntitiesByClass("Wonder", true).hasEntities() || 137 !gameState.ai.HQ.canBuild(gameState, "structures/{civ}_wonder")) 138 return; 139 140 if (!queues.wonder) 141 gameState.ai.queueManager.addQueue("wonder", 1000); 142 queues.wonder.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_wonder")); 143 }; 144 145 m.GameTypeManager.prototype.pickHeroRetreatLocation = function(gameState, heroEnt, emergency) 146 { 147 gameState.ai.HQ.defenseManager.garrisonAttackedUnit(gameState, heroEnt, emergency); 148 let plan = heroEnt.getMetadata(PlayerID, "plan"); 149 150 if (plan === -2 || plan === -3) 151 return; 152 153 // couldn't find a place to garrison, so the hero will flee from attacks 154 heroEnt.setStance("passive"); 155 let accessIndex = gameState.ai.accessibility.getAccessValue(heroEnt.position()); 156 let basePos = m.getBestBase(gameState, heroEnt); 157 if (basePos && basePos.accessIndex == accessIndex) 158 heroEnt.move(basePos.anchor.position()[0], basePos.anchor.position()[1]); 159 }; 160 161 m.GameTypeManager.prototype.assignHealerToRegicideHero = function(gameState, queues) 162 { 163 if (gameState.ai.HQ.saveResources || !gameState.getOwnEntitiesByClass("Temple", true).hasEntities()) 164 return; 165 166 let template = gameState.applyCiv("units/{civ}_support_healer_b"); 167 168 queues.villager.addPlan(new m.TrainingPlan(gameState, template, { "role": "regicideHealer", "base": 0 }, 1, 1)); 169 ++this.healersAssignedToHero; 170 }; 171 172 m.GameTypeManager.prototype.update = function(gameState, events, queues) 173 { 174 this.checkEvents(gameState, events); 175 176 if (gameState.getGameType() === "wonder") 177 this.buildWonder(gameState, queues); 178 else if (gameState.getGameType() === "regicide" && 179 this.healersAssignedToHero < Math.min(Math.round(this.Config.personality.defensive * 10), 4)) 180 this.assignHealerToRegicideHero(gameState, queues); 181 }; 182 183 m.GameTypeManager.prototype.Serialize = function() 184 { 185 return { 186 "heroGarrisonEmergency": this.heroGarrisonEmergency, 187 "healersAssignedToHero": this.healersAssignedToHero, 188 "heroGuards": this.heroGuards 189 }; 190 }; 191 192 m.GameTypeManager.prototype.Deserialize = function(data) 193 { 194 for (let key in data) 195 this[key] = data[key]; 196 }; 197 198 return m; 199 }(PETRA); -
binaries/data/mods/public/simulation/ai/petra/garrisonManager.js
Property changes on: binaries/data/mods/public/simulation/ai/petra/gameTypeManager.js ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property
221 221 MatchesClassList(ent.classes(), "Siege+!Melee")); 222 222 case 'decay': 223 223 return this.decayingStructures.has(holder.id()); 224 case 'emergency': // f.e. hero in regicide mode 225 return enemiesAround; 224 226 default: 225 227 if (ent.getMetadata(PlayerID, "onBoard") === "onBoard") // transport is not (yet ?) managed by garrisonManager 226 228 return true; -
binaries/data/mods/public/simulation/ai/petra/headquarters.js
51 51 this.researchManager = new m.ResearchManager(this.Config); 52 52 this.diplomacyManager = new m.DiplomacyManager(this.Config); 53 53 this.garrisonManager = new m.GarrisonManager(); 54 this.gameTypeManager = new m.GameTypeManager(this.Config); 54 55 }; 55 56 56 57 /** More initialisation for stuff that needs the gameState */ … … 166 167 }); 167 168 } 168 169 } 169 else if (ent.hasClass("Wonder") && gameState.getGameType() === "wonder")170 {171 // Let's get a few units from other bases there to build this.172 let base = this.getBaseByID(ent.getMetadata(PlayerID, "base"));173 let builders = this.bulkPickWorkers(gameState, base, 10);174 if (builders !== false)175 {176 builders.forEach(function (worker) {177 worker.setMetadata(PlayerID, "base", base.ID);178 worker.setMetadata(PlayerID, "subrole", "builder");179 worker.setMetadata(PlayerID, "target-foundation", ent.id());180 });181 }182 }183 170 } 184 171 185 172 for (let evt of events.ConstructionFinished) … … 1609 1596 queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_blacksmith")); 1610 1597 }; 1611 1598 1612 m.HQ.prototype.buildWonder = function(gameState, queues)1613 {1614 if (queues.wonder && queues.wonder.hasQueuedUnits())1615 return;1616 if (gameState.getOwnEntitiesByClass("Wonder", true).hasEntities())1617 return;1618 if (!this.canBuild(gameState, "structures/{civ}_wonder"))1619 return;1620 1621 if (!queues.wonder)1622 gameState.ai.queueManager.addQueue("wonder", 1000);1623 queues.wonder.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_wonder"));1624 };1625 1626 1599 /** 1627 1600 * Deals with constructing military buildings (barracks, stables…) 1628 1601 * They are mostly defined by Config.js. This is unreliable since changes could be done easily. … … 2140 2113 m.chatNewPhase(gameState, phaseName, false); 2141 2114 } 2142 2115 2143 if (gameState.getGameType() === "wonder")2144 this.buildWonder(gameState, queues);2145 2146 2116 if (this.numActiveBase() > 0) 2147 2117 { 2148 2118 this.trainMoreWorkers(gameState, queues); … … 2201 2171 2202 2172 this.diplomacyManager.update(gameState, events); 2203 2173 2174 this.gameTypeManager.update(gameState, events, queues); 2175 2204 2176 Engine.ProfileStop(); 2205 2177 }; 2206 2178 … … 2248 2220 API3.warn(" researchManager " + uneval(this.researchManager.Serialize())); 2249 2221 API3.warn(" diplomacyManager " + uneval(this.diplomacyManager.Serialize())); 2250 2222 API3.warn(" garrisonManager " + uneval(this.garrisonManager.Serialize())); 2223 API3.warn(" gameTypeManager " + uneval(this.gameTypeManager.Serialize())); 2251 2224 } 2252 2225 2253 2226 return { … … 2261 2234 "researchManager": this.researchManager.Serialize(), 2262 2235 "diplomacyManager": this.diplomacyManager.Serialize(), 2263 2236 "garrisonManager": this.garrisonManager.Serialize(), 2237 "gameTypeManager": this.gameTypeManager.Serialize(), 2264 2238 }; 2265 2239 }; 2266 2240 … … 2304 2278 2305 2279 this.garrisonManager = new m.GarrisonManager(); 2306 2280 this.garrisonManager.Deserialize(data.garrisonManager); 2281 2282 this.gameTypeManager = new m.GameTypeManager(this.Config); 2283 this.gameTypeManager.Deserialize(data.gameTypeManager); 2307 2284 }; 2308 2285 2309 2286 return m;