Ticket #4007: selection_panels_cleanup.3.diff

File selection_panels_cleanup.3.diff, 48.9 KB (added by sanderd17, 8 years ago)

Update to current SVN, revert all changes done to colour tags, implement some more remarks

  • 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
     
    2121 *   "icon":           gui Icon object
    2222 *   "guiSelection":   gui button Selection overlay
    2323 *   "countDisplay":   gui caption space
    24  * };
     24 * }
    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
    39 /* cache some formation info */
    40 var g_availableFormations = new Map();   // available formations per player
    41 var g_formationsInfo = new Map();
     30// Cache some formation info
     31// Available formations per player
     32let g_AvailableFormations = new Map();
     33let g_FormationsInfo = new Map();
    4234
    43 var g_SelectionPanels = {};
     35let g_SelectionPanels = {};
    4436
    4537// ALERT
    4638g_SelectionPanels.Alert = {
     
    5446            return [];
    5547        return ["increase", "end"];
    5648    },
    57     "setAction": function(data)
     49    "setupButton": function(data)
    5850    {
    5951        data.button.onPress = function() {
    6052            if (data.item == "increase")
     
    6254            else if (data.item == "end")
    6355                endOfAlert();
    6456        };
    65     },
    66     "setTooltip": function(data)
    67     {
     57
    6858        if (data.item == "increase")
    6959        {
    7060            if (data.unitEntState.alertRaiser.hasRaisedAlert)
     
    7464        }
    7565        else if (data.item == "end")
    7666            data.button.tooltip = translate("End of alert.");
    77     },
    78     "setGraphics": function(data)
    79     {
     67
    8068        if (data.item == "increase")
    8169        {
    8270            data.button.hidden = !data.unitEntState.alertRaiser.canIncreaseLevel;
     
    9179            data.icon.sprite = "stretched:session/icons/bell_level0.png";
    9280        }
    9381        data.button.enabled = !data.button.hidden && controlsPlayer(data.unitEntState.player);
     82
     83        setPanelObjectPosition(data.button, data.i, data.rowLength);
     84        return true;
    9485    }
    9586};
    9687
     
    10899        // ["food", "wood", "stone", "metal"]
    109100        return BARTER_RESOURCES;
    110101    },
    111     "addData": function(data)
     102    "setupButton": function(data)
    112103    {
    113104        // 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)
     105        let button = {};
     106        let icon = {};
     107        let amount = {};
     108        for (let a of BARTER_ACTIONS)
    118109        {
    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+"]");
     110            button[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Button[" + data.i + "]");
     111            icon[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Icon[" + data.i + "]");
     112            amount[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Amount[" + data.i + "]");
    122113        }
    123         data.selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection["+data.i+"]");
     114        let selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection[" + data.i + "]");
    124115
    125         data.amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
     116        let amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
    126117        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 = {
     118            amountToSell *= BARTER_BUNCH_MULTIPLIER;
     119
     120        amount.Sell.caption = "-" + amountToSell;
     121        let sellPrice = data.unitEntState.barterMarket.prices.sell[g_barterSell];
     122        let buyPrice = data.unitEntState.barterMarket.prices.buy[data.item];
     123        amount.Buy.caption = "+" + Math.round(sellPrice / buyPrice * amountToSell);
     124
     125        let resource = getLocalizedResourceName(data.item, "withinSentence");
     126        button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource });
     127        button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource });
     128
     129        button.Sell.onPress = function() { g_barterSell = data.item; };
     130        let exchangeResourcesParameters = {
    148131            "sell": g_barterSell,
    149132            "buy": data.item,
    150             "amount": data.amountToSell
     133            "amount": amountToSell
    151134        };
    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:" : "";
     135        button.Buy.onPress = function() { exchangeResources(exchangeResourcesParameters); };
    157136
     137        let isSelected = data.item == g_barterSell;
     138        let grayscale = isSelected ? "color: 0 0 0 100:grayscale:" : "";
     139
    158140        // do we have enough of this resource to sell?
    159         var neededRes = {};
    160         neededRes[data.item] = data.amountToSell;
    161         var canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
     141        let neededRes = {};
     142        neededRes[data.item] = amountToSell;
     143        let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
    162144            "cost": neededRes,
    163145            "player": data.unitEntState.player
    164146        }) ? "color:255 0 0 80:" : "";
     
    165147
    166148        // Let's see if we have enough resources to barter.
    167149        neededRes = {};
    168         neededRes[g_barterSell] = data.amountToSell;
    169         var canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
     150        neededRes[g_barterSell] = amountToSell;
     151        let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
    170152            "cost": neededRes,
    171153            "player": data.unitEntState.player
    172154        }) ? "color:255 0 0 80:" : "";
    173155
    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";
     156        icon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
     157        icon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
    176158
    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);
     159        button.Buy.hidden = isSelected;
     160        button.Buy.enabled = controlsPlayer(data.unitEntState.player);
     161        button.Sell.hidden = false;
     162        selectionIcon.hidden = !isSelected;
     163
     164        setPanelObjectPosition(button.Sell, data.i, data.rowLength);
     165        setPanelObjectPosition(button.Buy, data.i + data.rowLength, data.rowLength);
     166        return true;
    186167    }
    187168};
    188169
     
    198179
    199180        for (let c in g_EntityCommands)
    200181        {
    201             var info = g_EntityCommands[c].getInfo(unitEntState);
     182            let info = g_EntityCommands[c].getInfo(unitEntState);
    202183            if (!info)
    203184                continue;
    204185
     
    207188        }
    208189        return commands;
    209190    },
    210     "setTooltip": function(data)
     191    "setupButton": function(data)
    211192    {
    212193        data.button.tooltip = data.item.tooltip;
    213     },
    214     "setAction": function(data)
    215     {
     194
    216195        data.button.onPress = function() {
    217196            if (data.item.callback)
    218197                data.item.callback(data.item);
     
    219198            else
    220199                performCommand(data.unitEntState.id, data.item.name);
    221200        };
    222     },
    223     "setCountDisplay": function(data)
    224     {
     201
    225202        data.countDisplay.caption = data.item.count || "";
    226     },
    227     "setGraphics": function(data)
    228     {
     203
    229204        data.button.enabled = controlsPlayer(data.unitEntState.player);
    230205        let grayscale = data.button.enabled ? "" : "grayscale:";
    231206        data.icon.sprite = "stretched:" + grayscale + "session/icons/" + data.item.icon;
    232     },
    233     "setPosition": function(data)
    234     {
    235         var size = data.button.size;
     207
     208        let size = data.button.size;
    236209        // count on square buttons, so size.bottom is the width too
    237         var spacer = size.bottom + 1;
     210        let spacer = size.bottom + 1;
    238211        // relative to the center ( = 50%)
    239212        size.rleft = size.rright = 50;
    240213        // offset from the center calculation
     
    241214        size.left = (data.i - data.numberOfItems/2) * spacer;
    242215        size.right = size.left + size.bottom;
    243216        data.button.size = size;
     217        return true;
    244218    }
    245219};
    246220
     
    250224    {
    251225        return 2;
    252226    },
     227    "conflictsWith": ["Command"],
    253228    "getItems": function(unitEntState)
    254229    {
    255         var commands = [];
    256         for (var c in g_AllyEntityCommands)
     230        let commands = [];
     231        for (let c in g_AllyEntityCommands)
    257232        {
    258             var info = g_AllyEntityCommands[c].getInfo(unitEntState);
     233            let info = g_AllyEntityCommands[c].getInfo(unitEntState);
    259234            if (!info)
    260235                continue;
    261236            info.name = c;
     
    263238        }
    264239        return commands;
    265240    },
    266     "setTooltip": function(data)
     241    "setupButton": function(data)
    267242    {
    268243        data.button.tooltip = data.item.tooltip;
    269     },
    270     "setAction": function(data)
    271     {
     244
    272245        data.button.onPress = function() {
    273246            if (data.item.callback)
    274247                data.item.callback(data.item);
     
    275248            else
    276249                performAllyCommand(data.unitEntState.id, data.item.name);
    277250        };
    278     },
    279     "conflictsWith": ["Command"],
    280     "setCountDisplay": function(data)
    281     {
     251
    282252        data.countDisplay.caption = data.item.count || "";
    283     },
    284     "setGraphics": function(data)
    285     {
     253
    286254        data.button.enabled = data.item.count != undefined && data.item.count > 0;
    287255        let grayscale = data.button.enabled ? "" : "grayscale:";
    288256        data.icon.sprite = "stretched:" + grayscale + "session/icons/" + data.item.icon;
    289     },
    290     "setPosition": function(data)
    291     {
    292         var size = data.button.size;
     257
     258        let size = data.button.size;
    293259        // count on square buttons, so size.bottom is the width too
    294         var spacer = size.bottom + 1;
     260        let spacer = size.bottom + 1;
    295261        // relative to the center ( = 50%)
    296262        size.rleft = size.rright = 50;
    297263        // offset from the center calculation
     
    298264        size.left = (data.i - data.numberOfItems/2) * spacer;
    299265        size.right = size.left + size.bottom;
    300266        data.button.size = size;
     267
     268        setPanelObjectPosition(data.button, data.i, data.rowLength);
     269        return true;
    301270    }
    302271};
    303272
     
    311280    {
    312281        return getAllBuildableEntitiesFromSelection();
    313282    },
    314     "addData": function(data)
     283    "setupButton": function(data)
    315284    {
    316         data.entType = data.item;
    317         data.template = GetTemplateData(data.entType);
    318         if (!data.template) // abort if no template
     285        let template = GetTemplateData(data.item);
     286        if (!template)
    319287            return false;
    320288
    321         data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
    322             "tech": data.template.requiredTechnology,
     289        let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
     290            "tech": template.requiredTechnology,
    323291            "player": data.unitEntState.player
    324292        });
    325293
    326         if (data.template.cost)
    327             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
    328                 "cost": multiplyEntityCosts(data.template, 1),
     294        let neededResources;
     295        if (template.cost)
     296            neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     297                "cost": multiplyEntityCosts(template, 1),
    329298                "player": data.unitEntState.player
    330299            });
    331300
    332         data.limits = getEntityLimitAndCount(data.playerState, data.entType);
     301        let limits = getEntityLimitAndCount(data.playerState, data.item);
    333302
    334         if (data.template.wallSet)
    335             data.template.auras = GetTemplateData(data.template.wallSet.templates.long).auras;
     303        if (template.wallSet)
     304            template.auras = GetTemplateData(template.wallSet.templates.long).auras;
    336305
    337         return true;
    338     },
    339     "setAction": function(data)
    340     {
    341306        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);
    348307
    349         if (data.template.tooltip)
    350             tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
     308        let tooltip = getEntityNamesFormatted(template);
     309        tooltip += getVisibleEntityClassesFormatted(template);
     310        tooltip += getAurasTooltip(template);
    351311
    352         tooltip += "\n" + getEntityCostTooltip(data.template);
    353         tooltip += getPopulationBonusTooltip(data.template);
     312        if (template.tooltip)
     313            tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    354314
    355         tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
     315        tooltip += "\n" + getEntityCostTooltip(template);
     316        tooltip += getPopulationBonusTooltip(template);
    356317
    357         if (!data.technologyEnabled)
     318        tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
     319
     320        if (!technologyEnabled)
    358321            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), {
    359                 "technology": getEntityNames(GetTechnologyData(data.template.requiredTechnology))
     322                "technology": getEntityNames(GetTechnologyData(template.requiredTechnology))
    360323            });
    361324
    362         if (data.neededResources)
    363             tooltip += getNeededResourcesTooltip(data.neededResources);
     325        if (neededResources)
     326            tooltip += getNeededResourcesTooltip(neededResources);
    364327
    365328        data.button.tooltip = tooltip;
    366         return true;
    367     },
    368     "setGraphics": function(data)
    369     {
    370         var modifier = "";
    371         if (!data.technologyEnabled || data.limits.canBeAddedCount == 0)
     329
     330        let modifier = "";
     331        if (!technologyEnabled || limits.canBeAddedCount == 0)
    372332        {
    373333            data.button.enabled = false;
    374             modifier += "color: 0 0 0 127:";
    375             modifier += "grayscale:";
     334            modifier += "color:0 0 0 127:grayscale:";
    376335        }
    377         else if (data.neededResources)
     336        else if (neededResources)
    378337        {
    379338            data.button.enabled = false;
    380             modifier += resourcesToAlphaMask(data.neededResources) +":";
     339            modifier += resourcesToAlphaMask(neededResources) +":";
    381340        }
    382341        else
    383342            data.button.enabled = controlsPlayer(data.unitEntState.player);
    384343
    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();
     344        if (template.icon)
     345            data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     346
     347        let index = data.i + getNumberOfRightPanelButtons();
    391348        setPanelObjectPosition(data.button, index, data.rowLength);
     349        return true;
    392350    }
    393351};
    394352
     
    404362    {
    405363        if (!hasClass(unitEntState, "Unit") || hasClass(unitEntState, "Animal"))
    406364            return [];
    407         if (!g_availableFormations.has(unitEntState.player))
    408             g_availableFormations.set(unitEntState.player, Engine.GuiInterfaceCall("GetAvailableFormations", unitEntState.player));
    409         return g_availableFormations.get(unitEntState.player);
     365        if (!g_AvailableFormations.has(unitEntState.player))
     366            g_AvailableFormations.set(unitEntState.player, Engine.GuiInterfaceCall("GetAvailableFormations", unitEntState.player));
     367        return g_AvailableFormations.get(unitEntState.player);
    410368    },
    411     "addData": function(data)
     369    "setupButton": function(data)
    412370    {
    413         if (!g_formationsInfo.has(data.item))
    414             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", {
     371        if (!g_FormationsInfo.has(data.item))
     372            g_FormationsInfo.set(data.item, Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", { "templateName": data.item }));
     373
     374        let formationInfo = g_FormationsInfo.get(data.item);
     375        let formationOk = canMoveSelectionIntoFormation(data.item);
     376        let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
    418377            "ents": data.selection,
    419378            "formationTemplate": data.item
    420379        });
    421         return true;
    422     },
    423     "setAction": function(data)
    424     {
     380
    425381        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]";
     382
     383        let tooltip = translate(formationInfo.name);
     384        if (!formationOk && formationInfo.tooltip)
     385            tooltip += "\n" + "[color=\"red\"]" + translate(formationInfo.tooltip) + "[/color]";
    432386        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;
     387
     388        data.button.enabled = formationOk && controlsPlayer(data.unitEntState.player);
     389        let grayscale = formationOk ? "" : "grayscale:";
     390        data.guiSelection.hidden = !formationSelected;
     391        data.icon.sprite = "stretched:" + grayscale + "session/icons/" + formationInfo.icon;
     392
     393        setPanelObjectPosition(data.button, data.i, data.rowLength);
     394        return true;
    440395    }
    441396};
    442397
     
    451406    {
    452407        if (!unitEntState.garrisonHolder)
    453408            return [];
    454         var groups = new EntityGroups();
    455         for (var ent of selection)
     409        let groups = new EntityGroups();
     410        for (let ent of selection)
    456411        {
    457             var state = GetEntityState(ent);
     412            let state = GetEntityState(ent);
    458413            if (state.garrisonHolder)
    459414                groups.add(state.garrisonHolder.entities);
    460415        }
    461416        return groups.getEntsGrouped();
    462417    },
    463     "addData": function(data)
     418    "setupButton": function(data)
    464419    {
    465         data.entType = data.item.template;
    466         data.template = GetTemplateData(data.entType);
    467         if (!data.template)
     420        let template = GetTemplateData(data.item.template);
     421        if (!template)
    468422            return false;
    469         data.name = getEntityNames(data.template);
    470         data.count = data.item.ents.length;
    471         return true;
    472     },
    473     "setAction": function(data)
    474     {
     423
    475424        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";
     425
     426        let tooltip = sprintf(translate("Unload %(name)s"), { "name": getEntityNames(template) }) + "\n";
    480427        tooltip += translate("Single-click to unload 1. Shift-click to unload all of this type.");
    481428        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;
     429
     430        data.countDisplay.caption = data.item.ents.length || "";
     431
     432        let grayscale = "";
     433        let ents = data.item.ents;
     434        let entplayer = GetEntityState(ents[0]).player;
    492435        data.button.sprite = "color:" + rgbToGuiColor(g_Players[entplayer].color) +":";
    493436
    494437        if (!controlsPlayer(data.unitEntState.player) && !controlsPlayer(entplayer))
     
    497440            grayscale = "grayscale:";
    498441        }
    499442
    500         data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + data.template.icon;
     443        data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
     444
     445        setPanelObjectPosition(data.button, data.i, data.rowLength);
     446        return true;
    501447    }
    502448};
    503449
     
    510456    "getItems": function(unitEntState, selection)
    511457    {
    512458        // Allow long wall pieces to be converted to gates
    513         var longWallTypes = {};
    514         var walls = [];
    515         var gates = [];
    516         for (var ent of selection)
     459        let longWallTypes = {};
     460        let walls = [];
     461        let gates = [];
     462        for (let ent of selection)
    517463        {
    518             var state = GetEntityState(ent);
     464            let state = GetEntityState(ent);
    519465            if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
    520466            {
    521                 var gateTemplate = getWallGateTemplate(state.id);
     467                let gateTemplate = getWallGateTemplate(state.id);
    522468                if (gateTemplate)
    523469                {
    524                     var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
     470                    let tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
    525471                    if (!tooltipString)
    526472                    {
    527473                        warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
     
    554500            }
    555501            // Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
    556502            else if (state.gate && state.gate.locked != gates[0].gate.locked)
    557                 for (var j = 0; j < gates.length; ++j)
     503                for (let j = 0; j < gates.length; ++j)
    558504                    delete gates[j].gate.locked;
    559505        }
    560506
    561507        // Place wall conversion options after gate lock/unlock icons.
    562         var items = gates.concat(walls);
    563         return items;
     508        return gates.concat(walls);
    564509    },
    565     "setAction": function(data)
     510    "setupButton": function(data)
    566511    {
    567512        data.button.onPress = function() {data.item.callback(data.item); };
    568     },
    569     "setTooltip": function(data)
    570     {
    571         var tooltip = data.item.tooltip;
     513
     514        let tooltip = data.item.tooltip;
    572515        if (data.item.template)
    573516        {
    574517            data.template = GetTemplateData(data.item.template);
    575518            data.wallCount = data.selection.reduce(function (count, ent) {
    576                     var state = GetEntityState(ent);
     519                    let state = GetEntityState(ent);
    577520                    if (hasClass(state, "LongWall") && !state.gate)
    578521                        ++count;
    579522                    return count;
     
    589532                tooltip += getNeededResourcesTooltip(data.neededResources);
    590533        }
    591534        data.button.tooltip = tooltip;
    592     },
    593     "setGraphics": function(data)
    594     {
     535
    595536        data.button.enabled = controlsPlayer(data.unitEntState.player);
    596         var gateIcon;
     537        let gateIcon;
    597538        if (data.item.gate)
    598539        {
    599540            // If already a gate, show locking actions
     
    605546        }
    606547        else
    607548        {
    608             // otherwise show gate upgrade icon
    609             var template = GetTemplateData(data.item.template);
     549            // Otherwise show gate upgrade icon
     550            let template = GetTemplateData(data.item.template);
    610551            if (!template)
    611                 return;
     552                return false;
    612553            gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
    613554            data.guiSelection.hidden = true;
    614555        }
    615556
    616557        data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon;
    617     },
    618     "setPosition": function(data)
    619     {
    620         var index = data.i + getNumberOfRightPanelButtons();
     558
     559        let index = data.i + getNumberOfRightPanelButtons();
    621560        setPanelObjectPosition(data.button, index, data.rowLength);
     561        return true;
    622562    }
    623563};
    624564
     
    630570    },
    631571    "getItems": function(unitEntState, selection)
    632572    {
    633         var checks = {};
    634         for (var ent of selection)
     573        let checks = {};
     574        for (let ent of selection)
    635575        {
    636             var state = GetEntityState(ent);
     576            let state = GetEntityState(ent);
    637577            if (!state.pack)
    638578                continue;
    639579            if (state.pack.progress == 0)
     
    652592                    checks.unpackCancelButton = true;
    653593            }
    654594        }
    655         var items = [];
     595        let items = [];
    656596        if (checks.packButton)
    657597            items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } });
    658598        if (checks.unpackButton)
     
    663603            items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } });
    664604        return items;
    665605    },
    666     "setAction": function(data)
     606    "setupButton": function(data)
    667607    {
    668608        data.button.onPress = function() {data.item.callback(data.item); };
    669     },
    670     "setTooltip": function(data)
    671     {
     609
    672610        data.button.tooltip = data.item.tooltip;
    673     },
    674     "setGraphics": function(data)
    675     {
     611
    676612        if (data.item.packing)
    677613            data.icon.sprite = "stretched:session/icons/cancel.png";
    678614        else if (data.item.packed)
     
    681617            data.icon.sprite = "stretched:session/icons/pack.png";
    682618
    683619        data.button.enabled = controlsPlayer(data.unitEntState.player);
    684     },
    685     "setPosition": function(data)
    686     {
    687         var index = data.i + getNumberOfRightPanelButtons();
     620
     621        let index = data.i + getNumberOfRightPanelButtons();
    688622        setPanelObjectPosition(data.button, index, data.rowLength);
     623        return true;
    689624    }
    690625};
    691626
     
    701636    },
    702637    "resizePanel": function(numberOfItems, rowLength)
    703638    {
    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;
     639        let numRows = Math.ceil(numberOfItems / rowLength);
     640        let panel = Engine.GetGUIObjectByName("unitQueuePanel");
     641        let size = panel.size;
     642        let buttonSize = Engine.GetGUIObjectByName("unitQueueButton[0]").size.bottom;
     643        let margin = 4;
    709644        size.top = size.bottom - numRows*buttonSize - (numRows+2)*margin;
    710645        panel.size = size;
    711646    },
    712     "addData": function(data)
     647    "setupButton": function(data)
    713648    {
    714         // differentiate between units and techs
     649        // Differentiate between units and techs
     650        let template;
    715651        if (data.item.unitTemplate)
    716         {
    717             data.entType = data.item.unitTemplate;
    718             data.template = GetTemplateData(data.entType);
    719         }
     652            template = GetTemplateData(data.item.unitTemplate);
    720653        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) + "%";
     654            template = GetTechnologyData(data.item.technologyTemplate);
    726655
    727         return data.template;
    728     },
    729     "setAction": function(data)
    730     {
     656        if (!template)
     657            return false;
     658
    731659        data.button.onPress = function() { removeFromProductionQueue(data.item.producingEnt, data.item.id); };
    732     },
    733     "setTooltip": function(data)
    734     {
    735         var tooltip = getEntityNames(data.template);
     660
     661        let tooltip = getEntityNames(template);
    736662        if (data.item.neededSlots)
    737663        {
    738664            tooltip += "\n[color=\"red\"]" + translate("Insufficient population capacity:") + "\n[/color]";
     
    742668            });
    743669        }
    744670        data.button.tooltip = tooltip;
    745     },
    746     "setCountDisplay": function(data)
    747     {
     671
    748672        data.countDisplay.caption = data.item.count > 1 ? data.item.count : "";
    749     },
    750     "setProgressDisplay": function(data)
    751     {
    752         // show the progress number for the first item
     673
     674        // Show the progress number for the first item
    753675        if (data.i == 0)
    754             Engine.GetGUIObjectByName("queueProgress").caption = data.progress;
     676            Engine.GetGUIObjectByName("queueProgress").caption = Math.round(data.item.progress*100) + "%";
    755677
    756         var guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
    757         var size = guiObject.size;
     678        let guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
     679        let size = guiObject.size;
    758680
    759681        // Buttons are assumed to be square, so left/right offsets can be used for top/bottom.
    760682        size.top = size.left + Math.round(data.item.progress * (size.right - size.left));
    761683        guiObject.size = size;
    762     },
    763     "setGraphics": function(data)
    764     {
    765         if (data.template.icon)
    766             data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
    767684
     685        if (template.icon)
     686            data.icon.sprite = "stretched:session/portraits/" + template.icon;
     687
    768688        data.button.enabled = controlsPlayer(data.unitEntState.player);
     689
     690        setPanelObjectPosition(data.button, data.i, data.rowLength);
     691        return true;
    769692    }
    770693};
    771694
     
    793716        }
    794717        return [];
    795718    },
    796     "hideItem": function(i, rowLength) // called when no item is found
     719    "hideItem": function(i, rowLength) // Called when no item is found
    797720    {
    798         Engine.GetGUIObjectByName("unitResearchButton["+i+"]").hidden = true;
     721        Engine.GetGUIObjectByName("unitResearchButton[" + i + "]").hidden = true;
    799722        // 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;
     723        Engine.GetGUIObjectByName("unitResearchButton[" + (i + rowLength) + "]").hidden = true;
     724        Engine.GetGUIObjectByName("unitResearchPair[" + i + "]").hidden = true;
    802725    },
    803     "addData": function(data)
     726    "setupButton": function(data)
    804727    {
    805728        if (!data.item.tech)
    806729        {
     
    807730            g_SelectionPanels.Research.hideItem(data.i, data.rowLength);
    808731            return false;
    809732        }
    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;
     733        let techs = data.item.tech.pair ? [data.item.tech.bottom, data.item.tech.top] : [data.item.tech];
    815734
    816         for (let template of data.template)
    817             for (let res in template.cost)
    818                 template.cost[res] *= data.item.techCostMultiplier[res];
     735        // Start position (start at the bottom)
     736        let position = data.i + data.rowLength;
    819737
    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];
     738        // Only show the top button for pairs
     739        if (!data.item.tech.pair)
     740            Engine.GetGUIObjectByName("unitResearchButton[" + data.i + "]").hidden = true;
    824741
    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+"]"));
     742        // Set up the tech connector
     743        let pair = Engine.GetGUIObjectByName("unitResearchPair[" + data.i + "]");
     744        pair.hidden = data.item.tech.pair == null;
     745        setPanelObjectPosition(pair, data.i, data.rowLength);
    828746
    829         data.icon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchIcon["+p+"]"));
    830         data.unchosenIcon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchUnchosenIcon["+p+"]"));
     747        // Handle one or two techs
     748        for (let i in techs)
     749        {
     750            let tech = techs[i];
     751            let template = GetTechnologyData(tech);
     752            if (!template)
     753                return false;
    831754
    832         data.neededResources = data.template.map(t => Engine.GuiInterfaceCall("GetNeededResources", {
    833             "cost": t.cost,
    834             "player": data.unitEntState.player
    835         }));
     755            for (let res in template.cost)
     756                template.cost[res] *= data.item.techCostMultiplier[res];
    836757
    837         data.requirementsPassed = data.entType.map(e => Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
    838             "tech": e,
    839             "player": data.unitEntState.player
    840         }));
     758            let neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     759                "cost": template.cost,
     760                "player": data.unitEntState.player
     761            });
    841762
    842         data.pair = Engine.GetGUIObjectByName("unitResearchPair["+data.i+"]");
     763            let requirementsPassed = Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
     764                "tech": tech,
     765                "player": data.unitEntState.player
     766            });
    843767
    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];
    852             tooltip = getEntityNamesFormatted(template);
     768            let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]");
     769            let icon = Engine.GetGUIObjectByName("unitResearchIcon[" + position + "]");
     770
     771            let tooltip = getEntityNamesFormatted(template);
    853772            if (template.tooltip)
    854773                tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    855774
    856775            tooltip += "\n" + getEntityCostTooltip(template);
    857             if (!data.requirementsPassed[i])
     776            if (!requirementsPassed)
    858777            {
    859778                tooltip += "\n" + template.requirementsTooltip;
    860779                if (template.classRequirements)
    861780                {
    862                     var player = data.unitEntState.player;
    863                     var current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
    864                     var remaining = template.classRequirements.number - current;
     781                    let player = data.unitEntState.player;
     782                    let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
     783                    let remaining = template.classRequirements.number - current;
    865784                    tooltip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { "number": remaining });
    866785                }
    867786            }
    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;
     787            if (neededResources)
     788                tooltip += getNeededResourcesTooltip(neededResources);
     789            button.tooltip = tooltip;
     790
     791            button.onPress = function () {
     792                addResearchToQueue(data.unitEntState.id, tech);
     793            };
     794
     795            if (data.item.tech.pair)
     796            {
     797                // On mouse enter, show a cross over the other icon
     798                let otherPosition = (position + data.rowLength) % (2 * data.rowLength);
     799                let unchosenIcon = Engine.GetGUIObjectByName("unitResearchUnchosenIcon[" + otherPosition + "]");
     800                button.onMouseEnter = function() {
     801                        unchosenIcon.hidden = false;
    889802                };
    890             })(others, data.unchosenIcon);
    891             button.onmouseleave = (function(others, icons) {
    892                 return function() {
    893                     for (var j of others)
    894                         icons[j].hidden = true;
     803                button.onMouseLeave = function() {
     804                        unchosenIcon.hidden = true;
    895805                };
    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];
     806            }
     807
    904808            button.hidden = false;
    905             var modifier = "";
    906             if (!data.requirementsPassed[i])
     809            let modifier = "";
     810            if (!requirementsPassed)
    907811            {
    908812                button.enabled = false;
    909                 modifier += "color: 0 0 0 127:";
    910                 modifier += "grayscale:";
     813                modifier += "color:0 0 0 127:grayscale:";
    911814            }
    912             else if (data.neededResources[i])
     815            else if (neededResources)
    913816            {
    914817                button.enabled = false;
    915                 modifier += resourcesToAlphaMask(data.neededResources[i]) + ":";
     818                modifier += resourcesToAlphaMask(neededResources) + ":";
    916819            }
    917820            else
    918821                button.enabled = controlsPlayer(data.unitEntState.player);
    919822
    920             if (data.template[i].icon)
    921                 data.icon[i].sprite = modifier + "stretched:session/portraits/" + data.template[i].icon;
     823            if (template.icon)
     824                icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     825
     826            setPanelObjectPosition(button, position, data.rowLength);
     827
     828            // Prepare to handle the top button (if any)
     829            position -= data.rowLength;
    922830        }
    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);
     831
     832        return true;
    933833    }
    934834};
    935835
     
    946846            return [];
    947847        return g_Selection.groups.getTemplateNames();
    948848    },
    949     "addData": function(data)
     849    "setupButton": function(data)
    950850    {
    951         data.entType = data.item;
    952         data.template = GetTemplateData(data.entType);
    953         if (!data.template)
     851        let template = GetTemplateData(data.item);
     852        if (!template)
    954853            return false;
    955         data.name = getEntityNames(data.template);
    956854
    957         var ents = g_Selection.groups.getEntsByName(data.item);
    958         data.count = ents.length;
    959         for (var ent of ents)
     855        let ents = g_Selection.groups.getEntsByName(data.item);
     856        for (let ent of ents)
    960857        {
    961             var state = GetEntityState(ent);
     858            let state = GetEntityState(ent);
    962859
    963860            if (state.resourceCarrying && state.resourceCarrying.length !== 0)
    964861            {
    965862                if (!data.carried)
    966863                    data.carried = {};
    967                 var carrying = state.resourceCarrying[0];
     864                let carrying = state.resourceCarrying[0];
    968865                if (data.carried[carrying.type])
    969866                    data.carried[carrying.type] += carrying.amount;
    970867                else
     
    975872            {
    976873                if (!data.carried)
    977874                    data.carried = {};
    978                 var amount = state.trader.goods.amount;
    979                 var type = state.trader.goods.type;
    980                 var totalGain = amount.traderGain;
     875                let amount = state.trader.goods.amount;
     876                let type = state.trader.goods.type;
     877                let totalGain = amount.traderGain;
    981878                if (amount.market1Gain)
    982879                    totalGain += amount.market1Gain;
    983880                if (amount.market2Gain)
     
    988885                    data.carried[type] = totalGain;
    989886            }
    990887        }
    991         return true;
    992     },
    993     "setTooltip": function(data)
    994     {
    995         let tooltip = data.name;
     888
     889        let tooltip = getEntityNames(template);
    996890        if (data.carried)
    997891            tooltip += "\n" + Object.keys(data.carried).map(res =>
    998892                getCostComponentDisplayIcon(res) + data.carried[res]
    999893            ).join(" ");
    1000894        data.button.tooltip = tooltip;
    1001     },
    1002     "setCountDisplay": function(data)
    1003     {
    1004         data.countDisplay.caption = data.count || "";
    1005     },
    1006     "setAction": function(data)
    1007     {
    1008         data.button.onpressright = function() { changePrimarySelectionGroup(data.item, true); };
    1009         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;
     895
     896        data.countDisplay.caption = ents.length || "";
     897
     898        data.button.onPressRight = function() { changePrimarySelectionGroup(data.item, true); };
     899        data.button.onPress = function() { changePrimarySelectionGroup(data.item, false); };
     900
     901        if (template.icon)
     902            data.icon.sprite = "stretched:session/portraits/" + template.icon;
     903
     904        setPanelObjectPosition(data.button, data.i, data.rowLength);
     905        return true;
    1015906    }
    1016907};
    1017908
     
    1027918            return [];
    1028919        return unitEntState.unitAI.possibleStances;
    1029920    },
    1030     "addData": function(data)
     921    "setupButton": function(data)
    1031922    {
    1032         data.stanceSelected = Engine.GuiInterfaceCall("IsStanceSelected", {
     923        data.button.onPress = function() { performStance(data.unitEntState, data.item); };
     924
     925        data.button.tooltip = getStanceDisplayName(data.item) + "\n" +
     926            "[font=\"sans-13\"]" + getStanceTooltip(data.item) + "[/font]";
     927
     928        data.guiSelection.hidden = !Engine.GuiInterfaceCall("IsStanceSelected", {
    1033929            "ents": data.selection,
    1034930            "stance": data.item
    1035931        });
     932        data.icon.sprite = "stretched:session/icons/stances/" + data.item + ".png";
     933        data.button.enabled = controlsPlayer(data.unitEntState.player);
     934
     935        setPanelObjectPosition(data.button, data.i, data.rowLength);
    1036936        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);
    1051937    }
    1052938};
    1053939
     
    1061947    {
    1062948        return getAllTrainableEntitiesFromSelection();
    1063949    },
    1064     "addData": function(data)
     950    "setupButton": function(data)
    1065951    {
    1066         data.entType = data.item;
    1067         data.template = GetTemplateData(data.entType);
    1068         if (!data.template)
     952        let template = GetTemplateData(data.item);
     953        if (!template)
    1069954            return false;
    1070955
    1071         data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
    1072             "tech": data.template.requiredTechnology,
     956        let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
     957            "tech": template.requiredTechnology,
    1073958            "player": data.unitEntState.player
    1074959        });
    1075960
    1076         var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
    1077             getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.entType, data.selection);
     961        let [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
     962            getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.item, data.selection);
    1078963
    1079         data.buildingsCountToTrainFullBatch = buildingsCountToTrainFullBatch;
    1080         data.fullBatchSize = fullBatchSize;
    1081         data.remainderBatch = remainderBatch;
    1082 
    1083         data.trainNum = buildingsCountToTrainFullBatch || 1; // train at least one unit
     964        let trainNum = buildingsCountToTrainFullBatch || 1;
    1084965        if (Engine.HotkeyIsPressed("session.batchtrain"))
    1085             data.trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
     966            trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
    1086967
    1087         if (data.template.cost)
    1088             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
    1089                 "cost": multiplyEntityCosts(data.template, data.trainNum),
     968        let neededResources;
     969        if (template.cost)
     970            neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     971                "cost": multiplyEntityCosts(template, trainNum),
    1090972                "player": data.unitEntState.player
    1091973            });
    1092974
    1093         return true;
    1094     },
    1095     "setAction": function(data)
    1096     {
    1097975        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     {
     976
     977        data.countDisplay.caption = trainNum > 1 ? trainNum : "";
     978
    1105979        let tooltip = "[font=\"sans-bold-16\"]" +
    1106             colorizeHotkey("%(hotkey)s", "session.queueunit." + (data.i + 1)) +
    1107             "[/font]";
     980        colorizeHotkey("%(hotkey)s", "session.queueunit." + (data.i + 1)) +
     981            "[/font]"; 
    1108982
    1109         tooltip += getEntityNamesFormatted(data.template);
    1110         tooltip += getVisibleEntityClassesFormatted(data.template);
    1111         tooltip += getAurasTooltip(data.template);
     983        tooltip += getEntityNamesFormatted(template);
     984        tooltip += getVisibleEntityClassesFormatted(template);
     985        tooltip += getAurasTooltip(template);
    1112986
    1113         if (data.template.tooltip)
    1114             tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
     987        if (template.tooltip)
     988            tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    1115989
    1116         tooltip += "\n" + getEntityCostTooltip(data.template, data.trainNum, data.unitEntState.id);
     990        tooltip += "\n" + getEntityCostTooltip(template, trainNum, data.unitEntState.id);
    1117991
    1118         data.limits = getEntityLimitAndCount(data.playerState, data.entType);
     992        let limits = getEntityLimitAndCount(data.playerState, data.item);
    1119993
    1120         tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
     994        tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
    1121995        if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") === "true")
    1122996        {
    1123             if (data.template.health)
    1124                 tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + data.template.health;
    1125             if (data.template.attack)
    1126                 tooltip += "\n" + getAttackTooltip(data.template);
    1127             if (data.template.armour)
    1128                 tooltip += "\n" + getArmorTooltip(data.template.armour);
    1129             if (data.template.speed)
    1130                 tooltip += "\n" + getSpeedTooltip(data.template);
     997            if (template.health)
     998                tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + template.health;
     999            if (template.attack)
     1000                tooltip += "\n" + getAttackTooltip(template);
     1001            if (template.armour)
     1002                tooltip += "\n" + getArmorTooltip(template.armour);
     1003            if (template.speed)
     1004                tooltip += "\n" + getSpeedTooltip(template);
    11311005        }
    11321006
    11331007        tooltip += "[color=\"" + g_HotkeyColor + "\"]" +
    1134             formatBatchTrainingString(data.buildingsCountToTrainFullBatch, data.fullBatchSize, data.remainderBatch) +
     1008            formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch) +
    11351009            "[/color]";
    11361010
    1137         if (!data.technologyEnabled)
     1011        if (!technologyEnabled)
    11381012        {
    1139             var techName = getEntityNames(GetTechnologyData(data.template.requiredTechnology));
     1013            let techName = getEntityNames(GetTechnologyData(template.requiredTechnology));
    11401014            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { "technology": techName });
    11411015        }
    1142         if (data.neededResources)
    1143             tooltip += getNeededResourcesTooltip(data.neededResources);
     1016        if (neededResources)
     1017            tooltip += getNeededResourcesTooltip(neededResources);
    11441018
    11451019        data.button.tooltip = tooltip;
    1146     },
    1147     // disable and enable buttons in the same way as when you do for the construction
    1148     "setGraphics": g_SelectionPanels.Construction.setGraphics,
    1149     "setPosition": function(data)
    1150     {
    1151         var index = data.i + getNumberOfRightPanelButtons();
     1020
     1021        let modifier = "";
     1022        if (!technologyEnabled || limits.canBeAddedCount == 0)
     1023        {
     1024            data.button.enabled = false;
     1025            modifier = "color:0 0 0 127:grayscale:";
     1026        }
     1027        else if (neededResources)
     1028        {
     1029            data.button.enabled = false;
     1030            modifier = resourcesToAlphaMask(neededResources) +":";
     1031        }
     1032        else
     1033            data.button.enabled = controlsPlayer(data.unitEntState.player);
     1034
     1035        if (template.icon)
     1036        data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     1037
     1038        let index = data.i + getNumberOfRightPanelButtons();
    11521039        setPanelObjectPosition(data.button, index, data.rowLength);
     1040
     1041        return true;
    11531042    }
    11541043};
    11551044
     
    11611050 *
    11621051 * Note that the panel needs to appear in the list to get rendered.
    11631052 */
    1164 var g_PanelsOrder = [
     1053let g_PanelsOrder = [
    11651054    // LEFT PANE
    1166     "Barter", // must always be visible on markets
    1167     "Garrison", // more important than Formation, as you want to see the garrisoned units in ships
     1055    "Barter", // Must always be visible on markets
     1056    "Garrison", // More important than Formation, as you want to see the garrisoned units in ships
    11681057    "Alert",
    11691058    "Formation",
    1170     "Stance", // normal together with formation
     1059    "Stance", // Normal together with formation
    11711060
    11721061    // RIGHT PANE
    1173     "Gate", // must always be shown on gates
    1174     "Pack", // must always be shown on packable entities
     1062    "Gate", // Must always be shown on gates
     1063    "Pack", // Must always be shown on packable entities
    11751064    "Training",
    11761065    "Construction",
    1177     "Research", // normal together with training
     1066    "Research", // Normal together with training
    11781067
    11791068    // UNIQUE PANES (importance doesn't matter)
    11801069    "Command",
  • binaries/data/mods/public/gui/session/unit_commands.js

     
    4040    }
    4141    let selection = g_Selection.toList();
    4242
    43     var items = g_SelectionPanels[guiName].getItems(unitEntState, selection);
     43    let items = g_SelectionPanels[guiName].getItems(unitEntState, selection);
    4444
    4545    if (!items || !items.length)
    4646        return;
    4747
    48     var numberOfItems = items.length;
    49     var garrisonGroups = new EntityGroups();
     48    let numberOfItems = items.length;
    5049
    5150    // Determine how many buttons there should be
    52     var maxNumberOfItems = g_SelectionPanels[guiName].getMaxNumberOfItems();
     51    let maxNumberOfItems = g_SelectionPanels[guiName].getMaxNumberOfItems();
    5352    if (maxNumberOfItems < numberOfItems)
    5453        numberOfItems = maxNumberOfItems;
    5554
    56     var rowLength = g_SelectionPanels[guiName].rowLength || 8;
     55    let rowLength = g_SelectionPanels[guiName].rowLength || 8;
    5756
    5857    if (g_SelectionPanels[guiName].resizePanel)
    5958        g_SelectionPanels[guiName].resizePanel(numberOfItems, rowLength);
     
    6160    // Make buttons
    6261    for (let i = 0; i < numberOfItems; ++i)
    6362    {
    64         var item = items[i];
    65 
    6663        // STANDARD DATA
    6764        // add standard data
    68         var data = {
     65        let data = {
    6966            "i": i,
    70             "item": item,
     67            "item": items[i],
    7168            "selection": selection,
    7269            "playerState": playerState,
    7370            "unitEntState": unitEntState,
     
    9289            data.button.caption = "";
    9390        }
    9491
    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 
    10492        // 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         }
     93        if (g_SelectionPanels[guiName].setupButton)
     94            if (!g_SelectionPanels[guiName].setupButton(data))
     95                continue;
    11196
    112         // Special case: position
    113         if (!g_SelectionPanels[guiName].setPosition)
    114             setPanelObjectPosition(data.button, i, rowLength);
    115 
    11697        // TODO: we should require all entities to have icons, so this case never occurs
    11798        if (data.icon && !data.icon.sprite)
    11899            data.icon.sprite = "bkFillBlack";