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

Module:Game/Cards/Data

From Umamusume Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Game/Cards/Data/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 Data = {}
local Game = require("Module:Game")
local Utils = require("Module:Utils")
local BannerUtils = require("Module:Game/Banners/Utils")
local SkillGroups = require("Module:Game/Skills/Data/Groups")
local SkillEvolved = require("Module:Game/Skills/Data/Evolved")

---@class CardData
---@field id string
---@field characterId string
---@field nameJP string
---@field displayNameJP string
---@field nameEN string|nil
---@field displayNameEN string|nil
---@field availableEN boolean
---@field releaseDateJP string|nil
---@field releaseDateEN string|nil
---@field rarities CardDataRarity[]
---@field defaultRarity string
---@field skillRanks CardDataRank[]
---@field boons CardDataStatTable
---@field defaultRunningStyle string
---@field iconPage string
---@field standPage string

---@class CardDataRarity
---@field rarity string
---@field ultimateSkillId string
---@field outfitId string
---@field baseStats CardDataStatTable
---@field aptitudes CardDataAptitudes

---@class CardDataRank
---@field rank string
---@field skillIds string[]
---@field evolutions CardEvolvedSkill[]

---@class CardDataStatTable
---@field speed number
---@field stamina number
---@field power number
---@field guts number
---@field wit number

---@class CardDataAptitudes
---@field groundTurf string
---@field groundDirt string
---@field distanceShort string
---@field distanceMile string
---@field distanceMid string
---@field distanceLong string
---@field styleFront string
---@field stylePace string
---@field styleLate string
---@field styleEnd string

---Get card data by its ID
---@param cardId string
---@return CardData|nil
function Data.getCard(cardId)
    local card_data = Game.queryMaster {
        from = "card_data",
        where = string.format("id = %s", cardId),
        limit = 1
    }
    if #card_data == 0 then return nil end
    return Data._mapDatas(card_data)[Utils.normalString(cardId)]
end

---Get a batch of cards by list of IDs
---@param cardIds string[]
---@return CardData[]
function Data.getBatch(cardIds)
    local cardIdsInsert = table.concat(cardIds, ",")
    local card_datas = Game.queryMaster {
        from = "card_data",
        where = string.format("id IN (%s)", cardIdsInsert)
    }
    local mapped = Data._mapDatas(card_datas)
    local datas = {}
    for _, cardId in ipairs(cardIds) do
        table.insert(datas, mapped[Utils.normalString(cardId)])
    end
    return datas
end

---@param card_datas table[]
---@return CardData[]
function Data._mapDatas(card_datas)
    local cardIds = {}
    local cardSkillSetIdPairs = {}
    for _, card_data in ipairs(card_datas) do
        table.insert(cardIds, card_data.id)
        table.insert(cardSkillSetIdPairs, { card_data.id, card_data.available_skill_set_id })
    end
    local cardIdsInsert = table.concat(cardIds, ",")

    local namesJP = Game.getJPText(5, cardIds)
    local displayNamesJP = Game.getJPText(4, cardIds)

    local namesEN = Game.getENText(5, cardIds)
    local displayNamesEN = Game.getENText(4, cardIds)

    local rarityDataMap = Data._mapRarityDatas(cardIds)
    local rankDataMap = Data._mapRankDatas(cardSkillSetIdPairs)

    local card_datas_en = Game.queryMasterEN {
        from = "card_data",
        where = string.format("id IN (%s)", cardIdsInsert),
        data = { "id" }
    }
    local availabilityEN = {}
    for _, card_data in ipairs(card_datas_en) do
        if card_data[1] ~= nil then -- single results will mess up the iteration
            availabilityEN[card_data[1]] = true
        end
    end

    -- Get release dates for cards from gacha data
    local releaseDatesJP = BannerUtils.getReleaseDates(cardIds, "card", "JP")
    local releaseDatesEN = BannerUtils.getReleaseDates(cardIds, "card", "EN")

    local runningStyleMap = {
        ['1'] = "Front Runner",
        ['2'] = "Pace Chaser",
        ['3'] = "Late Surger",
        ['4'] = "End Closer"
    }

    local datas = {}
    for _, card_data in ipairs(card_datas) do
        local nameJP = namesJP[card_data.id] or ''
        local displayNameJP = displayNamesJP[card_data.id] or ''

        local nameEN = namesEN[card_data.id]
        local displayNameEN = displayNamesEN[card_data.id]

        local releaseDateJP = releaseDatesJP[card_data.id]  -- stores Unix timestamp
        local releaseDateEN = releaseDatesEN[card_data.id]  -- stores Unix timestamp

        local rarityData = rarityDataMap[card_data.id]
        local rankData = rankDataMap[card_data.id]

        local dressId = rarityData['3'].outfitId

        local data = { ---@type CardData
            id = card_data.id,
            characterId = card_data.chara_id,
            nameJP = nameJP,
            displayNameJP = displayNameJP,
            nameEN = nameEN,
            displayNameEN = displayNameEN,
            availableEN = availabilityEN[card_data.id] == true,
            releaseDateJP = releaseDateJP,
            releaseDateEN = releaseDateEN,
            rarities = rarityData,
            defaultRarity = card_data.default_rarity,
            skillRanks = rankData,
            boons = {
                speed = tonumber(card_data.talent_speed) or 0,
                stamina = tonumber(card_data.talent_stamina) or 0,
                power = tonumber(card_data.talent_pow) or 0,
                guts = tonumber(card_data.talent_guts) or 0,
                wit = tonumber(card_data.talent_wiz) or 0
            },
            defaultRunningStyle = runningStyleMap[card_data.running_style],
            iconPage = string.format("Game Playable Icon %s.png", card_data.id),
            standPage = string.format("Game Asset chara stand %s %06d.png", card_data.chara_id, dressId),
        }
        datas[card_data.id] = data
    end
    return datas
end

---@param cardIds string[]
---@return CardDataRarity[][]
function Data._mapRarityDatas(cardIds)
    local cardIdsInsert = table.concat(cardIds, ",")
    local card_rarity_datas = Game.queryMaster {
        from = "card_rarity_data",
        where = string.format("card_id IN (%s)", cardIdsInsert)
    }

    local skillSets = {}
    for _, card_rarity_data in ipairs(card_rarity_datas) do
        table.insert(skillSets, card_rarity_data.skill_set)
    end
    local skillSetMap = SkillGroups.getSkillSets(skillSets)

    local aptitudeMap = {
        ['1'] = "G",
        ['2'] = "F",
        ['3'] = "E",
        ['4'] = "D",
        ['5'] = "C",
        ['6'] = "B",
        ['7'] = "A",
        ['8'] = "S"
    }

    local datas = {}
    for _, card_rarity_data in ipairs(card_rarity_datas) do
        if datas[card_rarity_data.card_id] == nil then datas[card_rarity_data.card_id] = {} end

        local skillSet = skillSetMap[card_rarity_data.skill_set]
        local firstSkillSet = skillSet[1]
        local ultimateId = firstSkillSet and firstSkillSet[1] or nil

        local data = { ---@type CardDataRarity
            rarity = card_rarity_data.rarity,
            ultimateSkillId = ultimateId,
            outfitId = card_rarity_data.race_dress_id,
            baseStats = {
                speed = card_rarity_data.speed,
                stamina = card_rarity_data.stamina,
                power = card_rarity_data.pow,
                guts = card_rarity_data.guts,
                wit = card_rarity_data.wiz
            },
            aptitudes = {
                groundTurf = aptitudeMap[card_rarity_data.proper_ground_turf],
                groundDirt = aptitudeMap[card_rarity_data.proper_ground_dirt],
                distanceShort = aptitudeMap[card_rarity_data.proper_distance_short],
                distanceMile = aptitudeMap[card_rarity_data.proper_distance_mile],
                distanceMid = aptitudeMap[card_rarity_data.proper_distance_middle],
                distanceLong = aptitudeMap[card_rarity_data.proper_distance_long],
                styleFront = aptitudeMap[card_rarity_data.proper_running_style_nige],
                stylePace = aptitudeMap[card_rarity_data.proper_running_style_senko],
                styleLate = aptitudeMap[card_rarity_data.proper_running_style_sashi],
                styleEnd = aptitudeMap[card_rarity_data.proper_running_style_oikomi]
            },
        }
        datas[card_rarity_data.card_id][card_rarity_data.rarity] = data
    end

    return datas
end

---@param cardSkillSetIdPairs [string, string][]
---@return CardDataRank[]
function Data._mapRankDatas(cardSkillSetIdPairs)
    local cardIds = {}
    local skillSetIds = {}
    local skillSetIdCardIdMap = {}
    for _, cardSkillSetIdPair in ipairs(cardSkillSetIdPairs) do
        table.insert(cardIds, cardSkillSetIdPair[1])
        table.insert(skillSetIds, cardSkillSetIdPair[2])
        skillSetIdCardIdMap[cardSkillSetIdPair[2]] = cardSkillSetIdPair[1]
    end

    local skillSetIdsInsert = table.concat(skillSetIds, ",")
    local available_skill_sets = Game.queryMaster {
        from = "available_skill_set",
        where = string.format("available_skill_set_id IN (%s)", skillSetIdsInsert)
    }

    local evolvedSkillMap = SkillEvolved.getEvolvedSkillsForCards(cardIds)

    local datas = {}
    for _, available_skill_set in ipairs(available_skill_sets) do
        local cardId = skillSetIdCardIdMap[available_skill_set.available_skill_set_id]
        if datas[cardId] == nil then datas[cardId] = {} end

        local rank = available_skill_set.need_rank
        if rank == '0' then rank = '1' end -- rank 0 is equivalent to rank 1
        if datas[cardId][rank] ~= nil then
            table.insert(datas[cardId][rank].skillIds, available_skill_set.skill_id)
        else
            local evolutions = {}
            for _, evolvedSkill in ipairs(evolvedSkillMap[cardId]) do
                if evolvedSkill.rank == rank then
                    table.insert(evolutions, evolvedSkill)
                end
            end

            datas[cardId][rank] = { ---@type CardDataRank
                rank = rank,
                skillIds = { available_skill_set.skill_id },
                evolutions = evolutions
            }
        end
    end
    return datas
end

return Data