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

Module:Game/Cards

From Umamusume Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Game/Cards/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/Cards/Data")
local Characters = require("Module:Game/Characters")
local Cargo = require("Module:Cargo")
local Utils = require("Module:Utils")
local Game = require("Module:Game")

---Generate lists of arguments for the Game Card/link template
---Use to batch create lots of card links for pages that need them
---@param cardIds string[]
---@return any[] templateArgs keyed by card ID
function p.generateCardLinks(cardIds)
    local results = {}
    if #cardIds > 0 then
        local cardIdsInsert = table.concat(cardIds, ',')
        results = Cargo.query {
            from = 'Game_Cards',
            fields = { '_pageName=cardPage', 'card_id', 'name_ro', 'name_ww' },
            where = string.format('Game_Cards.card_id IN (%s)', cardIdsInsert),
            limit = #cardIds,
        }
    end

    local cardDatas = Data.getBatch(cardIds)

    local merged = Utils.pairObjects(cardDatas, 'id', results, 'card_id', true)

    -- fetch required character info (no page exists or need for display name)
    local charIds = {}
    for _, pair in ipairs(merged) do
        local cargoData, cardData = pair[2], pair[1] ---@type any
        if not cargoData or not (cardData.displayNameEN or cargoData.name_ww) then
            table.insert(charIds, cardData.characterId)
        end
    end
    local charInfos = Characters.getCharacterInfos(charIds)

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

        local charData = charInfos[cardData.characterId]
        if not cargoData then
            -- Generate expected page name for easy creation if the page does not exist
            cargoData = {
                cardPage = string.format("Game:%s (%s)", charData and charData.nameEN or '',
                    string.gsub(cardData.nameEN or cardData.nameJP, "[%[%]]", ""))
            }
        end

        local displayName = cardData.displayNameEN or cargoData.name_ww
        if displayName == nil then
            local charName = (charData and charData.nameEN) or ''
            local romanName = cargoData.name_ro and string.format("[%s]", cargoData.name_ro) or nil
            displayName = string.format("%s %s", romanName or cardData.nameJP, charName)
        end

        local templateArgs = {
            icon = cardData.iconPage,
            page = cargoData.cardPage,
            display = displayName
        }
        datas[cardData.id] = templateArgs
    end
    return datas
end

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

    local templateArgs = p.generateCardLinks({ cardId })[cardId]
    if not templateArgs then return end

    local text = frame:expandTemplate {
        title = "Game Card/link",
        args = templateArgs
    }
    return text
end

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

    local card_datas = Game.queryMaster {
        from = "card_data",
        where = string.format("chara_id = %s AND id < 9000000", charId),
        data = {
            id = "id"
        }
    }

    local cardIds = {}
    for _, card_data in ipairs(card_datas) do
        table.insert(cardIds, card_data.id)
    end
    table.sort(cardIds)

    local cardLinks = p.generateCardLinks(cardIds)

    local text = ''
    for _, cardId in ipairs(cardIds) do
        local templateArgs = cardLinks[cardId]
        text = text .. frame:expandTemplate {
            title = "Game Card/link",
            args = templateArgs
        } .. '\n\n'
    end
    if #text == 0 then
        return "''None.''"
    end
    return mw.text.trim(text)
end

function p.cardPage(frame)
    local cardId = mw.text.decode(frame.args[1])
    if not cardId then return end
    local cardData = Data.getCard(cardId)
    if not cardData then return end
    local charData = Characters.getCharacterInfo(cardData.characterId)
    if not charData then return end

    local nameRO = frame.args['name_ro']
    local nameWW = frame.args['name_ww']

    local function boonText(number)
        if number > 0 then return tostring(number) end
        return ''
    end

    local displayEN = nil
    if #nameRO > 0 then displayEN = nameRO end
    if #nameWW > 0 then displayEN = nameWW end

    local baseRarity = cardData.rarities['3']
    local defaultRarity = cardData.rarities[cardData.defaultRarity]
    local templateArgs = {
        image_page = cardData.standPage,
        name_jp = cardData.nameJP,
        name_full_jp = cardData.displayNameJP,
        name_en = cardData.nameEN or (displayEN and string.format("[%s]", displayEN) or ''),
        release_date_jp = cardData.releaseDateJP,
        release_date_en = cardData.releaseDateEN, -- timestamp
        available_en = cardData.availableEN,
        char_link = charData.pageName,
        rarity = cardData.defaultRarity,
        rank_turf = baseRarity.aptitudes.groundTurf,
        rank_dirt = baseRarity.aptitudes.groundDirt,
        rank_sprint = baseRarity.aptitudes.distanceShort,
        rank_mile = baseRarity.aptitudes.distanceMile,
        rank_middle = baseRarity.aptitudes.distanceMid,
        rank_long = baseRarity.aptitudes.distanceLong,
        rank_front = baseRarity.aptitudes.styleFront,
        rank_pace = baseRarity.aptitudes.stylePace,
        rank_late = baseRarity.aptitudes.styleLate,
        rank_end = baseRarity.aptitudes.styleEnd,
        boon_speed = boonText(cardData.boons.speed),
        boon_stamina = boonText(cardData.boons.stamina),
        boon_power = boonText(cardData.boons.power),
        boon_guts = boonText(cardData.boons.guts),
        boon_wit = boonText(cardData.boons.wit),
        skill_ult = baseRarity.ultimateSkillId,
        lower_skill_ult = (defaultRarity.ultimateSkillId ~= baseRarity.ultimateSkillId) and defaultRarity
            .ultimateSkillId or nil
    }

    for key, rarity in pairs(cardData.rarities) do
        templateArgs["stats_speed_" .. key] = tostring(rarity.baseStats.speed)
        templateArgs["stats_stamina_" .. key] = tostring(rarity.baseStats.stamina)
        templateArgs["stats_power_" .. key] = tostring(rarity.baseStats.power)
        templateArgs["stats_guts_" .. key] = tostring(rarity.baseStats.guts)
        templateArgs["stats_wit_" .. key] = tostring(rarity.baseStats.wit)
    end

    for key, rank in pairs(cardData.skillRanks) do
        for i, skillId in ipairs(rank.skillIds) do
            templateArgs["skill_" .. key .. "_" .. i] = skillId
        end
        for i, evo in ipairs(rank.evolutions) do
            templateArgs["skill_" .. key .. "_evo_" .. i] = evo.skillId
        end
    end

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

function p.cardListTable(frame)
    local results = Cargo.query {
        from = 'Game_Cards',
        fields = { '_pageName=cardPage', 'card_id', 'name_ro', 'name_ww' },
        orderBy = "card_id",
        limit = '9999',
    }

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

    local mwtable = mw.html.create('table'):addClass('wikitable'):addClass('sortable'):addClass('mw-collapsible')
    local headerRow = mw.html.create('tr')
    local headerTexts = { 'Icon', 'Name', 'Character', 'Release Date (JP)', 'Release Date (EN)', 'Rarity', 'Speed%',
        'Stamina%', 'Power%', 'Guts%', 'Wit%', 'Turf',
        'Dirt', 'Sprint', 'Mile', 'Middle', 'Long', 'Front', 'Pace', 'Late', 'End' }
    for _, value in ipairs(headerTexts) do
        headerRow:node(mw.html.create('th'):wikitext(value))
    end
    mwtable:node(headerRow)

    local charIds = {}
    for _, pair in ipairs(paired) do
        table.insert(charIds, pair[2].characterId)
    end
    charIds = Utils.dedupe(charIds)
    local charDatas = Characters.getCharacterInfos(charIds)

    for _, pair in ipairs(paired) do
        local cargoData, cardData = pair[1], pair[2] ---@type any
        local charData = charDatas[cardData.characterId]

        local baseRarity = cardData.rarities['3'] --- @type CardDataRarity

        local nameMain = cardData.nameEN or cargoData.name_ww or cargoData.name_ro ---@type string|nil
        local nameSub = cardData.nameJP ---@type string|nil
        if nameMain == nil then
            nameMain = nameSub
            nameSub = nil
        end

        nameMain = string.gsub(nameMain or '', "[%[%]]", "")
        if nameSub ~= nil then nameSub = string.gsub(nameSub, "[%[%]]", "") end

        local mainLink = string.format("'''[[%s|%s]]'''", cargoData.cardPage, nameMain)
        if not cardData.availableEN then
            mainLink = mainLink .. frame:expandTemplate {
                title = "tooltip",
                args = { "<sup>JP</sup>", "Only on the Japanese version" }
            }
        end

        local row = mw.html.create('tr')
        local lang = mw.language.getContentLanguage()
        local renderTexts = {
            '[[File:' .. cardData.iconPage .. '|48px]]',
            nameSub and string.format("%s<br/>''%s''", mainLink, nameSub) or mainLink,
            string.format("[[%s|%s]]", charData.pageName, charData.nameEN),
            cardData.releaseDateJP and lang:formatDate('Y-m-d', '@' .. cardData.releaseDateJP) or 'N/A',
            cardData.releaseDateEN and lang:formatDate('Y-m-d', '@' .. cardData.releaseDateEN) or 'N/A',
            cardData.defaultRarity == '1' and '★' or (cardData.defaultRarity == '2' and '★★' or '★★★'),
            cardData.boons.speed,
            cardData.boons.stamina,
            cardData.boons.power,
            cardData.boons.guts,
            cardData.boons.wit
        }
        local renderValues = {
            0,                                     -- Icon
            0,                                     -- Name
            0,                                     -- Character
            tonumber(cardData.releaseDateJP) or 0, -- Release Date JP
            tonumber(cardData.releaseDateEN) or 0, -- Release Date EN
            tonumber(cardData.defaultRarity),      -- Rarity
            0,                                     -- Speed%
            0,                                     -- Stamina%
            0,                                     -- Power%
            0,                                     -- Guts%
            0                                      -- Wit%
        }
        local function addAptitude(value)
            local sortVals = {
                ['S'] = 8,
                ['A'] = 7,
                ['B'] = 6,
                ['C'] = 5,
                ['D'] = 4,
                ['E'] = 3,
                ['F'] = 2,
                ['G'] = 1,
            }
            table.insert(renderTexts, string.format("[[File:Rank %s.png|28px]]", value))
            table.insert(renderValues, sortVals[value])
        end

        local aptitudes = baseRarity.aptitudes
        addAptitude(aptitudes.groundTurf)
        addAptitude(aptitudes.groundDirt)
        addAptitude(aptitudes.distanceShort)
        addAptitude(aptitudes.distanceMile)
        addAptitude(aptitudes.distanceMid)
        addAptitude(aptitudes.distanceLong)
        addAptitude(aptitudes.styleFront)
        addAptitude(aptitudes.stylePace)
        addAptitude(aptitudes.styleLate)
        addAptitude(aptitudes.styleEnd)

        for i, value in ipairs(renderTexts) do
            local nodeTd = mw.html.create('td'):wikitext(value)
            if renderValues[i] > 0 then
                nodeTd = nodeTd:attr('data-sort-value', renderValues[i])
            end
            row:node(nodeTd)
        end
        mwtable:node(row)
    end

    return tostring(mwtable)
end

return p