Ticket #1432: one_hero_2012_07_22.diff

File one_hero_2012_07_22.diff, 43.5 KB (added by fcxSanya, 12 years ago)
  • binaries/data/mods/public/gui/session/input.js

     
    14391439}
    14401440
    14411441// Called by GUI when user clicks training button
    1442 function addTrainingToQueue(selection, trainEntType)
     1442function addTrainingToQueue(selection, trainEntType, playerState)
    14431443{
     1444    // Create list of buildings which can train trainEntType
     1445    var appropriateBuildings = [];
     1446    for each (var selectedEntity in selection)
     1447    {
     1448        var state = GetEntityState(selectedEntity);
     1449        var canTrain = state && state.production && state.production.entities.length && state.production.entities.indexOf(trainEntType) != -1;
     1450        appropriateBuildings.push(selectedEntity);
     1451    }
     1452
     1453    // Check trainEntType entity limit and count
     1454    template = GetTemplateData(trainEntType);
     1455    var trainingCategory = null;
     1456    if (template.trainingRestrictions)
     1457        trainingCategory = template.trainingRestrictions.category;
     1458    var trainEntLimit = undefined;
     1459    var trainEntCount = undefined;
     1460    if (trainingCategory && playerState.entityLimits[trainingCategory])
     1461    {
     1462        trainEntLimit = playerState.entityLimits[trainingCategory];
     1463        trainEntCount = playerState.entityCounts[trainingCategory]
     1464    }
     1465
    14441466    if (Engine.HotkeyIsPressed("session.batchtrain"))
    14451467    {
    14461468        if (inputState == INPUT_BATCHTRAINING)
     
    14771499    }
    14781500    else
    14791501    {
    1480         // Non-batched - just create a single entity
    1481         Engine.PostNetworkCommand({"type": "train", "template": trainEntType, "count": 1, "entities": selection});
     1502        // Non-batched - just create a single entity in each building
     1503        // (but no more than entity limit allows)
     1504        var buildingsForTraining = appropriateBuildings;
     1505        if (trainEntLimit)
     1506        {
     1507            var canBeTrainedCount = trainEntLimit - trainEntCount;
     1508            buildingsForTraining = buildingsForTraining.slice(0, canBeTrainedCount);
     1509        }
     1510        Engine.PostNetworkCommand({"type": "train", "template": trainEntType, "count": 1, "entities": buildingsForTraining});
    14821511    }
    14831512}
    14841513
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    150150 * @param items Panel-specific data to construct the icons with.
    151151 * @param callback Callback function to argument to execute when an item's icon gets clicked. Takes a single 'item' argument.
    152152 */
    153 function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
     153function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, callback)
    154154{
    155155    usedPanels[guiName] = 1;
    156156
     
    359359                    tooltip += "\n" + getEntitySpeed(template);
    360360
    361361                tooltip += "\n\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train " + trainNum + ".[/font]";
    362 
    363362                break;
    364363               
    365364            case RESEARCH:
     
    507506        {
    508507            icon.sprite = "stretched:session/icons/" + item.icon;
    509508        }
     509        else if (guiName == TRAINING)
     510        {
     511            if (template.icon)
     512            {
     513                var trainingCategory = null;
     514                if (template.trainingRestrictions)
     515                    trainingCategory = template.trainingRestrictions.category;
     516                var grayscale = "";
     517                if (trainingCategory && playerState.entityLimits[trainingCategory] && playerState.entityCounts[trainingCategory] >= playerState.entityLimits[trainingCategory])
     518                    grayscale = "grayscale:";
     519                icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
     520            }
     521        }
    510522        else if (guiName == GATE)
    511523        {
    512524            var gateIcon;
     
    671683}
    672684
    673685// Sets up "unit barter panel" - special case for setupUnitPanel
    674 function setupUnitBarterPanel(unitEntState)
     686function setupUnitBarterPanel(unitEntState, playerState)
    675687{
    676688    // Amount of player's resource to exchange
    677689    var amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
     
    750762    var player = Engine.GetPlayerID();
    751763    if (entState.player == player || g_DevSettings.controlAll)
    752764    {
     765        // Get player state to check some constraints
     766        // e.g. presence of a hero or build limits
     767        var simState = Engine.GuiInterfaceCall("GetSimulationState");
     768        var playerState = simState.players[player];
     769
    753770        if (selection.length > 1)
    754             setupUnitPanel(SELECTION, usedPanels, entState, g_Selection.groups.getTemplateNames(),
     771            setupUnitPanel(SELECTION, usedPanels, entState, playerState, g_Selection.groups.getTemplateNames(),
    755772                function (entType) { changePrimarySelectionGroup(entType); } );
    756773
    757774        var commands = getEntityCommandsList(entState);
    758775        if (commands.length)
    759             setupUnitPanel(COMMAND, usedPanels, entState, commands,
     776            setupUnitPanel(COMMAND, usedPanels, entState, playerState, commands,
    760777                function (item) { performCommand(entState.id, item.name); } );
    761778
    762779        if (entState.garrisonHolder)
    763780        {
    764781            var groups = new EntityGroups();
    765782            groups.add(entState.garrisonHolder.entities);
    766             setupUnitPanel(GARRISON, usedPanels, entState, groups.getTemplateNames(),
     783            setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
    767784                function (item) { unload(entState.id, groups.getEntsByName(item)); } );
    768785        }
    769786
    770787        var formations = Engine.GuiInterfaceCall("GetAvailableFormations");
    771788        if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && formations.length)
    772789        {
    773             setupUnitPanel(FORMATION, usedPanels, entState, formations,
     790            setupUnitPanel(FORMATION, usedPanels, entState, playerState, formations,
    774791                function (item) { performFormation(entState.id, item); } );
    775792        }
    776793
     
    779796        var stances = ["violent", "aggressive", "passive", "defensive", "standground"];
    780797        if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && stances.length)
    781798        {
    782             setupUnitPanel(STANCE, usedPanels, entState, stances,
     799            setupUnitPanel(STANCE, usedPanels, entState, playerState, stances,
    783800                function (item) { performStance(entState.id, item); } );
    784801        }
    785802
     
    787804        if (entState.barterMarket)
    788805        {
    789806            usedPanels["Barter"] = 1;
    790             setupUnitBarterPanel(entState);
     807            setupUnitBarterPanel(entState, playerState);
    791808        }
    792809
    793810        var buildableEnts = [];
     
    808825
    809826        // The first selected entity's type has priority.
    810827        if (entState.buildEntities)
    811             setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
     828            setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts, startBuildingPlacement);
    812829        else if (entState.production && entState.production.entities)
    813             setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
    814                 function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
     830            setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
     831                function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
    815832        else if (entState.trader)
    816833            setupUnitTradingPanel(usedPanels, entState, selection);
    817834        else
     
    822839                setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
    823840            else if (trainableEnts.length)
    824841                setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
    825                     function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
     842                    function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
    826843        }
    827844
    828845        // Show technologies if the active panel has at most one row of icons.
     
    835852        }
    836853
    837854        if (entState.production && entState.production.queue.length)
    838             setupUnitPanel(QUEUE, usedPanels, entState, entState.production.queue,
     855            setupUnitPanel(QUEUE, usedPanels, entState, playerState, entState.production.queue,
    839856                function (item) { removeFromProductionQueue(entState.id, item.id); } );
    840857       
    841858        if(!entState.foundation && (entState.gate || hasClass(entState, "LongWall")))
  • binaries/data/mods/public/simulation/helpers/Templates.js

     
     1/**
     2 * Return template.Identity.Classes._string if exists
     3 */
     4function GetTemplateIdentityClassesString(template)
     5{
     6    var identityClassesString = undefined;
     7    if (template.Identity && template.Identity.Classes && "_string" in template.Identity.Classes)
     8        identityClassesString = template.Identity.Classes._string;
     9    return identityClassesString;
     10}
     11
     12/**
     13 * Check whether template.Identity.Classes contains specified class
     14 */
     15function TemplateHasIdentityClass(template, className)
     16{
     17    var identityClassesString = GetTemplateIdentityClassesString(template);
     18    var hasClass = identityClassesString && identityClassesString.indexOf(className) != -1;
     19    return hasClass;
     20}
     21
     22Engine.RegisterGlobal("GetTemplateIdentityClassesString", GetTemplateIdentityClassesString);
     23Engine.RegisterGlobal("TemplateHasIdentityClass", TemplateHasIdentityClass);
     24
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    142142
    143143    case "train":
    144144        var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
     145       
     146        // Check entity limits
     147        var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     148        var template = cmpTempMan.GetTemplate(cmd.template);
     149        var unitCategory = null;
     150        if (template.TrainingRestrictions)
     151            unitCategory = template.TrainingRestrictions.Category;
     152
    145153        // Verify that the building(s) can be controlled by the player
    146154        if (entities.length > 0)
    147155        {
    148156            for each (var ent in entities)
    149157            {
     158                if (unitCategory)
     159                {
     160                    var cmpPlayerEntityLimits = QueryOwnerInterface(ent, IID_EntityLimits);
     161                    if (!cmpPlayerEntityLimits.AllowedToTrain(unitCategory, cmd.count))
     162                    {
     163                        if (g_DebugCommands)
     164                        {
     165                            warn(unitCategory + " train limit is reached: "+uneval(cmd));
     166                        }
     167                        continue;
     168                    }
     169                }
     170
    150171                var cmpTechMan = QueryOwnerInterface(ent, IID_TechnologyManager);
    151172                // TODO: Enable this check once the AI gets technology support
    152173                if (cmpTechMan.CanProduce(cmd.template) || true)
     
    524545        return false;
    525546    }
    526547   
    527     // Check build limits
    528     var cmpBuildLimits = QueryPlayerIDInterface(player, IID_BuildLimits);
    529     if (!cmpBuildLimits || !cmpBuildLimits.AllowedToBuild(cmpBuildRestrictions.GetCategory()))
     548    // Check entity limits
     549    var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits);
     550    if (!cmpEntityLimits || !cmpEntityLimits.AllowedToBuild(cmpBuildRestrictions.GetCategory()))
    530551    {
    531552        if (g_DebugCommands)
    532553        {
  • binaries/data/mods/public/simulation/components/GuiInterface.js

     
    4949    for (var i = 0; i < n; ++i)
    5050    {
    5151        var playerEnt = cmpPlayerMan.GetPlayerByID(i);
    52         var cmpPlayerBuildLimits = Engine.QueryInterface(playerEnt, IID_BuildLimits);
     52        var cmpPlayerEntityLimits = Engine.QueryInterface(playerEnt, IID_EntityLimits);
    5353        var cmpPlayer = Engine.QueryInterface(playerEnt, IID_Player);
    5454       
    5555        // Work out what phase we are in
     
    8484            "phase": phase,
    8585            "isAlly": allies,
    8686            "isEnemy": enemies,
    87             "buildLimits": cmpPlayerBuildLimits.GetLimits(),
    88             "buildCounts": cmpPlayerBuildLimits.GetCounts(),
     87            "entityLimits": cmpPlayerEntityLimits.GetLimits(),
     88            "entityCounts": cmpPlayerEntityLimits.GetCounts(),
    8989            "techModifications": cmpTechnologyManager.GetTechModifications()
    9090        };
    9191        ret.players.push(playerData);
     
    373373            if (template.BuildRestrictions.Distance.MaxDistance) ret.buildRestrictions.distance.max = +template.BuildRestrictions.Distance.MaxDistance;
    374374        }
    375375    }
    376    
     376
     377    if (template.TrainingRestrictions)
     378    {
     379        ret.trainingRestrictions = {
     380            "category": template.TrainingRestrictions.Category,
     381        };
     382    }
     383
    377384    if (template.Cost)
    378385    {
    379386        ret.cost = {};
     
    442449        ret.icon = template.Identity.Icon;
    443450        ret.tooltip =  template.Identity.Tooltip;
    444451        ret.requiredTechnology = template.Identity.RequiredTechnology;
     452        ret.identityClassesString = GetTemplateIdentityClassesString(template);
    445453    }
    446454
    447455    if (template.UnitMotion)
  • binaries/data/mods/public/simulation/components/BuildLimits.js

     
    1 function BuildLimits() {}
    2 
    3 BuildLimits.prototype.Schema =
    4     "<a:help>Specifies per category limits on number of buildings that can be constructed for each player.</a:help>" +
    5     "<a:example>" +
    6         "<Limits>" +
    7           "<CivilCentre/>" +
    8           "<DefenseTower>25</DefenseTower>" +
    9           "<Fortress>10</Fortress>" +
    10           "<Special>" +
    11             "<LimitPerCivCentre>1</LimitPerCivCentre>" +
    12           "</Special>" +
    13         "</Limits>" +
    14     "</a:example>" +
    15     "<element name='LimitMultiplier'>" +
    16         "<ref name='positiveDecimal'/>" +
    17     "</element>" +
    18     "<element name='Limits'>" +
    19         "<zeroOrMore>" +
    20             "<element a:help='Specifies a category of building on which to apply this limit. See BuildRestrictions for list of categories.'>" +
    21                 "<anyName />" +
    22                 "<choice>" +
    23                     "<text />" +
    24                     "<element name='LimitPerCivCentre' a:help='Specifies that this limit is per number of civil centres.'>" +
    25                         "<data type='nonNegativeInteger'/>" +
    26                     "</element>" +
    27                 "</choice>" +
    28             "</element>" +
    29         "</zeroOrMore>" +
    30     "</element>";
    31 
    32 /*
    33  *  TODO: Use an inheriting player_{civ}.xml template for civ-specific limits
    34  */
    35 
    36 BuildLimits.prototype.Init = function()
    37 {
    38     this.limit = {};
    39     this.count = {};
    40     for (var category in this.template.Limits)
    41     {
    42         this.limit[category] = this.template.Limits[category];
    43         this.count[category] = 0;
    44     }
    45 };
    46 
    47 BuildLimits.prototype.IncrementCount = function(category)
    48 {
    49     if (this.count[category] !== undefined)
    50     {
    51         this.count[category]++;
    52     }
    53 };
    54 
    55 BuildLimits.prototype.DecrementCount = function(category)
    56 {
    57     if (this.count[category] !== undefined)
    58     {
    59         this.count[category]--;
    60     }
    61 };
    62 
    63 BuildLimits.prototype.GetLimits = function()
    64 {
    65     return this.limit;
    66 };
    67 
    68 BuildLimits.prototype.GetCounts = function()
    69 {
    70     return this.count;
    71 };
    72 
    73 BuildLimits.prototype.AllowedToBuild = function(category)
    74 {
    75     // TODO: The UI should reflect this before the user tries to place the building,
    76     //          since the limits are independent of placement location
    77 
    78     // Allow unspecified categories and those with no limit
    79     if (this.count[category] === undefined || this.limit[category] === undefined)
    80     {
    81         return true;
    82     }
    83    
    84     // Rather than complicating the schema unecessarily, just handle special cases here
    85     if (this.limit[category].LimitPerCivCentre !== undefined)
    86     {
    87         if (this.count[category] >= this.count["CivilCentre"] * this.limit[category].LimitPerCivCentre)
    88         {
    89             var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
    90             var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category].LimitPerCivCentre+" per civil centre reached"};
    91             var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    92             cmpGUIInterface.PushNotification(notification);
    93            
    94             return false;
    95         }
    96     }
    97     else if (this.count[category] >= this.limit[category])
    98     {
    99         var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
    100         var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category]+ " reached"};
    101         var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
    102         cmpGUIInterface.PushNotification(notification);
    103        
    104         return false;
    105     }
    106    
    107     return true;
    108 };
    109 
    110 BuildLimits.prototype.OnGlobalOwnershipChanged = function(msg)
    111 {
    112     // This automatically updates build counts
    113     var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions);
    114     if (cmpBuildRestrictions)
    115     {
    116         var playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID();
    117         if (msg.from == playerID)
    118         {
    119             this.DecrementCount(cmpBuildRestrictions.GetCategory());
    120         }
    121         if (msg.to == playerID)
    122         {
    123             this.IncrementCount(cmpBuildRestrictions.GetCategory());
    124         }
    125     }
    126 };
    127 
    128 Engine.RegisterComponentType(IID_BuildLimits, "BuildLimits", BuildLimits);
  • binaries/data/mods/public/simulation/components/tests/test_GuiInterface.js

     
    11Engine.LoadComponentScript("interfaces/Attack.js");
    22Engine.LoadComponentScript("interfaces/Barter.js");
    33Engine.LoadComponentScript("interfaces/Builder.js");
    4 Engine.LoadComponentScript("interfaces/BuildLimits.js");
    54Engine.LoadComponentScript("interfaces/DamageReceiver.js");
     5Engine.LoadComponentScript("interfaces/EntityLimits.js");
    66Engine.LoadComponentScript("interfaces/Foundation.js");
    77Engine.LoadComponentScript("interfaces/GarrisonHolder.js");
    88Engine.LoadComponentScript("interfaces/Gate.js");
     
    6767    IsEnemy: function() { return true; },
    6868});
    6969
    70 AddMock(100, IID_BuildLimits, {
     70AddMock(100, IID_EntityLimits, {
    7171    GetLimits: function() { return {"Foo": 10}; },
    7272    GetCounts: function() { return {"Foo": 5}; },
    7373});
     
    118118    IsEnemy: function() { return false; },
    119119});
    120120
    121 AddMock(101, IID_BuildLimits, {
     121AddMock(101, IID_EntityLimits, {
    122122    GetLimits: function() { return {"Bar": 20}; },
    123123    GetCounts: function() { return {"Bar": 0}; },
    124124});
     
    171171            phase: "",
    172172            isAlly: [false, false, false],
    173173            isEnemy: [true, true, true],
    174             buildLimits: {"Foo": 10},
    175             buildCounts: {"Foo": 5},
     174            entityLimits: {"Foo": 10},
     175            entityCounts: {"Foo": 5},
    176176            techModifications: {},
    177177        },
    178178        {
     
    189189            phase: "village",
    190190            isAlly: [true, true, true],
    191191            isEnemy: [false, false, false],
    192             buildLimits: {"Bar": 20},
    193             buildCounts: {"Bar": 0},
     192            entityLimits: {"Bar": 20},
     193            entityCounts: {"Bar": 0},
    194194            techModifications: {},
    195195        }
    196196    ],
     
    214214            phase: "",
    215215            isAlly: [false, false, false],
    216216            isEnemy: [true, true, true],
    217             buildLimits: {"Foo": 10},
    218             buildCounts: {"Foo": 5},
     217            entityLimits: {"Foo": 10},
     218            entityCounts: {"Foo": 5},
    219219            techModifications: {},
    220220            statistics: {
    221221                unitsTrained: 10,
     
    248248            phase: "village",
    249249            isAlly: [true, true, true],
    250250            isEnemy: [false, false, false],
    251             buildLimits: {"Bar": 20},
    252             buildCounts: {"Bar": 0},
     251            entityLimits: {"Bar": 20},
     252            entityCounts: {"Bar": 0},
    253253            techModifications: {},
    254254            statistics: {
    255255                unitsTrained: 10,
  • binaries/data/mods/public/simulation/components/ProductionQueue.js

     
    199199            if (!cmpPlayer.TrySubtractResources(totalCosts))
    200200                return;
    201201
     202            // Update entity count in the EntityLimits component
     203            if (template.TrainingRestrictions)
     204            {
     205                var unitCategory = template.TrainingRestrictions.Category;
     206                var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     207                cmpPlayerEntityLimits.IncreaseCount(unitCategory, count);
     208            }
     209
    202210            this.queue.push({
    203211                "id": this.nextID++,
    204212                "player": cmpPlayer.GetPlayerID(),
     
    292300       
    293301        var cmpPlayer = QueryPlayerIDInterface(item.player, IID_Player);
    294302
     303        // Update entity count in the EntityLimits component
     304        if (item.unitTemplate)
     305        {
     306            var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     307            var template = cmpTempMan.GetTemplate(item.unitTemplate);
     308            if (template.TrainingRestrictions)
     309            {
     310                var unitCategory = template.TrainingRestrictions.Category;
     311                var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     312                cmpPlayerEntityLimits.DecreaseCount(unitCategory, item.count);
     313            }
     314        }
     315
    295316        // Refund the resource cost for this batch
    296317        var totalCosts = {};
    297318        for each (var r in ["food", "wood", "stone", "metal"])
     
    401422        //  so only create them once and use as needed
    402423        for (var i = 0; i < count; ++i)
    403424        {
    404             this.entityCache.push(Engine.AddEntity(templateName));
     425            var ent = Engine.AddEntity(templateName);
     426            this.entityCache.push(ent);
     427
     428            // Decrement entity count in the EntityLimits component
     429            // since it will be increased by EntityLimits.OnGlobalOwnershipChanged function,
     430            // i.e. we replace a 'trained' entity to an 'alive' one
     431            var cmpTrainingRestrictions = Engine.QueryInterface(ent, IID_TrainingRestrictions);
     432            if (cmpTrainingRestrictions)
     433            {
     434                var unitCategory = cmpTrainingRestrictions.GetCategory();
     435                var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     436                cmpPlayerEntityLimits.DecrementCount(unitCategory);
     437            }
    405438        }
    406439    }
    407440
  • binaries/data/mods/public/simulation/components/Player.js

     
    343343Player.prototype.OnGlobalOwnershipChanged = function(msg)
    344344{
    345345    var isConquestCritical = false;
    346 
    347346    // Load class list only if we're going to need it
    348347    if (msg.from == this.playerID || msg.to == this.playerID)
    349348    {
    350349        var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
    351350        if (cmpIdentity)
    352351        {
    353             var classes = cmpIdentity.GetClassesList();
    354             isConquestCritical = classes.indexOf("ConquestCritical") != -1;
     352            isConquestCritical = cmpIdentity.HasClass("ConquestCritical");
    355353        }
    356354    }
    357    
    358355    if (msg.from == this.playerID)
    359356    {
    360357        if (isConquestCritical)
    361             this.conquestCriticalEntitiesCount--;   
    362 
     358            this.conquestCriticalEntitiesCount--;
    363359        var cost = Engine.QueryInterface(msg.entity, IID_Cost);
    364360        if (cost)
    365361        {
     
    367363            this.popBonuses -= cost.GetPopBonus();
    368364        }
    369365    }
    370    
    371366    if (msg.to == this.playerID)
    372367    {
    373368        if (isConquestCritical)
    374369            this.conquestCriticalEntitiesCount++;
    375            
    376370        var cost = Engine.QueryInterface(msg.entity, IID_Cost);
    377371        if (cost)
    378372        {
  • binaries/data/mods/public/simulation/components/EntityLimits.js

     
     1function EntityLimits() {}
     2
     3EntityLimits.prototype.Schema =
     4    "<a:help>Specifies per category limits on number of entities (buildings or units) that can be created for each player.</a:help>" +
     5    "<a:example>" +
     6        "<Limits>" +
     7          "<CivilCentre/>" +
     8          "<DefenseTower>25</DefenseTower>" +
     9          "<Fortress>10</Fortress>" +
     10          "<Hero>1</Hero>" +
     11          "<Special>" +
     12            "<LimitPerCivCentre>1</LimitPerCivCentre>" +
     13          "</Special>" +
     14        "</Limits>" +
     15    "</a:example>" +
     16    "<element name='LimitMultiplier'>" +
     17        "<ref name='positiveDecimal'/>" +
     18    "</element>" +
     19    "<element name='Limits'>" +
     20        "<zeroOrMore>" +
     21            "<element a:help='Specifies a category of building on which to apply this limit. See BuildRestrictions for list of categories.'>" +
     22                "<anyName />" +
     23                "<choice>" +
     24                    "<text />" +
     25                    "<element name='LimitPerCivCentre' a:help='Specifies that this limit is per number of civil centres.'>" +
     26                        "<data type='nonNegativeInteger'/>" +
     27                    "</element>" +
     28                "</choice>" +
     29            "</element>" +
     30        "</zeroOrMore>" +
     31    "</element>";
     32
     33/*
     34 *  TODO: Use an inheriting player_{civ}.xml template for civ-specific limits
     35 */
     36
     37EntityLimits.prototype.Init = function()
     38{
     39    this.limit = {};
     40    this.count = {};
     41    for (var category in this.template.Limits)
     42    {
     43        this.limit[category] = this.template.Limits[category];
     44        this.count[category] = 0;
     45    }
     46};
     47
     48EntityLimits.prototype.IncreaseCount = function(category, value)
     49{
     50    if (this.count[category] !== undefined)
     51    {
     52        this.count[category] += value;
     53    }
     54};
     55
     56EntityLimits.prototype.DecreaseCount = function(category, value)
     57{
     58    if (this.count[category] !== undefined)
     59    {
     60        this.count[category] -= value;
     61    }
     62};
     63
     64EntityLimits.prototype.IncrementCount = function(category)
     65{
     66    this.IncreaseCount(category, 1);
     67};
     68
     69EntityLimits.prototype.DecrementCount = function(category)
     70{
     71    this.DecreaseCount(category, 1);
     72};
     73
     74EntityLimits.prototype.GetLimits = function()
     75{
     76    return this.limit;
     77};
     78
     79EntityLimits.prototype.GetCounts = function()
     80{
     81    return this.count;
     82};
     83
     84EntityLimits.prototype.AllowedToBuild = function(category)
     85{
     86    // TODO: The UI should reflect this before the user tries to place the building,
     87    //          since the limits are independent of placement location
     88
     89    // Allow unspecified categories and those with no limit
     90    if (this.count[category] === undefined || this.limit[category] === undefined)
     91    {
     92        return true;
     93    }
     94   
     95    // Rather than complicating the schema unecessarily, just handle special cases here
     96    if (this.limit[category].LimitPerCivCentre !== undefined)
     97    {
     98        if (this.count[category] >= this.count["CivilCentre"] * this.limit[category].LimitPerCivCentre)
     99        {
     100            var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     101            var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category].LimitPerCivCentre+" per civil centre reached"};
     102            var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     103            cmpGUIInterface.PushNotification(notification);
     104           
     105            return false;
     106        }
     107    }
     108    else if (this.count[category] >= this.limit[category])
     109    {
     110        var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     111        var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" train limit of "+this.limit[category]+ " reached"};
     112        var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     113        cmpGUIInterface.PushNotification(notification);
     114       
     115        return false;
     116    }
     117   
     118    return true;
     119};
     120
     121EntityLimits.prototype.AllowedToTrain = function(category, count)
     122{
     123    // Allow unspecified categories and those with no limit
     124    if (this.count[category] === undefined || this.limit[category] === undefined)
     125    {
     126        return true;
     127    }
     128    if (this.count[category] + count > this.limit[category])
     129    {
     130        var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     131        var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" build limit of "+this.limit[category]+ " reached"};
     132        var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     133        cmpGUIInterface.PushNotification(notification);
     134       
     135        return false;
     136    }
     137    return true;
     138};
     139
     140EntityLimits.prototype.OnGlobalOwnershipChanged = function(msg)
     141{
     142    // This automatically updates entity counts
     143    var category = null;
     144    var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions);
     145    if (cmpBuildRestrictions)
     146        category = cmpBuildRestrictions.GetCategory();
     147    var cmpTrainingRestrictions = Engine.QueryInterface(msg.entity, IID_TrainingRestrictions);
     148    if (cmpTrainingRestrictions)
     149        category = cmpTrainingRestrictions.GetCategory();
     150    if (category)
     151    {
     152        var playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID();
     153        if (msg.from == playerID)
     154        {
     155            this.DecrementCount(category);
     156        }
     157        if (msg.to == playerID)
     158        {
     159            this.IncrementCount(category);
     160        }
     161    }
     162};
     163
     164Engine.RegisterComponentType(IID_EntityLimits, "EntityLimits", EntityLimits);
  • binaries/data/mods/public/simulation/components/interfaces/TrainingRestrictions.js

     
     1Engine.RegisterInterface("TrainingRestrictions");
     2
  • binaries/data/mods/public/simulation/components/interfaces/BuildLimits.js

     
    1 Engine.RegisterInterface("BuildLimits");
    2  No newline at end of file
  • binaries/data/mods/public/simulation/components/interfaces/EntityLimits.js

     
     1Engine.RegisterInterface("EntityLimits");
  • binaries/data/mods/public/simulation/components/TrainingRestrictions.js

     
     1function TrainingRestrictions() {}
     2
     3TrainingRestrictions.prototype.Schema =
     4    "<a:help>Specifies unit training restrictions, currently only unit category.</a:help>" +
     5    "<a:example>" +
     6        "<TrainingRestrictions>" +
     7            "<Category>Hero</Category>" +
     8        "</TrainingRestrictions>" +
     9    "</a:example>" +
     10    "<element name='Category' a:help='Specifies the category of this unit, for satisfying special constraints.'>" +
     11        "<choice>" +
     12            "<value>Hero</value>" +
     13            "<value>FemaleCitizen</value>" +
     14        "</choice>" +
     15    "</element>";
     16
     17TrainingRestrictions.prototype.GetCategory = function()
     18{
     19    return this.template.Category;
     20};
     21
     22Engine.RegisterComponentType(IID_TrainingRestrictions, "TrainingRestrictions", TrainingRestrictions);
  • binaries/data/mods/public/simulation/ai/qbot/military.js

     
    324324// Adds towers to the defenceBuilding queue
    325325MilitaryAttackManager.prototype.buildDefences = function(gameState, queues){
    326326    if (gameState.countEntitiesAndQueuedByType(gameState.applyCiv('structures/{civ}_defense_tower'))
    327             + queues.defenceBuilding.totalLength() < gameState.getBuildLimits()["DefenseTower"]) {
     327            + queues.defenceBuilding.totalLength() < gameState.getEntityLimits()["DefenseTower"]) {
    328328       
    329329       
    330330        gameState.getOwnEntities().forEach(function(dropsiteEnt) {
     
    343343        numFortresses += gameState.countEntitiesAndQueuedByType(gameState.applyCiv(this.bFort[i]));
    344344    }
    345345   
    346     if (numFortresses + queues.defenceBuilding.totalLength() < gameState.getBuildLimits()["Fortress"]) {
     346    if (numFortresses + queues.defenceBuilding.totalLength() < gameState.getEntityLimits()["Fortress"]) {
    347347        if (gameState.countEntitiesByType(gameState.applyCiv("units/{civ}_support_female_citizen")) > gameState.ai.modules["economy"].targetNumWorkers * 0.5){
    348348            if (gameState.getTimeElapsed() > 350 * 1000 * numFortresses){
    349349                if (gameState.ai.pathsToMe && gameState.ai.pathsToMe.length > 0){
  • binaries/data/mods/public/simulation/ai/qbot/gamestate.js

     
    300300    return this.updatingCollection("resource-" + resource, Filters.byResource(resource), this.getEntities());
    301301};
    302302
    303 GameState.prototype.getBuildLimits = function() {
    304     return this.playerData.buildLimits;
     303GameState.prototype.getEntityLimits = function() {
     304    return this.playerData.entityLimits;
    305305};
    306306
    307 GameState.prototype.getBuildCounts = function() {
    308     return this.playerData.buildCounts;
     307GameState.prototype.getEntityCounts = function() {
     308    return this.playerData.entityCounts;
    309309};
    310310
    311311// Checks whether the maximum number of buildings have been cnstructed for a certain catergory
    312 GameState.prototype.isBuildLimitReached = function(category) {
    313     if(this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
     312GameState.prototype.isEntityLimitReached = function(category) {
     313    if(this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
    314314        return false;
    315     if(this.playerData.buildLimits[category].LimitsPerCivCentre != undefined)
    316         return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
     315    if(this.playerData.entityLimits[category].LimitsPerCivCentre != undefined)
     316        return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
    317317    else
    318         return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
     318        return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
    319319};
  • binaries/data/mods/public/simulation/ai/jubot/gamestate.js

     
    415415     * Returns player build limits
    416416     * an object where each key is a category corresponding to a build limit for the player.
    417417     */
    418     getBuildLimits: function()
     418    getEntityLimits: function()
    419419    {
    420         return this.playerData.buildLimits;
     420        return this.playerData.entityLimits;
    421421    },
    422422   
    423423    /**
    424424     * Returns player build counts
    425425     * an object where each key is a category corresponding to the current building count for the player.
    426426     */
    427     getBuildCounts: function()
     427    getEntityCounts: function()
    428428    {
    429         return this.playerData.buildCounts;
     429        return this.playerData.entityCounts;
    430430    },
    431431   
    432432    /**
    433433     * Checks if the player's build limit has been reached for the given category.
    434434     * The category comes from the entity tenplate, specifically the BuildRestrictions component.
    435435     */
    436     isBuildLimitReached: function(category)
     436    isEntityLimitReached: function(category)
    437437    {
    438         if (this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
     438        if (this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
    439439            return false;
    440440       
    441441        // There's a special case of build limits per civ centre, so check that first
    442         if (this.playerData.buildLimits[category].LimitPerCivCentre !== undefined)
    443             return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
     442        if (this.playerData.entityLimits[category].LimitPerCivCentre !== undefined)
     443            return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
    444444        else
    445             return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
     445            return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
    446446    },
    447447});
  • binaries/data/mods/public/simulation/ai/testbot/gamestate.js

     
    268268     * Returns player build limits
    269269     * an object where each key is a category corresponding to a build limit for the player.
    270270     */
    271     getBuildLimits: function()
     271    getEntityLimits: function()
    272272    {
    273         return this.playerData.buildLimits;
     273        return this.playerData.entityLimits;
    274274    },
    275275   
    276276    /**
    277      * Returns player build counts
    278      * an object where each key is a category corresponding to the current building count for the player.
     277     * Returns player entity counts
     278     * an object where each key is a category corresponding to the current entity count for the player.
    279279     */
    280     getBuildCounts: function()
     280    getEntityCounts: function()
    281281    {
    282         return this.playerData.buildCounts;
     282        return this.playerData.entityCounts;
    283283    },
    284284   
    285285    /**
    286      * Checks if the player's build limit has been reached for the given category.
     286     * Checks if the player's entity limit has been reached for the given category.
    287287     * The category comes from the entity tenplate, specifically the BuildRestrictions component.
    288288     */
    289     isBuildLimitReached: function(category)
     289    isEntityLimitReached: function(category)
    290290    {
    291         if (this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
     291        if (this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
    292292            return false;
    293293       
    294294        // There's a special case of build limits per civ centre, so check that first
    295         if (this.playerData.buildLimits[category].LimitPerCivCentre !== undefined)
    296             return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
     295        if (this.playerData.entityLimits[category].LimitPerCivCentre !== undefined)
     296            return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
    297297        else
    298             return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
     298            return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
    299299    },
    300300});
  • binaries/data/mods/public/simulation/templates/template_unit_hero_ranged.xml

     
    5151      </Texture>
    5252    </Overlay>
    5353  </Selectable>
     54  <TrainingRestrictions>
     55    <Category>Hero</Category>
     56  </TrainingRestrictions>
    5457  <UnitMotion>
    5558    <WalkSpeed>8.5</WalkSpeed>
    5659    <Run>
  • binaries/data/mods/public/simulation/templates/special/player.xml

     
    11<?xml version="1.0" encoding="utf-8"?>
    22<Entity>
    3   <BuildLimits>
     3  <EntityLimits>
    44    <LimitMultiplier>1.0</LimitMultiplier>
    55    <Limits>
    66      <CivilCentre/>
    77      <DefenseTower>25</DefenseTower>
    88      <Fortress>10</Fortress>
     9      <FemaleCitizen>6</FemaleCitizen>
     10      <Hero>1</Hero>
    911    </Limits>
    10   </BuildLimits>
     12  </EntityLimits>
    1113  <Player/>
    1214  <StatisticsTracker/>
    1315  <TechnologyManager/>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml

     
    6969  <Stamina>
    7070    <Max>2500</Max>
    7171  </Stamina>
     72  <TrainingRestrictions>
     73    <Category>Hero</Category>
     74  </TrainingRestrictions>
    7275  <UnitMotion>
    7376    <WalkSpeed>11.0</WalkSpeed>
    7477    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero.xml

     
    6262      <death>actor/human/death/death.xml</death>
    6363    </SoundGroups>
    6464  </Sound>
     65  <TrainingRestrictions>
     66    <Category>Hero</Category>
     67  </TrainingRestrictions>
    6568  <UnitMotion>
    6669    <WalkSpeed>9.0</WalkSpeed>
    6770    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml

     
    6767  <Stamina>
    6868    <Max>1500</Max>
    6969  </Stamina>
     70  <TrainingRestrictions>
     71    <Category>Hero</Category>
     72  </TrainingRestrictions>
    7073  <UnitMotion>
    7174    <WalkSpeed>8.5</WalkSpeed>
    7275    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_archer.xml

     
    6161      </Texture>
    6262    </Overlay>
    6363  </Selectable>
     64  <TrainingRestrictions>
     65    <Category>Hero</Category>
     66  </TrainingRestrictions>
    6467</Entity>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_javelinist.xml

     
    6565      </Texture>
    6666    </Overlay>
    6767  </Selectable>
     68  <TrainingRestrictions>
     69    <Category>Hero</Category>
     70  </TrainingRestrictions>
    6871</Entity>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_archer.xml

     
    6969      </Texture>
    7070    </Overlay>
    7171  </Selectable>
     72  <TrainingRestrictions>
     73    <Category>Hero</Category>
     74  </TrainingRestrictions>
    7275  <UnitMotion>
    7376    <WalkSpeed>11.0</WalkSpeed>
    7477    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml

     
    5959      </Texture>
    6060    </Overlay>
    6161  </Selectable>
     62  <TrainingRestrictions>
     63    <Category>Hero</Category>
     64  </TrainingRestrictions>
    6265  <UnitMotion>
    6366    <WalkSpeed>11.5</WalkSpeed>
    6467    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_support_female_citizen.xml

     
    9191  <Stamina>
    9292    <Max>500</Max>
    9393  </Stamina>
     94  <TrainingRestrictions>
     95    <Category>FemaleCitizen</Category>
     96  </TrainingRestrictions>
    9497  <UnitMotion>
    9598    <WalkSpeed>8.0</WalkSpeed>
    9699    <Run>