Ticket #4142: 4142_gameTypeManager_v1.patch

File 4142_gameTypeManager_v1.patch, 19.1 KB (added by Sandarac, 7 years ago)
  • binaries/data/mods/public/simulation/ai/petra/attackPlan.js

     
    586586m.AttackPlan.prototype.assignUnits = function(gameState)
    587587{
    588588    let plan = this.name;
    589     let added = false;
    590589    // If we can not build units, assign all available except those affected to allied defense to the current attack
    591590    if (!this.canBuildUnits)
    592591    {
    593592        for (let ent of gameState.getOwnUnits().values())
    594593        {
    595             if (!ent.position())
     594            if (ent.getMetadata(PlayerID, "allied") || !this.isAvailableUnit(gameState, ent))
    596595                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;
    603596            ent.setMetadata(PlayerID, "plan", plan);
    604597            this.unitCollection.updateEnt(ent);
    605             added = true;
    606598        }
    607         return added;
     599        return;
    608600    }
    609601
    610602    if (this.type === "Raid")
     
    613605        let num = 0;
    614606        for (let ent of gameState.getOwnUnits().values())
    615607        {
    616             if (!ent.hasClass("Cavalry"))
     608            if (!this.isAvailableUnit(gameState, ent, true))
    617609                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;
    624610            if (num++ < 2)
    625611                continue;
    626612            ent.setMetadata(PlayerID, "plan", plan);
    627613            this.unitCollection.updateEnt(ent);
    628             added = true;
    629614        }
    630         return added;
     615        return;
    631616    }
    632617
    633618    // Assign all units without specific role
    634619    for (let ent of gameState.getOwnEntitiesByRole(undefined, true).values())
    635620    {
    636         if (!ent.hasClass("Unit"))
     621        if (!ent.hasClass("Unit") || !this.isAvailableUnit(gameState, ent))
    637622            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;
    644623        if (ent.hasClass("Ship") || ent.hasClass("Support") || ent.attackTypes() === undefined)
    645624            continue;
    646         if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8))
    647             continue;
    648625        ent.setMetadata(PlayerID, "plan", plan);
    649626        this.unitCollection.updateEnt(ent);
    650         added = true;
    651627    }
    652628    // Add units previously in a plan, but which left it because needed for defense or attack finished
    653629    for (let ent of gameState.ai.HQ.attackManager.outOfPlan.values())
    654630    {
    655         if (!ent.position())
     631        if (!this.isAvailableUnit(gameState, ent))
    656632            continue;
    657         if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)
    658             continue;
    659633        ent.setMetadata(PlayerID, "plan", plan);
    660634        this.unitCollection.updateEnt(ent);
    661         added = true;
    662635    }
    663636
    664637    // Finally add also some workers,
     
    669642    let keep = this.type === "Rush" ? Math.round(this.Config.popScaling * (12 + 4*this.Config.personality.defensive)) : 6;
    670643    for (let ent of gameState.getOwnEntitiesByRole("worker", true).values())
    671644    {
    672         if (!ent.position())
     645        if (!ent.hasClass("CitizenSoldier") || !this.isAvailableUnit(gameState, ent))
    673646            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;
    680647        let baseID = ent.getMetadata(PlayerID, "base");
    681648        if (baseID)
    682649            numbase[baseID] = numbase[baseID] ? ++numbase[baseID] : 1;
     
    692659            continue;
    693660        ent.setMetadata(PlayerID, "plan", plan);
    694661        this.unitCollection.updateEnt(ent);
    695         added = true;
    696662    }
    697     return added;
    698663};
    699664
     665m.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
    700678/** Reassign one (at each turn) Cav unit to fasten raid preparation. */
    701679m.AttackPlan.prototype.reassignCavUnit = function(gameState)
    702680{
     
    854832        if (targets.hasEntities())
    855833            return targets;
    856834    }
     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    }
    857841
    858842    targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("CivCentre"));
    859843    if (!targets.hasEntities())
  • binaries/data/mods/public/simulation/ai/petra/defenseManager.js

     
    388388            return;
    389389        if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)
    390390            return;
    391         if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && ent.healthLevel() < 0.8)
     391        if (gameState.getGameType() === "regicide" && ent.hasClass("Hero"))
    392392            return;
    393393        if (ent.getMetadata(PlayerID, "plan") !== undefined && ent.getMetadata(PlayerID, "plan") !== -1)
    394394        {
     
    457457 * If our defense structures are attacked, garrison soldiers inside when possible
    458458 * and if a support unit is attacked and has less than 55% health, garrison it inside the nearest healing structure
    459459 * 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
    460461 */
    461462m.DefenseManager.prototype.checkEvents = function(gameState, events)
    462463{
     
    477478        if (target.hasClass("Ship"))    // TODO integrate ships later   need to be sure it is accessible
    478479            continue;
    479480
     481        // If inside a started attack plan, let the plan deal with this unit
    480482        let plan = target.getMetadata(PlayerID, "plan");
    481 
    482         // Retreat the hero in regicide mode if wounded
    483         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 attacks
    504             {
    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 unit
    515483        if (plan !== undefined && plan >= 0)
    516484        {
    517485            let attack = gameState.ai.HQ.attackManager.getPlan(plan);
     
    562530        if (target.hasClass("Support") && target.healthLevel() < 0.55 &&
    563531            !target.getMetadata(PlayerID, "transport") && plan !== -2 && plan !== -3)
    564532        {
    565             this.garrisonUnitForHealing(gameState, target);
     533            this.garrisonAttackedUnit(gameState, target, false);
    566534            continue;
    567535        }
    568536
     
    658626        garrisonManager.garrison(gameState, unit, nearest, "protection");
    659627};
    660628
    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 */
     634m.DefenseManager.prototype.garrisonAttackedUnit = function(gameState, unit, emergency)
    663635{
    664636    let distmin = Math.min();
    665637    let nearest;
    666638    let unitAccess = gameState.ai.accessibility.getAccessValue(unit.position());
    667639    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;
    671644        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;
    675648        if (ent.hitpoints() < ent.garrisonEjectHealth() * ent.maxHitpoints())
    676             return;
     649            continue;
    677650        let entAccess = ent.getMetadata(PlayerID, "access");
    678651        if (!entAccess)
    679652        {
     
    681654            ent.setMetadata(PlayerID, "access", entAccess);
    682655        }
    683656        if (entAccess !== unitAccess)
    684             return;
     657            continue;
    685658        let dist = API3.SquareVectorDistance(ent.position(), unit.position());
    686659        if (dist > distmin)
    687             return;
     660            continue;
    688661        distmin = dist;
    689662        nearest = ent;
    690     });
    691     if (nearest)
     663    }
     664    if (!nearest)
     665        return;
     666
     667    if (!emergency)
     668    {
    692669        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");
    693680};
    694681
    695682/**
  • binaries/data/mods/public/simulation/ai/petra/gameTypeManager.js

     
     1var 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
     9m.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 */
     19m.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
     90m.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
     101m.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
     117m.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
     125m.GameTypeManager.prototype.Serialize = function()
     126{
     127    return { "heroGarrisonEmergency": this.heroGarrisonEmergency };
     128};
     129
     130m.GameTypeManager.prototype.Deserialize = function(data)
     131{
     132    for (let key in data)
     133        this[key] = data[key];
     134};
     135
     136return 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
     
    221221                   MatchesClassList(ent.classes(), "Siege+!Melee"));
    222222    case 'decay':
    223223        return this.decayingStructures.has(holder.id());
     224    case 'emergency': // f.e. hero in regicide mode
     225        return enemiesAround;
    224226    default:
    225227        if (ent.getMetadata(PlayerID, "onBoard") === "onBoard")  // transport is not (yet ?) managed by garrisonManager
    226228            return true;
  • binaries/data/mods/public/simulation/ai/petra/headquarters.js

     
    5151    this.researchManager = new m.ResearchManager(this.Config);
    5252    this.diplomacyManager = new m.DiplomacyManager(this.Config);
    5353    this.garrisonManager = new m.GarrisonManager();
     54    this.gameTypeManager = new m.GameTypeManager();
    5455};
    5556
    5657/** More initialisation for stuff that needs the gameState */
     
    166167                });
    167168            }
    168169        }
    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         }
    183170    }
    184171
    185172    for (let evt of events.ConstructionFinished)
     
    16091596        queues.militaryBuilding.addPlan(new m.ConstructionPlan(gameState, "structures/{civ}_blacksmith"));
    16101597};
    16111598
    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 
    16261599/**
    16271600 * Deals with constructing military buildings (barracks, stables…)
    16281601 * They are mostly defined by Config.js. This is unreliable since changes could be done easily.
     
    21402113        m.chatNewPhase(gameState, phaseName, false);
    21412114    }
    21422115
    2143     if (gameState.getGameType() === "wonder")
    2144         this.buildWonder(gameState, queues);
    2145 
    21462116    if (this.numActiveBase() > 0)
    21472117    {
    21482118        this.trainMoreWorkers(gameState, queues);
     
    22012171
    22022172    this.diplomacyManager.update(gameState, events);
    22032173
     2174    this.gameTypeManager.update(gameState, events, queues);
     2175
    22042176    Engine.ProfileStop();
    22052177};
    22062178
     
    22482220        API3.warn(" researchManager " + uneval(this.researchManager.Serialize()));
    22492221        API3.warn(" diplomacyManager " + uneval(this.diplomacyManager.Serialize()));
    22502222        API3.warn(" garrisonManager " + uneval(this.garrisonManager.Serialize()));
     2223        API3.warn(" gameTypeManager " + uneval(this.gameTypeManager.Serialize()));
    22512224    }
    22522225
    22532226    return {
     
    22612234        "researchManager": this.researchManager.Serialize(),
    22622235        "diplomacyManager": this.diplomacyManager.Serialize(),
    22632236        "garrisonManager": this.garrisonManager.Serialize(),
     2237        "gameTypeManager": this.gameTypeManager.Serialize(),
    22642238    };
    22652239};
    22662240
     
    23042278
    23052279    this.garrisonManager = new m.GarrisonManager();
    23062280    this.garrisonManager.Deserialize(data.garrisonManager);
     2281
     2282    this.gameTypeManager = new m.GameTypeManager();
     2283    this.gameTypeManager.Deserialize(data.gameTypeManager);
    23072284};
    23082285
    23092286return m;