Ticket #4142: 4142_petra_regicide_support_v1.3.patch

File 4142_petra_regicide_support_v1.3.patch, 22.1 KB (added by Sandarac, 7 years ago)
  • binaries/data/mods/public/simulation/ai/common-api/entity.js

     
    929929            if (item.progress < percentToStopAt)
    930930                Engine.PostCommand(PlayerID,{ "type": "stop-production", "entity": this.id(), "id": item.id });
    931931        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;
    932944    }
    933945});
    934946
  • binaries/data/mods/public/simulation/ai/petra/attackManager.js

     
    358358            return enemyPlayer;
    359359        }
    360360    }
     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);
    361366
     367        if (enemyPlayer)
     368            return enemyPlayer;
     369    }
     370
    362371    let veto = {};
    363372    for (let i in this.defeated)
    364373        veto[i] = true;
     
    390399            gameState.getEntities(this.currentEnemyPlayer).hasEntities())
    391400            return this.currentEnemyPlayer;
    392401
    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)
    423406            return enemyPlayer;
    424         }
    425407    }
    426408
    427409    // then let's target our strongest enemy (basically counting enemies units)
     
    453435    return enemyPlayer;
    454436};
    455437
     438m.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
    456470m.AttackManager.prototype.Serialize = function()
    457471{
    458472    let properties = {
  • binaries/data/mods/public/simulation/ai/petra/attackPlan.js

     
    600600                continue;
    601601            if (ent.getMetadata(PlayerID, "allied"))
    602602                continue;
     603            if (gameState.getGameType() === "regicide" && ent.hasClass("Hero"))
     604                continue;
    603605            ent.setMetadata(PlayerID, "plan", plan);
    604606            this.unitCollection.updateEnt(ent);
    605607            added = true;
     
    621623                continue;
    622624            if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)
    623625                continue;
     626            if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8))
     627                continue;
    624628            if (num++ < 2)
    625629                continue;
    626630            ent.setMetadata(PlayerID, "plan", plan);
     
    656660            continue;
    657661        if (ent.getMetadata(PlayerID, "transport") !== undefined || ent.getMetadata(PlayerID, "transporter") !== undefined)
    658662            continue;
     663        if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8))
     664            continue;
    659665        ent.setMetadata(PlayerID, "plan", plan);
    660666        this.unitCollection.updateEnt(ent);
    661667        added = true;
     
    707713            continue;
    708714        if (!ent.hasClass("Cavalry") || !ent.hasClass("CitizenSoldier"))
    709715            continue;
     716        if (gameState.getGameType() === "regicide" && ent.hasClass("Hero") && (this.overseas || ent.healthLevel() < 0.8))
     717            continue;
    710718        found = ent;
    711719        break;
    712720    }
     
    854862        if (targets.hasEntities())
    855863            return targets;
    856864    }
     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    }
    857871
    858872    targets = gameState.getEnemyStructures(playerEnemy).filter(API3.Filters.byClass("CivCentre"));
    859873    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(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 */
     22m.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
     134m.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
     145m.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
     161m.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
     172m.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
     183m.GameTypeManager.prototype.Serialize = function()
     184{
     185    return {
     186        "heroGarrisonEmergency": this.heroGarrisonEmergency,
     187        "healersAssignedToHero": this.healersAssignedToHero,
     188        "heroGuards": this.heroGuards
     189    };
     190};
     191
     192m.GameTypeManager.prototype.Deserialize = function(data)
     193{
     194    for (let key in data)
     195        this[key] = data[key];
     196};
     197
     198return 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
     
    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(this.Config);
    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(this.Config);
     2283    this.gameTypeManager.Deserialize(data.gameTypeManager);
    23072284};
    23082285
    23092286return m;