Welcome to the Umamusume Wiki! If you want to contribute, please read the guidelines.

Module:Game/Skills

From Umamusume Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Game/Skills/doc

--[[
    !! THIS PAGE IS MANAGED BY GITLAB !!
    ANY EDITS TO PAGE CONTENT WILL BE OVERWRITTEN
    TO MAKE CHANGES, PLEASE SUBMIT A MERGE REQUEST AT https://gitlab.com/umamusume-wiki/lua-modules
]]

local p = {}
local Data = require("Module:Game/Skills/Data")
local Cargo = require("Module:Cargo")
local Utils = require("Module:Utils")

---Generate lists of arguments for the Game Skill/tooltip template
---Use to batch create lots of skill links for pages that need them
---@param skillIds string[]
---@return any[] templateArgs keyed by support ID
function p.generateSkillLinks(skillIds)
    local skillIdsInsert = table.concat(skillIds, ',')
    local results = Cargo.query {
        from = 'Game_Skills',
        fields = { '_pageName=skillPage', 'skill_id', 'name_ro', 'name_ww', 'description_en' },
        where = string.format('skill_id IN (%s)', skillIdsInsert),
        limit = #skillIds
    }

    local skillDatas = Data.getBatch(skillIds)

    local merged = Utils.pairObjects(skillDatas, 'id', results, 'skill_id', true)

    local datas = {}
    for _, pair in ipairs(merged) do
        local cargoData, skillData = pair[2], pair[1] ---@type any

        if not cargoData then
            -- Generate expected page name for easy creation if the page does not exist
            cargoData = {
                skillPage = string.format("Game:Skills/%s", skillData.id)
            }
        end

        local templateArgs = {
            icon = skillData.iconPage,
            link = cargoData.skillPage,
            name_jp = skillData.nameJP,
            name_ro = cargoData.name_ro,
            name_ww = skillData.nameEN or cargoData.name_ww,
            description_jp = table.concat(skillData.descriptionJP, ""),
            description_en = skillData.descriptionEN and table.concat(skillData.descriptionEN, "") or
                cargoData.description_en,
        }
        datas[skillData.id] = templateArgs
    end
    return datas
end

function p.skillTooltip(frame)
    local skillId = Utils.normalString(mw.text.decode(frame.args[1]))
    if not skillId then return end

    local templateData = p.generateSkillLinks({ skillId })[skillId]
    if not templateData then return end

    local tooltipText = frame:expandTemplate {
        title = "Game Skill/tooltip",
        args = templateData
    }
    local text = string.format('[[File:%s|22px]] %s', templateData.icon, tooltipText)
    return text
end

function p.skillListTable(frame)
    local tableType = mw.text.decode(frame.args[1])
    local wheres = {
        ['ults'] = 'skill_type = "ult" OR skill_type = "ult_2"',
        ['inherited_ults'] = 'skill_type = "ult_inherit"',
        ['basic'] = 'skill_type = "basic"',
        ['debuff'] = 'skill_type = "debuff"',
        ['rare'] = 'skill_type = "rare"',
        ['evolved'] = 'skill_type = "upgraded_scenario" OR skill_type = "upgraded_card"'
    }
    local results = Cargo.query {
        from = 'Game_Skills',
        fields = { '_pageName=skillPage', 'skill_id', 'name_ro', 'name_ww', 'description_en' },
        where = wheres[tableType],
        limit = '9999',
        orderBy = 'sort_value'
    }

    local skillIds = {}
    for _, data in ipairs(results) do
        table.insert(skillIds, tostring(data.skill_id))
    end
    local skillDatas = Data.getBatch(skillIds)
    local paired = Utils.pairObjects(results, 'skill_id', skillDatas, 'id')

    local mwtable = mw.html.create('table'):addClass('wikitable'):addClass('sortable'):addClass('mw-collapsible')
    local headerRow = mw.html.create('tr')
    local headerTexts = { 'Icon', 'Skill Names', 'Description', 'Skill Points', 'Eval. Points', 'Point Ratio' }
    for _, value in ipairs(headerTexts) do
        headerRow:node(mw.html.create('th'):wikitext(value))
    end
    mwtable:node(headerRow)

    for _, pair in ipairs(paired) do
        local cargoData, skillData = pair[1], pair[2] ---@type any

        local mainName = skillData.nameEN or cargoData.name_ww or cargoData.name_ro
        local subName = skillData.nameJP ---@type string|nil
        if mainName == nil or mainName == '' then
            mainName = skillData.nameJP
            subName = nil
        end

        local description = table.concat(skillData.descriptionJP, "")
        if skillData.descriptionEN ~= nil then
            description = table.concat(skillData.descriptionEN, "")
        elseif cargoData.description_en ~= nil and cargoData.description_en ~= '' then
            description = cargoData.description_en
        end

        local mainLink = string.format("'''[[%s|%s]]'''", cargoData.skillPage, mainName)
        if not skillData.availableEN then
            mainLink = mainLink .. frame:expandTemplate {
                title = "tooltip",
                args = { "<sup>JP</sup>", "Only on the Japanese version" }
            }
        end
        local nameText = (subName == nil or subName == '') and mainLink or
            string.format("%s<br/>''%s''", mainLink, subName)

        local row = mw.html.create('tr')
        local renderTexts = {
            '[[File:' .. skillData.iconPage .. '|48px]]',
            nameText,
            description,
            skillData.skillPointCost and (skillData.skillPointCost + (skillData.skillPointCostAddl or 0)) or '-',
            skillData.evalPoint or '-',
            skillData.valueRatio and string.format("%.2f", skillData.valueRatio) or '-',
        }
        for _, value in ipairs(renderTexts) do
            row:node(mw.html.create('td'):wikitext(value))
        end
        mwtable:node(row)
    end

    return tostring(mwtable)
end

function p.skillPage(frame)
    local skillId = mw.text.decode(frame.args[1])
    if not skillId then return end
    local skillData = Data.getSkill(skillId)
    if not skillData then return end
    local isExternal = frame.args[2] == 'external'

    local link = nil
    local nameRO = frame.args['name_ro']
    local nameWW = frame.args['name_ww']
    local descriptionEN = frame.args['description_en']

    if isExternal then
        local results = Cargo.query {
            from = 'Game_Skills',
            fields = { '_pageName=skillPage', 'name_ro', 'name_ww', 'description_en' },
            where = string.format('skill_id = "%s"', skillId),
            limit = '1',
        }
        local cargoData = results[1]
        if cargoData then
            link = cargoData.skillPage
            nameRO = cargoData.name_ro
            nameWW = cargoData.name_ww
            descriptionEN = cargoData.description_en
        end
    end

    local templateArgs = {
        skill_id = skillId,
        page_link = link,
        icon = skillData.iconPage,
        name_jp = skillData.nameJP,
        name_ro = nameRO or '',
        name_ww = skillData.nameEN or nameWW or '',
        description_jp = table.concat(skillData.descriptionJP, ""),
        description_en = skillData.descriptionEN and table.concat(skillData.descriptionEN) or descriptionEN,
        available_en = skillData.availableEN,
        skill_point = skillData.skillPointCost,
        skill_point_addl = skillData.skillPointCostAddl or '',
        eval_point = skillData.evalPoint,
        eval_ratio = skillData.valueRatio and string.format("%.2f", skillData.valueRatio) or '',
        upgrades_from = skillData.upgradedFrom or '',
        upgrades_to = skillData.upgradedTo or '',
        evolves_from = skillData.evolvedFrom or '',
        evolves_from_card = skillData.evolvedForCard or '',
        ult_for_card = skillData.ultForCard or '',
    }

    local text = frame:expandTemplate {
        title = "Game Skill Definition/page",
        args = templateArgs
    }
    return text
end

function p.createConditionsTable(frame, tableElement, conditionsHeader, conditionsList, numRows, hasThird)
    local firstRow = mw.html.create('tr')
    local conditionsHead = mw.html.create('th'):wikitext(conditionsHeader)
    firstRow:node(conditionsHead)
    if numRows > 1 or hasThird then
        conditionsHead:attr('rowspan', numRows)
        local orText = mw.html.create('td'):wikitext(numRows > 1 and "'''OR'''" or ""):attr('rowspan', numRows)
        firstRow:node(orText)
    end
    tableElement:node(firstRow)
    for j, condOr in ipairs(conditionsList) do
        local conditionText = mw.html.create('td')
        for k, condAnd in ipairs(condOr) do
            if k > 1 then
                conditionText:wikitext(" '''AND''' ")
            end
            if condAnd.condition and condAnd.compare and condAnd.value then
                local text = string.format('%s %s %s', condAnd.condition, condAnd.compare, condAnd.value)
                if condAnd.hint then
                    conditionText:wikitext(frame:expandTemplate {
                        title = "Tooltip",
                        args = { text, condAnd.hint }
                    })
                else
                    conditionText:wikitext(text)
                end
            end
        end

        if j > 1 then
            local newRow = mw.html.create('tr')
            newRow:node(conditionText)
            tableElement:node(newRow)
        else
            firstRow:node(conditionText)
        end
    end
end

function p.effectsTables(frame)
    local skillId = mw.text.decode(frame.args[1])
    if not skillId then return end
    local skillData = Data.getSkill(skillId)
    if not skillData then return end

    local render = ''

    for i, effect in ipairs(skillData.effects) do
        local wrapperDiv = mw.html.create('div'):addClass('skill-effect-wrapper'):addClass('fit-content')

        local effectKeys = {}
        local effectValues = {}
        if effect.cooldown then
            table.insert(effectKeys, 'Cooldown')
            table.insert(effectValues, { value = string.format("%.1fs", effect.cooldown) })
        end
        if effect.duration then
            table.insert(effectKeys, 'Duration')
            local val = { value = string.format("%.1fs", effect.duration) }
            if effect.durationScaling ~= nil then
                val['valueTooltip'] = string.format("Scaled By: %s", effect.durationScaling)
            end
            table.insert(effectValues, val)
        end
        for _, ability in ipairs(effect.abilities) do
            table.insert(effectKeys, ability.ability)
            local val = {}
            if ability.valueFormat == 'pct' then
                val['value'] = tonumber(string.format("%.2f", ability.value * 100)) .. '%'
            elseif ability.valueFormat == 'dec' then
                val['value'] = tonumber(string.format("%f", ability.value))
            else
                val['value'] = nil
            end
            if ability.target then
                val['tooltip'] = string.format("Targets: %s", ability.target.target)
            end
            if ability.valueScaling then
                val['valueTooltip'] = string.format("Scaled By: %s", ability.valueScaling)
            end
            table.insert(effectValues, val)
        end

        local basicTable = mw.html.create('table'):addClass('wikitable')
        for index, key in ipairs(effectKeys) do
            local values = effectValues[index]
            local value = values['value']
            if values['tooltip'] then
                key = frame:expandTemplate {
                    title = "Tooltip",
                    args = { key, values['tooltip'] }
                }
            end
            if values['valueTooltip'] then
                value = frame:expandTemplate {
                    title = "Tooltip",
                    args = { value, values['valueTooltip'] }
                }
            end
            local row = mw.html.create('tr')
            local th = mw.html.create('th'):wikitext(key)
            local td = mw.html.create('td'):wikitext(value)
            row:node(th)
            row:node(td)
            basicTable:node(row)
        end

        local conditionsTable = mw.html.create('table'):addClass('wikitable')

        local numRowsPre = 0
        if effect.preconditions then
            for _, _ in pairs(effect.preconditions) do numRowsPre = numRowsPre + 1 end
        end
        local numRows = 0
        for _, _ in pairs(effect.conditions) do numRows = numRows + 1 end
        local hasThird = numRowsPre > 1 or numRows > 1

        if effect.preconditions then
            p.createConditionsTable(frame, conditionsTable, frame:expandTemplate {
                    title = "Tooltip",
                    args = { "Preconditions", "Must be valid at least once at any point in the race before the skill can activate" }
                },
                effect.preconditions, numRowsPre, hasThird)
        end
        if effect.conditions then
            p.createConditionsTable(frame, conditionsTable, frame:expandTemplate {
                title = "Tooltip",
                args = { "Conditions", "When valid, skill has a chance to activate" }
            }, effect.conditions, numRows, hasThird)
        end

        wrapperDiv:node(basicTable)
        wrapperDiv:node(conditionsTable)
        if i > 1 then render = render .. '\n\n' end
        render = render .. tostring(wrapperDiv)
    end

    return render
end

return p