Ticket #4007: selection_panels_cleanup.2.diff

File selection_panels_cleanup.2.diff, 48.9 KB (added by sanderd17, 8 years ago)
  • binaries/data/mods/public/gui/common/color.js

     
     1let g_GuiColors = {
     2    "hotkey": "255 251 131",
     3    "forbidden": "255 0 0 80",
     4    "limited": "0 0 0 127",
     5};
     6
    17/**
    28 * Concatenate integer color values to a string (for use in GUI objects)
    39 *
     
    124130
    125131    return [r, g, b].map(n => Math.round(n * 255));
    126132}
     133
     134/**
     135 * Colorize text for usage in the GUI
     136 * @param {string} color in the form of a GUI color string
     137 * @param {string} text to colorize
     138 * @returns {string}
     139 */
     140function colorizeText(color, text)
     141{
     142    return "[color=\"" + color + "\"]" + text + "[/color]";
     143};
  • 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    {
    5950        data.button.onPress = function() {
    6051            if (data.item == "increase")
     
    6253            else if (data.item == "end")
    6354                endOfAlert();
    6455        };
    65     },
    66     "setTooltip": function(data)
    67     {
     56
    6857        if (data.item == "increase")
    6958        {
    7059            if (data.unitEntState.alertRaiser.hasRaisedAlert)
     
    7463        }
    7564        else if (data.item == "end")
    7665            data.button.tooltip = translate("End of alert.");
    77     },
    78     "setGraphics": function(data)
    79     {
     66
    8067        if (data.item == "increase")
    8168        {
    8269            data.button.hidden = !data.unitEntState.alertRaiser.canIncreaseLevel;
     
    9178            data.icon.sprite = "stretched:session/icons/bell_level0.png";
    9279        }
    9380        data.button.enabled = !data.button.hidden && controlsPlayer(data.unitEntState.player);
     81
     82        setPanelObjectPosition(data.button, data.i, data.rowLength);
     83        return true;
    9484    }
    9585};
    9686
     
    10898        // ["food", "wood", "stone", "metal"]
    10999        return BARTER_RESOURCES;
    110100    },
    111     "addData": function(data)
     101    "setupButton": function(data)
    112102    {
    113103        // 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)
     104        let button = {};
     105        let icon = {};
     106        let amount = {};
     107        for (let a of BARTER_ACTIONS)
    118108        {
    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+"]");
     109            button[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Button[" + data.i + "]");
     110            icon[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Icon[" + data.i + "]");
     111            amount[a] = Engine.GetGUIObjectByName("unitBarter" + a + "Amount[" + data.i + "]");
    122112        }
    123         data.selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection["+data.i+"]");
     113        let selectionIcon = Engine.GetGUIObjectByName("unitBarterSellSelection[" + data.i + "]");
    124114
    125         data.amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
     115        let amountToSell = BARTER_RESOURCE_AMOUNT_TO_SELL;
    126116        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 = {
     117            amountToSell *= BARTER_BUNCH_MULTIPLIER;
     118
     119        amount.Sell.caption = "-" + amountToSell;
     120        let sellPrice = data.unitEntState.barterMarket.prices.sell[g_barterSell];
     121        let buyPrice = data.unitEntState.barterMarket.prices.buy[data.item];
     122        amount.Buy.caption = "+" + Math.round(sellPrice / buyPrice * amountToSell);
     123
     124        let resource = getLocalizedResourceName(data.item, "withinSentence");
     125        button.Buy.tooltip = sprintf(translate("Buy %(resource)s"), { "resource": resource });
     126        button.Sell.tooltip = sprintf(translate("Sell %(resource)s"), { "resource": resource });
     127
     128        button.Sell.onPress = function() { g_barterSell = data.item; };
     129        let exchangeResourcesParameters = {
    148130            "sell": g_barterSell,
    149131            "buy": data.item,
    150             "amount": data.amountToSell
     132            "amount": amountToSell
    151133        };
    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:" : "";
     134        button.Buy.onPress = function() { exchangeResources(exchangeResourcesParameters); };
    157135
     136        let isSelected = data.item == g_barterSell;
     137        let grayscale = isSelected ? "color: 0 0 0 100:grayscale:" : "";
     138
    158139        // do we have enough of this resource to sell?
    159         var neededRes = {};
    160         neededRes[data.item] = data.amountToSell;
    161         var canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
     140        let neededRes = {};
     141        neededRes[data.item] = amountToSell;
     142        let canSellCurrent = Engine.GuiInterfaceCall("GetNeededResources", {
    162143            "cost": neededRes,
    163144            "player": data.unitEntState.player
    164         }) ? "color:255 0 0 80:" : "";
     145        }) ? "color:" + g_GuiColors.forbidden + ":" : "";
    165146
    166147        // Let's see if we have enough resources to barter.
    167148        neededRes = {};
    168         neededRes[g_barterSell] = data.amountToSell;
    169         var canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
     149        neededRes[g_barterSell] = amountToSell;
     150        let canBuyAny = Engine.GuiInterfaceCall("GetNeededResources", {
    170151            "cost": neededRes,
    171152            "player": data.unitEntState.player
    172         }) ? "color:255 0 0 80:" : "";
     153        }) ? "color:" + g_GuiColors.forbidden + ":" : "";
    173154
    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";
     155        icon.Sell.sprite = canSellCurrent + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
     156        icon.Buy.sprite = canBuyAny + "stretched:" + grayscale + "session/icons/resources/" + data.item + ".png";
    176157
    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);
     158        button.Buy.hidden = isSelected;
     159        button.Buy.enabled = controlsPlayer(data.unitEntState.player);
     160        button.Sell.hidden = false;
     161        selectionIcon.hidden = !isSelected;
     162
     163        // Position
     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        // Position
     269        setPanelObjectPosition(data.button, data.i, data.rowLength);
     270        return true;
    301271    }
    302272};
    303273
     
    311281    {
    312282        return getAllBuildableEntitiesFromSelection();
    313283    },
    314     "addData": function(data)
     284    "setupButton": function(data)
    315285    {
    316         data.entType = data.item;
    317         data.template = GetTemplateData(data.entType);
    318         if (!data.template) // abort if no template
     286        let template = GetTemplateData(data.item);
     287        if (!template) // abort if no template
    319288            return false;
    320289
    321         data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
    322             "tech": data.template.requiredTechnology,
     290        let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
     291            "tech": template.requiredTechnology,
    323292            "player": data.unitEntState.player
    324293        });
    325294
    326         if (data.template.cost)
    327             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
    328                 "cost": multiplyEntityCosts(data.template, 1),
     295        let neededResources;
     296        if (template.cost)
     297            neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     298                "cost": multiplyEntityCosts(template, 1),
    329299                "player": data.unitEntState.player
    330300            });
    331301
    332         data.limits = getEntityLimitAndCount(data.playerState, data.entType);
     302        let limits = getEntityLimitAndCount(data.playerState, data.item);
    333303
    334         if (data.template.wallSet)
    335             data.template.auras = GetTemplateData(data.template.wallSet.templates.long).auras;
     304        if (template.wallSet)
     305            template.auras = GetTemplateData(template.wallSet.templates.long).auras;
    336306
    337         return true;
    338     },
    339     "setAction": function(data)
    340     {
    341307        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);
    348308
    349         if (data.template.tooltip)
    350             tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
     309        let tooltip = getEntityNamesFormatted(template);
     310        tooltip += getVisibleEntityClassesFormatted(template);
     311        tooltip += getAurasTooltip(template);
    351312
    352         tooltip += "\n" + getEntityCostTooltip(data.template);
    353         tooltip += getPopulationBonusTooltip(data.template);
     313        if (template.tooltip)
     314            tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    354315
    355         tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
     316        tooltip += "\n" + getEntityCostTooltip(template);
     317        tooltip += getPopulationBonusTooltip(template);
    356318
    357         if (!data.technologyEnabled)
     319        tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
     320
     321        if (!technologyEnabled)
    358322            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), {
    359                 "technology": getEntityNames(GetTechnologyData(data.template.requiredTechnology))
     323                "technology": getEntityNames(GetTechnologyData(template.requiredTechnology))
    360324            });
    361325
    362         if (data.neededResources)
    363             tooltip += getNeededResourcesTooltip(data.neededResources);
     326        if (neededResources)
     327            tooltip += getNeededResourcesTooltip(neededResources);
    364328
    365329        data.button.tooltip = tooltip;
    366         return true;
    367     },
    368     "setGraphics": function(data)
    369     {
    370         var modifier = "";
    371         if (!data.technologyEnabled || data.limits.canBeAddedCount == 0)
     330
     331        let modifier = "";
     332        if (!technologyEnabled || limits.canBeAddedCount == 0)
    372333        {
    373334            data.button.enabled = false;
    374             modifier += "color: 0 0 0 127:";
     335            modifier += "color: " + g_GuiColors.limited + ":";
    375336            modifier += "grayscale:";
    376337        }
    377         else if (data.neededResources)
     338        else if (neededResources)
    378339        {
    379340            data.button.enabled = false;
    380             modifier += resourcesToAlphaMask(data.neededResources) +":";
     341            modifier += resourcesToAlphaMask(neededResources) +":";
    381342        }
    382343        else
    383344            data.button.enabled = controlsPlayer(data.unitEntState.player);
    384345
    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();
     346        if (template.icon)
     347            data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     348
     349        let index = data.i + getNumberOfRightPanelButtons();
    391350        setPanelObjectPosition(data.button, index, data.rowLength);
     351        return true;
    392352    }
    393353};
    394354
     
    404364    {
    405365        if (!hasClass(unitEntState, "Unit") || hasClass(unitEntState, "Animal"))
    406366            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);
     367        if (!g_AvailableFormations.has(unitEntState.player))
     368            g_AvailableFormations.set(unitEntState.player, Engine.GuiInterfaceCall("GetAvailableFormations", unitEntState.player));
     369        return g_AvailableFormations.get(unitEntState.player);
    410370    },
    411     "addData": function(data)
     371    "setupButton": function(data)
    412372    {
    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", {
     373        if (!g_FormationsInfo.has(data.item))
     374            g_FormationsInfo.set(data.item, Engine.GuiInterfaceCall("GetFormationInfoFromTemplate", { "templateName": data.item }));
     375
     376        let formationInfo = g_FormationsInfo.get(data.item);
     377        let formationOk = canMoveSelectionIntoFormation(data.item);
     378        let formationSelected = Engine.GuiInterfaceCall("IsFormationSelected", {
    418379            "ents": data.selection,
    419380            "formationTemplate": data.item
    420381        });
    421         return true;
    422     },
    423     "setAction": function(data)
    424     {
     382
    425383        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]";
     384
     385        let tooltip = translate(formationInfo.name);
     386        if (!formationOk && formationInfo.tooltip)
     387            tooltip += "\n" + "[color=\"red\"]" + translate(formationInfo.tooltip) + "[/color]";
    432388        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;
     389
     390        data.button.enabled = formationOk && controlsPlayer(data.unitEntState.player);
     391        let grayscale = formationOk ? "" : "grayscale:";
     392        data.guiSelection.hidden = !formationSelected;
     393        data.icon.sprite = "stretched:" + grayscale + "session/icons/" + formationInfo.icon;
     394
     395        setPanelObjectPosition(data.button, data.i, data.rowLength);
     396        return true;
    440397    }
    441398};
    442399
     
    451408    {
    452409        if (!unitEntState.garrisonHolder)
    453410            return [];
    454         var groups = new EntityGroups();
    455         for (var ent of selection)
     411        let groups = new EntityGroups();
     412        for (let ent of selection)
    456413        {
    457             var state = GetEntityState(ent);
     414            let state = GetEntityState(ent);
    458415            if (state.garrisonHolder)
    459416                groups.add(state.garrisonHolder.entities);
    460417        }
    461418        return groups.getEntsGrouped();
    462419    },
    463     "addData": function(data)
     420    "setupButton": function(data)
    464421    {
    465         data.entType = data.item.template;
    466         data.template = GetTemplateData(data.entType);
    467         if (!data.template)
     422        let template = GetTemplateData(data.item.template);
     423        if (!template)
    468424            return false;
    469         data.name = getEntityNames(data.template);
    470         data.count = data.item.ents.length;
    471         return true;
    472     },
    473     "setAction": function(data)
    474     {
     425
    475426        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";
     427
     428        let tooltip = sprintf(translate("Unload %(name)s"), { "name": getEntityNames(template) }) + "\n";
    480429        tooltip += translate("Single-click to unload 1. Shift-click to unload all of this type.");
    481430        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;
     431
     432        data.countDisplay.caption = data.item.ents.length || "";
     433
     434        let grayscale = "";
     435        let ents = data.item.ents;
     436        let entplayer = GetEntityState(ents[0]).player;
    492437        data.button.sprite = "color:" + rgbToGuiColor(g_Players[entplayer].color) +":";
    493438
    494439        if (!controlsPlayer(data.unitEntState.player) && !controlsPlayer(entplayer))
     
    497442            grayscale = "grayscale:";
    498443        }
    499444
    500         data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + data.template.icon;
     445        data.icon.sprite = "stretched:" + grayscale + "session/portraits/" + template.icon;
     446
     447        setPanelObjectPosition(data.button, data.i, data.rowLength);
     448        return true;
    501449    }
    502450};
    503451
     
    510458    "getItems": function(unitEntState, selection)
    511459    {
    512460        // Allow long wall pieces to be converted to gates
    513         var longWallTypes = {};
    514         var walls = [];
    515         var gates = [];
    516         for (var ent of selection)
     461        let longWallTypes = {};
     462        let walls = [];
     463        let gates = [];
     464        for (let ent of selection)
    517465        {
    518             var state = GetEntityState(ent);
     466            let state = GetEntityState(ent);
    519467            if (hasClass(state, "LongWall") && !state.gate && !longWallTypes[state.template])
    520468            {
    521                 var gateTemplate = getWallGateTemplate(state.id);
     469                let gateTemplate = getWallGateTemplate(state.id);
    522470                if (gateTemplate)
    523471                {
    524                     var tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
     472                    let tooltipString = GetTemplateDataWithoutLocalization(state.template).gateConversionTooltip;
    525473                    if (!tooltipString)
    526474                    {
    527475                        warn(state.template + " is supposed to be convertable to a gate, but it's missing the GateConversionTooltip in the Identity template");
     
    554502            }
    555503            // Show both 'locked' and 'unlocked' as active if the selected gates have both lock states.
    556504            else if (state.gate && state.gate.locked != gates[0].gate.locked)
    557                 for (var j = 0; j < gates.length; ++j)
     505                for (let j = 0; j < gates.length; ++j)
    558506                    delete gates[j].gate.locked;
    559507        }
    560508
    561509        // Place wall conversion options after gate lock/unlock icons.
    562         var items = gates.concat(walls);
    563         return items;
     510        return gates.concat(walls);
    564511    },
    565     "setAction": function(data)
     512    "setupButton": function(data)
    566513    {
    567514        data.button.onPress = function() {data.item.callback(data.item); };
    568     },
    569     "setTooltip": function(data)
    570     {
    571         var tooltip = data.item.tooltip;
     515
     516        let tooltip = data.item.tooltip;
    572517        if (data.item.template)
    573518        {
    574519            data.template = GetTemplateData(data.item.template);
    575520            data.wallCount = data.selection.reduce(function (count, ent) {
    576                     var state = GetEntityState(ent);
     521                    let state = GetEntityState(ent);
    577522                    if (hasClass(state, "LongWall") && !state.gate)
    578523                        ++count;
    579524                    return count;
     
    589534                tooltip += getNeededResourcesTooltip(data.neededResources);
    590535        }
    591536        data.button.tooltip = tooltip;
    592     },
    593     "setGraphics": function(data)
    594     {
     537
    595538        data.button.enabled = controlsPlayer(data.unitEntState.player);
    596         var gateIcon;
     539        let gateIcon;
    597540        if (data.item.gate)
    598541        {
    599542            // If already a gate, show locking actions
     
    606549        else
    607550        {
    608551            // otherwise show gate upgrade icon
    609             var template = GetTemplateData(data.item.template);
     552            let template = GetTemplateData(data.item.template);
    610553            if (!template)
    611                 return;
     554                return false;
    612555            gateIcon = data.template.icon ? "portraits/" + data.template.icon : "icons/gate_closed.png";
    613556            data.guiSelection.hidden = true;
    614557        }
    615558
    616559        data.icon.sprite = (data.neededResources ? resourcesToAlphaMask(data.neededResources) + ":" : "") + "stretched:session/" + gateIcon;
    617     },
    618     "setPosition": function(data)
    619     {
    620         var index = data.i + getNumberOfRightPanelButtons();
     560
     561        let index = data.i + getNumberOfRightPanelButtons();
    621562        setPanelObjectPosition(data.button, index, data.rowLength);
     563        return true;
    622564    }
    623565};
    624566
     
    630572    },
    631573    "getItems": function(unitEntState, selection)
    632574    {
    633         var checks = {};
    634         for (var ent of selection)
     575        let checks = {};
     576        for (let ent of selection)
    635577        {
    636             var state = GetEntityState(ent);
     578            let state = GetEntityState(ent);
    637579            if (!state.pack)
    638580                continue;
    639581            if (state.pack.progress == 0)
     
    652594                    checks.unpackCancelButton = true;
    653595            }
    654596        }
    655         var items = [];
     597        let items = [];
    656598        if (checks.packButton)
    657599            items.push({ "packing": false, "packed": false, "tooltip": translate("Pack"), "callback": function() { packUnit(true); } });
    658600        if (checks.unpackButton)
     
    663605            items.push({ "packing": true, "packed": true, "tooltip": translate("Cancel Unpacking"), "callback": function() { cancelPackUnit(false); } });
    664606        return items;
    665607    },
    666     "setAction": function(data)
     608    "setupButton": function(data)
    667609    {
    668610        data.button.onPress = function() {data.item.callback(data.item); };
    669     },
    670     "setTooltip": function(data)
    671     {
     611
    672612        data.button.tooltip = data.item.tooltip;
    673     },
    674     "setGraphics": function(data)
    675     {
     613
    676614        if (data.item.packing)
    677615            data.icon.sprite = "stretched:session/icons/cancel.png";
    678616        else if (data.item.packed)
     
    681619            data.icon.sprite = "stretched:session/icons/pack.png";
    682620
    683621        data.button.enabled = controlsPlayer(data.unitEntState.player);
    684     },
    685     "setPosition": function(data)
    686     {
    687         var index = data.i + getNumberOfRightPanelButtons();
     622
     623        let index = data.i + getNumberOfRightPanelButtons();
    688624        setPanelObjectPosition(data.button, index, data.rowLength);
     625        return true;
    689626    }
    690627};
    691628
     
    701638    },
    702639    "resizePanel": function(numberOfItems, rowLength)
    703640    {
    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;
     641        let numRows = Math.ceil(numberOfItems / rowLength);
     642        let panel = Engine.GetGUIObjectByName("unitQueuePanel");
     643        let size = panel.size;
     644        let buttonSize = Engine.GetGUIObjectByName("unitQueueButton[0]").size.bottom;
     645        let margin = 4;
    709646        size.top = size.bottom - numRows*buttonSize - (numRows+2)*margin;
    710647        panel.size = size;
    711648    },
    712     "addData": function(data)
     649    "setupButton": function(data)
    713650    {
    714651        // differentiate between units and techs
     652        let template;
    715653        if (data.item.unitTemplate)
    716         {
    717             data.entType = data.item.unitTemplate;
    718             data.template = GetTemplateData(data.entType);
    719         }
     654            template = GetTemplateData(data.item.unitTemplate);
    720655        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) + "%";
     656            template = GetTechnologyData(data.item.technologyTemplate);
    726657
    727         return data.template;
    728     },
    729     "setAction": function(data)
    730     {
     658        if (!template)
     659            return false;
     660
    731661        data.button.onPress = function() { removeFromProductionQueue(data.item.producingEnt, data.item.id); };
    732     },
    733     "setTooltip": function(data)
    734     {
    735         var tooltip = getEntityNames(data.template);
     662
     663        let tooltip = getEntityNames(template);
    736664        if (data.item.neededSlots)
    737665        {
    738666            tooltip += "\n[color=\"red\"]" + translate("Insufficient population capacity:") + "\n[/color]";
     
    742670            });
    743671        }
    744672        data.button.tooltip = tooltip;
    745     },
    746     "setCountDisplay": function(data)
    747     {
     673
    748674        data.countDisplay.caption = data.item.count > 1 ? data.item.count : "";
    749     },
    750     "setProgressDisplay": function(data)
    751     {
     675
    752676        // show the progress number for the first item
    753677        if (data.i == 0)
    754             Engine.GetGUIObjectByName("queueProgress").caption = data.progress;
     678            Engine.GetGUIObjectByName("queueProgress").caption = Math.round(data.item.progress*100) + "%";
    755679
    756         var guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
    757         var size = guiObject.size;
     680        let guiObject = Engine.GetGUIObjectByName("unitQueueProgressSlider["+data.i+"]");
     681        let size = guiObject.size;
    758682
    759683        // Buttons are assumed to be square, so left/right offsets can be used for top/bottom.
    760684        size.top = size.left + Math.round(data.item.progress * (size.right - size.left));
    761685        guiObject.size = size;
    762     },
    763     "setGraphics": function(data)
    764     {
    765         if (data.template.icon)
    766             data.icon.sprite = "stretched:session/portraits/" + data.template.icon;
    767686
     687        if (template.icon)
     688            data.icon.sprite = "stretched:session/portraits/" + template.icon;
     689
    768690        data.button.enabled = controlsPlayer(data.unitEntState.player);
     691
     692        setPanelObjectPosition(data.button, data.i, data.rowLength);
     693        return true;
    769694    }
    770695};
    771696
     
    795720    },
    796721    "hideItem": function(i, rowLength) // called when no item is found
    797722    {
    798         Engine.GetGUIObjectByName("unitResearchButton["+i+"]").hidden = true;
     723        Engine.GetGUIObjectByName("unitResearchButton[" + i + "]").hidden = true;
    799724        // 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;
     725        Engine.GetGUIObjectByName("unitResearchButton[" + (i + rowLength) + "]").hidden = true;
     726        Engine.GetGUIObjectByName("unitResearchPair[" + i + "]").hidden = true;
    802727    },
    803     "addData": function(data)
     728    "setupButton": function(data)
    804729    {
    805730        if (!data.item.tech)
    806731        {
     
    807732            g_SelectionPanels.Research.hideItem(data.i, data.rowLength);
    808733            return false;
    809734        }
    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;
     735        let techs = data.item.tech.pair ? [data.item.tech.bottom, data.item.tech.top] : [data.item.tech];
    815736
    816         for (let template of data.template)
    817             for (let res in template.cost)
    818                 template.cost[res] *= data.item.techCostMultiplier[res];
     737        // start position (start at the bottom)
     738        let position = data.i + data.rowLength;
    819739
    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];
     740        // only show the top button for pairs
     741        if (!data.item.tech.pair)
     742            Engine.GetGUIObjectByName("unitResearchButton[" + data.i + "]").hidden = true;
    824743
    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+"]"));
     744        // set up the tech connector
     745        let pair = Engine.GetGUIObjectByName("unitResearchPair[" + data.i + "]");
     746        pair.hidden = data.item.tech.pair == null;
     747        setPanelObjectPosition(pair, data.i, data.rowLength);
    828748
    829         data.icon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchIcon["+p+"]"));
    830         data.unchosenIcon = data.positions.map(p => Engine.GetGUIObjectByName("unitResearchUnchosenIcon["+p+"]"));
     749        // handle one or two techs
     750        for (let i in techs)
     751        {
     752            let tech = techs[i];
     753            let template = GetTechnologyData(tech);
     754            if (!template)
     755                return false;
    831756
    832         data.neededResources = data.template.map(t => Engine.GuiInterfaceCall("GetNeededResources", {
    833             "cost": t.cost,
    834             "player": data.unitEntState.player
    835         }));
     757            for (let res in template.cost)
     758                template.cost[res] *= data.item.techCostMultiplier[res];
    836759
    837         data.requirementsPassed = data.entType.map(e => Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
    838             "tech": e,
    839             "player": data.unitEntState.player
    840         }));
     760            let neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     761                "cost": template.cost,
     762                "player": data.unitEntState.player
     763            });
    841764
    842         data.pair = Engine.GetGUIObjectByName("unitResearchPair["+data.i+"]");
     765            let requirementsPassed = Engine.GuiInterfaceCall("CheckTechnologyRequirements", {
     766                "tech": tech,
     767                "player": data.unitEntState.player
     768            });
    843769
    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];
     770            let button = Engine.GetGUIObjectByName("unitResearchButton[" + position + "]");
     771            let icon = Engine.GetGUIObjectByName("unitResearchIcon[" + position + "]");
     772
     773            let tooltip = "";
    852774            tooltip = getEntityNamesFormatted(template);
    853775            if (template.tooltip)
    854776                tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    855777
    856778            tooltip += "\n" + getEntityCostTooltip(template);
    857             if (!data.requirementsPassed[i])
     779            if (!requirementsPassed)
    858780            {
    859781                tooltip += "\n" + template.requirementsTooltip;
    860782                if (template.classRequirements)
    861783                {
    862                     var player = data.unitEntState.player;
    863                     var current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
    864                     var remaining = template.classRequirements.number - current;
     784                    let player = data.unitEntState.player;
     785                    let current = GetSimState().players[player].classCounts[template.classRequirements.class] || 0;
     786                    let remaining = template.classRequirements.number - current;
    865787                    tooltip += " " + sprintf(translatePlural("Remaining: %(number)s to build.", "Remaining: %(number)s to build.", remaining), { "number": remaining });
    866788                }
    867789            }
    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;
     790            if (neededResources)
     791                tooltip += getNeededResourcesTooltip(neededResources);
     792            button.tooltip = tooltip;
     793
     794            button.onpress = function () {
     795                addResearchToQueue(data.unitEntState.id, tech);
     796            };
     797
     798            if (data.item.tech.pair)
     799            {
     800                // on mouse enter, show a cross over the other icon
     801                let otherPosition = (position + data.rowLength) % (2 * data.rowLength);
     802                let unchosenIcon = Engine.GetGUIObjectByName("unitResearchUnchosenIcon[" + otherPosition + "]");
     803                button.onmouseenter = function() {
     804                        unchosenIcon.hidden = false;
    889805                };
    890             })(others, data.unchosenIcon);
    891             button.onmouseleave = (function(others, icons) {
    892                 return function() {
    893                     for (var j of others)
    894                         icons[j].hidden = true;
     806                button.onmouseleave = function() {
     807                        unchosenIcon.hidden = true;
    895808                };
    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];
     809            }
     810
    904811            button.hidden = false;
    905             var modifier = "";
    906             if (!data.requirementsPassed[i])
     812            let modifier = "";
     813            if (!requirementsPassed)
    907814            {
    908815                button.enabled = false;
    909                 modifier += "color: 0 0 0 127:";
     816                modifier += "color: " + g_GuiColors.limited + ":";
    910817                modifier += "grayscale:";
    911818            }
    912             else if (data.neededResources[i])
     819            else if (neededResources)
    913820            {
    914821                button.enabled = false;
    915                 modifier += resourcesToAlphaMask(data.neededResources[i]) + ":";
     822                modifier += resourcesToAlphaMask(neededResources) + ":";
    916823            }
    917824            else
    918825                button.enabled = controlsPlayer(data.unitEntState.player);
    919826
    920             if (data.template[i].icon)
    921                 data.icon[i].sprite = modifier + "stretched:session/portraits/" + data.template[i].icon;
     827            if (template.icon)
     828                icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     829
     830            setPanelObjectPosition(button, position, data.rowLength);
     831
     832            // prepare to handle the top button (if any)
     833            position -= data.rowLength;
    922834        }
    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);
     835
     836
     837        return true;
    933838    }
    934839};
    935840
     
    946851            return [];
    947852        return g_Selection.groups.getTemplateNames();
    948853    },
    949     "addData": function(data)
     854    "setupButton": function(data)
    950855    {
    951         data.entType = data.item;
    952         data.template = GetTemplateData(data.entType);
    953         if (!data.template)
     856        let template = GetTemplateData(data.item);
     857        if (!template)
    954858            return false;
    955         data.name = getEntityNames(data.template);
    956859
    957         var ents = g_Selection.groups.getEntsByName(data.item);
    958         data.count = ents.length;
    959         for (var ent of ents)
     860        let ents = g_Selection.groups.getEntsByName(data.item);
     861        for (let ent of ents)
    960862        {
    961             var state = GetEntityState(ent);
     863            let state = GetEntityState(ent);
    962864
    963865            if (state.resourceCarrying && state.resourceCarrying.length !== 0)
    964866            {
    965867                if (!data.carried)
    966868                    data.carried = {};
    967                 var carrying = state.resourceCarrying[0];
     869                let carrying = state.resourceCarrying[0];
    968870                if (data.carried[carrying.type])
    969871                    data.carried[carrying.type] += carrying.amount;
    970872                else
     
    975877            {
    976878                if (!data.carried)
    977879                    data.carried = {};
    978                 var amount = state.trader.goods.amount;
    979                 var type = state.trader.goods.type;
    980                 var totalGain = amount.traderGain;
     880                let amount = state.trader.goods.amount;
     881                let type = state.trader.goods.type;
     882                let totalGain = amount.traderGain;
    981883                if (amount.market1Gain)
    982884                    totalGain += amount.market1Gain;
    983885                if (amount.market2Gain)
     
    988890                    data.carried[type] = totalGain;
    989891            }
    990892        }
    991         return true;
    992     },
    993     "setTooltip": function(data)
    994     {
    995         let tooltip = data.name;
     893
     894        let tooltip = getEntityNames(template);
    996895        if (data.carried)
    997896            tooltip += "\n" + Object.keys(data.carried).map(res =>
    998897                getCostComponentDisplayIcon(res) + data.carried[res]
    999898            ).join(" ");
    1000899        data.button.tooltip = tooltip;
    1001     },
    1002     "setCountDisplay": function(data)
    1003     {
    1004         data.countDisplay.caption = data.count || "";
    1005     },
    1006     "setAction": function(data)
    1007     {
     900
     901        data.countDisplay.caption = ents.length || "";
     902
    1008903        data.button.onpressright = function() { changePrimarySelectionGroup(data.item, true); };
    1009904        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;
     905
     906        if (template.icon)
     907            data.icon.sprite = "stretched:session/portraits/" + template.icon;
     908
     909        setPanelObjectPosition(data.button, data.i, data.rowLength);
     910        return true;
    1015911    }
    1016912};
    1017913
     
    1027923            return [];
    1028924        return unitEntState.unitAI.possibleStances;
    1029925    },
    1030     "addData": function(data)
     926    "setupButton": function(data)
    1031927    {
    1032         data.stanceSelected = Engine.GuiInterfaceCall("IsStanceSelected", {
     928        data.button.onPress = function() { performStance(data.unitEntState, data.item); };
     929
     930        data.button.tooltip = getStanceDisplayName(data.item) + "\n" +
     931            "[font=\"sans-13\"]" + getStanceTooltip(data.item) + "[/font]";
     932
     933        data.guiSelection.hidden = !Engine.GuiInterfaceCall("IsStanceSelected", {
    1033934            "ents": data.selection,
    1034935            "stance": data.item
    1035936        });
     937        data.icon.sprite = "stretched:session/icons/stances/" + data.item + ".png";
     938        data.button.enabled = controlsPlayer(data.unitEntState.player);
     939
     940        setPanelObjectPosition(data.button, data.i, data.rowLength);
    1036941        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);
    1051942    }
    1052943};
    1053944
     
    1061952    {
    1062953        return getAllTrainableEntitiesFromSelection();
    1063954    },
    1064     "addData": function(data)
     955    "setupButton": function(data)
    1065956    {
    1066         data.entType = data.item;
    1067         data.template = GetTemplateData(data.entType);
    1068         if (!data.template)
     957        let template = GetTemplateData(data.item);
     958        if (!template)
    1069959            return false;
    1070960
    1071         data.technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
    1072             "tech": data.template.requiredTechnology,
     961        let technologyEnabled = Engine.GuiInterfaceCall("IsTechnologyResearched", {
     962            "tech": template.requiredTechnology,
    1073963            "player": data.unitEntState.player
    1074964        });
    1075965
    1076         var [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
    1077             getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.entType, data.selection);
     966        let [buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch] =
     967            getTrainingBatchStatus(data.playerState, data.unitEntState.id, data.item, data.selection);
    1078968
    1079         data.buildingsCountToTrainFullBatch = buildingsCountToTrainFullBatch;
    1080         data.fullBatchSize = fullBatchSize;
    1081         data.remainderBatch = remainderBatch;
    1082 
    1083         data.trainNum = buildingsCountToTrainFullBatch || 1; // train at least one unit
     969        let trainNum = buildingsCountToTrainFullBatch || 1;
    1084970        if (Engine.HotkeyIsPressed("session.batchtrain"))
    1085             data.trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
     971            trainNum = buildingsCountToTrainFullBatch * fullBatchSize + remainderBatch;
    1086972
    1087         if (data.template.cost)
    1088             data.neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
    1089                 "cost": multiplyEntityCosts(data.template, data.trainNum),
     973        let neededResources;
     974        if (template.cost)
     975            neededResources = Engine.GuiInterfaceCall("GetNeededResources", {
     976                "cost": multiplyEntityCosts(template, trainNum),
    1090977                "player": data.unitEntState.player
    1091978            });
    1092979
    1093         return true;
    1094     },
    1095     "setAction": function(data)
    1096     {
    1097980        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));
     981
     982        data.countDisplay.caption = trainNum > 1 ? trainNum : "";
     983
     984        let tooltip = "";
     985        let key = Engine.ConfigDB_GetValue("user", "hotkey.session.queueunit." + (data.i + 1));
    1107986        if (key)
    1108             tooltip += "[color=\"255 251 131\"][font=\"sans-bold-16\"]\\[" + key + "][/font][/color] ";
     987            tooltip += colorizeText(g_GuiColors.hotkey, "[font=\"sans-bold-16\"]\\[" + key + "][/font] ");
    1109988
    1110         tooltip += getEntityNamesFormatted(data.template);
    1111         tooltip += getVisibleEntityClassesFormatted(data.template);
    1112         tooltip += getAurasTooltip(data.template);
     989        tooltip += getEntityNamesFormatted(template);
     990        tooltip += getVisibleEntityClassesFormatted(template);
     991        tooltip += getAurasTooltip(template);
    1113992
    1114         if (data.template.tooltip)
    1115             tooltip += "\n[font=\"sans-13\"]" + data.template.tooltip + "[/font]";
     993        if (template.tooltip)
     994            tooltip += "\n[font=\"sans-13\"]" + template.tooltip + "[/font]";
    1116995
    1117         tooltip += "\n" + getEntityCostTooltip(data.template, data.trainNum, data.unitEntState.id);
     996        tooltip += "\n" + getEntityCostTooltip(template, trainNum, data.unitEntState.id);
    1118997
    1119         data.limits = getEntityLimitAndCount(data.playerState, data.entType);
     998        let limits = getEntityLimitAndCount(data.playerState, data.item);
    1120999
    1121         tooltip += formatLimitString(data.limits.entLimit, data.limits.entCount, data.limits.entLimitChangers);
    1122         if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") === "true")
     1000        tooltip += formatLimitString(limits.entLimit, limits.entCount, limits.entLimitChangers);
     1001        if (Engine.ConfigDB_GetValue("user", "showdetailedtooltips") == "true")
    11231002        {
    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);
     1003            if (template.health)
     1004                tooltip += "\n[font=\"sans-bold-13\"]" + translate("Health:") + "[/font] " + template.health;
     1005            if (template.attack)
     1006                tooltip += "\n" + getAttackTooltip(template);
     1007            if (template.armour)
     1008                tooltip += "\n" + getArmorTooltip(template.armour);
     1009            if (template.speed)
     1010                tooltip += "\n" + getSpeedTooltip(template);
    11321011        }
    1133         tooltip += "[color=\"255 251 131\"]" + formatBatchTrainingString(data.buildingsCountToTrainFullBatch, data.fullBatchSize, data.remainderBatch) + "[/color]";
    1134         if (!data.technologyEnabled)
     1012        tooltip += colorizeText(g_GuiColors.hotkey, formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize, remainderBatch));
     1013        if (!technologyEnabled)
    11351014        {
    1136             var techName = getEntityNames(GetTechnologyData(data.template.requiredTechnology));
     1015            let techName = getEntityNames(GetTechnologyData(template.requiredTechnology));
    11371016            tooltip += "\n" + sprintf(translate("Requires %(technology)s"), { "technology": techName });
    11381017        }
    1139         if (data.neededResources)
    1140             tooltip += getNeededResourcesTooltip(data.neededResources);
     1018        if (neededResources)
     1019            tooltip += getNeededResourcesTooltip(neededResources);
    11411020
    11421021        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();
     1022
     1023        let modifier = "";
     1024        if (!technologyEnabled || limits.canBeAddedCount == 0)
     1025        {
     1026            data.button.enabled = false;
     1027            modifier += "color: " + g_GuiColors.limited + ":grayscale:";
     1028        }
     1029        else if (neededResources)
     1030        {
     1031            data.button.enabled = false;
     1032            modifier += resourcesToAlphaMask(neededResources) +":";
     1033        }
     1034        else
     1035            data.button.enabled = controlsPlayer(data.unitEntState.player);
     1036
     1037        if (template.icon)
     1038            data.icon.sprite = modifier + "stretched:session/portraits/" + template.icon;
     1039
     1040        let index = data.i + getNumberOfRightPanelButtons();
    11491041        setPanelObjectPosition(data.button, index, data.rowLength);
     1042        return true;
    11501043    }
    11511044};
    11521045
     
    11581051 *
    11591052 * Note that the panel needs to appear in the list to get rendered.
    11601053 */
    1161 var g_PanelsOrder = [
     1054let g_PanelsOrder = [
    11621055    // LEFT PANE
    11631056    "Barter", // must always be visible on markets
    11641057    "Garrison", // more important than Formation, as you want to see the garrisoned units in ships
  • 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";