Ticket #1432: one_hero_2012_10_23.diff

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

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

     
    139139}
    140140
    141141/**
     142 * Format batch training string for tooltip
     143 * Examples:
     144 * buildingsCountToTrainFullBatch = 1, fullBatchSize = 5, remainderBatch = 0:
     145 * "Shift-click to train 5"
     146 * buildingsCountToTrainFullBatch = 2, fullBatchSize = 5, remainderBatch = 0:
     147 * "Shift-click to train 10 (2*5)"
     148 * buildingsCountToTrainFullBatch = 1, fullBatchSize = 15, remainderBatch = 12:
     149 * "Shift-click to train 27 (15 + 12)"
     150 */
     151function formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch)
     152{
     153    var totalBatchTrainingCount = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
     154    // Don't show the batch training tooltip if either units of this type can't be trained at all
     155    // or only one unit can be trained
     156    if (totalBatchTrainingCount < 2)
     157        return "";
     158    var batchTrainingString = "";
     159    var fullBatchesString = "";
     160    if (buildingsCountToTrainFullBatch > 0)
     161    {
     162        if (buildingsCountToTrainFullBatch > 1)
     163            fullBatchesString += buildingsCountToTrainFullBatch + "*";
     164        fullBatchesString += fullBatchSize;
     165    }
     166    var remainderBatchString = remainderBatch > 0 ? remainderBatch : "";
     167    var batchDetailsString = buildingsCountToTrainFullBatch > 1 || (buildingsCountToTrainFullBatch == 1 && remainderBatch > 0) ?
     168        " (" + [fullBatchesString, remainderBatchString].filter(function(string) { return string != "" }).join(" + ") + ")" :
     169        "";
     170    return "\n\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train "
     171        + totalBatchTrainingCount + batchDetailsString + ".[/font]";
     172}
     173
     174/**
    142175 * Helper function for updateUnitCommands; sets up "unit panels" (i.e. panels with rows of icons) for the currently selected
    143176 * unit.
    144177 *
     
    150183 * @param items Panel-specific data to construct the icons with.
    151184 * @param callback Callback function to argument to execute when an item's icon gets clicked. Takes a single 'item' argument.
    152185 */
    153 function setupUnitPanel(guiName, usedPanels, unitEntState, items, callback)
     186function setupUnitPanel(guiName, usedPanels, unitEntState, playerState, items, callback)
    154187{
    155188    usedPanels[guiName] = 1;
    156189
     
    347380            case GATE:
    348381                var tooltip = item.tooltip;
    349382                if (item.template)
    350                 {
     383                {
    351384                    var template = GetTemplateData(item.template);
    352                     tooltip += "\n" + getEntityCostTooltip(template);
    353 
    354                     var affordableMask = getGUIObjectByName("unitGateUnaffordable["+i+"]");
     385                    tooltip += "\n" + getEntityCostTooltip(template);
     386
     387                    var affordableMask = getGUIObjectByName("unitGateUnaffordable["+i+"]");
    355388                    affordableMask.hidden = true;
    356 
    357                     var neededResources = Engine.GuiInterfaceCall("GetNeededResources", template.cost);
    358                     if (neededResources)
    359                     {
    360                         affordableMask.hidden = false;
    361                         tooltip += getNeededResourcesTooltip(neededResources);
    362                     }
    363                 }
     389
     390                    var neededResources = Engine.GuiInterfaceCall("GetNeededResources", template.cost);
     391                    if (neededResources)
     392                    {
     393                        affordableMask.hidden = false;
     394                        tooltip += getNeededResourcesTooltip(neededResources);
     395                    }
     396                }
    364397                break;
    365398
    366399            case STANCE:
     
    374407                if (template.tooltip)
    375408                    tooltip += "\n[font=\"serif-13\"]" + template.tooltip + "[/font]";
    376409
    377                 var [batchSize, batchIncrement] = getTrainingBatchStatus(unitEntState.id, entType);
    378                 var trainNum = batchSize ? batchSize+batchIncrement : batchIncrement;
     410                var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] = getTrainingBatchStatus(playerState, unitEntState.id, entType, selection);
    379411
    380412                tooltip += "\n" + getEntityCostTooltip(template);
    381413
     
    388420                if (template.speed)
    389421                    tooltip += "\n" + getEntitySpeed(template);
    390422
    391                 tooltip += "\n\n[font=\"serif-bold-13\"]Shift-click[/font][font=\"serif-13\"] to train " + trainNum + ".[/font]";
    392 
     423                tooltip += formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch);
    393424                break;
    394425               
    395426            case RESEARCH:
     
    444475        // Button
    445476        var button = getGUIObjectByName("unit"+guiName+"Button["+i+"]");
    446477        var button1 = getGUIObjectByName("unit"+guiName+"Button["+(i+rowLength)+"]");
    447         var affordableMask = getGUIObjectByName("unit"+guiName+"Unaffordable["+i+"]");
    448         var affordableMask1 = getGUIObjectByName("unit"+guiName+"Unaffordable["+(i+rowLength)+"]");
     478        var affordableMask = getGUIObjectByName("unit"+guiName+"Unaffordable["+i+"]");
     479        var affordableMask1 = getGUIObjectByName("unit"+guiName+"Unaffordable["+(i+rowLength)+"]");
    449480        var icon = getGUIObjectByName("unit"+guiName+"Icon["+i+"]");
    450481        var selection = getGUIObjectByName("unit"+guiName+"Selection["+i+"]");
    451482        var pair = getGUIObjectByName("unit"+guiName+"Pair["+i+"]");
     
    583614           
    584615            if (guiName == RESEARCH)
    585616            {
    586                 // Check resource requirements for first button
    587                 affordableMask.hidden = true;
    588                 var neededResources = Engine.GuiInterfaceCall("GetNeededResources", template.cost);
    589                 if (neededResources)
    590                 {
    591                     if (button.enabled !== false)
    592                     {
    593                         button.enabled = false;
    594                         affordableMask.hidden = false;
    595                     }
    596                     button.tooltip += getNeededResourcesTooltip(neededResources);
    597                 }
    598 
     617                // Check resource requirements for first button
     618                affordableMask.hidden = true;
     619                var neededResources = Engine.GuiInterfaceCall("GetNeededResources", template.cost);
     620                if (neededResources)
     621                {
     622                    if (button.enabled !== false)
     623                    {
     624                        button.enabled = false;
     625                        affordableMask.hidden = false;
     626                    }
     627                    button.tooltip += getNeededResourcesTooltip(neededResources);
     628                }
     629
    599630                if (item.pair)
    600631                {
    601632                    grayscale = "";
    602633                    button1.enabled = true;
    603 
    604                     if (!Engine.GuiInterfaceCall("CheckTechnologyRequirements", entType1))
     634
     635                    if (!Engine.GuiInterfaceCall("CheckTechnologyRequirements", entType1))
    605636                    {
    606637                        button1.enabled = false;
    607638                        button1.tooltip += "\n" + GetTechnologyData(entType1).requirementsTooltip;
    608639                        grayscale = "grayscale:";
    609640                    }
    610641                    icon1.sprite = "stretched:" + grayscale + "session/portraits/" +template1.icon;
    611 
    612                     // Check resource requirements for second button
    613                     affordableMask1.hidden = true;
    614                     neededResources = Engine.GuiInterfaceCall("GetNeededResources", template.cost);
    615                     if (neededResources)
    616                     {
    617                         if (button1.enabled !== false)
    618                         {
    619                             button1.enabled = false;
    620                             affordableMask1.hidden = false;
    621                         }
    622                         button1.tooltip += getNeededResourcesTooltip(neededResources);
    623                     }
     642
     643                    // Check resource requirements for second button
     644                    affordableMask1.hidden = true;
     645                    neededResources = Engine.GuiInterfaceCall("GetNeededResources", template.cost);
     646                    if (neededResources)
     647                    {
     648                        if (button1.enabled !== false)
     649                        {
     650                            button1.enabled = false;
     651                            affordableMask1.hidden = false;
     652                        }
     653                        button1.tooltip += getNeededResourcesTooltip(neededResources);
     654                    }
    624655                }
    625656                else
    626657                {
    627658                    pair.hidden = true;
    628659                    button1.hidden = true;
    629                     affordableMask1.hidden = true;
    630                 }
    631             }
    632             else if (guiName == CONSTRUCTION || guiName == TRAINING)
    633             {
    634                 affordableMask.hidden = true;
    635                 var totalCosts = {};
    636                 var trainNum = 1;
    637                 if (Engine.HotkeyIsPressed("session.batchtrain") && guiName == TRAINING)
    638                 {
    639                     var [batchSize, batchIncrement] = getTrainingBatchStatus(unitEntState.id, entType);
    640                     trainNum = batchSize + batchIncrement;
     660                    affordableMask1.hidden = true;
    641661                }
    642 
    643                 // Walls have no cost defined.
     662            }
     663            else if (guiName == CONSTRUCTION || guiName == TRAINING)
     664            {
     665                if (guiName == TRAINING)
     666                {
     667                    var trainingCategory = null;
     668                    if (template.trainingRestrictions)
     669                        trainingCategory = template.trainingRestrictions.category;
     670                    var grayscale = "";
     671                    if (trainingCategory && playerState.entityLimits[trainingCategory] && playerState.entityCounts[trainingCategory] >= playerState.entityLimits[trainingCategory])
     672                        grayscale = "grayscale:";
     673                    icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
     674                }
     675
     676                affordableMask.hidden = true;
     677                var totalCosts = {};
     678                var trainNum = 1;
     679                if (Engine.HotkeyIsPressed("session.batchtrain") && guiName == TRAINING)
     680                {
     681                    var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] = getTrainingBatchStatus(playerState, unitEntState.id, entType, selection);
     682                    trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
     683                }
     684
     685                // Walls have no cost defined.
    644686                if (template.cost !== undefined)
    645687                    for (var r in template.cost)
    646688                        totalCosts[r] = Math.floor(template.cost[r] * trainNum);
    647 
    648                 var neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCosts);
    649                 if (neededResources)
     689
     690                var neededResources = Engine.GuiInterfaceCall("GetNeededResources", totalCosts);
     691                if (neededResources)
    650692                {
    651                     var totalCost = 0;
    652                     if (button.enabled !== false)
     693                    var totalCost = 0;
     694                    if (button.enabled !== false)
    653695                    {
    654696                        for each (var resource in neededResources)
    655697                            totalCost += resource;
    656 
    657                         button.enabled = false;
    658                         affordableMask.hidden = false;
    659                         var alpha = 75 + totalCost/6;
    660                         alpha = alpha > 150 ? 150 : alpha;
    661                         affordableMask.sprite = "colour: 255 0 0 " + (alpha);
    662                     }
    663                     button.tooltip += getNeededResourcesTooltip(neededResources);
    664                 }
     698
     699                        button.enabled = false;
     700                        affordableMask.hidden = false;
     701                        var alpha = 75 + totalCost/6;
     702                        alpha = alpha > 150 ? 150 : alpha;
     703                        affordableMask.sprite = "colour: 255 0 0 " + (alpha);
     704                    }
     705                    button.tooltip += getNeededResourcesTooltip(neededResources);
     706                }
    665707            }
    666708        }
    667709        else
     
    767809}
    768810
    769811// Sets up "unit barter panel" - special case for setupUnitPanel
    770 function setupUnitBarterPanel(unitEntState)
     812function setupUnitBarterPanel(unitEntState, playerState)
    771813{
    772814    // Amount of player's resource to exchange
    773815    var amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
     
    862904    var player = Engine.GetPlayerID();
    863905    if (entState.player == player || g_DevSettings.controlAll)
    864906    {
     907        // Get player state to check some constraints
     908        // e.g. presence of a hero or build limits
     909        var simState = Engine.GuiInterfaceCall("GetSimulationState");
     910        var playerState = simState.players[player];
     911
    865912        if (selection.length > 1)
    866             setupUnitPanel(SELECTION, usedPanels, entState, g_Selection.groups.getTemplateNames(),
     913            setupUnitPanel(SELECTION, usedPanels, entState, playerState, g_Selection.groups.getTemplateNames(),
    867914                function (entType) { changePrimarySelectionGroup(entType); } );
    868915
    869916        var commands = getEntityCommandsList(entState);
    870917        if (commands.length)
    871             setupUnitPanel(COMMAND, usedPanels, entState, commands,
     918            setupUnitPanel(COMMAND, usedPanels, entState, playerState, commands,
    872919                function (item) { performCommand(entState.id, item.name); } );
    873920
    874921        if (entState.garrisonHolder)
     
    881928                    groups.add(state.garrisonHolder.entities)
    882929            }
    883930
    884             setupUnitPanel(GARRISON, usedPanels, entState, groups.getTemplateNames(),
     931            setupUnitPanel(GARRISON, usedPanels, entState, playerState, groups.getTemplateNames(),
    885932                function (item) { unloadTemplate(item); } );
    886933        }
    887934
    888935        var formations = Engine.GuiInterfaceCall("GetAvailableFormations");
    889936        if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && !entState.garrisonHolder && formations.length)
    890937        {
    891             setupUnitPanel(FORMATION, usedPanels, entState, formations,
     938            setupUnitPanel(FORMATION, usedPanels, entState, playerState, formations,
    892939                function (item) { performFormation(entState.id, item); } );
    893940        }
    894941
     
    897944        var stances = ["violent", "aggressive", "passive", "defensive", "standground"];
    898945        if (hasClass(entState, "Unit") && !hasClass(entState, "Animal") && stances.length)
    899946        {
    900             setupUnitPanel(STANCE, usedPanels, entState, stances,
     947            setupUnitPanel(STANCE, usedPanels, entState, playerState, stances,
    901948                function (item) { performStance(entState.id, item); } );
    902949        }
    903950
     
    905952        if (entState.barterMarket)
    906953        {
    907954            usedPanels["Barter"] = 1;
    908             setupUnitBarterPanel(entState);
     955            setupUnitBarterPanel(entState, playerState);
    909956        }
    910957
    911958        var buildableEnts = [];
     
    929976
    930977        // The first selected entity's type has priority.
    931978        if (entState.buildEntities)
    932             setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
     979            setupUnitPanel(CONSTRUCTION, usedPanels, entState, playerState, buildableEnts, startBuildingPlacement);
    933980        else if (entState.production && entState.production.entities)
    934             setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
    935                 function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
     981            setupUnitPanel(TRAINING, usedPanels, entState, playerState, trainableEnts,
     982                function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
    936983        else if (entState.trader)
    937984            setupUnitTradingPanel(usedPanels, entState, selection);
    938985        else if (!entState.foundation && entState.gate || hasClass(entState, "LongWall"))
     
    9941041                setupUnitPanel(CONSTRUCTION, usedPanels, entState, buildableEnts, startBuildingPlacement);
    9951042            else if (trainableEnts.length)
    9961043                setupUnitPanel(TRAINING, usedPanels, entState, trainableEnts,
    997                     function (trainEntType) { addTrainingToQueue(selection, trainEntType); } );
     1044                    function (trainEntType) { addTrainingToQueue(selection, trainEntType, playerState); } );
    9981045        }
    9991046
    10001047        // Show technologies if the active panel has at most one row of icons.
     
    10071054        }
    10081055
    10091056        if (entState.production && entState.production.queue.length)
    1010             setupUnitPanel(QUEUE, usedPanels, entState, entState.production.queue,
     1057            setupUnitPanel(QUEUE, usedPanels, entState, playerState, entState.production.queue,
    10111058                function (item) { removeFromProductionQueue(entState.id, item.id); } );
    10121059       
    10131060        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>