Mudanças entre as edições de "Módulo:Info.Skills"
Ir para navegação
Ir para pesquisar
m |
m Etiqueta: Revertido |
||
| Linha 64: | Linha 64: | ||
end | end | ||
local meta = fetchSkillMeta(char, ref) | local meta = fetchSkillMeta(char, ref) | ||
-- Se não encontrou no cache, tenta buscar diretamente do módulo | |||
if not meta then | |||
local data = requireCharacterModule(char) or {} | |||
local idx = tonumber(ref) | |||
if idx then | |||
local order = data.order or {} | |||
local key = order[idx] or "" | |||
if key ~= "" then | |||
-- Cria meta básico do módulo (só nome e descrição, atributos vêm do template) | |||
meta = { | |||
name = key, | |||
icon = "", -- será herdado do template quando disponível | |||
level = "", | |||
video = "", | |||
energy = nil, | |||
powerpve = nil, | |||
powerpvp = nil, | |||
cooldown = nil | |||
} | |||
end | |||
end | |||
end | |||
if not meta then | if not meta then | ||
return sub | return sub | ||
| Linha 82: | Linha 106: | ||
hydrated[k] = v | hydrated[k] = v | ||
end | end | ||
hydrated.name = pick(sub.name or sub.n, meta.name) | hydrated.name = pick(sub.name or sub.n, meta.name) or (meta.name or "") | ||
hydrated.icon = pick(sub.icon, meta.icon) | hydrated.icon = pick(sub.icon, meta.icon) or "Nada.png" | ||
hydrated.level = pick(sub.level, meta.level) | hydrated.level = pick(sub.level, meta.level) or (meta.level or "") | ||
hydrated.video = pick(sub.video, meta.video) | hydrated.video = pick(sub.video, meta.video) or (meta.video or "") | ||
hydrated.energy = pick(sub.energy, meta.energy) | hydrated.energy = pick(sub.energy, meta.energy) or meta.energy | ||
hydrated.powerpve = pick(sub.powerpve, meta.powerpve) | hydrated.powerpve = pick(sub.powerpve, meta.powerpve) or meta.powerpve | ||
hydrated.powerpvp = pick(sub.powerpvp, meta.powerpvp) | hydrated.powerpvp = pick(sub.powerpvp, meta.powerpvp) or meta.powerpvp | ||
hydrated.cooldown = pick(sub.cooldown, meta.cooldown) | hydrated.cooldown = pick(sub.cooldown, meta.cooldown) or meta.cooldown | ||
-- Garante que name nunca seja vazio | |||
if not hydrated.name or trim(hydrated.name) == "" then | |||
hydrated.name = hydrated.n or meta.name or ("Skill " .. tostring(ref)) | |||
end | |||
if not hydrated.n or trim(hydrated.n) == "" then | |||
hydrated.n = hydrated.name | |||
end | |||
hydrated.desc = pick(sub.desc, meta.desc) | hydrated.desc = pick(sub.desc, meta.desc) | ||
| Linha 254: | Linha 286: | ||
local ok, sk = pcall(mw.text.jsonDecode, chunk) | local ok, sk = pcall(mw.text.jsonDecode, chunk) | ||
if ok and type(sk) == "table" then | if ok and type(sk) == "table" then | ||
-- Garante que objetos com refM sempre tenham name válido | |||
if (sk.refM or sk.M or sk.m) and (not sk.name or trim(sk.name) == "") and (not sk.n or trim(sk.n) == "") then | |||
local refIdx = tonumber(sk.refM or sk.M or sk.m) | |||
if refIdx then | |||
local order = data.order or {} | |||
local key = order[refIdx] or "" | |||
if key ~= "" then | |||
sk.name = key | |||
sk.n = key | |||
end | |||
end | |||
end | |||
table.insert(subsArr, sk) | table.insert(subsArr, sk) | ||
end | end | ||
end | end | ||
-- Fallback: | -- Fallback: se tem poucos objetos ou nenhum, busca do módulo .lua | ||
if #subsArr == 0 and a.M and tonumber(a.M) then | -- Isso garante que mesmo que {{Subskill |M=1}} não retorne JSON válido, | ||
-- ainda conseguimos buscar do módulo e aplicar herança depois | |||
if (#subsArr == 0 or (#subsArr < 3 and a.M and tonumber(a.M))) and a.M and tonumber(a.M) then | |||
local m = tonumber(a.M) | local m = tonumber(a.M) | ||
local order = (data and data.order) or {} | local order = (data and data.order) or {} | ||
| Linha 405: | Linha 451: | ||
local n = trim(a.n or "") | local n = trim(a.n or "") | ||
local refM = trim(a.m or a.M or "") | local refM = trim(a.m or a.M or "") | ||
local char = resolveCharFromFrames(frame, a) | |||
-- Se só tem refM (sem nome), | -- Se só tem refM (sem nome), resolve nome e atributos DIRETAMENTE do módulo | ||
if n == "" and refM ~= "" then | if n == "" and refM ~= "" then | ||
local idx = tonumber(refM) | |||
local data = requireCharacterModule(char) or {} | |||
local order = data.order or {} | |||
local key = idx and order[idx] or "" | |||
-- Tenta buscar do cache primeiro (pode não estar pronto ainda) | |||
local meta = fetchSkillMeta(char, refM) | |||
-- Se não tem no cache, busca direto do módulo | |||
if not meta and key ~= "" then | |||
local sk = (data.skills or {})[key] or {} | |||
local name = trim(sk.name or key or "") | |||
if name ~= "" then | |||
meta = { | |||
name = name, | |||
icon = "", -- será herdado do template Skill quando disponível | |||
level = "", | |||
video = "", | |||
energy = nil, | |||
powerpve = nil, | |||
powerpvp = nil, | |||
cooldown = nil | |||
} | |||
end | |||
end | |||
-- Garante que sempre tenha um name válido (mesmo que temporário) | |||
local resolvedName = meta and meta.name or key or ("Skill " .. refM) | |||
local obj = { | local obj = { | ||
refM = refM, | refM = refM, | ||
icon = trim(a.icon or ""), | name = resolvedName, | ||
level = trim(a.level or ""), | n = resolvedName, -- também como 'n' para compatibilidade | ||
video = trim(a.video or ""), | icon = trim(a.icon or "") ~= "" and trim(a.icon or "") or (meta and meta.icon or ""), | ||
energy = nz(a.energy), | level = trim(a.level or "") ~= "" and trim(a.level or "") or (meta and meta.level or ""), | ||
powerpve = nz(a.powerpve), | video = trim(a.video or "") ~= "" and trim(a.video or "") or (meta and meta.video or ""), | ||
powerpvp = nz(a.powerpvp), | energy = nz(a.energy) or (meta and meta.energy), | ||
cooldown = nz(a.cooldown), | powerpve = nz(a.powerpve) or (meta and meta.powerpve), | ||
powerpvp = nz(a.powerpvp) or (meta and meta.powerpvp), | |||
cooldown = nz(a.cooldown) or (meta and meta.cooldown), | |||
back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil | back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil | ||
} | } | ||
-- Busca descrição do módulo | |||
if key ~= "" then | |||
local sk = (data.skills or {})[key] or {} | |||
local resolvedDesc = resolveDescWithInheritance(data, key, sk.desc) | |||
if resolvedDesc then | |||
local desc_i18n = {} | |||
local langs = { "pt", "en", "es", "pl" } | |||
for _, code in ipairs(langs) do | |||
local d = resolvedDesc[code] | |||
if d and trim(d) ~= "" then | |||
desc_i18n[code] = colorize(d) | |||
end | |||
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 | |||
end | |||
end | |||
local flagsArr = parseFlags(a.flags) | local flagsArr = parseFlags(a.flags) | ||
if #flagsArr > 0 then | if #flagsArr > 0 then | ||
obj.flags = flagsArr | obj.flags = flagsArr | ||
end | end | ||
-- Aplica herança do cache se disponível (sobrescreve com dados mais completos) | |||
obj = inheritSubFromMeta(char, obj) | |||
-- Garante que name nunca seja vazio | |||
if not obj.name or trim(obj.name) == "" then | |||
obj.name = obj.n or key or ("Skill " .. refM) | |||
end | |||
if not obj.n or trim(obj.n) == "" then | |||
obj.n = obj.name | |||
end | |||
-- Garante que icon nunca seja vazio (fallback para Nada.png) | |||
if not obj.icon or trim(obj.icon) == "" then | |||
obj.icon = "Nada.png" | |||
end | |||
return mw.text.jsonEncode(obj) | return mw.text.jsonEncode(obj) | ||
end | end | ||
Edição das 18h48min 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
-- ===== Cache de metadados das skills para herança dos Subskills =====
local SkillMetaStore = {}
local function currentPageKey()
local title = mw.title.getCurrentTitle()
return title and title.fullText or "__page__"
end
local function metaBucket(char)
local key = currentPageKey() .. "::" .. (trim(char or "") ~= "" and trim(char) or "__char__")
if not SkillMetaStore[key] then
SkillMetaStore[key] = { byIndex = {}, byName = {} }
end
return SkillMetaStore[key]
end
local function storeSkillMeta(char, index, name, data)
if not data then return end
local bucket = metaBucket(char)
if index then
local idx = tonumber(index) or index
bucket.byIndex[idx] = data
end
if name and trim(name) ~= "" then
bucket.byName[trim(name)] = data
end
end
local function fetchSkillMeta(char, ref)
if not ref then return nil end
local bucket = metaBucket(char)
local idxNum = tonumber(ref)
if idxNum and bucket.byIndex[idxNum] then
return bucket.byIndex[idxNum]
end
if bucket.byIndex[ref] then
return bucket.byIndex[ref]
end
local refName = trim(ref)
if refName ~= "" and bucket.byName[refName] then
return bucket.byName[refName]
end
return nil
end
local function inheritSubFromMeta(char, sub)
if type(sub) ~= "table" then return sub end
local ref = sub.refM or sub.M or sub.m or sub.name or sub.n
if not ref or trim(ref) == "" then
return sub
end
local meta = fetchSkillMeta(char, ref)
-- Se não encontrou no cache, tenta buscar diretamente do módulo
if not meta then
local data = requireCharacterModule(char) or {}
local idx = tonumber(ref)
if idx then
local order = data.order or {}
local key = order[idx] or ""
if key ~= "" then
-- Cria meta básico do módulo (só nome e descrição, atributos vêm do template)
meta = {
name = key,
icon = "", -- será herdado do template quando disponível
level = "",
video = "",
energy = nil,
powerpve = nil,
powerpvp = nil,
cooldown = nil
}
end
end
end
if not meta then
return sub
end
local function pick(current, fallback)
if current == false or current == 0 or current == "0" then
return current
end
if current ~= nil and trim(tostring(current)) ~= "" then
return current
end
return fallback
end
local hydrated = {}
for k, v in pairs(sub) do
hydrated[k] = v
end
hydrated.name = pick(sub.name or sub.n, meta.name) or (meta.name or "")
hydrated.icon = pick(sub.icon, meta.icon) or "Nada.png"
hydrated.level = pick(sub.level, meta.level) or (meta.level or "")
hydrated.video = pick(sub.video, meta.video) or (meta.video or "")
hydrated.energy = pick(sub.energy, meta.energy) or meta.energy
hydrated.powerpve = pick(sub.powerpve, meta.powerpve) or meta.powerpve
hydrated.powerpvp = pick(sub.powerpvp, meta.powerpvp) or meta.powerpvp
hydrated.cooldown = pick(sub.cooldown, meta.cooldown) or meta.cooldown
-- Garante que name nunca seja vazio
if not hydrated.name or trim(hydrated.name) == "" then
hydrated.name = hydrated.n or meta.name or ("Skill " .. tostring(ref))
end
if not hydrated.n or trim(hydrated.n) == "" then
hydrated.n = hydrated.name
end
hydrated.desc = pick(sub.desc, meta.desc)
hydrated.descPt = pick(sub.descPt, meta.descPt)
hydrated.descEn = pick(sub.descEn, meta.descEn)
hydrated.descEs = pick(sub.descEs, meta.descEs)
hydrated.descPl = pick(sub.descPl, meta.descPl)
if type(sub.desc_i18n) == "table" then
hydrated.desc_i18n = sub.desc_i18n
else
hydrated.desc_i18n = {
pt = hydrated.descPt,
en = hydrated.descEn,
es = hydrated.descEs,
pl = hydrated.descPl
}
end
if type(sub.subs) == "table" then
hydrated.subs = {}
for i, nested in ipairs(sub.subs) do
hydrated.subs[i] = inheritSubFromMeta(char, nested)
end
end
return hydrated
end
-- ===== Herança automática de descrições =====
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
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
local function resolveDescWithInheritance(data, skillName, directDesc)
if directDesc and type(directDesc) == "table" and next(directDesc) then
return directDesc
end
local fromSkills = findDescInSkills(data, skillName)
if fromSkills then
return fromSkills
end
local fromSubskills = findDescInAnySubskills(data, skillName)
if fromSubskills then
return fromSubskills
end
return nil
end
-- ===== Weapon =====
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())
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 ""
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
-- Registra metadados desta skill para herança futura
if a.M and trim(a.M) ~= "" then
local metaData = {
name = obj.name or name or "",
icon = obj.icon,
level = obj.level,
energy = obj.energy,
powerpve = obj.powerpve,
powerpvp = obj.powerpvp,
cooldown = obj.cooldown,
video = obj.video,
desc = obj.desc,
descPt = obj.descPt,
descEn = obj.descEn,
descEs = obj.descEs,
descPl = obj.descPl
}
storeSkillMeta(char, tonumber(a.M) or a.M, metaData.name, metaData)
end
-- Subskills vindas via |subs=
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
-- Garante que objetos com refM sempre tenham name válido
if (sk.refM or sk.M or sk.m) and (not sk.name or trim(sk.name) == "") and (not sk.n or trim(sk.n) == "") then
local refIdx = tonumber(sk.refM or sk.M or sk.m)
if refIdx then
local order = data.order or {}
local key = order[refIdx] or ""
if key ~= "" then
sk.name = key
sk.n = key
end
end
end
table.insert(subsArr, sk)
end
end
-- Fallback: se tem poucos objetos ou nenhum, busca do módulo .lua
-- Isso garante que mesmo que {{Subskill |M=1}} não retorne JSON válido,
-- ainda conseguimos buscar do módulo e aplicar herança depois
if (#subsArr == 0 or (#subsArr < 3 and a.M and tonumber(a.M))) 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
local resolvedDesc = resolveDescWithInheritance(data, n, sub.desc)
local subObj = {
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
}
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
local nestedDesc = resolveDescWithInheritance(data, nn, s2.desc)
table.insert(nested, {
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
for i, sub in ipairs(subsArr) do
subsArr[i] = inheritSubFromMeta(char, sub)
end
obj.subs = subsArr
end
-- suborder do módulo
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
do
local weaponPacked = trim(a.weapon or "")
if weaponPacked ~= "" then
local ok, weaponData = pcall(mw.text.jsonDecode, weaponPacked)
if ok and type(weaponData) == "table" then
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
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 =====
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 =====
-- Gera JSON simples. Herança COMPLETA de atributos é feita no JavaScript.
-- Se |M= for passado sem |n=, retorna apenas refM para o JS resolver.
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 "")
local refM = trim(a.m or a.M or "")
local char = resolveCharFromFrames(frame, a)
-- Se só tem refM (sem nome), resolve nome e atributos DIRETAMENTE do módulo
if n == "" and refM ~= "" then
local idx = tonumber(refM)
local data = requireCharacterModule(char) or {}
local order = data.order or {}
local key = idx and order[idx] or ""
-- Tenta buscar do cache primeiro (pode não estar pronto ainda)
local meta = fetchSkillMeta(char, refM)
-- Se não tem no cache, busca direto do módulo
if not meta and key ~= "" then
local sk = (data.skills or {})[key] or {}
local name = trim(sk.name or key or "")
if name ~= "" then
meta = {
name = name,
icon = "", -- será herdado do template Skill quando disponível
level = "",
video = "",
energy = nil,
powerpve = nil,
powerpvp = nil,
cooldown = nil
}
end
end
-- Garante que sempre tenha um name válido (mesmo que temporário)
local resolvedName = meta and meta.name or key or ("Skill " .. refM)
local obj = {
refM = refM,
name = resolvedName,
n = resolvedName, -- também como 'n' para compatibilidade
icon = trim(a.icon or "") ~= "" and trim(a.icon or "") or (meta and meta.icon or ""),
level = trim(a.level or "") ~= "" and trim(a.level or "") or (meta and meta.level or ""),
video = trim(a.video or "") ~= "" and trim(a.video or "") or (meta and meta.video or ""),
energy = nz(a.energy) or (meta and meta.energy),
powerpve = nz(a.powerpve) or (meta and meta.powerpve),
powerpvp = nz(a.powerpvp) or (meta and meta.powerpvp),
cooldown = nz(a.cooldown) or (meta and meta.cooldown),
back = (trim(a.back or "") == "yes" or trim(a.back or "") == "true") and true or nil
}
-- Busca descrição do módulo
if key ~= "" then
local sk = (data.skills or {})[key] or {}
local resolvedDesc = resolveDescWithInheritance(data, key, sk.desc)
if resolvedDesc then
local desc_i18n = {}
local langs = { "pt", "en", "es", "pl" }
for _, code in ipairs(langs) do
local d = resolvedDesc[code]
if d and trim(d) ~= "" then
desc_i18n[code] = colorize(d)
end
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
end
end
local flagsArr = parseFlags(a.flags)
if #flagsArr > 0 then
obj.flags = flagsArr
end
-- Aplica herança do cache se disponível (sobrescreve com dados mais completos)
obj = inheritSubFromMeta(char, obj)
-- Garante que name nunca seja vazio
if not obj.name or trim(obj.name) == "" then
obj.name = obj.n or key or ("Skill " .. refM)
end
if not obj.n or trim(obj.n) == "" then
obj.n = obj.name
end
-- Garante que icon nunca seja vazio (fallback para Nada.png)
if not obj.icon or trim(obj.icon) == "" then
obj.icon = "Nada.png"
end
return mw.text.jsonEncode(obj)
end
-- Se não tem nome nem refM, retorna vazio
if n == "" then
return mw.text.jsonEncode({})
end
local char = resolveCharFromFrames(frame, a)
local data = requireCharacterModule(char) or {}
-- Busca descrição do módulo .lua
local desc_i18n = {}
local foundDesc = nil
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
if not foundDesc then
foundDesc = resolveDescWithInheritance(data, n, nil)
end
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
local flagsArr = parseFlags(a.flags)
-- Objeto com nome explícito
local obj = {
name = n,
icon = trim(a.icon or ""),
level = trim(a.level or ""),
video = trim(a.video or ""),
energy = nz(a.energy),
powerpve = nz(a.powerpve),
powerpvp = nz(a.powerpvp),
cooldown = nz(a.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
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