Ticket #1432: one_hero_2012_10_27.diff

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

     
    14671467var batchTrainingEntities;
    14681468var batchTrainingType;
    14691469var batchTrainingCount;
     1470var batchTrainingEntityAllowedCount;
    14701471const batchIncrementSize = 5;
    14711472
    14721473function flushTrainingBatch()
    14731474{
    1474     Engine.PostNetworkCommand({"type": "train", "entities": batchTrainingEntities, "template": batchTrainingType, "count": batchTrainingCount});
     1475    var appropriateBuildings = getBuidlingsWhichCanTrainEntity(batchTrainingEntities, batchTrainingType);
     1476    // If training limits don't allow us to train batchTrainingCount in each appropriate building
     1477    if (batchTrainingEntityAllowedCount !== undefined && batchTrainingEntityAllowedCount < batchTrainingCount * appropriateBuildings.length)
     1478    {
     1479        // Train as many full batches as we can
     1480        var buildingsCountToTrainFullBatch = Math.floor(batchTrainingEntityAllowedCount / batchTrainingCount);
     1481        var buildingsToTrainFullBatch = appropriateBuildings.slice(0, buildingsCountToTrainFullBatch);
     1482        Engine.PostNetworkCommand({"type": "train", "entities": buildingsToTrainFullBatch, "template": batchTrainingType, "count": batchTrainingCount});
     1483
     1484        // Train remainer in one more building
     1485        var remainderToTrain = batchTrainingEntityAllowedCount % batchTrainingCount;
     1486        Engine.PostNetworkCommand({"type": "train", "entities": [ appropriateBuildings[buildingsCountToTrainFullBatch] ], "template": batchTrainingType, "count": remainderToTrain});
     1487    }
     1488    else
     1489    {
     1490        Engine.PostNetworkCommand({"type": "train", "entities": appropriateBuildings, "template": batchTrainingType, "count": batchTrainingCount});
     1491    }
    14751492}
    14761493
     1494function getBuidlingsWhichCanTrainEntity(entitiesToCheck, trainEntType)
     1495{
     1496    return entitiesToCheck.filter(function(entity) {
     1497        var state = GetEntityState(entity);
     1498        var canTrain = state && state.production && state.production.entities.length && state.production.entities.indexOf(trainEntType) != -1;
     1499        return canTrain;
     1500    });
     1501}
     1502
     1503function getEntityLimitAndCount(playerState, entType)
     1504{
     1505    var template = GetTemplateData(entType);
     1506    var trainingCategory = null;
     1507    if (template.trainingRestrictions)
     1508        trainingCategory = template.trainingRestrictions.category;
     1509    var trainEntLimit = undefined;
     1510    var trainEntCount = undefined;
     1511    var canBeTrainedCount = undefined;
     1512    if (trainingCategory && playerState.entityLimits[trainingCategory])
     1513    {
     1514        trainEntLimit = playerState.entityLimits[trainingCategory];
     1515        trainEntCount = playerState.entityCounts[trainingCategory];
     1516        canBeTrainedCount = trainEntLimit - trainEntCount;
     1517    }
     1518    return [trainEntLimit, trainEntCount, canBeTrainedCount];
     1519}
     1520
    14771521// Called by GUI when user clicks training button
    1478 function addTrainingToQueue(selection, trainEntType)
     1522function addTrainingToQueue(selection, trainEntType, playerState)
    14791523{
    1480     if (Engine.HotkeyIsPressed("session.batchtrain"))
     1524    // Create list of buildings which can train trainEntType
     1525    var appropriateBuildings = getBuidlingsWhichCanTrainEntity(selection, trainEntType);
     1526
     1527    // Check trainEntType entity limit and count
     1528    var [trainEntLimit, trainEntCount, canBeTrainedCount] = getEntityLimitAndCount(playerState, trainEntType)
     1529
     1530    // Batch training possible if we can train at least 2 units
     1531    var batchTrainingPossible = canBeTrainedCount !== undefined && canBeTrainedCount > 1;
     1532
     1533    if (Engine.HotkeyIsPressed("session.batchtrain") && batchTrainingPossible)
    14811534    {
    14821535        if (inputState == INPUT_BATCHTRAINING)
    14831536        {
     
    14941547                }
    14951548            }
    14961549            // If we're already creating a batch of this unit (in the same building(s)), then just extend it
     1550            // (if training limits allow)
    14971551            if (sameEnts && batchTrainingType == trainEntType)
    14981552            {
    1499                 batchTrainingCount += batchIncrementSize;
     1553                if (canBeTrainedCount !== undefined && canBeTrainedCount > batchTrainingCount * appropriateBuildings.length)
     1554                    batchTrainingCount += batchIncrementSize;
     1555                batchTrainingEntityAllowedCount = canBeTrainedCount;
    15001556                return;
    15011557            }
    15021558            // Otherwise start a new one
     
    15091565        inputState = INPUT_BATCHTRAINING;
    15101566        batchTrainingEntities = selection;
    15111567        batchTrainingType = trainEntType;
     1568        batchTrainingEntityAllowedCount = canBeTrainedCount;
    15121569        batchTrainingCount = batchIncrementSize;
    15131570    }
    15141571    else
    15151572    {
    1516         // Non-batched - just create a single entity
    1517         Engine.PostNetworkCommand({"type": "train", "template": trainEntType, "count": 1, "entities": selection});
     1573        // Non-batched - just create a single entity in each building
     1574        // (but no more than entity limit allows)
     1575        var buildingsForTraining = appropriateBuildings;
     1576        if (trainEntLimit)
     1577            buildingsForTraining = buildingsForTraining.slice(0, canBeTrainedCount);
     1578        Engine.PostNetworkCommand({"type": "train", "template": trainEntType, "count": 1, "entities": buildingsForTraining});
    15181579    }
    15191580}
    15201581
     
    15261587
    15271588// Returns the number of units that will be present in a batch if the user clicks
    15281589// the training button with shift down
    1529 function getTrainingBatchStatus(entity, trainEntType)
     1590function getTrainingBatchStatus(playerState, entity, trainEntType, selection)
    15301591{
     1592    var apporpriateBuildings = [entity];
     1593    if (selection && selection.indexOf(entity) != -1)
     1594        appropriateBuildings = getBuidlingsWhichCanTrainEntity(selection, trainEntType);
     1595    var nextBatchTrainingCount = 0;
    15311596    if (inputState == INPUT_BATCHTRAINING && batchTrainingEntities.indexOf(entity) != -1 && batchTrainingType == trainEntType)
    1532         return [batchTrainingCount, batchIncrementSize];
     1597    {
     1598        nextBatchTrainingCount = batchTrainingCount;
     1599        var canBeTrainedCount = batchTrainingEntityAllowedCount;
     1600    }
    15331601    else
    1534         return [0, batchIncrementSize];
     1602    {
     1603        var [trainEntLimit, trainEntCount, canBeTrainedCount] = getEntityLimitAndCount(playerState, trainEntType);
     1604        var batchSize = Math.min(canBeTrainedCount, batchIncrementSize);
     1605    }
     1606    // We need to calculate count after the next increment if it's possible
     1607    if (canBeTrainedCount == undefined || canBeTrainedCount > nextBatchTrainingCount * appropriateBuildings.length)
     1608        nextBatchTrainingCount += batchIncrementSize;
     1609    // If training limits don't allow us to train batchTrainingCount in each appropriate building
     1610    // train as many full batches as we can and remainer in one more building.
     1611    var buildingsCountToTrainFullBatch = appropriateBuildings.length;
     1612    var remainderToTrain = 0;
     1613    if (canBeTrainedCount !== undefined && canBeTrainedCount < nextBatchTrainingCount * appropriateBuildings.length)
     1614    {
     1615        buildingsCountToTrainFullBatch = Math.floor(canBeTrainedCount / nextBatchTrainingCount);
     1616        remainderToTrain = canBeTrainedCount % nextBatchTrainingCount;
     1617    }
     1618    return [buildingsCountToTrainFullBatch, nextBatchTrainingCount, remainderToTrain];
    15351619}
    15361620
    15371621// Called by GUI when user clicks production queue item
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    139139}
    140140
    141141/**
     142 * Format entity count/limit message for the tooltip
     143 */
     144function formatLimitString(trainEntLimit, trainEntCount)
     145{
     146    if (trainEntLimit == undefined)
     147        return "";
     148    var text = "\n\nCurrent count: " + trainEntCount + ", limit: " + trainEntLimit + ".";
     149    if (trainEntCount >= trainEntLimit)
     150        text = "[color=\"red\"]" + text + "[/color]";
     151    return text;
     152}
     153
     154/**
     155 * Format batch training string for the tooltip
     156 * Examples:
     157 * buildingsCountToTrainFullBatch = 1, fullBatchSize = 5, remainderBatch = 0:
     158 * "Shift-click to train 5"
     159 * buildingsCountToTrainFullBatch = 2, fullBatchSize = 5, remainderBatch = 0:
     160 * "Shift-click to train 10 (2*5)"
     161 * buildingsCountToTrainFullBatch = 1, fullBatchSize = 15, remainderBatch = 12:
     162 * "Shift-click to train 27 (15 + 12)"
     163 */
     164function formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch)
     165{
     166    var totalBatchTrainingCount = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
     167    // Don't show the batch training tooltip if either units of this type can't be trained at all
     168    // or only one unit can be trained
     169    if (totalBatchTrainingCount < 2)
     170        return "";
     171    var batchTrainingString = "";
     172    var fullBatchesString = "";
     173    if (buildingsCountToTrainFullBatch > 0)
     174    {
     175        if (buildingsCountToTrainFullBatch > 1)
     176            fullBatchesString += buildingsCountToTrainFullBatch + "*";
     177        fullBatchesString += fullBatchSize;
     178    }
     179    var remainderBatchString = remainderBatch > 0 ? remainderBatch : "";
     180    var batchDetailsString = buildingsCountToTrainFullBatch > 1 || (buildingsCountToTrainFullBatch == 1 && remainderBatch > 0) ?
     181        " (" + [fullBatchesString, remainderBatchString].filter(function(string) { return string != "" }).join(" + ") + ")" :
     182        "";
     183    return "\n\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train "
     184        + totalBatchTrainingCount + batchDetailsString + ".[/font]";
     185}
     186
     187/**
    142188 * Helper function for updateUnitCommands; sets up "unit panels" (i.e. panels with rows of icons) for the currently selected
    143189 * unit.
    144190 *
     
    150196 * @param items Panel-specific data to construct the icons with.
    151197 * @param callback Callback function to argument to execute when an item's icon gets clicked. Takes a single 'item' argument.
    152198 */
    153 function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
     199function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, callback)
    154200{
    155201    usedPanels[guiName] = 1;
    156202
     
    374420                if (template.tooltip)
    375421                    tooltip += "\n[font=\"serif-13\"]" + template.tooltip + "[/font]";
    376422
    377                 var [batchSize, batchIncrement] = getTrainingBatchStatus(unitEntState.id, entType);
    378                 var trainNum = batchSize ? batchSize+batchIncrement : batchIncrement;
    379 
    380423                tooltip += "\n" + getEntityCostTooltip(template);
    381424
    382425                if (template.health)
     
    388431                if (template.speed)
    389432                    tooltip += "\n" + getEntitySpeed(template);
    390433
    391                 tooltip += "\n\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train " + trainNum + ".[/font]";
     434                var [trainEntLimit, trainEntCount, canBeTrainedCount] = getEntityLimitAndCount(playerState, entType)
     435                tooltip += formatLimitString(trainEntLimit, trainEntCount);
    392436
     437                var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] = getTrainingBatchStatus(playerState, unitEntState.id, entType, selection);
     438                tooltip += formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch);
    393439                break;
    394440               
    395441            case RESEARCH:
     
    627673                    pair.hidden = true;
    628674                    button1.hidden = true;
    629675                    affordableMask1.hidden = true;
    630                 }
     676                }
    631677            }
    632678            else if (guiName == CONSTRUCTION || guiName == TRAINING)
    633679            {
     680                if (guiName == TRAINING)
     681                {
     682                    var trainingCategory = null;
     683                    if (template.trainingRestrictions)
     684                        trainingCategory = template.trainingRestrictions.category;
     685                    var grayscale = "";
     686                    if (trainingCategory && playerState.entityLimits[trainingCategory] && playerState.entityCounts[trainingCategory] >= playerState.entityLimits[trainingCategory])
     687                        grayscale = "grayscale:";
     688                    icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
     689                }
     690
    634691                affordableMask.hidden = true;
    635692                var totalCosts = {};
    636693                var trainNum = 1;
    637694                if (Engine.HotkeyIsPressed("session.batchtrain") && guiName == TRAINING)
    638695                {
    639                     var [batchSize, batchIncrement] = getTrainingBatchStatus(unitEntState.id, entType);
    640                     trainNum = batchSize + batchIncrement;
     696                    var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] = getTrainingBatchStatus(playerState, unitEntState.id, entType, selection);
     697                    trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
    641698                }
    642699
    643700                // Walls have no cost defined.
     
    767824}
    768825
    769826// Sets up "unit barter panel" - special case for setupUnitPanel
    770 function setupUnitBarterPanel(unitEntState)
     827function setupUnitBarterPanel(unitEntState, playerState)
    771828{
    772829    // Amount of player's resource to exchange
    773830    var amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
     
    862919    var player = Engine.GetPlayerID();
    863920    if (entState.player == player || g_DevSettings.controlAll)
    864921    {
     922        // Get player state to check some constraints
     923        // e.g. presence of a hero or build limits
     924        var simState = Engine.GuiInterfaceCall("GetSimulationState");
     925        var playerState = simState.players[player];
     926
    865927        if (selection.length > 1)
    866             setupUnitPanel(SELECTION, usedPanels, entState, g_Selection.groups.getTemplateNames(),
     928            setupUnitPanel(SELECTION, usedPanels, entState, playerState, g_Selection.groups.getTemplateNames(),
    867929                function (entType) { changePrimarySelectionGroup(entType); } );
    868930
    869931        var commands = getEntityCommandsList(entState);
    870932        if (commands.length)
    871             setupUnitPanel(COMMAND, usedPanels, entState, commands,
     933            setupUnitPanel(COMMAND, usedPanels, entState, playerState, commands,
    872934                function (item) { performCommand(entState.id, item.name); } );
    873935
    874936        if (entState.garrisonHolder)
     
    881943                    groups.add(state.garrisonHolder.entities)
    882944            }
    883945
    884             setupUnitPanel(GARRISON, usedPanels, entState, groups.getTemplateNames(),
     946            setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
    885947                function (item) { unloadTemplate(item); } );
    886948        }
    887949
    888950        var formations = Engine.GuiInterfaceCall("GetAvailableFormations");
    889951        if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && formations.length)
    890952        {
    891             setupUnitPanel(FORMATION, usedPanels, entState, formations,
     953            setupUnitPanel(FORMATION, usedPanels, entState, playerState, formations,
    892954                function (item) { performFormation(entState.id, item); } );
    893955        }
    894956
     
    897959        var stances = ["violent", "aggressive", "passive", "defensive", "standground"];
    898960        if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && stances.length)
    899961        {
    900             setupUnitPanel(STANCE, usedPanels, entState, stances,
     962            setupUnitPanel(STANCE, usedPanels, entState, playerState, stances,
    901963                function (item) { performStance(entState.id, item); } );
    902964        }
    903965
     
    905967        if (entState.barterMarket)
    906968        {
    907969            usedPanels["Barter"] = 1;
    908             setupUnitBarterPanel(entState);
     970            setupUnitBarterPanel(entState, playerState);
    909971        }
    910972
    911973        var buildableEnts = [];
     
    929991
    930992        // The first selected entity's type has priority.
    931993        if (entState.buildEntities)
    932             setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
     994            setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts, startBuildingPlacement);
    933995        else if (entState.production && entState.production.entities)
    934             setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
    935                 function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
     996            setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
     997                function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
    936998        else if (entState.trader)
    937999            setupUnitTradingPanel(usedPanels, entState, selection);
    9381000        else if (!entState.foundation && entState.gate || hasClass(entState, "LongWall"))
     
    9941056                setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
    9951057            else if (trainableEnts.length)
    9961058                setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
    997                     function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
     1059                    function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
    9981060        }
    9991061
    10001062        // Show technologies if the active panel has at most one row of icons.
     
    10071069        }
    10081070
    10091071        if (entState.production && entState.production.queue.length)
    1010             setupUnitPanel(QUEUE, usedPanels, entState, entState.production.queue,
     1072            setupUnitPanel(QUEUE, usedPanels, entState, playerState, entState.production.queue,
    10111073                function (item) { removeFromProductionQueue(entState.id, item.id); } );
    10121074       
    10131075        supplementalDetailsPanel.hidden = false;
  • binaries/data/mods/public/simulation/ai/jubot/gamestate.js

     
    416416     * Returns player build limits
    417417     * an object where each key is a category corresponding to a build limit for the player.
    418418     */
    419     getBuildLimits: function()
     419    getEntityLimits: function()
    420420    {
    421         return this.playerData.buildLimits;
     421        return this.playerData.entityLimits;
    422422    },
    423423   
    424424    /**
    425425     * Returns player build counts
    426426     * an object where each key is a category corresponding to the current building count for the player.
    427427     */
    428     getBuildCounts: function()
     428    getEntityCounts: function()
    429429    {
    430         return this.playerData.buildCounts;
     430        return this.playerData.entityCounts;
    431431    },
    432432   
    433433    /**
    434434     * Checks if the player's build limit has been reached for the given category.
    435435     * The category comes from the entity tenplate, specifically the BuildRestrictions component.
    436436     */
    437     isBuildLimitReached: function(category)
     437    isEntityLimitReached: function(category)
    438438    {
    439         if (this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
     439        if (this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
    440440            return false;
    441441       
    442442        // There's a special case of build limits per civ centre, so check that first
    443         if (this.playerData.buildLimits[category].LimitPerCivCentre !== undefined)
    444             return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
     443        if (this.playerData.entityLimits[category].LimitPerCivCentre !== undefined)
     444            return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
    445445        else
    446             return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
     446            return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
    447447    },
    448448});
  • binaries/data/mods/public/simulation/ai/qbot/gamestate.js

     
    303303    return this.updatingCollection("resource-" + resource, Filters.byResource(resource), this.getEntities());
    304304};
    305305
    306 GameState.prototype.getBuildLimits = function() {
    307     return this.playerData.buildLimits;
     306GameState.prototype.getEntityLimits = function() {
     307    return this.playerData.entityLimits;
    308308};
    309309
    310 GameState.prototype.getBuildCounts = function() {
    311     return this.playerData.buildCounts;
     310GameState.prototype.getEntityCounts = function() {
     311    return this.playerData.entityCounts;
    312312};
    313313
    314314// Checks whether the maximum number of buildings have been cnstructed for a certain catergory
    315 GameState.prototype.isBuildLimitReached = function(category) {
    316     if(this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
     315GameState.prototype.isEntityLimitReached = function(category) {
     316    if(this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
    317317        return false;
    318     if(this.playerData.buildLimits[category].LimitsPerCivCentre != undefined)
    319         return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
     318    if(this.playerData.entityLimits[category].LimitsPerCivCentre != undefined)
     319        return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
    320320    else
    321         return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
     321        return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
    322322};
  • 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/testbot/gamestate.js

     
    269269     * Returns player build limits
    270270     * an object where each key is a category corresponding to a build limit for the player.
    271271     */
    272     getBuildLimits: function()
     272    getEntityLimits: function()
    273273    {
    274         return this.playerData.buildLimits;
     274        return this.playerData.entityLimits;
    275275    },
    276276   
    277277    /**
    278      * Returns player build counts
    279      * an object where each key is a category corresponding to the current building count for the player.
     278     * Returns player entity counts
     279     * an object where each key is a category corresponding to the current entity count for the player.
    280280     */
    281     getBuildCounts: function()
     281    getEntityCounts: function()
    282282    {
    283         return this.playerData.buildCounts;
     283        return this.playerData.entityCounts;
    284284    },
    285285   
    286286    /**
    287      * Checks if the player's build limit has been reached for the given category.
     287     * Checks if the player's entity limit has been reached for the given category.
    288288     * The category comes from the entity tenplate, specifically the BuildRestrictions component.
    289289     */
    290     isBuildLimitReached: function(category)
     290    isEntityLimitReached: function(category)
    291291    {
    292         if (this.playerData.buildLimits[category] === undefined || this.playerData.buildCounts[category] === undefined)
     292        if (this.playerData.entityLimits[category] === undefined || this.playerData.entityCounts[category] === undefined)
    293293            return false;
    294294       
    295295        // There's a special case of build limits per civ centre, so check that first
    296         if (this.playerData.buildLimits[category].LimitPerCivCentre !== undefined)
    297             return (this.playerData.buildCounts[category] >= this.playerData.buildCounts["CivilCentre"]*this.playerData.buildLimits[category].LimitPerCivCentre);
     296        if (this.playerData.entityLimits[category].LimitPerCivCentre !== undefined)
     297            return (this.playerData.entityCounts[category] >= this.playerData.entityCounts["CivilCentre"]*this.playerData.entityLimits[category].LimitPerCivCentre);
    298298        else
    299             return (this.playerData.buildCounts[category] >= this.playerData.buildLimits[category]);
     299            return (this.playerData.entityCounts[category] >= this.playerData.entityLimits[category]);
    300300    },
    301301});
  • 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/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/unit on which to apply this limit. See BuildRestrictions/TrainingRestrictions 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
     37const TRAINING = "training";
     38const BUILD = "build";
     39
     40EntityLimits.prototype.Init = function()
     41{
     42    this.limit = {};
     43    this.count = {};
     44    for (var category in this.template.Limits)
     45    {
     46        this.limit[category] = this.template.Limits[category];
     47        this.count[category] = 0;
     48    }
     49};
     50
     51EntityLimits.prototype.IncreaseCount = function(category, value)
     52{
     53    if (this.count[category] !== undefined)
     54    {
     55        this.count[category] += value;
     56    }
     57};
     58
     59EntityLimits.prototype.DecreaseCount = function(category, value)
     60{
     61    if (this.count[category] !== undefined)
     62    {
     63        this.count[category] -= value;
     64    }
     65};
     66
     67EntityLimits.prototype.IncrementCount = function(category)
     68{
     69    this.IncreaseCount(category, 1);
     70};
     71
     72EntityLimits.prototype.DecrementCount = function(category)
     73{
     74    this.DecreaseCount(category, 1);
     75};
     76
     77EntityLimits.prototype.GetLimits = function()
     78{
     79    return this.limit;
     80};
     81
     82EntityLimits.prototype.GetCounts = function()
     83{
     84    return this.count;
     85};
     86
     87EntityLimits.prototype.AllowedToCreate = function(limitType, category, count)
     88{
     89    // TODO: The UI should reflect this before the user tries to place the building,
     90    //          since the limits are independent of placement location
     91
     92    // Allow unspecified categories and those with no limit
     93    if (this.count[category] === undefined || this.limit[category] === undefined)
     94    {
     95        return true;
     96    }
     97   
     98    // Rather than complicating the schema unecessarily, just handle special cases here
     99    if (this.limit[category].LimitPerCivCentre !== undefined)
     100    {
     101        if (this.count[category] >= this.count["CivilCentre"] * this.limit[category].LimitPerCivCentre)
     102        {
     103            var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     104            var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" "+limitType+" limit of "+this.limit[category].LimitPerCivCentre+" per civil centre reached"};
     105            var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     106            cmpGUIInterface.PushNotification(notification);
     107           
     108            return false;
     109        }
     110    }
     111    else if (this.count[category] + count > this.limit[category])
     112    {
     113        var cmpPlayer = Engine.QueryInterface(this.entity, IID_Player);
     114        var notification = {"player": cmpPlayer.GetPlayerID(), "message": category+" "+limitType+" limit of "+this.limit[category]+ " reached"};
     115        var cmpGUIInterface = Engine.QueryInterface(SYSTEM_ENTITY, IID_GuiInterface);
     116        cmpGUIInterface.PushNotification(notification);
     117       
     118        return false;
     119    }
     120   
     121    return true;
     122}
     123
     124EntityLimits.prototype.AllowedToBuild = function(category)
     125{
     126    // TODO: The UI should reflect this before the user tries to place the building,
     127    //          since the limits are independent of placement location
     128
     129    return this.AllowedToCreate(BUILD, category, 1);
     130};
     131
     132EntityLimits.prototype.AllowedToTrain = function(category, count)
     133{
     134    return this.AllowedToCreate(TRAINING, category, count);
     135};
     136
     137EntityLimits.prototype.OnGlobalOwnershipChanged = function(msg)
     138{
     139    // This automatically updates entity counts
     140    var category = null;
     141    var cmpBuildRestrictions = Engine.QueryInterface(msg.entity, IID_BuildRestrictions);
     142    if (cmpBuildRestrictions)
     143        category = cmpBuildRestrictions.GetCategory();
     144    var cmpTrainingRestrictions = Engine.QueryInterface(msg.entity, IID_TrainingRestrictions);
     145    if (cmpTrainingRestrictions)
     146        category = cmpTrainingRestrictions.GetCategory();
     147    if (category)
     148    {
     149        var playerID = (Engine.QueryInterface(this.entity, IID_Player)).GetPlayerID();
     150        if (msg.from == playerID)
     151        {
     152            this.DecrementCount(category);
     153        }
     154        if (msg.to == playerID)
     155        {
     156            this.IncrementCount(category);
     157        }
     158    }
     159};
     160
     161Engine.RegisterComponentType(IID_EntityLimits, "EntityLimits", EntityLimits);
  • 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
     
    8888            "isAlly": allies,
    8989            "isNeutral": neutrals,
    9090            "isEnemy": enemies,
    91             "buildLimits": cmpPlayerBuildLimits.GetLimits(),
    92             "buildCounts": cmpPlayerBuildLimits.GetCounts(),
     91            "entityLimits": cmpPlayerEntityLimits.GetLimits(),
     92            "entityCounts": cmpPlayerEntityLimits.GetCounts(),
    9393            "techModifications": cmpTechnologyManager.GetTechModifications()
    9494        };
    9595        ret.players.push(playerData);
     
    384384            if (template.BuildRestrictions.Distance.MaxDistance) ret.buildRestrictions.distance.max = +template.BuildRestrictions.Distance.MaxDistance;
    385385        }
    386386    }
    387    
     387
     388    if (template.TrainingRestrictions)
     389    {
     390        ret.trainingRestrictions = {
     391            "category": template.TrainingRestrictions.Category,
     392        };
     393    }
     394
    388395    if (template.Cost)
    389396    {
    390397        ret.cost = {};
     
    453460        ret.icon = template.Identity.Icon;
    454461        ret.tooltip =  template.Identity.Tooltip;
    455462        ret.requiredTechnology = template.Identity.RequiredTechnology;
     463        ret.identityClassesString = GetTemplateIdentityClassesString(template);
    456464    }
    457465
    458466    if (template.UnitMotion)
  • binaries/data/mods/public/simulation/components/Player.js

     
    417417Player.prototype.OnGlobalOwnershipChanged = function(msg)
    418418{
    419419    var isConquestCritical = false;
    420 
    421420    // Load class list only if we're going to need it
    422421    if (msg.from == this.playerID || msg.to == this.playerID)
    423422    {
    424423        var cmpIdentity = Engine.QueryInterface(msg.entity, IID_Identity);
    425424        if (cmpIdentity)
    426425        {
    427             var classes = cmpIdentity.GetClassesList();
    428             isConquestCritical = classes.indexOf("ConquestCritical") != -1;
     426            isConquestCritical = cmpIdentity.HasClass("ConquestCritical");
    429427        }
    430428    }
    431    
    432429    if (msg.from == this.playerID)
    433430    {
    434431        if (isConquestCritical)
    435432            this.conquestCriticalEntitiesCount--;
    436 
    437433        var cost = Engine.QueryInterface(msg.entity, IID_Cost);
    438434        if (cost)
    439435        {
     
    441437            this.popBonuses -= cost.GetPopBonus();
    442438        }
    443439    }
    444    
    445440    if (msg.to == this.playerID)
    446441    {
    447442        if (isConquestCritical)
    448443            this.conquestCriticalEntitiesCount++;
    449            
    450444        var cost = Engine.QueryInterface(msg.entity, IID_Cost);
    451445        if (cost)
    452446        {
  • binaries/data/mods/public/simulation/components/ProductionQueue.js

     
    212212            if (!cmpPlayer.TrySubtractResources(totalCosts))
    213213                return;
    214214
     215            // Update entity count in the EntityLimits component
     216            if (template.TrainingRestrictions)
     217            {
     218                var unitCategory = template.TrainingRestrictions.Category;
     219                var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     220                cmpPlayerEntityLimits.IncreaseCount(unitCategory, count);
     221            }
     222
    215223            this.queue.push({
    216224                "id": this.nextID++,
    217225                "player": cmpPlayer.GetPlayerID(),
     
    307315       
    308316        var cmpPlayer = QueryPlayerIDInterface(item.player, IID_Player);
    309317
     318        // Update entity count in the EntityLimits component
     319        if (item.unitTemplate)
     320        {
     321            var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     322            var template = cmpTempMan.GetTemplate(item.unitTemplate);
     323            if (template.TrainingRestrictions)
     324            {
     325                var unitCategory = template.TrainingRestrictions.Category;
     326                var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     327                cmpPlayerEntityLimits.DecreaseCount(unitCategory, item.count);
     328            }
     329        }
     330
    310331        // Refund the resource cost for this batch
    311332        var totalCosts = {};
    312333        var cmpStatisticsTracker = QueryOwnerInterface(this.entity, IID_StatisticsTracker);
     
    422443        //  so only create them once and use as needed
    423444        for (var i = 0; i < count; ++i)
    424445        {
    425             this.entityCache.push(Engine.AddEntity(templateName));
     446            var ent = Engine.AddEntity(templateName);
     447            this.entityCache.push(ent);
     448
     449            // Decrement entity count in the EntityLimits component
     450            // since it will be increased by EntityLimits.OnGlobalOwnershipChanged function,
     451            // i.e. we replace a 'trained' entity to an 'alive' one
     452            var cmpTrainingRestrictions = Engine.QueryInterface(ent, IID_TrainingRestrictions);
     453            if (cmpTrainingRestrictions)
     454            {
     455                var unitCategory = cmpTrainingRestrictions.GetCategory();
     456                var cmpPlayerEntityLimits = QueryOwnerInterface(this.entity, IID_EntityLimits);
     457                cmpPlayerEntityLimits.DecrementCount(unitCategory);
     458            }
    426459        }
    427460    }
    428461
  • 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/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/interfaces/TrainingRestrictions.js

     
     1Engine.RegisterInterface("TrainingRestrictions");
     2
  • 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");
     
    6969    IsEnemy: function() { return true; },
    7070});
    7171
    72 AddMock(100, IID_BuildLimits, {
     72AddMock(100, IID_EntityLimits, {
    7373    GetLimits: function() { return {"Foo": 10}; },
    7474    GetCounts: function() { return {"Foo": 5}; },
    7575});
     
    122122    IsEnemy: function() { return false; },
    123123});
    124124
    125 AddMock(101, IID_BuildLimits, {
     125AddMock(101, IID_EntityLimits, {
    126126    GetLimits: function() { return {"Bar": 20}; },
    127127    GetCounts: function() { return {"Bar": 0}; },
    128128});
     
    177177            isAlly: [false, false, false],
    178178            isNeutral: [false, false, false],
    179179            isEnemy: [true, true, true],
    180             buildLimits: {"Foo": 10},
    181             buildCounts: {"Foo": 5},
     180            entityLimits: {"Foo": 10},
     181            entityCounts: {"Foo": 5},
    182182            techModifications: {},
    183183        },
    184184        {
     
    197197            isAlly: [true, true, true],
    198198            isNeutral: [false, false, false],
    199199            isEnemy: [false, false, false],
    200             buildLimits: {"Bar": 20},
    201             buildCounts: {"Bar": 0},
     200            entityLimits: {"Bar": 20},
     201            entityCounts: {"Bar": 0},
    202202            techModifications: {},
    203203        }
    204204    ],
     
    224224            isAlly: [false, false, false],
    225225            isNeutral: [false, false, false],
    226226            isEnemy: [true, true, true],
    227             buildLimits: {"Foo": 10},
    228             buildCounts: {"Foo": 5},
     227            entityLimits: {"Foo": 10},
     228            entityCounts: {"Foo": 5},
    229229            techModifications: {},
    230230            statistics: {
    231231                unitsTrained: 10,
     
    260260            isAlly: [true, true, true],
    261261            isNeutral: [false, false, false],
    262262            isEnemy: [false, false, false],
    263             buildLimits: {"Bar": 20},
    264             buildCounts: {"Bar": 0},
     263            entityLimits: {"Bar": 20},
     264            entityCounts: {"Bar": 0},
    265265            techModifications: {},
    266266            statistics: {
    267267                unitsTrained: 10,
  • binaries/data/mods/public/simulation/data/technologies/no_heroes.json

     
    1 {   
    2     "genericName": "Future Alpha",
    3     "description": "Temporarily Disable Heroes",
    4     "cost": {"food": 0, "wood": 100, "stone": 0, "metal": 0},
    5     "requirements": {"tech": "phase_city"},
    6     "requirementsTooltip": "Unlocked in City Phase.",
    7     "icon": "arrow.png",
    8     "researchTime": 20,
    9     "tooltip": "Need build limits before renabling this."
    10 }
  • binaries/data/mods/public/simulation/helpers/Commands.js

     
    169169
    170170    case "train":
    171171        var entities = FilterEntityList(cmd.entities, player, controlAllUnits);
     172       
     173        // Check entity limits
     174        var cmpTempMan = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     175        var template = cmpTempMan.GetTemplate(cmd.template);
     176        var unitCategory = null;
     177        if (template.TrainingRestrictions)
     178            unitCategory = template.TrainingRestrictions.Category;
     179
    172180        // Verify that the building(s) can be controlled by the player
    173181        if (entities.length > 0)
    174182        {
    175183            for each (var ent in entities)
    176184            {
     185                if (unitCategory)
     186                {
     187                    var cmpPlayerEntityLimits = QueryOwnerInterface(ent, IID_EntityLimits);
     188                    if (!cmpPlayerEntityLimits.AllowedToTrain(unitCategory, cmd.count))
     189                    {
     190                        if (g_DebugCommands)
     191                        {
     192                            warn(unitCategory + " train limit is reached: " + uneval(cmd));
     193                        }
     194                        continue;
     195                    }
     196                }
     197
    177198                var cmpTechnologyManager = QueryOwnerInterface(ent, IID_TechnologyManager);
    178199                // TODO: Enable this check once the AI gets technology support
    179200                if (cmpTechnologyManager.CanProduce(cmd.template) || true)
     
    568589        return false;
    569590    }
    570591   
    571     // Check build limits
    572     var cmpBuildLimits = QueryPlayerIDInterface(player, IID_BuildLimits);
    573     if (!cmpBuildLimits || !cmpBuildLimits.AllowedToBuild(cmpBuildRestrictions.GetCategory()))
     592    // Check entity limits
     593    var cmpEntityLimits = QueryPlayerIDInterface(player, IID_EntityLimits);
     594    if (!cmpEntityLimits || !cmpEntityLimits.AllowedToBuild(cmpBuildRestrictions.GetCategory()))
    574595    {
    575596        if (g_DebugCommands)
    576597        {
  • 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/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>32</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.xml

     
    3636  <Identity>
    3737    <GenericName>Hero</GenericName>
    3838    <Classes datatype="tokens">Hero Organic</Classes>
    39     <RequiredTechnology>no_heroes</RequiredTechnology>
    4039  </Identity>
    4140  <Loot>
    4241    <xp>400</xp>
     
    6362      <death>actor/human/death/death.xml</death>
    6463    </SoundGroups>
    6564  </Sound>
     65  <TrainingRestrictions>
     66    <Category>Hero</Category>
     67  </TrainingRestrictions>
    6668  <UnitMotion>
    6769    <WalkSpeed>9.0</WalkSpeed>
    6870    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry.xml

     
    4040  <Identity>
    4141    <GenericName>Hero Cavalry</GenericName>
    4242    <Classes datatype="tokens">Hero Cavalry</Classes>
    43     <RequiredTechnology>no_heroes</RequiredTechnology>
    4443  </Identity>
    4544  <Loot>
    4645    <xp>500</xp>
     
    7069  <Stamina>
    7170    <Max>2500</Max>
    7271  </Stamina>
     72  <TrainingRestrictions>
     73    <Category>Hero</Category>
     74  </TrainingRestrictions>
    7375  <UnitMotion>
    7476    <WalkSpeed>11.0</WalkSpeed>
    7577    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_archer.xml

     
    4949    <GenericName>Hero Cavalry Archer</GenericName>
    5050    <Tooltip>Hero Aura: n/a.
    5151Ranged attack 2x vs. spearmen. Ranged attack 1.5x vs. Swordsmen.</Tooltip>
    52     <RequiredTechnology>no_heroes</RequiredTechnology>
    5352  </Identity>
    5453  <Loot>
    5554    <xp>450</xp>
     
    7069      </Texture>
    7170    </Overlay>
    7271  </Selectable>
     72  <TrainingRestrictions>
     73    <Category>Hero</Category>
     74  </TrainingRestrictions>
    7375  <UnitMotion>
    7476    <WalkSpeed>11.0</WalkSpeed>
    7577    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_cavalry_javelinist.xml

     
    3939  <Identity>
    4040    <Classes datatype="tokens">Hero</Classes>
    4141    <GenericName>Hero Cavalry Skirmisher</GenericName>
    42     <RequiredTechnology>no_heroes</RequiredTechnology>
    4342  </Identity>
    4443  <Loot>
    4544    <xp>450</xp>
     
    6059      </Texture>
    6160    </Overlay>
    6261  </Selectable>
     62  <TrainingRestrictions>
     63    <Category>Hero</Category>
     64  </TrainingRestrictions>
    6365  <UnitMotion>
    6466    <WalkSpeed>11.5</WalkSpeed>
    6567    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry.xml

     
    3838  <Identity>
    3939    <Classes datatype="tokens">Hero Infantry</Classes>
    4040    <GenericName>Hero</GenericName>
    41     <RequiredTechnology>no_heroes</RequiredTechnology>
    4241  </Identity>
    4342  <Loot>
    4443    <xp>400</xp>
     
    6867  <Stamina>
    6968    <Max>1500</Max>
    7069  </Stamina>
     70  <TrainingRestrictions>
     71    <Category>Hero</Category>
     72  </TrainingRestrictions>
    7173  <UnitMotion>
    7274    <WalkSpeed>8.5</WalkSpeed>
    7375    <Run>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_archer.xml

     
    4141    <Classes datatype="tokens">Hero</Classes>
    4242    <Tooltip>Hero Archer.
    4343Counters Swordsmen and Cavalry Spearmen. Countered by Skirmishers and other Cavalry types.</Tooltip>
    44     <RequiredTechnology>no_heroes</RequiredTechnology>
    4544  </Identity>
    4645  <Loot>
    4746    <xp>350</xp>
     
    6261      </Texture>
    6362    </Overlay>
    6463  </Selectable>
     64  <TrainingRestrictions>
     65    <Category>Hero</Category>
     66  </TrainingRestrictions>
    6567</Entity>
  • binaries/data/mods/public/simulation/templates/template_unit_hero_infantry_javelinist.xml

     
    4545  <Identity>
    4646    <Classes datatype="tokens">Hero</Classes>
    4747    <GenericName>Hero Skirmisher</GenericName>
    48     <RequiredTechnology>no_heroes</RequiredTechnology>
    4948  </Identity>
    5049  <Loot>
    5150    <xp>350</xp>
     
    6665      </Texture>
    6766    </Overlay>
    6867  </Selectable>
     68  <TrainingRestrictions>
     69    <Category>Hero</Category>
     70  </TrainingRestrictions>
    6971</Entity>
  • 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/template_unit_support_female_citizen.xml

     
    9999  <Stamina>
    100100    <Max>500</Max>
    101101  </Stamina>
     102  <TrainingRestrictions>
     103    <Category>FemaleCitizen</Category>
     104  </TrainingRestrictions>
    102105  <UnitMotion>
    103106    <WalkSpeed>8.0</WalkSpeed>
    104107    <Run>