Mudanças entre as edições de "Módulo:Info.Skills"

De Wiki Gla
Ir para navegação Ir para pesquisar
m
m
Linha 39: Linha 39:
     end
     end
     return val
     return val
end
local function currentPageKey()
    local title = mw.title.getCurrentTitle()
    if title and title.fullText and trim(title.fullText) ~= "" then
        return title.fullText
    end
    return "__page"
end
end


local function ensureSkillMeta(char)
local function ensureSkillMeta(char)
     if not char or char == "" then
    local key = char
         return nil
     if not key or key == "" then
         key = currentPageKey()
     end
     end
     if not SKILL_META[char] then
     if not SKILL_META[key] then
         SKILL_META[char] = {
         SKILL_META[key] = {
             byIndex = {},
             byIndex = {},
             byName = {}
             byName = {}
         }
         }
     end
     end
     return SKILL_META[char]
     return SKILL_META[key]
end
end


Linha 65: Linha 74:
     if name and name ~= "" then
     if name and name ~= "" then
         bucket.byName[name] = meta
         bucket.byName[name] = meta
    end
    -- também guarda por página para páginas especiais (ex.: Usuário:Gurren)
    local pageBucket = ensureSkillMeta(currentPageKey())
    if pageBucket ~= bucket then
        if index then
            pageBucket.byIndex[index] = meta
        end
        if name and name ~= "" then
            pageBucket.byName[name] = meta
        end
     end
     end
end
end


local function fetchSkillMeta(char, index, name)
local function fetchSkillMeta(char, index, name)
     local bucket = SKILL_META[char or ""]
     local candidates = {}
    if not bucket then
    if char and char ~= "" and SKILL_META[char] then
         return nil
         table.insert(candidates, SKILL_META[char])
     end
     end
     if index and bucket.byIndex[index] then
    local pageKey = currentPageKey()
         return bucket.byIndex[index]
     if SKILL_META[pageKey] then
         table.insert(candidates, SKILL_META[pageKey])
     end
     end
     if name and name ~= "" then
     for _, bucket in ipairs(candidates) do
        return bucket.byName[name]
        if index and bucket.byIndex[index] then
            return bucket.byIndex[index]
        end
        if name and name ~= "" and bucket.byName[name] then
            return bucket.byName[name]
        end
     end
     end
     return nil
     return nil

Edição das 16h27min de 30 de novembro de 2025

A documentação para este módulo pode ser criada em Módulo:Info.Skills/doc

-- Módulo:Info.Skills — skill(), subskill(), emote()
local p = {}

local utils = require("Módulo:Info.Utils")
local trim = utils.trim
local safeArgs = utils.safeArgs
local collectJsonObjects = utils.collectJsonObjects
local requireCharacterModule = utils.requireCharacterModule
local resolveCharFromFrames = utils.resolveCharFromFrames
local colorize = utils.colorize
local nz = utils.nz
local parseFlags = utils.parseFlags

local SKILL_META = {}

local function norm(val)
    val = trim(val or "")
    return val ~= "" and val or nil
end

local function normExcept(val, placeholder)
    val = trim(val or "")
    if val == "" then
        return nil
    end
    if placeholder and mw.ustring.upper(val) == placeholder then
        return nil
    end
    return val
end

local function normIcon(val)
    val = trim(val or "")
    if val == "" then
        return nil
    end
    if mw.ustring.lower(val) == "nada.png" then
        return nil
    end
    return val
end

local function currentPageKey()
    local title = mw.title.getCurrentTitle()
    if title and title.fullText and trim(title.fullText) ~= "" then
        return title.fullText
    end
    return "__page"
end

local function ensureSkillMeta(char)
    local key = char
    if not key or key == "" then
        key = currentPageKey()
    end
    if not SKILL_META[key] then
        SKILL_META[key] = {
            byIndex = {},
            byName = {}
        }
    end
    return SKILL_META[key]
end

local function storeSkillMeta(char, index, name, meta)
    meta = meta or {}
    local bucket = ensureSkillMeta(char)
    if not bucket then
        return
    end
    if index then
        bucket.byIndex[index] = meta
    end
    if name and name ~= "" then
        bucket.byName[name] = meta
    end
    -- também guarda por página para páginas especiais (ex.: Usuário:Gurren)
    local pageBucket = ensureSkillMeta(currentPageKey())
    if pageBucket ~= bucket then
        if index then
            pageBucket.byIndex[index] = meta
        end
        if name and name ~= "" then
            pageBucket.byName[name] = meta
        end
    end
end

local function fetchSkillMeta(char, index, name)
    local candidates = {}
    if char and char ~= "" and SKILL_META[char] then
        table.insert(candidates, SKILL_META[char])
    end
    local pageKey = currentPageKey()
    if SKILL_META[pageKey] then
        table.insert(candidates, SKILL_META[pageKey])
    end
    for _, bucket in ipairs(candidates) do
        if index and bucket.byIndex[index] then
            return bucket.byIndex[index]
        end
        if name and name ~= "" and bucket.byName[name] then
            return bucket.byName[name]
        end
    end
    return nil
end

local function hydrateSubAttrsFromMeta(char, sub)
    if not sub then
        return sub
    end
    local ref = trim(sub.refM or sub.m or sub.M or "")
    local idx = tonumber(ref)
    local inherited = fetchSkillMeta(char, idx, sub.name or sub.n)
    if not inherited then
        return sub
    end
    sub.icon = normIcon(sub.icon) or inherited.icon or "Nada.png"
    sub.level = normExcept(sub.level, "NIVEL") or inherited.level or sub.level
    sub.video = norm(sub.video) or inherited.video or sub.video
    sub.energy = sub.energy or inherited.energy
    sub.powerpve = sub.powerpve or inherited.powerpve
    sub.powerpvp = sub.powerpvp or inherited.powerpvp
    sub.cooldown = sub.cooldown or inherited.cooldown
    if ref ~= "" then
        sub.refM = ref
    end
    return sub
end

-- ===== Herança automática de descrições =====

-- Busca descrição em skills principais
local function findDescInSkills(data, skillName)
    if not data or not data.skills then return nil end
    local sk = data.skills[skillName]
    if sk and type(sk.desc) == "table" then
        return sk.desc
    end
    return nil
end

-- Busca descrição em qualquer subskills do módulo
local function findDescInAnySubskills(data, skillName)
    if not data or not data.skills then return nil end
    for _, sk in pairs(data.skills) do
        if type(sk) == "table" and type(sk.subskills) == "table" then
            local sub = sk.subskills[skillName]
            if sub and type(sub.desc) == "table" then
                return sub.desc
            end
        end
    end
    return nil
end

-- Busca descrição com herança automática
local function resolveDescWithInheritance(data, skillName, directDesc)
    -- 1. Se tem desc direta, usa ela
    if directDesc and type(directDesc) == "table" and next(directDesc) then
        return directDesc
    end

    -- 2. Busca em skills principais
    local fromSkills = findDescInSkills(data, skillName)
    if fromSkills then
        return fromSkills
    end

    -- 3. Busca em outras subskills
    local fromSubskills = findDescInAnySubskills(data, skillName)
    if fromSubskills then
        return fromSubskills
    end

    return nil
end

-- ===== Weapon (gera JSON com atributos da arma para uso em {{Skill}}) =====

function p.weapon(frame)
    local a = safeArgs(frame)
    local obj = {
        icon = (trim(a.icon or "") ~= "" and a.icon or "Nada.png"),
        energy = nz(a.energy),
        powerpve = nz(a.powerpve),
        powerpvp = nz(a.powerpvp),
        cooldown = nz(a.cooldown),
        video = a.video or ""
    }
    return mw.text.jsonEncode(obj)
end

-- ===== Skill (com M) =====

function p.skill(frame)
    local a = safeArgs(frame)
    local lang = trim((a.lang or (frame:getParent() and frame:getParent().args.lang) or "pt"):lower())
    -- Usa resolveCharFromFrames que busca recursivamente em todos os parent frames
    local char = resolveCharFromFrames(frame, a)
    local data = requireCharacterModule(char) or {}

    local name, desc_i18n = nil, {}
    if a.M and trim(a.M) ~= "" then
        local m = tonumber(a.M) or 0
        local order = data.order or {}
        local key = order[m] or ""
        local sk = (data.skills or {})[key] or {}
        name = trim(sk.name or key or "")
        if type(sk.desc) == "table" then
            local langs = { "pt", "en", "es", "pl" }
            for _, code in ipairs(langs) do
                local d = sk.desc[code]
                if d and trim(d) ~= "" then
                    desc_i18n[code] = colorize(d)
                end
            end
        elseif sk.desc and trim(sk.desc) ~= "" then
            desc_i18n["pt"] = colorize(sk.desc)
        end
    end

    local chosen = desc_i18n[lang] or desc_i18n["pt"] or ""

    -- Parse flags CSV
    local flagsArr = parseFlags(a.flags)

    local obj = {
        icon = (trim(a.icon or "") ~= "" and a.icon or "Nada.png"),
        level = (trim(a.level or "") ~= "" and a.level or "NIVEL"),
        energy = nz(a.energy),
        powerpve = nz(a.powerpve),
        powerpvp = nz(a.powerpvp),
        cooldown = nz(a.cooldown),
        video = a.video or "",
        name = name
    }
    if #flagsArr > 0 then
        obj.flags = flagsArr
    end

    if chosen ~= "" then
        obj.desc = chosen
    end
    if next(desc_i18n) ~= nil then
        obj.desc_i18n = desc_i18n
        obj.descPt = desc_i18n.pt
        obj.descEn = desc_i18n.en
        obj.descEs = desc_i18n.es
        obj.descPl = desc_i18n.pl
    end

    do
        local metaIndex = tonumber(a.M)
        local storedName = name or key or ""
        storeSkillMeta(char, metaIndex, storedName ~= "" and storedName or nil, {
            icon = normIcon(a.icon),
            level = normExcept(a.level, "NIVEL"),
            energy = norm(a.energy),
            powerpve = norm(a.powerpve),
            powerpvp = norm(a.powerpvp),
            cooldown = norm(a.cooldown),
            video = norm(a.video)
        })
    end

    -- empacota subskills vindas via |subs= (cada {{Subskill}} vira um objeto JSON)
    do
        local subsPacked = a.subs or ""
        local subsArr = {}
        for _, chunk in ipairs(collectJsonObjects(subsPacked)) do
            local ok, sk = pcall(mw.text.jsonDecode, chunk)
            if ok and type(sk) == "table" then
                table.insert(subsArr, hydrateSubAttrsFromMeta(char, sk))
            end
        end
        -- se não vier via |subs=, tenta obter do módulo (suborder/subskills)
        if #subsArr == 0 and a.M and tonumber(a.M) then
            local m = tonumber(a.M)
            local order = (data and data.order) or {}
            local key = order[m] or ""
            local sk = (data and data.skills and data.skills[key]) or {}
            local suborder = type(sk) == "table" and sk.suborder or nil
            local subskills = type(sk) == "table" and sk.subskills or nil
            if type(suborder) == "table" and type(subskills) == "table" then
                for _, n in ipairs(suborder) do
                    local sub = subskills[n]
                    if type(sub) == "table" then
                        -- HERANÇA: se sub não tem desc, busca automaticamente
                        local resolvedDesc = resolveDescWithInheritance(data, n, sub.desc)

                        local subObj = hydrateSubAttrsFromMeta(char, {
                            n = n,
                            name = n,
                            icon = sub.icon or "",
                            level = sub.level or "",
                            video = sub.video or "",
                            energy = sub.energy,
                            powerpve = sub.powerpve,
                            powerpvp = sub.powerpvp,
                            cooldown = sub.cooldown,
                            desc_i18n = resolvedDesc,
                            descPt = resolvedDesc and resolvedDesc.pt or nil,
                            descEn = resolvedDesc and resolvedDesc.en or nil,
                            descEs = resolvedDesc and resolvedDesc.es or nil,
                            descPl = resolvedDesc and resolvedDesc.pl or nil
                        })
                        -- nested subs do módulo (sub.suborder/sub.subskills)
                        if type(sub.suborder) == "table" and type(sub.subskills) == "table" then
                            local nested = {}
                            for _, nn in ipairs(sub.suborder) do
                                local s2 = sub.subskills[nn]
                                if type(s2) == "table" then
                                    -- HERANÇA para nested também
                                    local nestedDesc = resolveDescWithInheritance(data, nn, s2.desc)

                                    table.insert(nested, hydrateSubAttrsFromMeta(char, {
                                        n = nn,
                                        name = nn,
                                        icon = s2.icon or "",
                                        level = s2.level or "",
                                        video = s2.video or "",
                                        energy = s2.energy,
                                        powerpve = s2.powerpve,
                                        powerpvp = s2.powerpvp,
                                        cooldown = s2.cooldown,
                                        desc_i18n = nestedDesc,
                                        descPt = nestedDesc and nestedDesc.pt or nil,
                                        descEn = nestedDesc and nestedDesc.en or nil,
                                        descEs = nestedDesc and nestedDesc.es or nil,
                                        descPl = nestedDesc and nestedDesc.pl or nil
                                    }))
                                end
                            end
                            if #nested > 0 then subObj.subs = nested end
                        end
                        table.insert(subsArr, subObj)
                    end
                end
            end
        end
        if #subsArr > 0 then
            obj.subs = subsArr
        end

        -- suborder do módulo (se existir pra esta skill M)
        if a.M and tonumber(a.M) then
            local m = tonumber(a.M)
            local order = (data and data.order) or {}
            local key = order[m] or ""
            local sk = (data and data.skills and data.skills[key]) or {}
            if type(sk) == "table" and type(sk.suborder) == "table" then
                obj.suborder = sk.suborder
            end
        end
    end

    -- ===== Weapon: parsear |weapon= e buscar desc do módulo =====
    do
        local weaponPacked = trim(a.weapon or "")
        if weaponPacked ~= "" then
            -- Tentar parsear o JSON do {{Weapon}}
            local ok, weaponData = pcall(mw.text.jsonDecode, weaponPacked)
            if ok and type(weaponData) == "table" then
                -- Buscar descrição da arma no módulo (skills[key].weapon.desc)
                local weaponDesc_i18n = {}
                if a.M and tonumber(a.M) then
                    local m = tonumber(a.M)
                    local order = (data and data.order) or {}
                    local key = order[m] or ""
                    local sk = (data and data.skills and data.skills[key]) or {}
                    if type(sk) == "table" and type(sk.weapon) == "table" and type(sk.weapon.desc) == "table" then
                        local langs = { "pt", "en", "es", "pl" }
                        for _, code in ipairs(langs) do
                            local d = sk.weapon.desc[code]
                            if d and trim(d) ~= "" then
                                weaponDesc_i18n[code] = colorize(d)
                            end
                        end
                    end
                end

                -- Montar objeto weapon com atributos do template + desc do módulo
                local weaponObj = {
                    icon = weaponData.icon or "Nada.png",
                    energy = weaponData.energy,
                    powerpve = weaponData.powerpve,
                    powerpvp = weaponData.powerpvp,
                    cooldown = weaponData.cooldown,
                    video = weaponData.video or ""
                }
                if next(weaponDesc_i18n) ~= nil then
                    weaponObj.desc_i18n = weaponDesc_i18n
                    weaponObj.descPt = weaponDesc_i18n.pt
                    weaponObj.descEn = weaponDesc_i18n.en
                    weaponObj.descEs = weaponDesc_i18n.es
                    weaponObj.descPl = weaponDesc_i18n.pl
                end

                obj.weapon = weaponObj
            end
        end
    end

    return mw.text.jsonEncode(obj)
end

-- ===== Emote (sem M) =====

function p.emote(frame)
    local a = safeArgs(frame)
    local obj = {
        name = "Emote",
        desc = "",
        icon = (trim(a.icon or "") ~= "" and a.icon or "Nada.png"),
        level = (trim(a.level or "") ~= "" and a.level or "NIVEL"),
        video = a.video or ""
    }
    return mw.text.jsonEncode(obj)
end

-- ===== Subskill (nome n EXATO do suborder; agrega desc_i18n do módulo + atributos locais) =====

function p.subskill(frame)
    local a = safeArgs(frame)
    local lang = trim((a.lang or (frame:getParent() and frame:getParent().args.lang) or "pt"):lower())
    local n = trim(a.n or "")
    if n == "" then
        return mw.text.jsonEncode({})
    end

    -- Usa resolveCharFromFrames que busca recursivamente em todos os parent frames
    local char = resolveCharFromFrames(frame, a)
    local data = requireCharacterModule(char) or {}

    -- HERANÇA AUTOMÁTICA: busca desc com fallback
    local desc_i18n = {}
    local foundDesc = nil

    -- Primeiro tenta encontrar em subskills (comportamento original)
    for _, key in ipairs(data.order or {}) do
        local sk = (data.skills or {})[key]
        if type(sk) == "table" and type(sk.subskills) == "table" then
            local sub = sk.subskills[n]
            if sub and type(sub.desc) == "table" then
                foundDesc = sub.desc
                break
            end
        end
    end

    -- Se não encontrou, aplica herança automática
    if not foundDesc then
        foundDesc = resolveDescWithInheritance(data, n, nil)
    end

    -- Coloriza as descrições encontradas
    if foundDesc then
        local langs = { "pt", "en", "es", "pl" }
        for _, code in ipairs(langs) do
            local d = foundDesc[code]
            if d and trim(d) ~= "" then
                desc_i18n[code] = colorize(d)
            end
        end
    end

    -- Parse flags CSV
    local flagsArr = parseFlags(a.flags)

    local refM = trim(a.m or a.M or "")
    local metaIndex = tonumber(refM)
    local inherited = fetchSkillMeta(char, metaIndex, n)

    local obj = {
        name = n,
        icon = normIcon(a.icon) or (inherited and inherited.icon) or "Nada.png",
        level = normExcept(a.level, "NIVEL") or (inherited and inherited.level) or "",
        video = norm(a.video) or (inherited and inherited.video) or "",
        energy = nz(a.energy) or (inherited and inherited.energy),
        powerpve = nz(a.powerpve) or (inherited and inherited.powerpve),
        powerpvp = nz(a.powerpvp) or (inherited and inherited.powerpvp),
        cooldown = nz(a.cooldown) or (inherited and inherited.cooldown),
        back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil
    }

    if refM ~= "" then
        obj.refM = refM
    end

    if #flagsArr > 0 then
        obj.flags = flagsArr
    end

    if next(desc_i18n) ~= nil then
        obj.desc_i18n = desc_i18n
        obj.descPt = desc_i18n.pt
        obj.descEn = desc_i18n.en
        obj.descEs = desc_i18n.es
        obj.descPl = desc_i18n.pl
    end

    -- Nested subs support: allow |subs= inside a subskill
    do
        local subsPacked = a.subs or ""
        local subsArr = {}
        for _, chunk in ipairs(collectJsonObjects(subsPacked)) do
            local ok, sub = pcall(mw.text.jsonDecode, chunk)
            if ok and type(sub) == "table" then
                table.insert(subsArr, sub)
            end
        end
        if #subsArr > 0 then
            obj.subs = subsArr
        end
    end

    return mw.text.jsonEncode(obj)
end

return p