Ticket #3670: 3670_reqTooltips_v1-2.patch

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

Show list of templates that match the required class instead of the class itself (http://i.imgur.com/iigxJm2.jpg) Works with both Classes or VisibleClasses.

  • 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..1826f4046 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                requirementTexts.push(requiredEnts.join(translate(" and ")));
     236                // Translation: This is used to join multiple technology requirements
     237        }
     238
     239        optionalTexts.push(requirementTexts.join(translate(" and ")));
     240        // Translation: This is used to join multiple technology requirements
     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..31667a6c6 100644
    a b GuiInterface.prototype.GetSimulationState = function()  
    106106            "teamsLocked": cmpPlayer.GetLockTeams(),
    107107            "cheatsEnabled": cmpPlayer.GetCheatsEnabled(),
    108108            "disabledTemplates": cmpPlayer.GetDisabledTemplates(),
     109            "templatesByClass": cmpPlayer.GetTemplateNamesByClass(),
    109110            "hasSharedDropsites": cmpPlayer.HasSharedDropsites(),
    110111            "hasSharedLos": cmpPlayer.HasSharedLos(),
    111112            "phase": phase,
  • binaries/data/mods/public/simulation/components/Player.js

    diff --git a/binaries/data/mods/public/simulation/components/Player.js b/binaries/data/mods/public/simulation/components/Player.js
    index 1c017bc30..747cd8f85 100644
    a b Player.prototype.Init = function()  
    3838    this.disabledTemplates = {};
    3939    this.disabledTechnologies = {};
    4040    this.startingTechnologies = [];
     41    this.classedTemplates = undefined;
    4142
    4243    // Initial resources and trading goods probability in steps of 5
    4344    let resCodes = Resources.GetCodes();
    Player.prototype.AddDisabledTemplate = function(template)  
    792793        "type": "resetselectionpannel",
    793794        "players": [this.GetPlayerID()]
    794795    });
     796    this.classedTemplates = undefined;
    795797};
    796798
    797799Player.prototype.RemoveDisabledTemplate = function(template)
    Player.prototype.RemoveDisabledTemplate = function(template)  
    804806        "type": "resetselectionpannel",
    805807        "players": [this.GetPlayerID()]
    806808    });
     809    this.classedTemplates = undefined;
    807810};
    808811
    809812Player.prototype.SetDisabledTemplates = function(templates)
    Player.prototype.SetDisabledTemplates = function(templates)  
    818821        "type": "resetselectionpannel",
    819822        "players": [this.GetPlayerID()]
    820823    });
     824    this.classedTemplates = undefined;
    821825};
    822826
    823827Player.prototype.GetDisabledTemplates = function()
    Player.prototype.SetStartingTechnologies = function(techs)  
    861865    this.startingTechnologies = techs;
    862866};
    863867
     868Player.prototype.GetTemplateNamesByClass = function()
     869{
     870    if (this.classedTemplates)
     871        return this.classedTemplates;
     872
     873    // todo: find a better starting point
     874    let cmpTechnologyManager = Engine.QueryInterface(this.entity, IID_TechnologyManager);
     875    if (!cmpTechnologyManager)
     876        return {};
     877
     878    let cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
     879    let entsByClass = {};
     880    let toParse = Object.keys(cmpTechnologyManager.GetTypeCountsByClass().Structure);
     881    let templateData = new Map();
     882
     883    while (toParse.length)
     884    {
     885        let toParseNext = [];
     886
     887        for (let templateName of toParse)
     888        {
     889            if (!cmpTemplateManager.TemplateExists(templateName))
     890                continue;
     891
     892            let template = cmpTemplateManager.GetTemplate(templateName);
     893            templateData.set(templateName, {
     894                "parent": template["@parent"],
     895                "classes": GetIdentityClasses(template.Identity)
     896            });
     897
     898            let entStrings = [];
     899            if (template.ProductionQueue && template.ProductionQueue.Entities)
     900                entStrings.push(template.ProductionQueue.Entities._string || "");
     901
     902            if (template.Builder && template.Builder.Entities)
     903                entStrings.push(template.Builder.Entities._string || "");
     904
     905            for (let ent of entStrings.join(" ").replace(/\{civ\}/g, this.civ).split(/\s+/))
     906                if (ent && !templateData.has(ent) && toParseNext.indexOf(ent) < 0 && !this.disabledTemplates[ent])
     907                    toParseNext.push(ent);
     908        }
     909        toParse = toParseNext;
     910    }
     911
     912    for (let template of templateData)
     913    {
     914        // Crudely filter out *_house, *_trireme and *_barracks copies
     915        if (templateData[template[1].parent])
     916            continue;
     917
     918        for (let cls of template[1].classes)
     919        {
     920            if (!entsByClass[cls])
     921                entsByClass[cls] = [];
     922            entsByClass[cls].push(template[0]);
     923        }
     924    }
     925
     926    this.classedTemplates = entsByClass;
     927    return this.classedTemplates;
     928}
     929
    864930Engine.RegisterComponentType(IID_Player, "Player", Player);