Ticket #1432: entitylimits_single_hero_maegereg.diff

File entitylimits_single_hero_maegereg.diff, 27.4 KB (added by Maegereg, 12 years ago)

Includes added components

  • binaries/data/mods/public/simulation/components/ProductionQueue.js

     
    212212            if (!cmpPlayer.TrySubtractResources(totalCosts))
    213213                return;
    214214
     215            //Update player's entity limits to reflect queued units
     216            var cmpEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     217            for (var i = 0; i < count; ++i)
     218            {
     219                cmpEntityLimits.AddUnitByTemplate(template);
     220            }
     221               
    215222            this.queue.push({
    216223                "id": this.nextID++,
    217224                "player": cmpPlayer.GetPlayerID(),
     
    305312       
    306313        var cmpPlayer = QueryPlayerIDInterface(item.player, IID_Player);
    307314
     315        var cmpEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     316        var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     317        cmpEntityLimits.RemoveUnitByTemplate(cmpTempMan.GetTemplate(item.unitTemplate), true);
     318       
    308319        // Refund the resource cost for this batch
    309320        var totalCosts = {};
    310321        for each (var r in ["food", "wood", "stone", "metal"])
     
    414425        //  so only create them once and use as needed
    415426        for (var i = 0; i < count; ++i)
    416427        {
    417             this.entityCache.push(Engine.AddEntity(templateName));
     428            var ent = Engine.AddEntity(templateName);
     429           
     430            //Remove the virtual unit from the unit count that represented this unit in the queue- it will be replaced by the actual unit being spawned
     431            var cmpEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     432            cmpEntityLimits.RemoveUnit(ent, true);
     433           
     434            this.entityCache.push(ent);
    418435        }
    419436    }
    420437
  • binaries/data/mods/public/simulation/components/EntityLimit.js

     
     1function EntityLimit() {}
     2
     3EntityLimit.prototype.Schema =
     4    "<a:help>Specifies the number of this unit that can be build, and the categories of units that count toward the limit</a:help>" +
     5    "<a:example>" +
     6        "<ConcurrentLimit>" +
     7            "<Limit>" +
     8                "5" +
     9            "</Limit>" +
     10            "<Classes> Hero </Classes>" +
     11        "</ConcurrentLimit>" +
     12    "</a:example>" +
     13    "<interleave>" +
     14        "<optional>" +
     15            "<element name='ConcurrentLimit' a:help='A limit on the number of this unit that can be built by a player at any one time.'>"+
     16                "<interleave>" +
     17                    "<element name='Limit'>" +
     18                        "<data type='positiveInteger' />" +
     19                    "</element>" +
     20                    "<optional>" +
     21                        "<element name='Classes'>" +
     22                            "<attribute name='datatype'>" +
     23                                "<value>tokens</value>" +
     24                            "</attribute>" +
     25                            "<text/>" +
     26                        "</element>" +
     27                    "</optional>" +
     28                "</interleave>" +
     29            "</element>" +
     30        "</optional>" +
     31        "<optional>" +
     32            "<element name='TotalLimit' a:help='A limit on the total number of this unit that can be built by a player during a game.'>" +
     33                "<interleave>" +
     34                    "<element name='Limit'>" +
     35                        "<data type='positiveInteger' />" +
     36                    "</element>" +
     37                    "<optional>"+
     38                        "<element name='Classes' a:help='See Classes under ConcurrentLimit'>" +
     39                            "<attribute name='datatype'>" +
     40                                "<value>tokens</value>" +
     41                            "</attribute>" +
     42                            "<text />" +
     43                        "</element>" +
     44                    "</optional>" +
     45                "</interleave>" +
     46            "</element>" +
     47        "</optional>" +
     48    "</interleave>";
     49
     50EntityLimit.prototype.Init = function()
     51{
     52};
     53
     54/*
     55 Return value of 0 indicates no limit
     56 */
     57EntityLimit.prototype.GetConcurrentLimit = function()
     58{
     59    if(this.template.ConcurrentLimit)
     60    {
     61        return this.template.ConcurrentLimit.Limit;
     62    }
     63    return 0;
     64}
     65
     66/*
     67 Empty list return value indicates no classes
     68 */
     69EntityLimit.prototype.GetConcurrentLimitClasses = function()
     70{
     71    if(this.template.ConcurrentLimit && this.template.ConcurrentLimit.Classes && "_string" in this.template.ConcurrentLimit.Classes)
     72    {
     73        var string = this.template.ConcurrentLimit.Classes._string;
     74        return string.split(/\s+/);
     75    }
     76    return [];
     77}
     78
     79/*
     80 Return value of 0 indicates no limit
     81 */
     82EntityLimit.prototype.GetTotalLimit = function()
     83{
     84    if(this.template.TotalLimit)
     85    {
     86        return this.template.TotalLimit.Limit;
     87    }
     88    return 0;
     89}
     90
     91/*
     92 Empty list return value indicates no classes
     93 */
     94EntityLimit.prototype.GetTotalLimitClasses = function()
     95{
     96    if(this.template.TotalLimit && this.template.TotalLimit.Classes && "_string" in this.template.TotalLimit.Classes)
     97    {
     98        var string = this.template.TotalLimit.Classes._string;
     99        return string.split(/\s+/);
     100    }
     101    return [];
     102}
     103
     104Engine.RegisterComponentType(IID_EntityLimit, "EntityLimit", EntityLimit);
     105 No newline at end of file
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    122122    return ret;
    123123};
    124124
     125GuiInterface.prototype.AllowedToBuild = function(player, template)
     126{
     127    var cmpPlayerMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager);
     128    var cmpEntityLimits = Engine.QueryInterface(cmpPlayerMan.GetPlayerByID(player), IID_EntityLimits);
     129    return cmpEntityLimits.AllowedToBuild(template);
     130};
     131
    125132GuiInterface.prototype.GetRenamedEntities = function(player)
    126133{
    127134    return this.renamedEntities;
     
    385392        if (template.Cost.PopulationBonus) ret.cost.populationBonus = GetTechModifiedProperty(techMods, template, "Cost/PopulationBonus", +template.Cost.PopulationBonus);
    386393    }
    387394   
     395    if (template.EntityLimit)
     396    {
     397        ret.entityLimit = {};
     398        if (template.EntityLimit.ConcurrentLimit)
     399        {
     400            ret.EntityLimit = template.EntityLimit;
     401        }
     402    }
     403   
    388404    if (template.Footprint)
    389405    {
    390406        ret.footprint = {"height": template.Footprint.Height};
     
    442458        ret.icon = template.Identity.Icon;
    443459        ret.tooltip =  template.Identity.Tooltip;
    444460        ret.requiredTechnology = template.Identity.RequiredTechnology;
     461        if(template.Identity.Classes)
     462        {
     463            ret.classes = template.Identity.Classes;
     464        }
    445465    }
    446466
    447467    if (template.UnitMotion)
     
    15711585   
    15721586    "GetSimulationState": 1,
    15731587    "GetExtendedSimulationState": 1,
     1588    "AllowedToBuild": 1,
    15741589    "GetRenamedEntities": 1,
    15751590    "ClearRenamedEntities": 1,
    15761591    "GetEntityState": 1,
  • binaries/data/mods/public/simulation/components/Identity.js

     
    8787    return this.template.Civ;
    8888};
    8989
     90Identity.prototype.GetGenericName = function()
     91{
     92    return this.template.GenericName;
     93};
     94
    9095Identity.prototype.GetRank = function()
    9196{
    9297    return (this.template.Rank || "");
  • binaries/data/mods/public/simulation/components/EntityLimits.js

     
     1function EntityLimits() {}
     2
     3EntityLimits.prototype.schema =
     4    "<a:help>Tracks limits on numbers of certain units that can be built</a:help>" +
     5    "<a:component type='system'/><empty/>";
     6   
     7EntityLimits.prototype.Init = function()
     8{
     9    this.CurrentClassCounts = {};
     10    this.TotalClassCounts = {};
     11    this.CurrentEntityCounts = {};
     12    this.TotalEntityCounts = {};
     13};
     14
     15/*Add a newly created unit to the counts via its entity*/
     16EntityLimits.prototype.AddUnit = function(entity)
     17{
     18    var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
     19    if(cmpIdentity)
     20    {
     21        var classes = cmpIdentity.GetClassesList();
     22        var entityName = cmpIdentity.GetGenericName();
     23        this.Add(entityName, classes);
     24    }
     25};
     26
     27/*Remove a unit from the counts via its entity
     28 removeTotals should be true to force the unit to be removed from Total count tables.
     29 Normally this is not desired- we want to keep track of all the units that have ever
     30 been built in those tables. Only set it to true if the unit is being replaced (as in
     31 the production queue). Do not include the parameter if you don't want to remove from totals.*/
     32EntityLimits.prototype.RemoveUnit = function(entity, removeTotals)
     33{
     34    var cmpIdentity = Engine.QueryInterface(entity, IID_Identity);
     35    if(cmpIdentity)
     36    {
     37        var classes = cmpIdentity.GetClassesList();
     38        var entityName = cmpIdentity.GetGenericName();
     39        this.Remove(entityName, classes);
     40        if (removeTotals)
     41        {
     42            this.RemoveTotals(entityName, classes);
     43        }
     44    }
     45};
     46
     47/*Adds a unit based on its template. Can either use the gui interface template, or the actual template*/
     48EntityLimits.prototype.AddUnitByTemplate = function(template)
     49{
     50    var classes = [];
     51    if (template.classes && template.classes._string)
     52    {
     53        classes = template.classes._string.split(/\s/);
     54    }
     55    else if (template.Identity && template.Identity.Classes && template.Identity.Classes._string)
     56    {
     57        classes = template.Identity.Classes._string.split(/\s/);
     58    }
     59    if (template.name)
     60    {
     61        var entityName = template.name.generic;
     62    }
     63    else if (template.Identity)
     64    {
     65        var entityName = template.Identity.GenericName;
     66    }
     67    this.Add(entityName, classes);
     68};
     69
     70/*Removed a unit based on its template. Can either use the gui interface template, or the actual template
     71 removeTotals- see the same parameter on RemoveUnit*/
     72EntityLimits.prototype.RemoveUnitByTemplate = function(template, removeTotals)
     73{
     74    var classes = [];
     75    if (template.classes && template.classes._string)
     76    {
     77        classes = template.classes._string.split(/\s/);
     78    }
     79    else if (template.Identity && template.Identity.Classes && template.Identity.Classes._string)
     80    {
     81        classes = template.Identity.Classes._string.split(/\s/);
     82    }
     83    if (template.name)
     84    {
     85        var entityName = template.name.generic;
     86    }
     87    else if (template.Identity)
     88    {
     89        var entityName = template.Identity.GenericName;
     90    }
     91    this.Remove(entityName, classes);
     92    if (removeTotals)
     93    {
     94        this.RemoveTotals(entityName, classes);
     95    }
     96}
     97
     98/*Internal function for adding a unit. Do not call directly.*/
     99EntityLimits.prototype.Add = function(entityName, classes)
     100{
     101    if (!(entityName in this.CurrentEntityCounts) || (isNaN(this.CurrentEntityCounts[entityName])))
     102    {
     103        this.CurrentEntityCounts[entityName] = 1;
     104    }
     105    else
     106    {
     107        ++this.CurrentEntityCounts[entityName];
     108    }
     109    if (!(entityName in this.TotalEntityCounts) || (isNaN(this.TotalEntityCounts[entityName])))
     110    {
     111        this.TotalEntityCounts[entityName] = 1;
     112    }
     113    else
     114    {
     115        ++this.TotalEntityCounts[entityName];
     116    }
     117    for (var i = 0; i<classes.length; ++i)
     118    {
     119        var entityClass = classes[i];
     120        if (!(entityClass in this.CurrentClassCounts) || (isNaN(this.CurrentClassCounts[entityClass])))
     121        {
     122            this.CurrentClassCounts[entityClass] = 1;
     123        }
     124        else
     125        {
     126            ++this.CurrentClassCounts[entityClass];
     127        }
     128        if (!(entityClass in this.TotalClassCounts) || (isNaN(this.TotalClassCounts[entityClass])))
     129        {
     130            this.TotalClassCounts[entityClass] = 1;
     131        }
     132        else
     133        {
     134            ++this.TotalClassCounts[entityClass];
     135        }
     136    }
     137};
     138
     139/*Internal function for removing a unit. Do not call directly.*/
     140EntityLimits.prototype.Remove = function(entityName, classes)
     141{
     142    if (!(entityName in this.CurrentEntityCounts) || (isNaN(this.CurrentEntityCounts[entityName])))
     143    {
     144        this.CurrentEntityCounts[entityName] = 0;
     145    }
     146    else
     147    {
     148        --this.CurrentEntityCounts[entityName];
     149    }
     150    for (var i = 0; i<classes.length; ++i)
     151    {
     152        var entityClass = classes[i];
     153        if (!(entityClass in this.CurrentClassCounts) || (isNaN(this.CurrentClassCounts[entityClass])))
     154        {
     155            this.CurrentClassCounts[entityClass] = 0;
     156        }
     157        else
     158        {
     159            --this.CurrentClassCounts[entityClass];
     160        }
     161    }
     162};
     163
     164/*Internal function for removing a unit from the total counts. Do not call directly.*/
     165EntityLimits.prototype.RemoveTotals= function(entityName, classes)
     166{
     167    if (!(entityName in this.TotalEntityCounts) || (isNaN(this.TotalEntityCounts[entityName])))
     168    {
     169        this.TotalEntityCounts[entityName] = 0;
     170    }
     171    else
     172    {
     173        --this.TotalEntityCounts[entityName];
     174    }
     175    for (var i = 0; i<classes.length; ++i)
     176    {
     177        var entityClass = classes[i];
     178        if (!(entityClass in this.TotalClassCounts) || (isNaN(this.TotalClassCounts[entityClass])))
     179        {
     180            this.TotalClassCounts[entityClass] = 0;
     181        }
     182        else
     183        {
     184            --this.TotalClassCounts[entityClass];
     185        }
     186    }
     187};
     188
     189EntityLimits.prototype.GetCurrentEntityCount = function(entityName)
     190{
     191    if (entityName in this.CurrentEntityCounts)
     192    {
     193        return this.CurrentEntityCounts[entityName];
     194    }
     195    return 0;
     196};
     197
     198EntityLimits.prototype.GetCurrentClassCount = function(className)
     199{
     200    if (className in this.CurrentClassCounts)
     201    {
     202        return this.CurrentClassCounts[className];
     203    }
     204    return 0;
     205};
     206
     207EntityLimits.prototype.GetTotalEntityCount = function(entityName)
     208{
     209    if (entityName in this.TotalEntityCounts)
     210    {
     211        return this.TotalEntityCounts[entityName];
     212    }
     213    return 0;
     214};
     215
     216EntityLimits.prototype.GetTotalClassCount = function(className)
     217{
     218    if (className in this.TotalClassCounts)
     219    {
     220        return this.TotalClassCounts[className];
     221    }
     222    return 0;
     223};
     224
     225/*Checks if the unit can be built, based on a gui-interface template. It would be relatively trivial to change this to work on actual templates too.
     226 Will return false if a unit cannot be built because its limits are currently exceeded.*/
     227EntityLimits.prototype.AllowedToBuild = function(template)
     228{
     229    if (template.EntityLimit){
     230        /*Total limits are placed first because if both total and concurrent limits are present, total limits are more likely to be exceeded*/
     231        var entityName = template.name.generic;
     232        if (template.EntityLimit.TotalLimit)
     233        {
     234            var totalLimit = template.EntityLimit.TotalLimit.Limit;
     235            if (this.GetTotalEntityCount(entityName) >= totalLimit)
     236            {
     237                return false;
     238            }
     239            if (template.EntityLimit.TotalLimit.Classes && "_string" in template.EntityLimit.TotalLimit.Classes)
     240            {
     241                var limitClasses = template.EntityLimit.TotalLimit.Classes._string.split(/\s/);
     242                var entityClasses = [];
     243                if (template.classes && "_string" in template.classes)
     244                {
     245                    entityClasses = template.classes._string.split(/\s/);
     246                }
     247                var intersections = 0;
     248                var curNumber = this.GetTotalEntityCount(entityName);
     249                for (var i =0; i<limitClasses.length; ++i)
     250                {
     251                    curNumber = curNumber+this.GetTotalClassCount(limitClasses[i]);
     252                    if (entityClasses.indexOf(limitClasses[i]) != -1)
     253                    {
     254                        ++intersections;
     255                    }
     256                }
     257                curNumber = curNumber - intersections*this.GetTotalEntityCount(entityName);
     258                if (curNumber >= totalLimit)
     259                {
     260                    return false;
     261                }
     262            }
     263        }
     264        if (template.EntityLimit.ConcurrentLimit)
     265        {
     266            var concurrentLimit = template.EntityLimit.ConcurrentLimit.Limit;
     267            var entityName = template.name.generic;
     268            if (this.GetCurrentEntityCount(entityName) >= concurrentLimit)
     269            {
     270                return false;
     271            }
     272            if (template.EntityLimit.ConcurrentLimit.Classes && "_string" in template.EntityLimit.ConcurrentLimit.Classes)
     273            {
     274                var limitClasses = template.EntityLimit.ConcurrentLimit.Classes._string.split(/\s/);
     275                var entityClasses = [];
     276                if (template.classes && "_string" in template.classes)
     277                {
     278                    entityClasses = template.classes._string.split(/\s/);
     279                }
     280                var intersections = 0;
     281                var curNumber = this.GetCurrentEntityCount(entityName);
     282                for (var i =0; i<limitClasses.length; ++i)
     283                {
     284                    curNumber = curNumber+this.GetCurrentClassCount(limitClasses[i]);
     285                    if (entityClasses.indexOf(limitClasses[i]) != -1)
     286                    {
     287                        ++intersections;
     288                    }
     289                }
     290                curNumber = curNumber - intersections*this.GetCurrentEntityCount(entityName);
     291                if (curNumber >= concurrentLimit)
     292                {
     293                    return false;
     294                }
     295            }
     296        }
     297    }
     298    return true;
     299};
     300
     301/*
     302 Does not prevent ownership change, even if the change would exceed limits
     303 */
     304EntityLimits.prototype.OnGlobalOwnershipChanged = function(msg)
     305{
     306    var playerID = Engine.QueryInterface(this.entity, IID_Player).GetPlayerID();
     307    if (msg.from == playerID)
     308    {
     309        this.RemoveUnit(msg.entity);
     310    }
     311    else if (msg.to == playerID)
     312    {
     313        this.AddUnit(msg.entity);
     314    }
     315};
     316
     317Engine.RegisterComponentType(IID_EntityLimits, "EntityLimits", EntityLimits);
     318 No newline at end of file
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_swordsman.xml

     
    4848      </Bonuses>
    4949    </Charge>
    5050  </Attack>
     51    <EntityLimit>
     52        <ConcurrentLimit>
     53            <Limit>1</Limit>
     54            <Classes datatype="tokens">Hero</Classes>
     55        </ConcurrentLimit>
     56    </EntityLimit>
    5157  <Identity>
    5258    <Classes datatype="tokens">Melee Sword</Classes>
    5359    <GenericName>Hero Swordsman</GenericName>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml

     
    2727      <metal>200</metal>
    2828    </Resources>
    2929  </Cost>
     30    <EntityLimit>
     31        <ConcurrentLimit>
     32            <Limit>1</Limit>
     33            <Classes datatype="tokens">Hero</Classes>
     34        </ConcurrentLimit>
     35    </EntityLimit>
    3036  <Footprint>
    3137    <Circle radius="2.0"/>
    3238    <Height>2.5</Height>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_archer.xml

     
    4444    <Max>500</Max>
    4545    <RegenRate>0.5</RegenRate>
    4646  </Health>
     47    <EntityLimit>
     48        <ConcurrentLimit>
     49            <Limit>1</Limit>
     50            <Classes>Hero</Classes>
     51        </ConcurrentLimit>
     52    </EntityLimit>
    4753  <Identity>
    4854    <Classes datatype="tokens">Hero Bow -Javelin</Classes>
    4955    <GenericName>Hero Cavalry Archer</GenericName>
  • binaries/data/mods/public/simulation/templates/template_structure_defense_defense_tower.xml

     
    3232      <stone>100</stone>
    3333    </Resources>
    3434  </Cost>
     35  <EntityLimit>
     36      <ConcurrentLimit>
     37          <Limit>
     38              25
     39          </Limit>
     40      </ConcurrentLimit>
     41  </EntityLimit>
    3542  <Footprint>
    3643    <Square width="10.0" depth="10.0"/>
    3744    <Height>15.0</Height>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml

     
    2828      <metal>75</metal>
    2929    </Resources>
    3030  </Cost>
     31    <EntityLimit>
     32        <ConcurrentLimit>
     33            <Limit>1</Limit>
     34            <Classes>Hero</Classes>
     35        </ConcurrentLimit>
     36    </EntityLimit>
    3137  <Footprint>
    3238    <Circle radius="3.0"/>
    3339    <Height>3.0</Height>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml

     
    2929      <metal>250</metal>
    3030    </Resources>
    3131  </Cost>
     32    <EntityLimit>
     33        <ConcurrentLimit>
     34            <Limit>1</Limit>
     35            <Classes datatype="tokens">Hero </Classes>
     36        </ConcurrentLimit>
     37    </EntityLimit>
    3238  <Footprint>
    3339    <Circle radius="3.0"/>
    3440    <Height>3.0</Height>
  • binaries/data/mods/public/simulation/templates/special/player.xml

     
    88      <Fortress>10</Fortress>
    99    </Limits>
    1010  </BuildLimits>
     11  <EntityLimits />
    1112  <Player/>
    1213  <StatisticsTracker/>
    1314  <TechnologyManager/>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_javelinist.xml

     
    3434      <wood>200</wood>
    3535    </Resources>
    3636  </Cost>
     37    <EntityLimit>
     38        <ConcurrentLimit>
     39            <Limit>1</Limit>
     40            <Classes datatype="tokens">Hero</Classes>
     41        </ConcurrentLimit>
     42    </EntityLimit>
    3743  <Footprint>
    3844    <Circle radius="2.0"/>
    3945    <Height>2.5</Height>
  • binaries/data/mods/public/simulation/templates/template_structure_military_fortress.xml

     
    3333      <stone>650</stone>
    3434    </Resources>
    3535  </Cost>
     36  <EntityLimit>
     37      <ConcurrentLimit>
     38          <Limit>10</Limit>
     39      </ConcurrentLimit>
     40  </EntityLimit>
    3641  <Footprint>
    3742    <Square width="30.0" depth="30.0"/>
    3843    <Height>8.0</Height>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_spearman.xml

     
    2323      </Bonuses>
    2424    </Charge>
    2525  </Attack>
     26    <EntityLimit>
     27        <ConcurrentLimit>
     28            <Limit>1</Limit>
     29            <Classes datatype="tokens">Hero</Classes>
     30        </ConcurrentLimit>
     31    </EntityLimit>
    2632  <Identity>
    2733    <Classes datatype="tokens">Melee Spear</Classes>
    2834    <GenericName>Hero Spearman</GenericName>
  • binaries/data/mods/public/simulation/templates/template_unit_hero.xml

     
    2929      <metal>250</metal>
    3030    </Resources>
    3131  </Cost>
     32    <EntityLimit>
     33        <ConcurrentLimit>
     34            <Limit>1</Limit>
     35            <Classes datatype="tokens">Hero</Classes>
     36        </ConcurrentLimit>
     37    </EntityLimit>
    3238  <Health>
    3339    <Max>400</Max>
    3440    <RegenRate>0.5</RegenRate>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_spearman.xml

     
    6161      <PreferredClasses datatype="tokens">Organic Siege</PreferredClasses>
    6262    </Charge>
    6363  </Attack>
     64    <EntityLimit>
     65        <ConcurrentLimit>
     66            <Limit>1</Limit>
     67            <Classes>Hero</Classes>
     68        </ConcurrentLimit>
     69    </EntityLimit>
    6470  <Identity>
    6571    <Classes datatype="tokens">Melee Spear</Classes>
    6672    <GenericName>Hero Cavalry Spearman</GenericName>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_pikeman.xml

     
    3030      </Bonuses>
    3131    </Charge>
    3232  </Attack>
     33    <EntityLimit>
     34        <ConcurrentLimit>
     35            <Limit>1</Limit>
     36            <Classes datatype="tokens">Hero</Classes>
     37        </ConcurrentLimit>
     38    </EntityLimit>
    3339  <Identity>
    3440    <Classes datatype="tokens">Melee Spear</Classes>
    3541    <GenericName>Hero Pikeman</GenericName>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_ranged.xml

     
    2424      <wood>500</wood>
    2525    </Resources>
    2626  </Cost>
     27    <EntityLimit>
     28        <ConcurrentLimit>
     29            <Limit>1</Limit>
     30            <Classes datatype="tokens">Hero</Classes>
     31        </ConcurrentLimit>
     32    </EntityLimit>
    2733  <Health>
    2834    <Max>400</Max>
    2935    <RegenRate>0.2</RegenRate>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_archer.xml

     
    2828      <wood>200</wood>
    2929    </Resources>
    3030  </Cost>
     31    <EntityLimit>
     32        <ConcurrentLimit>
     33            <Limit>1</Limit>
     34            <Classes datatype="tokens">Hero</Classes>
     35        </ConcurrentLimit>
     36    </EntityLimit>
    3137  <Footprint>
    3238    <Circle radius="2.0"/>
    3339    <Height>2.5</Height>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_swordsman.xml

     
    4848      <PreferredClasses datatype="tokens">Organic Siege</PreferredClasses>
    4949    </Charge>
    5050  </Attack>
     51    <EntityLimit>
     52        <ConcurrentLimit>
     53            <Limit>1</Limit>
     54            <Classes>Hero</Classes>
     55        </ConcurrentLimit>
     56    </EntityLimit>
    5157  <Identity>
    5258    <Classes datatype="tokens">Sword</Classes>
    5359    <GenericName>Hero Cavalry Swordsman</GenericName>
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    538539                button.tooltip += "\nRequires " + techName;
    539540                grayscale = "grayscale:";
    540541            }
    541            
     542            if (guiName == TRAINING && template.EntityLimit)
     543            {
     544                if (!Engine.GuiInterfaceCall("AllowedToBuild", template))
     545                {
     546                    grayscale = "grayscale:";
     547                    button.enabled = false;
     548                    button.tooltip += "\nLimit Reached: No more can be produced";
     549                }
     550            }
    542551            if (guiName == RESEARCH && !Engine.GuiInterfaceCall("CheckTechnologyRequirements", entType))
    543552            {
    544553                button.enabled = false;