Module:Tooltip/Types

This module depends on the following other modules:
Module:Arguments
Module:TableTools
Module:Tooltip

Documentation for this module may be created at Module:Tooltip/Types/doc

This module holds the main functions of Module:Building, Module:Law, Module:Production method, and Module:Technology. This is done to avoid loops.

------------------------------------------------------------------------------
-- 
--                                 Module:Tooltip/Types
-- 
-- Due to interconnected nature of tooltips, they must be held in the same module to avoid circular loops
------------------------------------------------------------------------------

local iconify = require('Module:Iconify').iconify
local getArgs = require('Module:Arguments').getArgs
local ttUtils = require('Module:Tooltip')
local size = require('Module:TableTools').size
local uString = mw.ustring
local uFormat = uString.format
local htmlCreate = mw.html.create
-- set tooltip style, imitates in-game tooltips
local gameTT = 'color: #c2bdb8; background: linear-gradient(to bottom,#47373a,#211b1c); border: solid #b19656; border-radius: 10px; width: max-content; padding: 5px;'


local p = {};

function p.main(frame)
    local args = getArgs(frame)
    local func = args.func or nil
    if type(p[func]) == 'function' then
        return p[func](args)
    else
        return '<span style="color:red; font-size:11px;">(unrecognized function "' .. func .. '" for [[Module:Tooltip/Types]])</span>[[Category:Pages with unrecognized module functions]]'
    end
end

function p.building(args)
    local index = tonumber(args.index)
    local buildID = uString.lower(args[1])
    local buildData = mw.loadData('Module:Building/List')[buildID] --load building data

    -- catch unknown/error
    if not buildData then
        return '<span style="color:red; font-size:11px;">(unrecognized building "' .. args[1] .. '" for [[Module:Building]])</span>[[Category:Pages with unrecognized buildings]]'
    end

    local loc = buildData.canLoc
    if (args[1] ~= loc) and (args[1] ~= buildData.altLoc) then
        args[1] = loc -- Don't use 'building_X' in display
    end

    local fromText = (args.from == 'pm') and 'Used by ' or ''

    local icon = buildData.icon or "Building "..uString.lower(args[1])..".png"

    -- set tooltip display as icon and text, short-circuit tooltip if out of index
    local displayText = uFormat("%s[[File:%s|%s|link=List of buildings#%s|%s]] %s", fromText, icon, args.width or '24px', loc, args[1], args[1])
    --local displayText = iconify{icon=args[1], group="building", image=icon, link="List of buildings#"..loc, mod=fromText, extra=args[1]}

    if args.fromfrom == 'building' then index = 4 end
    if type(index) ~= 'number' or index > 3 then -- any non number or index over 3 returns basic display
        return args.from and ttUtils.tooltip(displayText) or displayText
    end

    -- set up header subtitle, with building basic info
    local constr = uFormat([[%s <span title="Construction">Constr. '''%s'''</span>]], "[[File:State status construction.png|16px|link=Construction]]", buildData.cost)
    local infra = uFormat([[%s <span title="Infrastructure">Infra. '''%s'''</span>]], "[[File:State status infrastructure.png|16px|link=Infrastructure]]", buildData.infra)
    local urban = uFormat([[<span title="Urbanization">Urban. '''%s'''</span>]], buildData.urb)
    local subtitle = uFormat("<span>%s | %s | %s</span>", constr, infra, urban)

    -- start header with float left icon
    local header = ttUtils.headerStyle('2LR', {
        leftText=uFormat("[[File:%s|48px|link=List of buildings#%s|%s]]", icon, loc, loc),
        --leftText=iconify{icon=loc, group="building", image=icon, width="48px", text=0, link="List of buildings#"..loc, extra=loc},
        rightText=buildData.category,
        mainText=loc,
        subText=subtitle
    })

    -- start body with reqs and blockers
    local body = '<hr>'
    local reqs, notes
    if buildData.reqs and size(buildData.reqs) > 0 then
        reqs = '<div>Requires:' .. ttUtils.ttList(buildData.reqs, {index=(index + 1), from="building", fromfrom=args.from})..'</div>'
    end
    if buildData.notes and buildData.notes[1] then
        notes = ''
        for _, v in ipairs(buildData.notes) do
            notes = uFormat("%s<div>%s</div>", notes, v)
        end
        notes = '<div>Additional info:<div style="display:grid; margin-left:1em;">' .. notes .. '</div></div>'
    end
    if reqs or notes then
        reqs, notes = reqs or '', notes or ''
        body = body .. '<div style="display:grid; grid-template-columns: auto auto; gap:5px;">' .. reqs .. notes .. '</div><hr>'
    end

    -- no recursive tooltips please
    local pmIndex = index + 1
    if args.from == "pm" then pmIndex = 4 end

    -- continue tooltip for PMs, grid and flexbox to align them neatly
    if buildData.pm and buildData.pm[1] then
        body = body .. '<div style="display:grid; grid-template-columns: auto auto; gap:5px 10px;">'
        for _,pmg in ipairs(buildData.pm) do
            body = body .. '<div style="display:flex; flex-direction: column; align-items: flex-start">'
            if pmg.group then
               body = body .. tostring(htmlCreate('div'):cssText("color:white; font-weight:bold;"):wikitext(pmg.group))
            end
            for _,u in ipairs(pmg) do
                body = body..p.productionMethod{u, index=pmIndex, from="building", fromfrom=args.from}
            end
            body = body .. '</div>'
        end
        body = body .. '</div>'
    end

    return ttUtils.tooltip(displayText, header, body, {style=gameTT, index=index})
end

function p.productionMethod(args)
    local index = tonumber(args.index)
    local pmID = uString.lower(args[1])
    local pmData = mw.loadData('Module:Production method/List')[pmID] --load pm data

    -- catch unknown/error
    if not pmData then
        return '<span style="color:red; font-size:11px;">(unrecognized production method "' .. args[1] .. '" for [[Module:Production method]])</span>[[Category:Pages with unrecognized production methods]]'
    end

    -- translate from game icon list to wiki icon list, could offload to data page
    local icon = "Method "..pmData.icon..".png"

    local loc = pmData.loc

    -- set tooltip display as icon and text, short-circuit tooltip if out of index 
    local displayText = uFormat("[[File:%s|%s|link=List of production methods#%s|%s]] %s", icon, args.width or '24px', loc, loc, loc)
    --local displayText = iconify{icon=loc, image=icon, link="List of production methods#"..loc, extra=loc}
    if args.fromfrom == 'pm' then index = 4 end
    if type(index) ~= 'number' or index > 3 then -- any non number or index over 3 returns basic display
        if not args.from then
            return displayText
        else
            return ttUtils.tooltip(displayText)
        end
    end

    -- set up header subtitle, with building for pm, tooltip building if pm called directly
    local subtitle = ''
    if pmData.building == "Multiple" then
        subtitle = "Used by multiple buildings"
    elseif args.from ~= "building" then
        subtitle = p.building{pmData.building, index=(index+1), from="pm", fromfrom=args.from}
    else
        subtitle = uFormat("Used by [[File:Building %s.png|24px|link=List of buildings#%s|%s]] %s", uString.lower(pmData.building), pmData.building, pmData.building, pmData.building)
        --subtitle = iconify{icon=pmData.building, group="building", mod="Used by", link="List of buildings#"..pmData.building}
    end
    subtitle = '<span>' .. subtitle .. '</span>'

    local header = ttUtils.headerStyle('2LR', {
        leftText=uFormat("[[File:%s|48px|link=List of production methods#%s|%s]]", icon, loc, loc),
        --leftText=iconify{icon=loc, image=icon, width="48px", text=0, link="List of production methods#"..loc, extra=loc},
        rightText=pmData.group,
        mainText=loc,
        subText=subtitle
    })

    -- start body with reqs and blockers
    local body = '<hr>'
    local reqs, blockers
    if pmData.reqs and size(pmData.reqs) > 0 then
        reqs = '<div>Requires:' .. ttUtils.ttList(pmData.reqs, {index=(index + 1), from="pm", fromfrom=args.from})..'</div>'
    end
    if pmData.blockers and size(pmData.blockers) > 0 then
        blockers = '<div>Blocked by:' .. ttUtils.ttList(pmData.blockers, {index=(index+1), from="pm", fromfrom=args.from}) .. '</div>'
    end
    if reqs or blockers then
        reqs, blockers = reqs or '', blockers or ''
        body = body .. '<div style="display:grid; grid-template-columns: auto auto; gap:5px;">' .. reqs .. blockers .. '</div><hr>'
    end

    local effects = ''
    -- iterate effects
    if pmData.input and pmData.input[1] then
        effects = effects .. '<div>Input:<div style="margin-left:1em;">' .. ttUtils.effectList(pmData.input,{icon="Goods"}) .. '</div></div>'
    end
    if pmData.output and pmData.output[1] then
        effects = effects .. '<div>Output:<div style="margin-left:1em;">' .. ttUtils.effectList(pmData.output,{icon="Goods"}) .. '</div></div>'
    end
    if pmData.workforce and pmData.workforce[1] then
        effects = effects .. '<div>Workforce:<div style="margin-left:1em;">' .. ttUtils.effectList(pmData.workforce,{icon="pop",page="Profession"}) .. '</div></div>'
    end
    local modifiers = { ws = { }, ls = { }, us = { } }
    local modList = ''
    local function modConcat(t)
    	for k, _ in pairs(t) do
            for _, v in ipairs(t[k]) do
                table.insert(modifiers[k], v)
            end
    	end
    end
    if pmData.bm then modConcat(pmData.bm) end
    if pmData.sm then modConcat(pmData.sm) end
    if pmData.cm then modConcat(pmData.cm) end

    if modifiers.ws[1] then modList = modList..ttUtils.effectList(modifiers.ws) end
    if modifiers.ls[1] then modList = modList..'<br>Level scaled<br>'..ttUtils.effectList(modifiers.ls) end
    if modifiers.us[1] then modList = modList..'<br>Unscaled<br>'..ttUtils.effectList(modifiers.us) end
    if modList ~= '' then
        effects = effects .. '<div>Modifiers:<div style="margin-left:1em;">' .. modList .. '</div></div>'
    end
    if effects ~= '' then
        body = body .. tostring(htmlCreate('div'):cssText("display:grid; grid-template-columns: auto auto; gap:5px 10px;"):wikitext(effects))
    else
        body = body .. "No effects"
    end
    return ttUtils.tooltip(displayText, header, body, {style=gameTT, index=index})
end

function p.tech(args)
    local index = tonumber(args.index)
    local techID = uString.lower(args[1])
    local techData = mw.loadData('Module:Technology/List')[techID] --load pm data

    -- catch unknown/error
    if not techData then
        return '<span style="color:red; font-size:11px;">(unrecognized technology "' .. args[1] .. '" for [[Module:Technology]])</span>[[Category:Pages with unrecognized technologies]]'
    end

    local loc = techData.loc

    -- set tooltip display as icon and text, short-circuit tooltip if out of index
    --local displayText = uFormat("[[File:Invention %s.png|%s|link=%s technology#%s|%s]] %s", uString.lower(loc), args.width or '24px', techData.category, loc, loc, loc)
    local displayText = iconify{icon=loc, group="Invention", width=args.width or '24px', link=techData.category.." technology#"..loc, extra=loc} --[[File:Invention loc.png|24px|loc|link=cat tech#loc]]
    if args.fromfrom == 'tech' then index = 4 end
    if type(index) ~= 'number' or index > 3 then -- any non number or index over 3 returns basic display
        if not args.from then
            return displayText
        else
            return ttUtils.tooltip(displayText)
        end
    end

    -- set up header subtitle, with building for pm, tooltip building if pm called directly
    local cost = { "7,000", "10,000", "12,500", "15,000", "17,500" }
    cost = uFormat("Base cost: [[File:Innovation.png|16px|link=Innovation]] '''%s'''", cost[techData.era])
    local subtitle = uFormat("<span>Era: %s</span> | %s", techData.era, cost)

    local header = ttUtils.headerStyle('2LR', {
        --leftText=uFormat("[[File:Invention %s.png|48px|link=%s technology#%s|%s]]", uString.lower(loc), techData.category, loc, loc),
        leftText=iconify{icon=loc, group="Invention", width="48px", text=0, link=techData.category.." technology#"..loc, extra=loc},
        rightText=techData.category.." technology",
        mainText=loc,
        subText=subtitle
    })

    -- start body with reqs
    local body = '<hr>'
    if techData.reqs and techData.reqs[1] then
        local reqText = "Requires:"
        local reqList = {}
        if techData.reqs[2] then reqText = "Requires all of:" end
        for i, v in ipairs(techData.reqs) do
            reqList[i] = p.tech{v, index=index+1, from="tech", fromfrom=args.from}
        end
        body = body .. tostring(htmlCreate('div'):wikitext(reqText)
            :tag('div'):cssText('display:grid; justify-items: start; margin-left:1em;'):wikitext(table.concat(reqList)):done())..'<hr>'
        --body = body .. reqText .. ttUtils.ttList(techData.reqs, {index=(index+1), from="tech", fromfrom=args.from, multiKey="All of"})
    end

    local effects = ''

    if techData.unlocks and size(techData.unlocks) > 0 then
        effects = effects .. '<div>Unlocks:' .. ttUtils.ttList(techData.unlocks,{index=(index+1), from="tech", fromfrom=args.from, multiKey=""}) .. '</div>'
    end
    if techData.modifiers and techData.modifiers[1] then
        effects = effects .. '<div>Modifiers:<div style="margin-left:1em;">' .. ttUtils.effectList(techData.modifiers) .. '</div></div>'
    end

    if effects ~= '' then
        body = body .. tostring(htmlCreate('div'):cssText("display:grid; grid-template-columns: auto auto; gap:5px 10px;"):wikitext(effects))
    else
        body = body .. "No effects"
    end
    if techData.desc then
        body = body .. '<hr><div style="font-size:smaller; font-style:italic; line-height:1.2em; max-width:350px">' .. techData.desc .. '</div>'
    end
    return ttUtils.tooltip(displayText, header, body, {style=gameTT, index=index})
end

function p.law(args)
    local index = tonumber(args.index)
    local lawID = uString.lower(args[1])
    local lawData = mw.loadData('Module:Law/List')[lawID] --load pm data

    -- catch unknown/error
    if not lawData then
        return '<span style="color:red; font-size:11px;">(unrecognized law "' .. args[1] .. '" for [[Module:Law]])</span>[[Category:Pages with unrecognized laws]]'
    end

    local loc = lawData.loc
    -- set tooltip display as icon and text, short-circuit tooltip if out of index 
    --local displayText = uFormat("[[File:Law %s.png|%s|link=%s#%s|%s]] %s", uString.lower(loc), args.width or '24px', lawData.category, loc, loc, loc)
    local displayText = iconify{icon=loc, group="Law", width=args.width or '24px', link=uString.lower(lawData.category).."#"..loc, extra=loc}
    if args.fromfrom == 'law' then index = 4 end
    if type(index) ~= 'number' or index > 3 then -- any non number or index over 3 returns basic display
        if not args.from then
            return displayText
        else
            return ttUtils.tooltip(displayText)
        end
    end

    -- set up header subtitle, with building for pm, tooltip building if pm called directly
    subtitle = '<span>Law group: ' .. lawData.group .. '</span>'

    local header = ttUtils.headerStyle('2LR', {
        --leftText=uFormat("[[File:Law %s.png|48px|link=%s#%s|%s]]", uString.lower(loc), lawData.category, loc, loc),
        leftText=iconify{icon=loc, group="Law", width="48px", text=0, link=uString.lower(lawData.category).."#"..loc, extra=loc},
        rightText=lawData.category,
        mainText=loc,
        subText=subtitle
    })

    -- start body with reqs and blockers
    local body = '<hr>'
    local reqs, blockers
    if lawData.reqs and size(lawData.reqs) > 0 then
        reqs = '<div>Requires:' .. ttUtils.ttList(lawData.reqs, {index=(index + 1), from="law", fromfrom=args.from})..'</div>'
    end
    if lawData.blockers and size(lawData.blockers) > 0 then
        blockers = '<div>Blocked by:' .. ttUtils.ttList(lawData.blockers, {index=(index+1), from="law", fromfrom=args.from}) .. '</div>'
    end
    if reqs or blockers then
        reqs, blockers = reqs or '', blockers or ''
        body = body .. '<div style="display:grid; grid-template-columns: auto auto; gap:5px;">' .. reqs .. blockers .. '</div><hr>'
    end

    local effects = ''
    -- iterate effects
    if lawData.effects and lawData.effects[1] then
        effects = effects .. '<div>Effects:<div style="margin-left:1em;">' .. ttUtils.effectList(lawData.effects) .. '</div></div>'
    end
    if lawData.enact and lawData.enact[1] then
        effects = effects .. '<hr><div>When enacted:<div style="margin-left:1em;">' .. ttUtils.effectList(lawData.enact) .. '</div></div>'
    end
    if effects ~= '' then
        body = body .. tostring(htmlCreate('div'):wikitext(effects))
    else
        body = body .. "No effects"
    end
    if lawData.desc then
        body = body .. '<hr><div style="font-size:smaller; font-style:italic; line-height:1.2em; max-width:350px">' .. lawData.desc .. '</div>'
    end
    return ttUtils.tooltip(displayText, header, body, {style=gameTT, index=index})
end

return p