Ticket #3670: 3670_reqTooltips_v1-3.patch

File 3670_reqTooltips_v1-3.patch, 10.6 KB (added by s0600204, 7 years ago)

Relocate GetTemplateNamesByClass function to GuiInterface

  • binaries/data/mods/public/globalscripts/Templates.js

    diff --git a/binaries/data/mods/public/globalscripts/Templates.js b/binaries/data/mods/public/globalscripts/Templates.js
    index db54ca469..a782518d7 100644
    a b function GetTechnologyDataHelper(template, civ, resources)  
    376376
    377377    ret.description = template.description;
    378378
     379    if (template.top)
     380        ret.pair = [template.top, template.bottom];
     381
    379382    return ret;
    380383}
    381384
  • binaries/data/mods/public/gui/common/tooltips.js

    diff --git a/binaries/data/mods/public/gui/common/tooltips.js b/binaries/data/mods/public/gui/common/tooltips.js
    index 460c52061..00f28471d 100644
    a b const g_TooltipTextFormats = {  
    22    "unit": ['[font="sans-10"][color="orange"]', '[/color][/font]'],
    33    "header": ['[font="sans-bold-13"]', '[/font]'],
    44    "body": ['[font="sans-13"]', '[/font]'],
    5     "comma": ['[font="sans-12"]', '[/font]']
     5    "comma": ['[font="sans-12"]', '[/font]'],
     6    "emphasis": ['[font="sans-bold-13"]', '[/font]']
    67};
    78
    89const g_AttackTypes = {
    function commaFont(text)  
    4243    return g_TooltipTextFormats.comma[0] + text + g_TooltipTextFormats.comma[1];
    4344}
    4445
     46function emphasisFont(text)
     47{
     48    return g_TooltipTextFormats.emphasis[0] + text + g_TooltipTextFormats.emphasis[1];
     49}
     50
    4551function getEntityTooltip(template)
    4652{
    4753    if (!template.tooltip)
    function getRequiredTechnologyTooltip(technologyEnabled, requiredTechnology, civ  
    465471        return "";
    466472
    467473    return sprintf(translate("Requires %(technology)s"), {
    468         "technology": getEntityNames(GetTechnologyData(requiredTechnology, civ))
     474        "technology": emphasisFont(getEntityNames(GetTechnologyData(requiredTechnology, civ)))
    469475    });
    470476}
    471477
  • binaries/data/mods/public/gui/session/selection_panels.js

    diff --git a/binaries/data/mods/public/gui/session/selection_panels.js b/binaries/data/mods/public/gui/session/selection_panels.js
    index 74789e0cd..6a7b98265 100644
    a b g_SelectionPanels.Research = {  
    828828            ].map(func => func(template));
    829829
    830830            if (!requirementsPassed)
    831             {
    832                 let tip = template.requirementsTooltip;
    833                 let reqs = template.reqs;
    834                 for (let req of reqs)
    835                 {
    836                     if (!req.entities)
    837                         continue;
    838 
    839                     let entityCounts = [];
    840                     for (let entity of req.entities)
    841                     {
    842                         let current = 0;
    843                         switch (entity.check)
    844                         {
    845                         case "count":
    846                             current = playerState.classCounts[entity.class] || 0;
    847                             break;
    848 
    849                         case "variants":
    850                             current = playerState.typeCountsByClass[entity.class] ?
    851                                 Object.keys(playerState.typeCountsByClass[entity.class]).length : 0;
    852                             break;
    853                         }
    854 
    855                         let remaining = entity.number - current;
    856                         if (remaining < 1)
    857                             continue;
    858 
    859                         entityCounts.push(sprintf(translatePlural("%(number)s entity of class %(class)s", "%(number)s entities of class %(class)s", remaining), {
    860                             "number": remaining,
    861                             "class": entity.class
    862                         }));
    863                     }
    864 
    865                     tip += " " + sprintf(translate("Remaining: %(entityCounts)s"), {
    866                         "entityCounts": entityCounts.join(translate(", "))
    867                     });
    868                 }
    869                 tooltips.push(tip);
    870             }
     831                tooltips.push(template.requirementsTooltip || deriveTechRequirementsTooltip(template.reqs, player));
     832
    871833            tooltips.push(getNeededResourcesTooltip(neededResources));
    872834            button.tooltip = tooltips.filter(tip => tip).join("\n");
    873835
  • binaries/data/mods/public/gui/session/selection_panels_helpers.js

    diff --git a/binaries/data/mods/public/gui/session/selection_panels_helpers.js b/binaries/data/mods/public/gui/session/selection_panels_helpers.js
    index 6b9f507d6..cb5656eac 100644
    a b function formatBatchTrainingString(buildingsCountToTrainFullBatch, fullBatchSize  
    144144    }) + "[/font]";
    145145}
    146146
     147/**
     148 *
     149 * @param reqs {array} Civ-specific requirements
     150 */
     151function deriveTechRequirementsTooltip(reqs, player)
     152{
     153    let optionalTexts = [];
     154    let playerState = GetSimState().players[player];
     155
     156    for (let req of reqs)
     157    {
     158        let requirementTexts = [];
     159
     160        if (req.techs)
     161        {
     162            let requiredTechs = [];
     163
     164            for (let tech of req.techs)
     165                if (!playerState.researchedTechs[tech])
     166                {
     167                    if (tech.slice(0, 4) == "pair")
     168                    {
     169                        let pair = GetTechnologyData(tech).pair;
     170                        requiredTechs.push(sprintf(translate("%(firstTechOfPair)s or %(secondTechOfPair)s"), {
     171                            "firstTechOfPair": emphasisFont(getEntityNames(GetTechnologyData(pair[0]))),
     172                            "secondTechOfPair": emphasisFont(getEntityNames(GetTechnologyData(pair[1])))
     173                        }));
     174                    }
     175                    else
     176                        requiredTechs.push(emphasisFont(getEntityNames(GetTechnologyData(tech))));
     177                }
     178
     179            if (requiredTechs.length)
     180            {
     181                if (requiredTechs.length == 1)
     182                    requirementTexts.push(requiredTechs[0]);
     183                else
     184                    requirementTexts.push(sprintf(translate("%(listOfTechs)s and %(lastTechInList)s"), {
     185                        // Translation: This comma is used for separating first to penultimate elements in the list
     186                        "listOfTechs": requiredTechs.slice(0,-1).join(translate(", ")),
     187                        "lastTechInList": requiredTechs.slice(-1)
     188                    }));
     189            }
     190        }
     191
     192        if (req.entities)
     193        {
     194            let requiredEnts = [];
     195            for (let entity of req.entities)
     196            {
     197                let current = 0;
     198                switch (entity.check)
     199                {
     200                case "count":
     201                    current = playerState.classCounts[entity.class] || 0;
     202                    break;
     203
     204                case "variants":
     205                    current = playerState.typeCountsByClass[entity.class] ?
     206                        Object.keys(playerState.typeCountsByClass[entity.class]).length : 0;
     207                    break;
     208                }
     209
     210                let remaining = entity.number - current;
     211                if (remaining < 1)
     212                    continue;
     213
     214                let nameList = [];
     215                for (let templateName of playerState.templatesByClass[entity.class])
     216                    // We use the generic name only to keep the tooltip short
     217                    nameList.push(emphasisFont(GetTemplateData(templateName).name.generic));
     218
     219                if (nameList.length == 1)
     220                    nameList = nameList[0];
     221                else
     222                    nameList = sprintf(translate("%(listOfEntityNames)s or %(lastEntityNameInList)s"), {
     223                        // Translation: This comma is used for separating first to penultimate elements in the list
     224                        "listOfEntityNames": nameList.slice(0,-1).join(translate(", ")),
     225                        "lastEntityNameInList": nameList.slice(-1)
     226                    });
     227
     228                requiredEnts.push(sprintf(translate("%(number)s %(listOfTemplateNames)s (%(remaining)s remaining)"), {
     229                    "number": entity.number,
     230                    "listOfTemplateNames": nameList,
     231                    "remaining": remaining,
     232                }));
     233            }
     234            if (requiredEnts.length)
     235                // Translation: This is used to join multiple technology requirements
     236                requirementTexts.push(requiredEnts.join(translate(" and ")));
     237        }
     238
     239        // Translation: This is used to join multiple technology requirements
     240        optionalTexts.push(requirementTexts.join(translate(" and ")));
     241    }
    147242
     243    return sprintf(translate("Requires %(requirementTexts)s"), {
     244        // Translation: This is used to join multiple technology requirements
     245        "requirementTexts": optionalTexts.join(translate("; or "))
     246    });
     247}
  • binaries/data/mods/public/simulation/components/GuiInterface.js

    diff --git a/binaries/data/mods/public/simulation/components/GuiInterface.js b/binaries/data/mods/public/simulation/components/GuiInterface.js
    index 04ced0a3e..50cb7a819 100644
    a b GuiInterface.prototype.Init = function()  
    2929    this.notifications = [];
    3030    this.renamedEntities = [];
    3131    this.miragedEntities = [];
     32    this.playerTemplatesByClass = [];
    3233    this.timeNotificationID = 1;
    3334    this.timeNotifications = [];
    3435    this.entsRallyPointsDisplayed = [];
    GuiInterface.prototype.GetSimulationState = function()  
    106107            "teamsLocked": cmpPlayer.GetLockTeams(),
    107108            "cheatsEnabled": cmpPlayer.GetCheatsEnabled(),
    108109            "disabledTemplates": cmpPlayer.GetDisabledTemplates(),
     110            "templatesByClass": this.GetPlayerTemplatesByName(i),
    109111            "hasSharedDropsites": cmpPlayer.HasSharedDropsites(),
    110112            "hasSharedLos": cmpPlayer.HasSharedLos(),
    111113            "phase": phase,
    GuiInterface.prototype.GetStartedResearch = function(player)  
    718720    return ret;
    719721};
    720722
     723/**
     724 * For a given player, produces an object that has classes as keys and an array of template names as values
     725 */
     726GuiInterface.prototype.GetPlayerTemplatesByName = function(player)
     727{
     728    if (this.playerTemplatesByClass[player])
     729        return this.playerTemplatesByClass[player];
     730
     731    let playerEntity = Engine.QueryInterface(SYSTEM_ENTITY, IID_PlayerManager).GetPlayerByID(player);
     732
     733    // todo: find a better starting point
     734    let cmpTechnologyManager = Engine.QueryInterface(playerEntity, IID_TechnologyManager);
     735    if (!cmpTechnologyManager)
     736        return {};
     737
     738    let cmpPlayer = Engine.QueryInterface(playerEntity, IID_Player);
     739    let disabledTemplates = cmpPlayer.GetDisabledTemplates();
     740
     741    let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     742    let entsByClass = {};
     743    let toParse = Object.keys(cmpTechnologyManager.GetTypeCountsByClass().Structure);
     744    let templateData = new Map();
     745   
     746
     747    while (toParse.length)
     748    {
     749        let toParseNext = [];
     750
     751        for (let templateName of toParse)
     752        {
     753            if (!cmpTemplateManager.TemplateExists(templateName))
     754                continue;
     755
     756            let template = cmpTemplateManager.GetTemplate(templateName);
     757            templateData.set(templateName, {
     758                "parent": template["@parent"],
     759                "classes": GetIdentityClasses(template.Identity)
     760            });
     761
     762            let entStrings = [];
     763            if (template.ProductionQueue && template.ProductionQueue.Entities)
     764                entStrings.push(template.ProductionQueue.Entities._string || "");
     765
     766            if (template.Builder && template.Builder.Entities)
     767                entStrings.push(template.Builder.Entities._string || "");
     768
     769            for (let ent of entStrings.join(" ").replace(/\{civ\}/g, cmpPlayer.GetCiv()).split(/\s+/))
     770                if (ent && !templateData.has(ent) && toParseNext.indexOf(ent) < 0 && !disabledTemplates[ent])
     771                    toParseNext.push(ent);
     772        }
     773        toParse = toParseNext;
     774    }
     775
     776    for (let template of templateData)
     777    {
     778        // Crudely filter out *_house, *_trireme and *_barracks copies
     779        if (templateData[template[1].parent])
     780            continue;
     781
     782        for (let cls of template[1].classes)
     783        {
     784            if (!entsByClass[cls])
     785                entsByClass[cls] = [];
     786            entsByClass[cls].push(template[0]);
     787        }
     788    }
     789
     790    this.playerTemplatesByClass[player] = entsByClass;
     791    return this.playerTemplatesByClass[player];
     792};
     793
     794GuiInterface.prototype.OnDisabledTemplatesChanged = function(msg)
     795{
     796    // No player identifier is passed, so erase all
     797    this.playerTemplatesByClass = [];
     798};
     799
    721800// Returns the battle state of the player.
    722801GuiInterface.prototype.GetBattleState = function(player)
    723802{