Ticket #4007: selection_panels_cleanup.diff

File selection_panels_cleanup.diff, 47.3 KB (added by sanderd17, 8 years ago)
  • binaries/data/mods/public/gui/session/selection_panels.js

     
    99 * the item and some other standard data is added to a data object.
    1010 *
    1111 * The standard data is
    12  * var data = {
     12 * {
    1313 *   "i":              index
    1414 *   "item":           item coming from the getItems function
    1515 *   "selection":      list of currently selected items
     
    2323 *   "countDisplay":   gui caption space
    2424 * };
    2525 *
    26  * Then, addData is called, and can be used to abort the processing
    27  * of the current item by returning false.
    28  * It should return true if you want the panel to be filled.
    29  *
    30  * addData is used to add data to the data object on top
    31  * (or instead of) the standard data.
    32  * addData is not obligated, the function will just continue
    33  * with the content setters if no addData is present.
    34  *
    35  * After the addData, all functions starting with "set" are called.
    36  * These are used to set various parts of content.
     26 * Then for every data object, the setupButton function is called which
     27 * sets the view and handlers of the button.
    3728 */
    3829
    3930/* cache some formation info */
    40 var g_availableFormations = new Map();   // available formations per player
    41 var g_formationsInfo = new Map();
     31let g_availableFormations = new Map();   // available formations per player
     32let g_formationsInfo = new Map();
    4233
    43 var g_SelectionPanels = {};
     34let g_SelectionPanels = {};
    4435
    4536// ALERT
    4637g_SelectionPanels.Alert = {
     
    5445            return [];
    5546        return ["increase", "end"];
    5647    },
    57     "setAction": function(data)
     48    "setupButton": function(data)
    5849    {
     50        // Action
    5951        data.button.onPress = function() {
    6052            if (data.item == "increase")
    6153                increaseAlertLevel();
     
    6254            else if (data.item == "end")
    6355                endOfAlert();
    6456        };
    65     },
    66     "setTooltip": function(data)
    67     {
     57
     58        // Tooltip
    6859        if (data.item == "increase")
    6960        {
    7061            if (data.unitEntState.alertRaiser.hasRaisedAlert)
     
    7465        }
    7566        else if (data.item == "end")
    7667            data.button.tooltip = translate("End of alert.");
    77     },
    78     "setGraphics": function(data)
    79     {
     68
     69        // Graphics
    8070        if (data.item == "increase")
    8171        {
    8272            data.button.hidden = !data.unitEntState.alertRaiser.canIncreaseLevel;
     
    9181            data.icon.sprite = "stretched:session/icons/bell_level0.png";
    9282        }
    9383        data.button.enabled = !data.button.hidden && controlsPlayer(data.unitEntState.player);
     84
     85        // Position
     86        setPanelObjectPosition(data.button, data.i, data.rowLength);
     87        return true;
    9488    }
    9589};
    9690
     
    108102        // ["food", "wood", "stone", "metal"]
    109103        return BARTER_RESOURCES;
    110104    },
    111     "addData": function(data)
     105    "setupButton": function(data)
    112106    {
     107        // Get Gui objects
    113108        // data.item is the resource name in this case
    114         data.button = {};
    115         data.icon = {};
    116         data.amount = {};
    117         for (var a of BARTER_ACTIONS)
     109        let button = {};
     110        let icon = {};
     111        let amount = {};
     112        for (let a of BARTER_ACTIONS)
    118113        {
    119             data.button[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Button["+data.i+"]");
    120             data.icon[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Icon["+data.i+"]");
    121             data.amount[a] = Engine.GetGUIObjectByName("unitBarter"+a+"Amount["+data.i+"]");
     114            button[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Button[" + data.i + "]");
     115            icon[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Icon[" + data.i + "]");
     116            amount[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Amount[" + data.i + "]");
    122117        }
    123         data.selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection["+data.i+"]");
     118        let selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection[" + data.i + "]");
    124119
    125         data.amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
     120        let amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
    126121        if (Engine.HotkeyIsPressed("session.massbarter"))
    127             data.amountToSell *= BARTER_BUNCH_MULTIPLIER;
    128         data.isSelected = data.item == g_barterSell;
    129         return true;
    130     },
    131     "setCountDisplay": function(data)
    132     {
    133         data.amount.Sell.caption = "-" + data.amountToSell;
    134         var sellPrice = data.unitEntState.barterMarket.prices.sell[g_barterSell];
    135         var buyPrice = data.unitEntState.barterMarket.prices.buy[data.item];
    136         data.amount.Buy.caption = "+" + Math.round(sellPrice / buyPrice * data.amountToSell);
    137     },
    138     "setTooltip": function(data)
    139     {
    140         var resource = getLocalizedResourceName(data.item, "withinSentence");
    141         data.button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource });
    142         data.button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource });
    143     },
    144     "setAction": function(data)
    145     {
    146         data.button.Sell.onPress = function() { g_barterSell = data.item; };
    147         var exchangeResourcesParameters = {
     122            amountToSell *= BARTER_BUNCH_MULTIPLIER;
     123
     124        // Caption
     125        amount.Sell.caption = "-" + amountToSell;
     126        let sellPrice = data.unitEntState.barterMarket.prices.sell[g_barterSell];
     127        let buyPrice = data.unitEntState.barterMarket.prices.buy[data.item];
     128        amount.Buy.caption = "+" + Math.round(sellPrice / buyPrice * amountToSell);
     129
     130        // Tooltip
     131        let resource = getLocalizedResourceName(data.item, "withinSentence");
     132        button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource });
     133        button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource });
     134
     135        // Action
     136        button.Sell.onPress = function() { g_barterSell = data.item; };
     137        let exchangeResourcesParameters = {
    148138            "sell": g_barterSell,
    149139            "buy": data.item,
    150             "amount": data.amountToSell
     140            "amount": amountToSell
    151141        };
    152         data.button.Buy.onPress = function() { exchangeResources(exchangeResourcesParameters); };
    153     },
    154     "setGraphics": function(data)
    155     {
    156         var grayscale = data.isSelected ? "color: 0 0 0 100:grayscale:" : "";
     142        button.Buy.onPress = function() { exchangeResources(exchangeResourcesParameters); };
    157143
     144        // Graphics
     145        let isSelected = data.item == g_barterSell;
     146        let grayscale = isSelected ? "color: 0 0 0 100:grayscale:" : "";
     147
    158148        // do we have enough of this resource to sell?
    159         var neededRes = {};
    160         neededRes[data.item] = data.amountToSell;
    161         var canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
     149        let neededRes = {};
     150        neededRes[data.item] = amountToSell;
     151        let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
    162152            "cost": neededRes,
    163153            "player": data.unitEntState.player
    164154        }) ? "color:255 0 0 80:" : "";
     
    165155
    166156        // Let's see if we have enough resources to barter.
    167157        neededRes = {};
    168         neededRes[g_barterSell] = data.amountToSell;
    169         var canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
     158        neededRes[g_barterSell] = amountToSell;
     159        let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
    170160            "cost": neededRes,
    171161            "player": data.unitEntState.player
    172162        }) ? "color:255 0 0 80:" : "";
    173163
    174         data.icon.Sell.sprite = canSellCurrent + "stretched:"+grayscale+"session/icons/resources/" + data.item + ".png";
    175         data.icon.Buy.sprite = canBuyAny + "stretched:"+grayscale+"session/icons/resources/" + data.item + ".png";
     164        icon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
     165        icon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
    176166
    177         data.button.Buy.hidden = data.isSelected;
    178         data.button.Buy.enabled = controlsPlayer(data.unitEntState.player);
    179         data.button.Sell.hidden = false;
    180         data.selectionIcon.hidden = !data.isSelected;
    181     },
    182     "setPosition": function(data)
    183     {
    184         setPanelObjectPosition(data.button.Sell, data.i, data.rowLength);
    185         setPanelObjectPosition(data.button.Buy, data.i + data.rowLength, data.rowLength);
     167        button.Buy.hidden = isSelected;
     168        button.Buy.enabled = controlsPlayer(data.unitEntState.player);
     169        button.Sell.hidden = false;
     170        selectionIcon.hidden = !isSelected;
     171
     172        // Position
     173        setPanelObjectPosition(button.Sell, data.i, data.rowLength);
     174        setPanelObjectPosition(button.Buy, data.i + data.rowLength, data.rowLength);
     175        return true;
    186176    }
    187177};
    188178
     
    198188
    199189        for (let c in g_EntityCommands)
    200190        {
    201             var info = g_EntityCommands[c].getInfo(unitEntState);
     191            let info = g_EntityCommands[c].getInfo(unitEntState);
    202192            if (!info)
    203193                continue;
    204194
     
    207197        }
    208198        return commands;
    209199    },
    210     "setTooltip": function(data)
     200    "setupButton": function(data)
    211201    {
     202        // Tooltip
    212203        data.button.tooltip = data.item.tooltip;
    213     },
    214     "setAction": function(data)
    215     {
     204
     205        // Action
    216206        data.button.onPress = function() {
    217207            if (data.item.callback)
    218208                data.item.callback(data.item);
     
    219209            else
    220210                performCommand(data.unitEntState.id, data.item.name);
    221211        };
    222     },
    223     "setCountDisplay": function(data)
    224     {
     212
     213        // Count
    225214        data.countDisplay.caption = data.item.count || "";
    226     },
    227     "setGraphics": function(data)
    228     {
     215
     216        // Graphics
    229217        data.button.enabled = controlsPlayer(data.unitEntState.player);
    230218        let grayscale = data.button.enabled ? "" : "grayscale:";
    231219        data.icon.sprite = "stretched:" + grayscale + "session/icons/" + data.item.icon;
    232     },
    233     "setPosition": function(data)
    234     {
    235         var size = data.button.size;
     220
     221        // Position
     222        let size = data.button.size;
    236223        // count on square buttons, so size.bottom is the width too
    237         var spacer = size.bottom + 1;
     224        let spacer = size.bottom + 1;
    238225        // relative to the center ( = 50%)
    239226        size.rleft = size.rright = 50;
    240227        // offset from the center calculation
     
    241228        size.left = (data.i - data.numberOfItems/2) * spacer;
    242229        size.right = size.left + size.bottom;
    243230        data.button.size = size;
     231        return true;
    244232    }
    245233};
    246234
     
    250238    {
    251239        return 2;
    252240    },
     241    "conflictsWith": ["Command"],
    253242    "getItems": function(unitEntState)
    254243    {
    255         var commands = [];
    256         for (var c in g_AllyEntityCommands)
     244        let commands = [];
     245        for (let c in g_AllyEntityCommands)
    257246        {
    258             var info = g_AllyEntityCommands[c].getInfo(unitEntState);
     247            let info = g_AllyEntityCommands[c].getInfo(unitEntState);
    259248            if (!info)
    260249                continue;
    261250            info.name = c;
     
    263252        }
    264253        return commands;
    265254    },
    266     "setTooltip": function(data)
     255    "setupButton": function(data)
    267256    {
     257        // Tooltip
    268258        data.button.tooltip = data.item.tooltip;
    269     },
    270     "setAction": function(data)
    271     {
     259
     260        // Action
    272261        data.button.onPress = function() {
    273262            if (data.item.callback)
    274263                data.item.callback(data.item);
     
    275264            else
    276265                performAllyCommand(data.unitEntState.id, data.item.name);
    277266        };
    278     },
    279     "conflictsWith": ["Command"],
    280     "setCountDisplay": function(data)
    281     {
     267
     268        // Count
    282269        data.countDisplay.caption = data.item.count || "";
    283     },
    284     "setGraphics": function(data)
    285     {
     270
     271        // Graphics
    286272        data.button.enabled = data.item.count != undefined && data.item.count > 0;
    287273        let grayscale = data.button.enabled ? "" : "grayscale:";
    288274        data.icon.sprite = "stretched:" + grayscale + "session/icons/" + data.item.icon;
    289     },
    290     "setPosition": function(data)
    291     {
    292         var size = data.button.size;
     275
     276        // Position
     277        let size = data.button.size;
    293278        // count on square buttons, so size.bottom is the width too
    294         var spacer = size.bottom + 1;
     279        let spacer = size.bottom + 1;
    295280        // relative to the center ( = 50%)
    296281        size.rleft = size.rright = 50;
    297282        // offset from the center calculation
     
    298283        size.left = (data.i - data.numberOfItems/2) * spacer;
    299284        size.right = size.left + size.bottom;
    300285        data.button.size = size;
     286
     287        // Position
     288        setPanelObjectPosition(data.button, data.i, data.rowLength);
     289        return true;
    301290    }
    302291};
    303292
     
    311300    {
    312301        return getAllBuildableEntitiesFromSelection();
    313302    },
    314     "addData": function(data)
     303    "setupButton": function(data)
    315304    {
    316         data.entType = data.item;
    317         data.template = GetTemplateData(data.entType);
    318         if (!data.template) // abort if no template
     305        let template = GetTemplateData(data.item);
     306        if (!template) // abort if no template
    319307            return false;
    320308
    321         data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
    322             "tech": data.template.requiredTechnology,
     309        let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
     310            "tech": template.requiredTechnology,
    323311            "player": data.unitEntState.player
    324312        });
    325313
    326         if (data.template.cost)
    327             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
    328                 "cost": multiplyEntityCosts(data.template, 1),
     314        let neededResources;
     315        if (template.cost)
     316            neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     317                "cost": multiplyEntityCosts(template, 1),
    329318                "player": data.unitEntState.player
    330319            });
    331320
    332         data.limits = getEntityLimitAndCount(data.playerState, data.entType);
     321        let limits = getEntityLimitAndCount(data.playerState, data.item);
    333322
    334         if (data.template.wallSet)
    335             data.template.auras = GetTemplateData(data.template.wallSet.templates.long).auras;
     323        if (template.wallSet)
     324            template.auras = GetTemplateData(template.wallSet.templates.long).auras;
    336325
    337         return true;
    338     },
    339     "setAction": function(data)
    340     {
     326        // Action
    341327        data.button.onPress = function () { startBuildingPlacement(data.item, data.playerState); };
    342     },
    343     "setTooltip": function(data)
    344     {
    345         var tooltip = getEntityNamesFormatted(data.template);
    346         tooltip += getVisibleEntityClassesFormatted(data.template);
    347         tooltip += getAurasTooltip(data.template);
    348328
    349         if (data.template.tooltip)
    350             tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
     329        // Tooltip
     330        let tooltip = getEntityNamesFormatted(template);
     331        tooltip += getVisibleEntityClassesFormatted(template);
     332        tooltip += getAurasTooltip(template);
    351333
    352         tooltip += "\n" + getEntityCostTooltip(data.template);
    353         tooltip += getPopulationBonusTooltip(data.template);
     334        if (template.tooltip)
     335            tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    354336
    355         tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
     337        tooltip += "\n" + getEntityCostTooltip(template);
     338        tooltip += getPopulationBonusTooltip(template);
    356339
    357         if (!data.technologyEnabled)
     340        tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
     341
     342        if (!technologyEnabled)
    358343            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), {
    359                 "technology": getEntityNames(GetTechnologyData(data.template.requiredTechnology))
     344                "technology": getEntityNames(GetTechnologyData(template.requiredTechnology))
    360345            });
    361346
    362         if (data.neededResources)
    363             tooltip += getNeededResourcesTooltip(data.neededResources);
     347        if (neededResources)
     348            tooltip += getNeededResourcesTooltip(neededResources);
    364349
    365350        data.button.tooltip = tooltip;
    366         return true;
    367     },
    368     "setGraphics": function(data)
    369     {
    370         var modifier = "";
    371         if (!data.technologyEnabled || data.limits.canBeAddedCount == 0)
     351
     352        // Graphics
     353        let modifier = "";
     354        if (!technologyEnabled || limits.canBeAddedCount == 0)
    372355        {
    373356            data.button.enabled = false;
    374357            modifier += "color: 0 0 0 127:";
    375358            modifier += "grayscale:";
    376359        }
    377         else if (data.neededResources)
     360        else if (neededResources)
    378361        {
    379362            data.button.enabled = false;
    380             modifier += resourcesToAlphaMask(data.neededResources) +":";
     363            modifier += resourcesToAlphaMask(neededResources) +":";
    381364        }
    382365        else
    383366            data.button.enabled = controlsPlayer(data.unitEntState.player);
    384367
    385         if (data.template.icon)
    386             data.icon.sprite = modifier + "stretched:session/portraits/" + data.template.icon;
    387     },
    388     "setPosition": function(data)
    389     {
    390         var index = data.i + getNumberOfRightPanelButtons();
     368        if (template.icon)
     369            data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     370
     371        // Position
     372        let index = data.i + getNumberOfRightPanelButtons();
    391373        setPanelObjectPosition(data.button, index, data.rowLength);
     374        return true;
    392375    }
    393376};
    394377
     
    408391            g_availableFormations.set(unitEntState.player, Engine.GuiInterfaceCall("GetAvailableFormations", unitEntState.player));
    409392        return g_availableFormations.get(unitEntState.player);
    410393    },
    411     "addData": function(data)
     394    "setupButton": function(data)
    412395    {
     396        // Get Data
    413397        if (!g_formationsInfo.has(data.item))
    414398            g_formationsInfo.set(data.item, Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", { "templateName": data.item }));
    415         data.formationInfo = g_formationsInfo.get(data.item);
    416         data.formationOk = canMoveSelectionIntoFormation(data.item);
    417         data.formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
     399
     400        let formationInfo = g_formationsInfo.get(data.item);
     401        let formationOk = canMoveSelectionIntoFormation(data.item);
     402        let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
    418403            "ents": data.selection,
    419404            "formationTemplate": data.item
    420405        });
    421         return true;
    422     },
    423     "setAction": function(data)
    424     {
     406
     407        // Action
    425408        data.button.onPress = function() { performFormation(data.unitEntState.id, data.item); };
    426     },
    427     "setTooltip": function(data)
    428     {
    429         var tooltip = translate(data.formationInfo.name);
    430         if (!data.formationOk && data.formationInfo.tooltip)
    431             tooltip += "\n" + "[color=\"red\"]" + translate(data.formationInfo.tooltip) + "[/color]";
     409
     410        // Tooltip
     411        let tooltip = translate(formationInfo.name);
     412        if (!formationOk && formationInfo.tooltip)
     413            tooltip += "\n" + "[color=\"red\"]" + translate(formationInfo.tooltip) + "[/color]";
    432414        data.button.tooltip = tooltip;
    433     },
    434     "setGraphics": function(data)
    435     {
    436         data.button.enabled = data.formationOk && controlsPlayer(data.unitEntState.player);
    437         var grayscale = data.formationOk ? "" : "grayscale:";
    438         data.guiSelection.hidden = !data.formationSelected;
    439         data.icon.sprite = "stretched:"+grayscale+"session/icons/"+data.formationInfo.icon;
     415
     416        // Graphics
     417        data.button.enabled = formationOk && controlsPlayer(data.unitEntState.player);
     418        let grayscale = formationOk ? "" : "grayscale:";
     419        data.guiSelection.hidden = !formationSelected;
     420        data.icon.sprite = "stretched:" + grayscale + "session/icons/" + formationInfo.icon;
     421
     422        // Position
     423        setPanelObjectPosition(data.button, data.i, data.rowLength);
     424        return true;
    440425    }
    441426};
    442427
     
    451436    {
    452437        if (!unitEntState.garrisonHolder)
    453438            return [];
    454         var groups = new EntityGroups();
    455         for (var ent of selection)
     439        let groups = new EntityGroups();
     440        for (let ent of selection)
    456441        {
    457             var state = GetEntityState(ent);
     442            let state = GetEntityState(ent);
    458443            if (state.garrisonHolder)
    459444                groups.add(state.garrisonHolder.entities);
    460445        }
    461446        return groups.getEntsGrouped();
    462447    },
    463     "addData": function(data)
     448    "setupButton": function(data)
    464449    {
    465         data.entType = data.item.template;
    466         data.template = GetTemplateData(data.entType);
    467         if (!data.template)
     450        // Get Data
     451        let template = GetTemplateData(data.item.template);
     452        if (!template)
    468453            return false;
    469         data.name = getEntityNames(data.template);
    470         data.count = data.item.ents.length;
    471         return true;
    472     },
    473     "setAction": function(data)
    474     {
     454
     455        // Action
    475456        data.button.onPress = function() { unloadTemplate(data.item.template); };
    476     },
    477     "setTooltip": function(data)
    478     {
    479         var tooltip = sprintf(translate("Unload %(name)s"), { "name": data.name }) + "\n";
     457
     458        // Tooltip
     459        let tooltip = sprintf(translate("Unload %(name)s"), { "name": getEntityNames(template) }) + "\n";
    480460        tooltip += translate("Single-click to unload 1. Shift-click to unload all of this type.");
    481461        data.button.tooltip = tooltip;
    482     },
    483     "setCountDisplay": function(data)
    484     {
    485         data.countDisplay.caption = data.count || "";
    486     },
    487     "setGraphics": function(data)
    488     {
    489         var grayscale = "";
    490         var ents = data.item.ents;
    491         var entplayer = GetEntityState(ents[0]).player;
     462
     463        // Count
     464        data.countDisplay.caption = data.item.ents.length || "";
     465
     466        // Graphics
     467        let grayscale = "";
     468        let ents = data.item.ents;
     469        let entplayer = GetEntityState(ents[0]).player;
    492470        data.button.sprite = "color:" + rgbToGuiColor(g_Players[entplayer].color) +":";
    493471
    494472        if (!controlsPlayer(data.unitEntState.player) && !controlsPlayer(entplayer))
     
    497475            grayscale = "grayscale:";
    498476        }
    499477
    500         data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + data.template.icon;
     478        data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
     479
     480        // Position
     481        setPanelObjectPosition(data.button, data.i, data.rowLength);
     482        return true;
    501483    }
    502484};
    503485
     
    510492    "getItems": function(unitEntState, selection)
    511493    {
    512494        // Allow long wall pieces to be converted to gates
    513         var longWallTypes = {};
    514         var walls = [];
    515         var gates = [];
    516         for (var ent of selection)
     495        let longWallTypes = {};
     496        let walls = [];
     497        let gates = [];
     498        for (let ent of selection)
    517499        {
    518             var state = GetEntityState(ent);
     500            let state = GetEntityState(ent);
    519501            if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
    520502            {
    521                 var gateTemplate = getWallGateTemplate(state.id);
     503                let gateTemplate = getWallGateTemplate(state.id);
    522504                if (gateTemplate)
    523505                {
    524                     var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
     506                    let tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
    525507                    if (!tooltipString)
    526508                    {
    527509                        warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
     
    554536            }
    555537            // Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
    556538            else if (state.gate && state.gate.locked != gates[0].gate.locked)
    557                 for (var j = 0; j < gates.length; ++j)
     539                for (let j = 0; j < gates.length; ++j)
    558540                    delete gates[j].gate.locked;
    559541        }
    560542
    561543        // Place wall conversion options after gate lock/unlock icons.
    562         var items = gates.concat(walls);
    563         return items;
     544        return gates.concat(walls);
    564545    },
    565     "setAction": function(data)
     546    "setupButton": function(data)
    566547    {
     548        // Action
    567549        data.button.onPress = function() {data.item.callback(data.item); };
    568     },
    569     "setTooltip": function(data)
    570     {
    571         var tooltip = data.item.tooltip;
     550
     551        // Tooltip
     552        let tooltip = data.item.tooltip;
    572553        if (data.item.template)
    573554        {
    574555            data.template = GetTemplateData(data.item.template);
    575556            data.wallCount = data.selection.reduce(function (count, ent) {
    576                     var state = GetEntityState(ent);
     557                    let state = GetEntityState(ent);
    577558                    if (hasClass(state, "LongWall") && !state.gate)
    578559                        ++count;
    579560                    return count;
     
    589570                tooltip += getNeededResourcesTooltip(data.neededResources);
    590571        }
    591572        data.button.tooltip = tooltip;
    592     },
    593     "setGraphics": function(data)
    594     {
     573
     574        // Graphics
    595575        data.button.enabled = controlsPlayer(data.unitEntState.player);
    596         var gateIcon;
     576        let gateIcon;
    597577        if (data.item.gate)
    598578        {
    599579            // If already a gate, show locking actions
     
    606586        else
    607587        {
    608588            // otherwise show gate upgrade icon
    609             var template = GetTemplateData(data.item.template);
     589            let template = GetTemplateData(data.item.template);
    610590            if (!template)
    611                 return;
     591                return false;
    612592            gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
    613593            data.guiSelection.hidden = true;
    614594        }
    615595
    616596        data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon;
    617     },
    618     "setPosition": function(data)
    619     {
    620         var index = data.i + getNumberOfRightPanelButtons();
     597
     598        // Position
     599        let index = data.i + getNumberOfRightPanelButtons();
    621600        setPanelObjectPosition(data.button, index, data.rowLength);
     601        return true;
    622602    }
    623603};
    624604
     
    630610    },
    631611    "getItems": function(unitEntState, selection)
    632612    {
    633         var checks = {};
    634         for (var ent of selection)
     613        let checks = {};
     614        for (let ent of selection)
    635615        {
    636             var state = GetEntityState(ent);
     616            let state = GetEntityState(ent);
    637617            if (!state.pack)
    638618                continue;
    639619            if (state.pack.progress == 0)
     
    652632                    checks.unpackCancelButton = true;
    653633            }
    654634        }
    655         var items = [];
     635        let items = [];
    656636        if (checks.packButton)
    657637            items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } });
    658638        if (checks.unpackButton)
     
    663643            items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } });
    664644        return items;
    665645    },
    666     "setAction": function(data)
     646    "setupButton": function(data)
    667647    {
     648        // Action
    668649        data.button.onPress = function() {data.item.callback(data.item); };
    669     },
    670     "setTooltip": function(data)
    671     {
     650
     651        // Tooltip
    672652        data.button.tooltip = data.item.tooltip;
    673     },
    674     "setGraphics": function(data)
    675     {
     653
     654        // Graphics
    676655        if (data.item.packing)
    677656            data.icon.sprite = "stretched:session/icons/cancel.png";
    678657        else if (data.item.packed)
     
    681660            data.icon.sprite = "stretched:session/icons/pack.png";
    682661
    683662        data.button.enabled = controlsPlayer(data.unitEntState.player);
    684     },
    685     "setPosition": function(data)
    686     {
    687         var index = data.i + getNumberOfRightPanelButtons();
     663
     664        // Position
     665        let index = data.i + getNumberOfRightPanelButtons();
    688666        setPanelObjectPosition(data.button, index, data.rowLength);
     667        return true;
    689668    }
    690669};
    691670
     
    701680    },
    702681    "resizePanel": function(numberOfItems, rowLength)
    703682    {
    704         var numRows = Math.ceil(numberOfItems / rowLength);
    705         var panel = Engine.GetGUIObjectByName("unitQueuePanel");
    706         var size = panel.size;
    707         var buttonSize = Engine.GetGUIObjectByName("unitQueueButton[0]").size.bottom;
    708         var margin = 4;
     683        let numRows = Math.ceil(numberOfItems / rowLength);
     684        let panel = Engine.GetGUIObjectByName("unitQueuePanel");
     685        let size = panel.size;
     686        let buttonSize = Engine.GetGUIObjectByName("unitQueueButton[0]").size.bottom;
     687        let margin = 4;
    709688        size.top = size.bottom - numRows*buttonSize - (numRows+2)*margin;
    710689        panel.size = size;
    711690    },
    712     "addData": function(data)
     691    "setupButton": function(data)
    713692    {
    714693        // differentiate between units and techs
     694        let template;
    715695        if (data.item.unitTemplate)
    716         {
    717             data.entType = data.item.unitTemplate;
    718             data.template = GetTemplateData(data.entType);
    719         }
     696            template = GetTemplateData(data.item.unitTemplate);
    720697        else if (data.item.technologyTemplate)
    721         {
    722             data.entType = data.item.technologyTemplate;
    723             data.template = GetTechnologyData(data.entType);
    724         }
    725         data.progress = Math.round(data.item.progress*100) + "%";
     698            template = GetTechnologyData(data.item.technologyTemplate);
    726699
    727         return data.template;
    728     },
    729     "setAction": function(data)
    730     {
     700        if (!template)
     701            return false;
     702
     703        // Action
    731704        data.button.onPress = function() { removeFromProductionQueue(data.item.producingEnt, data.item.id); };
    732     },
    733     "setTooltip": function(data)
    734     {
    735         var tooltip = getEntityNames(data.template);
     705
     706        // Tooltip
     707        let tooltip = getEntityNames(template);
    736708        if (data.item.neededSlots)
    737709        {
    738710            tooltip += "\n[color=\"red\"]" + translate("Insufficient population capacity:") + "\n[/color]";
     
    742714            });
    743715        }
    744716        data.button.tooltip = tooltip;
    745     },
    746     "setCountDisplay": function(data)
    747     {
     717
     718        // Count
    748719        data.countDisplay.caption = data.item.count > 1 ? data.item.count : "";
    749     },
    750     "setProgressDisplay": function(data)
    751     {
     720
     721        // Graphics
    752722        // show the progress number for the first item
    753723        if (data.i == 0)
    754             Engine.GetGUIObjectByName("queueProgress").caption = data.progress;
     724            Engine.GetGUIObjectByName("queueProgress").caption = Math.round(data.item.progress*100) + "%";
    755725
    756         var guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
    757         var size = guiObject.size;
     726        let guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
     727        let size = guiObject.size;
    758728
    759729        // Buttons are assumed to be square, so left/right offsets can be used for top/bottom.
    760730        size.top = size.left + Math.round(data.item.progress * (size.right - size.left));
    761731        guiObject.size = size;
    762     },
    763     "setGraphics": function(data)
    764     {
    765         if (data.template.icon)
    766             data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
    767732
     733        if (template.icon)
     734            data.icon.sprite = "stretched:session/portraits/" + template.icon;
     735
    768736        data.button.enabled = controlsPlayer(data.unitEntState.player);
     737
     738        // Position
     739        setPanelObjectPosition(data.button, data.i, data.rowLength);
     740        return true;
    769741    }
    770742};
    771743
     
    795767    },
    796768    "hideItem": function(i, rowLength) // called when no item is found
    797769    {
    798         Engine.GetGUIObjectByName("unitResearchButton["+i+"]").hidden = true;
     770        Engine.GetGUIObjectByName("unitResearchButton[" + i + "]").hidden = true;
    799771        // We also remove the paired tech and the pair symbol
    800         Engine.GetGUIObjectByName("unitResearchButton["+(i+rowLength)+"]").hidden = true;
    801         Engine.GetGUIObjectByName("unitResearchPair["+i+"]").hidden = true;
     772        Engine.GetGUIObjectByName("unitResearchButton[" + (i + rowLength) + "]").hidden = true;
     773        Engine.GetGUIObjectByName("unitResearchPair[" + i + "]").hidden = true;
    802774    },
    803     "addData": function(data)
     775    "setupButton": function(data)
    804776    {
     777        // Get Data
    805778        if (!data.item.tech)
    806779        {
    807780            g_SelectionPanels.Research.hideItem(data.i, data.rowLength);
    808781            return false;
    809782        }
    810         data.entType = data.item.tech.pair ? [data.item.tech.top, data.item.tech.bottom] : [data.item.tech];
    811         data.template = data.entType.map(GetTechnologyData);
    812         // abort if no template found for any of the techs
    813         if (data.template.some(v => !v))
    814             return false;
     783        let techs = data.item.tech.pair ? [data.item.tech.bottom, data.item.tech.top] : [data.item.tech];
    815784
    816         for (let template of data.template)
    817             for (let res in template.cost)
    818                 template.cost[res] *= data.item.techCostMultiplier[res];
     785        // start position (start at the bottom)
     786        let position = data.i + data.rowLength;
    819787
    820         // index one row below
    821         var shiftedIndex = data.i + data.rowLength;
    822         data.positions = data.item.tech.pair ? [data.i, shiftedIndex] : [shiftedIndex];
    823         data.positionsToHide = data.item.tech.pair ? [] : [data.i];
     788        // only show the top button for pairs
     789        if (!data.item.tech.pair)
     790            Engine.GetGUIObjectByName("unitResearchButton[" + data.i + "]").hidden = true;
    824791
    825         // add top buttons to the data
    826         data.button = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchButton["+p+"]"));
    827         data.buttonsToHide = data.positionsToHide.map(p => Engine.GetGUIObjectByName("unitResearchButton["+p+"]"));
     792        // set up the tech connector
     793        let pair = Engine.GetGUIObjectByName("unitResearchPair[" + data.i + "]");
     794        pair.hidden = data.item.tech.pair == null;
     795        setPanelObjectPosition(pair, data.i, data.rowLength);
    828796
    829         data.icon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchIcon["+p+"]"));
    830         data.unchosenIcon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchUnchosenIcon["+p+"]"));
     797        // handle one or two techs
     798        for (let i in techs)
     799        {
     800            let tech = techs[i];
     801            let template = GetTechnologyData(tech);
     802            if (!template)
     803                return false;
    831804
    832         data.neededResources = data.template.map(t => Engine.GuiInterfaceCall("GetNeededResources", {
    833             "cost": t.cost,
    834             "player": data.unitEntState.player
    835         }));
     805            for (let res in template.cost)
     806                template.cost[res] *= data.item.techCostMultiplier[res];
    836807
    837         data.requirementsPassed = data.entType.map(e => Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
    838             "tech": e,
    839             "player": data.unitEntState.player
    840         }));
     808            let neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     809                "cost": template.cost,
     810                "player": data.unitEntState.player
     811            });
    841812
    842         data.pair = Engine.GetGUIObjectByName("unitResearchPair["+data.i+"]");
     813            let requirementsPassed = Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
     814                "tech": tech,
     815                "player": data.unitEntState.player
     816            });
    843817
    844         return true;
    845     },
    846     "setTooltip": function(data)
    847     {
    848         for (var i in data.entType)
    849         {
    850             var tooltip = "";
    851             var template = data.template[i];
     818            let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]");
     819            let icon = Engine.GetGUIObjectByName("unitResearchIcon[" + position + "]");
     820
     821            // Tooltip
     822            let tooltip = "";
    852823            tooltip = getEntityNamesFormatted(template);
    853824            if (template.tooltip)
    854825                tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    855826
    856827            tooltip += "\n" + getEntityCostTooltip(template);
    857             if (!data.requirementsPassed[i])
     828            if (!requirementsPassed)
    858829            {
    859830                tooltip += "\n" + template.requirementsTooltip;
    860831                if (template.classRequirements)
    861832                {
    862                     var player = data.unitEntState.player;
    863                     var current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
    864                     var remaining = template.classRequirements.number - current;
     833                    let player = data.unitEntState.player;
     834                    let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
     835                    let remaining = template.classRequirements.number - current;
    865836                    tooltip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { "number": remaining });
    866837                }
    867838            }
    868             if (data.neededResources[i])
    869                 tooltip += getNeededResourcesTooltip(data.neededResources[i]);
    870             data.button[i].tooltip = tooltip;
    871         }
    872     },
    873     "setAction": function(data)
    874     {
    875         for (var i in data.entType)
    876         {
    877             // array containing the indices other buttons
    878             var others = Object.keys(data.template);
    879             others.splice(i, 1);
    880             var button = data.button[i];
    881             // as we're in a loop, we need to limit the scope with a closure
    882             // else the last value of the loop will be taken, rather than the current one
    883             button.onpress = (function(template) { return function () { addResearchToQueue(data.unitEntState.id, template); }; })(data.entType[i]);
    884             // on mouse enter, show a cross over the other icons
    885             button.onmouseenter = (function(others, icons) {
    886                 return function() {
    887                     for (var j of others)
    888                         icons[j].hidden = false;
     839            if (neededResources)
     840                tooltip += getNeededResourcesTooltip(neededResources);
     841            button.tooltip = tooltip;
     842
     843            // Action
     844            button.onpress = function () {
     845                addResearchToQueue(data.unitEntState.id, tech);
     846            };
     847
     848            if (data.item.tech.pair)
     849            {
     850                // on mouse enter, show a cross over the other icon
     851                let otherPosition = (position + data.rowLength) % (2 * data.rowLength);
     852                let unchosenIcon = Engine.GetGUIObjectByName("unitResearchUnchosenIcon[" + otherPosition + "]");
     853                button.onmouseenter = function() {
     854                        unchosenIcon.hidden = false;
    889855                };
    890             })(others, data.unchosenIcon);
    891             button.onmouseleave = (function(others, icons) {
    892                 return function() {
    893                     for (var j of others)
    894                         icons[j].hidden = true;
     856                button.onmouseleave = function() {
     857                        unchosenIcon.hidden = true;
    895858                };
    896             })(others, data.unchosenIcon);
    897         }
    898     },
    899     "setGraphics": function(data)
    900     {
    901         for (var i in data.entType)
    902         {
    903             let button = data.button[i];
     859            }
     860
     861            // Graphics
    904862            button.hidden = false;
    905             var modifier = "";
    906             if (!data.requirementsPassed[i])
     863            let modifier = "";
     864            if (!requirementsPassed)
    907865            {
    908866                button.enabled = false;
    909867                modifier += "color: 0 0 0 127:";
    910868                modifier += "grayscale:";
    911869            }
    912             else if (data.neededResources[i])
     870            else if (neededResources)
    913871            {
    914872                button.enabled = false;
    915                 modifier += resourcesToAlphaMask(data.neededResources[i]) + ":";
     873                modifier += resourcesToAlphaMask(neededResources) + ":";
    916874            }
    917875            else
    918876                button.enabled = controlsPlayer(data.unitEntState.player);
    919877
    920             if (data.template[i].icon)
    921                 data.icon[i].sprite = modifier + "stretched:session/portraits/" + data.template[i].icon;
     878            if (template.icon)
     879                icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     880
     881            // Position
     882            setPanelObjectPosition(button, position, data.rowLength);
     883
     884            // prepare to handle the top button (if any)
     885            position -= data.rowLength;
    922886        }
    923         for (let button of data.buttonsToHide)
    924             button.hidden = true;
    925         // show the tech connector
    926         data.pair.hidden = data.item.tech.pair == null;
    927     },
    928     "setPosition": function(data)
    929     {
    930         for (var i in data.button)
    931             setPanelObjectPosition(data.button[i], data.positions[i], data.rowLength);
    932         setPanelObjectPosition(data.pair, data.i, data.rowLength);
     887
     888
     889        return true;
    933890    }
    934891};
    935892
     
    946903            return [];
    947904        return g_Selection.groups.getTemplateNames();
    948905    },
    949     "addData": function(data)
     906    "setupButton": function(data)
    950907    {
    951         data.entType = data.item;
    952         data.template = GetTemplateData(data.entType);
    953         if (!data.template)
     908        // Get Data
     909        let template = GetTemplateData(data.item);
     910        if (!template)
    954911            return false;
    955         data.name = getEntityNames(data.template);
    956912
    957         var ents = g_Selection.groups.getEntsByName(data.item);
    958         data.count = ents.length;
    959         for (var ent of ents)
     913        let ents = g_Selection.groups.getEntsByName(data.item);
     914        for (let ent of ents)
    960915        {
    961             var state = GetEntityState(ent);
     916            let state = GetEntityState(ent);
    962917
    963918            if (state.resourceCarrying && state.resourceCarrying.length !== 0)
    964919            {
    965920                if (!data.carried)
    966921                    data.carried = {};
    967                 var carrying = state.resourceCarrying[0];
     922                let carrying = state.resourceCarrying[0];
    968923                if (data.carried[carrying.type])
    969924                    data.carried[carrying.type] += carrying.amount;
    970925                else
     
    975930            {
    976931                if (!data.carried)
    977932                    data.carried = {};
    978                 var amount = state.trader.goods.amount;
    979                 var type = state.trader.goods.type;
    980                 var totalGain = amount.traderGain;
     933                let amount = state.trader.goods.amount;
     934                let type = state.trader.goods.type;
     935                let totalGain = amount.traderGain;
    981936                if (amount.market1Gain)
    982937                    totalGain += amount.market1Gain;
    983938                if (amount.market2Gain)
     
    988943                    data.carried[type] = totalGain;
    989944            }
    990945        }
    991         return true;
    992     },
    993     "setTooltip": function(data)
    994     {
    995         let tooltip = data.name;
     946
     947        // Tooltip
     948        let tooltip = getEntityNames(template);
    996949        if (data.carried)
    997950            tooltip += "\n" + Object.keys(data.carried).map(res =>
    998951                getCostComponentDisplayIcon(res) + data.carried[res]
    999952            ).join(" ");
    1000953        data.button.tooltip = tooltip;
    1001     },
    1002     "setCountDisplay": function(data)
    1003     {
    1004         data.countDisplay.caption = data.count || "";
    1005     },
    1006     "setAction": function(data)
    1007     {
     954
     955        // Count
     956        data.countDisplay.caption = ents.length || "";
     957
     958        // Action
    1008959        data.button.onpressright = function() { changePrimarySelectionGroup(data.item, true); };
    1009960        data.button.onpress = function() { changePrimarySelectionGroup(data.item, false); };
    1010     },
    1011     "setGraphics": function(data)
    1012     {
    1013         if (data.template.icon)
    1014             data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
     961
     962        // Graphics
     963        if (template.icon)
     964            data.icon.sprite = "stretched:session/portraits/" + template.icon;
     965
     966        // Position
     967        setPanelObjectPosition(data.button, data.i, data.rowLength);
     968        return true;
    1015969    }
    1016970};
    1017971
     
    1027981            return [];
    1028982        return unitEntState.unitAI.possibleStances;
    1029983    },
    1030     "addData": function(data)
     984    "setupButton": function(data)
    1031985    {
    1032         data.stanceSelected = Engine.GuiInterfaceCall("IsStanceSelected", {
     986        // Action
     987        data.button.onPress = function() { performStance(data.unitEntState, data.item); };
     988
     989        // Tooltip
     990        data.button.tooltip = getStanceDisplayName(data.item) + "\n[font=\"sans-13\"]" + getStanceTooltip(data.item) + "[/font]";
     991
     992        // Graphics
     993        data.guiSelection.hidden = !Engine.GuiInterfaceCall("IsStanceSelected", {
    1033994            "ents": data.selection,
    1034995            "stance": data.item
    1035996        });
     997        data.icon.sprite = "stretched:session/icons/stances/" + data.item + ".png";
     998        data.button.enabled = controlsPlayer(data.unitEntState.player);
     999
     1000        // Position
     1001        setPanelObjectPosition(data.button, data.i, data.rowLength);
    10361002        return true;
    1037     },
    1038     "setAction": function(data)
    1039     {
    1040         data.button.onPress = function() { performStance(data.unitEntState, data.item); };
    1041     },
    1042     "setTooltip": function(data)
    1043     {
    1044         data.button.tooltip = getStanceDisplayName(data.item) + "\n[font=\"sans-13\"]" + getStanceTooltip(data.item) + "[/font]";
    1045     },
    1046     "setGraphics": function(data)
    1047     {
    1048         data.guiSelection.hidden = !data.stanceSelected;
    1049         data.icon.sprite = "stretched:session/icons/stances/"+data.item+".png";
    1050         data.button.enabled = controlsPlayer(data.unitEntState.player);
    10511003    }
    10521004};
    10531005
     
    10611013    {
    10621014        return getAllTrainableEntitiesFromSelection();
    10631015    },
    1064     "addData": function(data)
     1016    "setupButton": function(data)
    10651017    {
    1066         data.entType = data.item;
    1067         data.template = GetTemplateData(data.entType);
    1068         if (!data.template)
     1018        // Get Data
     1019        let template = GetTemplateData(data.item);
     1020        if (!template)
    10691021            return false;
    10701022
    1071         data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
    1072             "tech": data.template.requiredTechnology,
     1023        let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
     1024            "tech": template.requiredTechnology,
    10731025            "player": data.unitEntState.player
    10741026        });
    10751027
    1076         var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
    1077             getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.entType, data.selection);
     1028        let [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
     1029            getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.item, data.selection);
    10781030
    1079         data.buildingsCountToTrainFullBatch = buildingsCountToTrainFullBatch;
    1080         data.fullBatchSize = fullBatchSize;
    1081         data.remainderBatch = remainderBatch;
    1082 
    1083         data.trainNum = buildingsCountToTrainFullBatch || 1; // train at least one unit
     1031        let trainNum = buildingsCountToTrainFullBatch || 1; // train at least one unit
    10841032        if (Engine.HotkeyIsPressed("session.batchtrain"))
    1085             data.trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
     1033            trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
    10861034
    1087         if (data.template.cost)
    1088             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
    1089                 "cost": multiplyEntityCosts(data.template, data.trainNum),
     1035        let neededResources;
     1036        if (template.cost)
     1037            neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     1038                "cost": multiplyEntityCosts(template, trainNum),
    10901039                "player": data.unitEntState.player
    10911040            });
    10921041
    1093         return true;
    1094     },
    1095     "setAction": function(data)
    1096     {
     1042        // Action
    10971043        data.button.onPress = function() { addTrainingToQueue(data.selection, data.item, data.playerState); };
    1098     },
    1099     "setCountDisplay": function(data)
    1100     {
    1101         data.countDisplay.caption = data.trainNum > 1 ? data.trainNum : "";
    1102     },
    1103     "setTooltip": function(data)
    1104     {
    1105         var tooltip = "";
    1106         var key = Engine.ConfigDB_GetValue("user", "hotkey.session.queueunit." + (data.i + 1));
     1044
     1045        // Count
     1046        data.countDisplay.caption = trainNum > 1 ? trainNum : "";
     1047
     1048        // Tooltip
     1049        let tooltip = "";
     1050        let key = Engine.ConfigDB_GetValue("user", "hotkey.session.queueunit." + (data.i + 1));
    11071051        if (key)
    11081052            tooltip += "[color=\"255 251 131\"][font=\"sans-bold-16\"]\\[" + key + "][/font][/color] ";
    11091053
    1110         tooltip += getEntityNamesFormatted(data.template);
    1111         tooltip += getVisibleEntityClassesFormatted(data.template);
    1112         tooltip += getAurasTooltip(data.template);
     1054        tooltip += getEntityNamesFormatted(template);
     1055        tooltip += getVisibleEntityClassesFormatted(template);
     1056        tooltip += getAurasTooltip(template);
    11131057
    1114         if (data.template.tooltip)
    1115             tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
     1058        if (template.tooltip)
     1059            tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    11161060
    1117         tooltip += "\n" + getEntityCostTooltip(data.template, data.trainNum, data.unitEntState.id);
     1061        tooltip += "\n" + getEntityCostTooltip(template, trainNum, data.unitEntState.id);
    11181062
    1119         data.limits = getEntityLimitAndCount(data.playerState, data.entType);
     1063        let limits = getEntityLimitAndCount(data.playerState, data.item);
    11201064
    1121         tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
     1065        tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
    11221066        if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") === "true")
    11231067        {
    1124             if (data.template.health)
    1125                 tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + data.template.health;
    1126             if (data.template.attack)
    1127                 tooltip += "\n" + getAttackTooltip(data.template);
    1128             if (data.template.armour)
    1129                 tooltip += "\n" + getArmorTooltip(data.template.armour);
    1130             if (data.template.speed)
    1131                 tooltip += "\n" + getSpeedTooltip(data.template);
     1068            if (template.health)
     1069                tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + template.health;
     1070            if (template.attack)
     1071                tooltip += "\n" + getAttackTooltip(template);
     1072            if (template.armour)
     1073                tooltip += "\n" + getArmorTooltip(template.armour);
     1074            if (template.speed)
     1075                tooltip += "\n" + getSpeedTooltip(template);
    11321076        }
    1133         tooltip += "[color=\"255 251 131\"]" + formatBatchTrainingString(data.buildingsCountToTrainFullBatch, data.fullBatchSize, data.remainderBatch) + "[/color]";
    1134         if (!data.technologyEnabled)
     1077        tooltip += "[color=\"255 251 131\"]" + formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) + "[/color]";
     1078        if (!technologyEnabled)
    11351079        {
    1136             var techName = getEntityNames(GetTechnologyData(data.template.requiredTechnology));
     1080            let techName = getEntityNames(GetTechnologyData(template.requiredTechnology));
    11371081            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { "technology": techName });
    11381082        }
    1139         if (data.neededResources)
    1140             tooltip += getNeededResourcesTooltip(data.neededResources);
     1083        if (neededResources)
     1084            tooltip += getNeededResourcesTooltip(neededResources);
    11411085
    11421086        data.button.tooltip = tooltip;
    1143     },
    1144     // disable and enable buttons in the same way as when you do for the construction
    1145     "setGraphics": g_SelectionPanels.Construction.setGraphics,
    1146     "setPosition": function(data)
    1147     {
    1148         var index = data.i + getNumberOfRightPanelButtons();
     1087
     1088        // Graphics
     1089        let modifier = "";
     1090        if (!technologyEnabled || limits.canBeAddedCount == 0)
     1091        {
     1092            data.button.enabled = false;
     1093            modifier += "color: 0 0 0 127:";
     1094            modifier += "grayscale:";
     1095        }
     1096        else if (neededResources)
     1097        {
     1098            data.button.enabled = false;
     1099            modifier += resourcesToAlphaMask(neededResources) +":";
     1100        }
     1101        else
     1102            data.button.enabled = controlsPlayer(data.unitEntState.player);
     1103
     1104        if (template.icon)
     1105            data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     1106
     1107        // Position
     1108        let index = data.i + getNumberOfRightPanelButtons();
    11491109        setPanelObjectPosition(data.button, index, data.rowLength);
     1110        return true;
    11501111    }
    11511112};
    11521113
     
    11581119 *
    11591120 * Note that the panel needs to appear in the list to get rendered.
    11601121 */
    1161 var g_PanelsOrder = [
     1122let g_PanelsOrder = [
    11621123    // LEFT PANE
    11631124    "Barter", // must always be visible on markets
    11641125    "Garrison", // more important than Formation, as you want to see the garrisoned units in ships
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    6161    // Make buttons
    6262    for (let i = 0; i < numberOfItems; ++i)
    6363    {
    64         var item = items[i];
    65 
    6664        // STANDARD DATA
    6765        // add standard data
    68         var data = {
     66        let data = {
    6967            "i": i,
    70             "item": item,
     68            "item": items[i],
    7169            "selection": selection,
    7270            "playerState": playerState,
    7371            "unitEntState": unitEntState,
     
    9290            data.button.caption = "";
    9391        }
    9492
    95         // GENERAL DATA
    96         // add general data, and a chance to abort on faulty data
    97         if (g_SelectionPanels[guiName].addData)
    98         {
    99             var success = g_SelectionPanels[guiName].addData(data);
    100             if (!success)
    101                 continue; // ignore faulty data
    102         }
    103 
    10493        // SET CONTENT
    105         // run all content setters
    106         for (var f in g_SelectionPanels[guiName])
    107         {
    108             if (f.match(/^set/))
    109                 g_SelectionPanels[guiName][f](data);
    110         }
     94        if (g_SelectionPanels[guiName].setupButton)
     95            if (!g_SelectionPanels[guiName].setupButton(data))
     96                continue;
    11197
    112         // Special case: position
    113         if (!g_SelectionPanels[guiName].setPosition)
    114             setPanelObjectPosition(data.button, i, rowLength);
    115 
    11698        // TODO: we should require all entities to have icons, so this case never occurs
    11799        if (data.icon && !data.icon.sprite)
    118100            data.icon.sprite = "bkFillBlack";