Ticket #4142: 4142_gameTypeManager_v1.1.patch
File 4142_gameTypeManager_v1.1.patch, 19.6 KB (added by , 7 years ago) |
---|
-
binaries/data/mods/public/simulation/ai/petra/attackPlan.js
592 592 { 593 593 for (let ent of gameState.getOwnUnits().values()) 594 594 { 595 if ( !ent.position())595 if (ent.getMetadata(PlayerID, "allied") || !this.isAvailableUnit(gameState, ent)) 596 596 continue; 597 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)598 continue;599 if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)600 continue;601 if (ent.getMetadata(PlayerID, "allied"))602 continue;603 597 ent.setMetadata(PlayerID, "plan", plan); 604 598 this.unitCollection.updateEnt(ent); 605 599 added = true; … … 613 607 let num = 0; 614 608 for (let ent of gameState.getOwnUnits().values()) 615 609 { 616 if (!ent.hasClass("Cavalry") )610 if (!ent.hasClass("Cavalry") || !this.isAvailableUnit(gameState, ent)) 617 611 continue; 618 if (!ent.position())619 continue;620 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)621 continue;622 if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)623 continue;624 612 if (num++ < 2) 625 613 continue; 626 614 ent.setMetadata(PlayerID, "plan", plan); … … 633 621 // Assign all units without specific role 634 622 for (let ent of gameState.getOwnEntitiesByRole(undefined, true).values()) 635 623 { 636 if (!ent.hasClass("Unit") )624 if (!ent.hasClass("Unit") || !this.isAvailableUnit(gameState, ent)) 637 625 continue; 638 if (!ent.position())639 continue;640 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)641 continue;642 if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)643 continue;644 626 if (ent.hasClass("Ship") || ent.hasClass("Support") || ent.attackTypes() === undefined) 645 627 continue; 646 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8))647 continue;648 628 ent.setMetadata(PlayerID, "plan", plan); 649 629 this.unitCollection.updateEnt(ent); 650 630 added = true; … … 652 632 // Add units previously in a plan, but which left it because needed for defense or attack finished 653 633 for (let ent of gameState.ai.HQ.attackManager.outOfPlan.values()) 654 634 { 655 if (! ent.position())635 if (!this.isAvailableUnit(gameState, ent)) 656 636 continue; 657 if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)658 continue;659 637 ent.setMetadata(PlayerID, "plan", plan); 660 638 this.unitCollection.updateEnt(ent); 661 639 added = true; … … 669 647 let keep = this.type === "Rush" ? Math.round(this.Config.popScaling * (12 + 4*this.Config.personality.defensive)) : 6; 670 648 for (let ent of gameState.getOwnEntitiesByRole("worker", true).values()) 671 649 { 672 if (!ent. position())650 if (!ent.hasClass("CitizenSoldier") || !this.isAvailableUnit(gameState, ent)) 673 651 continue; 674 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)675 continue;676 if (ent.getMetadata(PlayerID, "transport") !== undefined)677 continue;678 if (!ent.hasClass("CitizenSoldier"))679 continue;680 652 let baseID = ent.getMetadata(PlayerID, "base"); 681 653 if (baseID) 682 654 numbase[baseID] = numbase[baseID] ? ++numbase[baseID] : 1; … … 697 669 return added; 698 670 }; 699 671 672 m.AttackPlan.prototype.isAvailableUnit = function(gameState, ent) 673 { 674 if (!ent.position()) 675 return false; 676 if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1 || 677 ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined) 678 return false; 679 // TODO if more than one hero in regicide, prevent only the "right one" from being affected 680 if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8)) 681 return false; 682 return true; 683 }; 684 700 685 /** Reassign one (at each turn) Cav unit to fasten raid preparation. */ 701 686 m.AttackPlan.prototype.reassignCavUnit = function(gameState) 702 687 { … … 849 834 { 850 835 let targets; 851 836 if (gameState.getGameType() === "wonder") 852 {853 837 targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("Wonder")); 854 if (targets.hasEntities()) 855 return targets; 856 } 838 else if (gameState.getGameType() === "regicide") 839 targets = gameState.getEnemyUnits(playerEnemy).filter(API3.Filters.byClass("Hero")); 840 if (targets && targets.hasEntities()) 841 return targets; 857 842 858 843 targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("CivCentre")); 859 844 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); 566 534 continue; 567 535 } 568 536 … … 640 608 return; 641 609 if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints()) 642 610 return; 643 let entAccess = ent.getMetadata(PlayerID, "access"); 644 if (!entAccess) 645 { 646 entAccess = gameState.ai.accessibility.getAccessValue(ent.position()); 647 ent.setMetadata(PlayerID, "access", entAccess); 648 } 649 if (entAccess !== unitAccess) 611 if (m.GetLandAccess(gameState, ent) !== unitAccess) 650 612 return; 651 613 let dist = API3.SquareVectorDistance(ent.position(), unit.position()); 652 614 if (dist > distmin) … … 658 620 garrisonManager.garrison(gameState, unit, nearest, "protection"); 659 621 }; 660 622 661 /** garrison a hurt unit inside the nearest healing structure */ 662 m.DefenseManager.prototype.garrisonUnitForHealing = function(gameState, unit) 623 /** 624 * Garrison a hurt unit inside a player-owned or allied structure 625 * If emergency is true, the unit will be garrisoned in the closest possible structure 626 * Otherwise, it will garrison in the closest healing structure 627 */ 628 m.DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit, emergency = false) 663 629 { 664 630 let distmin = Math.min(); 665 631 let nearest; 666 632 let unitAccess = gameState.ai.accessibility.getAccessValue(unit.position()); 667 633 let garrisonManager = gameState.ai.HQ.garrisonManager; 668 gameState.getAllyStructures().forEach(function(ent) { 669 if (!ent.buffHeal()) 670 return; 634 for (let ent of gameState.getAllyStructures().values()) 635 { 636 if (!emergency && !ent.buffHeal()) 637 continue; 671 638 if (!MatchesClassList(unit.classes(), ent.garrisonableClasses())) 672 return; 673 if (garrisonManager.numberOfGarrisonedUnits(ent) >= ent.garrisonMax()) 674 return; 639 continue; 640 if (garrisonManager.numberOfGarrisonedUnits(ent) >= ent.garrisonMax() && 641 (!emergency || !ent.garrisoned().length)) 642 continue; 675 643 if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints()) 676 return; 677 let entAccess = ent.getMetadata(PlayerID, "access"); 678 if (!entAccess) 679 { 680 entAccess = gameState.ai.accessibility.getAccessValue(ent.position()); 681 ent.setMetadata(PlayerID, "access", entAccess); 682 } 683 if (entAccess !== unitAccess) 684 return; 644 continue; 645 if (m.GetLandAccess(gameState, ent) !== unitAccess) 646 continue; 685 647 let dist = API3.SquareVectorDistance(ent.position(), unit.position()); 686 648 if (dist > distmin) 687 return;649 continue; 688 650 distmin = dist; 689 651 nearest = ent; 690 }); 691 if (nearest) 652 } 653 if (!nearest) 654 return; 655 656 if (!emergency) 657 { 692 658 garrisonManager.garrison(gameState, unit, nearest, "protection"); 659 return; 660 } 661 if (garrisonManager.numberOfGarrisonedUnits(nearest) >= nearest.garrisonMax()) // make room for this ent 662 nearest.unload(nearest.garrisoned()[0]); 663 664 garrisonManager.garrison(gameState, unit, nearest, nearest.buffHeal() ? "protection" : "emergency"); 693 665 }; 694 666 695 667 /** -
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: Handle when there is more than one hero in regicide 7 * TODO: Assign military units to guard the hero in regicide 8 */ 9 10 m.GameTypeManager = function() 11 { 12 this.heroGarrisonEmergency = false; 13 }; 14 15 /** 16 * In regicide mode, if the hero has less than 70% health, try to garrison it in a healing structure 17 * If it is less than 40%, try to garrison in the closest possible structure 18 * If the hero cannot garrison, retreat it to the closest base 19 */ 20 m.GameTypeManager.prototype.checkEvents = function(gameState, events) 21 { 22 if (gameState.getGameType() === "wonder") 23 for (let evt of events.Create) 24 { 25 let ent = gameState.getEntityById(evt.entity); 26 if (!ent || !ent.isOwn(PlayerID) || ent.foundationProgress() === undefined || 27 !ent.hasClass("Wonder")) 28 continue; 29 30 // Let's get a few units from other bases to build the wonder. 31 let base = gameState.ai.HQ.getBaseByID(ent.getMetadata(PlayerID, "base")); 32 let builders = gameState.ai.HQ.bulkPickWorkers(gameState, base, 10); 33 if (builders) 34 for (let worker of builders.values()) 35 { 36 worker.setMetadata(PlayerID, "base", base.ID); 37 worker.setMetadata(PlayerID, "subrole", "builder"); 38 worker.setMetadata(PlayerID, "target-foundation", ent.id()); 39 } 40 } 41 42 if (gameState.getGameType() !== "regicide") 43 return; 44 45 for (let evt of events.Attacked) 46 { 47 let target = gameState.getEntityById(evt.target); 48 if (!target || !gameState.isEntityOwn(target) || !target.position() || 49 !target.hasClass("Hero") || target.healthLevel() > 0.7) 50 continue; 51 52 let plan = target.getMetadata(PlayerID, "plan"); 53 if (plan !== -2 && plan !== -3) 54 { 55 target.stopMoving(); 56 57 if (plan >= 0) 58 { 59 let attackPlan = gameState.ai.HQ.attackManager.getPlan(plan); 60 if (attackPlan) 61 attackPlan.removeUnit(target, true); 62 } 63 64 if (target.getMetadata(PlayerID, "PartOfArmy")) 65 { 66 let army = gameState.ai.HQ.defenseManager.getArmy(target.getMetadata(PlayerID, "PartOfArmy")); 67 if (army) 68 army.removeOwn(gameState, target.id()); 69 } 70 71 let emergency = target.healthLevel() < 0.4; 72 this.pickHeroRetreatLocation(gameState, target, emergency); 73 } 74 else if (target.healthLevel() < 0.4 && !this.heroGarrisonEmergency) 75 { 76 // the hero is severely wounded, try to retreat/garrison quicker 77 gameState.ai.HQ.garrisonManager.leaveGarrison(target); 78 target.stopMoving(); 79 this.pickHeroRetreatLocation(gameState, target, true); 80 this.heroGarrisonEmergency = true; 81 } 82 } 83 84 if (!this.heroGarrisonEmergency) 85 return; 86 87 for (let evt of events.Garrison) 88 if (gameState.getEntityById(evt.entity).hasClass("Hero")) 89 this.heroGarrisonEmergency = false; 90 }; 91 92 m.GameTypeManager.prototype.buildWonder = function(gameState, queues) 93 { 94 if (queues.wonder && queues.wonder.hasQueuedUnits() || 95 gameState.getOwnEntitiesByClass("Wonder", true).hasEntities() || 96 !gameState.ai.HQ.canBuild(gameState, "structures/{civ}_wonder")) 97 return; 98 99 if (!queues.wonder) 100 gameState.ai.queueManager.addQueue("wonder", 1000); 101 queues.wonder.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_wonder")); 102 }; 103 104 m.GameTypeManager.prototype.pickHeroRetreatLocation = function(gameState, heroEnt, emergency) 105 { 106 gameState.ai.HQ.defenseManager.garrisonAttackedUnit(gameState, heroEnt, emergency); 107 let plan = heroEnt.getMetadata(PlayerID, "plan"); 108 109 if (plan === -2 || plan === -3) 110 return; 111 112 // couldn't find a place to garrison, so the hero will flee from attacks 113 heroEnt.setStance("passive"); 114 let accessIndex = gameState.ai.accessibility.getAccessValue(heroEnt.position()); 115 let basePos = m.getBestBase(gameState, heroEnt); 116 if (basePos && basePos.accessIndex == accessIndex) 117 heroEnt.move(basePos.anchor.position()[0], basePos.anchor.position()[1]); 118 }; 119 120 m.GameTypeManager.prototype.update = function(gameState, events, queues) 121 { 122 this.checkEvents(gameState, events); 123 124 if (gameState.getGameType() === "wonder") 125 this.buildWonder(gameState, queues); 126 else if (gameState.getGameType() === "regicide" && gameState.ai.playedTurn % 50 === 0) 127 { 128 let heroEnt = gameState.getOwnEntitiesByClass("Hero", true).toEntityArray()[0]; 129 if (heroEnt && heroEnt.healthLevel() > 0.7) 130 heroEnt.setStance("aggressive"); 131 } 132 }; 133 134 m.GameTypeManager.prototype.Serialize = function() 135 { 136 return { "heroGarrisonEmergency": this.heroGarrisonEmergency }; 137 }; 138 139 m.GameTypeManager.prototype.Deserialize = function(data) 140 { 141 for (let key in data) 142 this[key] = data[key]; 143 }; 144 145 return m; 146 }(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(); 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(); 2283 this.gameTypeManager.Deserialize(data.gameTypeManager); 2307 2284 }; 2308 2285 2309 2286 return m;