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