Modul:HeroCard: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 14: | Zeile 14: | ||
end | end | ||
-- | -- Lazy load der ausgelagerten Module | ||
local CFG, I18N | local CFG, I18N | ||
local function loadConfig() | local function loadConfig() | ||
if CFG then return CFG end | if CFG then return CFG end | ||
for _,t in ipairs{ "Modul:HeroConfig", "Module:HeroConfig" } do | for _,t in ipairs{ "Modul:HeroConfig", "Module:HeroConfig" } do | ||
local ok, mod = pcall(require, t) | local ok, mod = pcall(require, t); if ok and type(mod)=="table" then CFG=mod; return CFG end | ||
end | end | ||
error("HeroConfig not found") | error("HeroConfig not found") | ||
| Zeile 27: | Zeile 26: | ||
if I18N then return I18N end | if I18N then return I18N end | ||
for _,t in ipairs{ "Modul:HeroI18n", "Module:HeroI18n" } do | for _,t in ipairs{ "Modul:HeroI18n", "Module:HeroI18n" } do | ||
local ok, mod = pcall(require, t) | local ok, mod = pcall(require, t); if ok and type(mod)=="table" then I18N=mod; return I18N end | ||
end | end | ||
I18N = { i18n = {}, aw_desc = {}, ui = {} }; return I18N | I18N = { i18n = {}, aw_desc = {}, ui = {} }; return I18N | ||
end | end | ||
-- | -- HeroData (de/en) | ||
local function loadHeroes() | local function loadHeroes() | ||
for _,t in ipairs{ "Modul:HeroData", "Module:HeroData" } do | for _,t in ipairs{ "Modul:HeroData", "Module:HeroData" } do | ||
| Zeile 44: | Zeile 42: | ||
end | end | ||
local function fileWikitext(filename, opts) | local function fileWikitext(filename, opts) | ||
if isempty(filename) then return "" end | if isempty(filename) then return "" end | ||
| Zeile 51: | Zeile 48: | ||
if opts and opts.link then table.insert(parts, "link="..opts.link) end | if opts and opts.link then table.insert(parts, "link="..opts.link) end | ||
if opts and opts.class then table.insert(parts, "class="..opts.class) end | if opts and opts.class then table.insert(parts, "class="..opts.class) end | ||
if opts and opts.lazy then table.insert(parts, "loading=lazy") end | |||
return string.format("[[%s]]", table.concat(parts, "|")) | return string.format("[[%s]]", table.concat(parts, "|")) | ||
end | end | ||
-- | -- Übersetzen per HeroI18n | ||
local function tr(kind, value) | local function tr(kind, value) | ||
if isempty(value) then return value end | if isempty(value) then return value end | ||
| Zeile 61: | Zeile 59: | ||
end | end | ||
-- | -- Tooltip-HTML | ||
local function addTooltip(box, title, desc) | local function addTooltip(box, title, desc) | ||
if (isempty(title) and isempty(desc)) then return end | if (isempty(title) and isempty(desc)) then return end | ||
| Zeile 70: | Zeile 68: | ||
end | end | ||
-- | -- Kleine Bausteine | ||
local function pill(parent, label, val, icon, showLabels, desc) | local function pill(parent, label, val, icon, showLabels, desc) | ||
if isempty(val) then return end | if isempty(val) then return end | ||
local box = parent:tag("div"):addClass("kr-hc-pill kr-tip") | local box = parent:tag("div"):addClass("kr-hc-pill kr-tip") | ||
local left = box:tag("div"):addClass("kr-hc-pill-icon") | local left = box:tag("div"):addClass("kr-hc-pill-icon") | ||
if not isempty(icon) then left:wikitext(fileWikitext(icon, {size="36x36px", link=""})) end | if not isempty(icon) then left:wikitext(fileWikitext(icon, {size="36x36px", link="", lazy=true})) end | ||
local text = box:tag("div"):addClass("kr-hc-pill-text") | local text = box:tag("div"):addClass("kr-hc-pill-text") | ||
local lab = text:tag("div"):addClass("kr-hc-pill-label"):wikitext(label or "") | local lab = text:tag("div"):addClass("kr-hc-pill-label"):wikitext(label or "") | ||
| Zeile 87: | Zeile 85: | ||
local box = parent:tag("div"):addClass("kr-hc-adv kr-tip") | local box = parent:tag("div"):addClass("kr-hc-adv kr-tip") | ||
local left = box:tag("div"):addClass("kr-hc-adv-icon") | local left = box:tag("div"):addClass("kr-hc-adv-icon") | ||
if not isempty(icon) then left:wikitext(fileWikitext(icon, {size="36x36px", link=""})) end | if not isempty(icon) then left:wikitext(fileWikitext(icon, {size="36x36px", link="", lazy=true})) end | ||
local main = box:tag("div"):addClass("kr-hc-adv-main") | local main = box:tag("div"):addClass("kr-hc-adv-main") | ||
local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(label or "") | local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(label or "") | ||
| Zeile 95: | Zeile 93: | ||
end | end | ||
-- | -- Unlock-Text | ||
local function unlock_text_de(s) | local function unlock_text_de(s) | ||
if isempty(s) then return nil end | if isempty(s) then return nil end | ||
local t = norm(s) | local t = norm(s) | ||
local n = t:match("stage%s*(%d+)"); if n then return "Freischaltung bei Abschnitt: " .. n end | local n = t:match("stage%s*(%d+)"); if n then return "Freischaltung bei Abschnitt: " .. n end | ||
if t:match("pirate%s*ship") then return "Wird | if t:match("pirate%s*ship") then return "Wird auf dem Piratenschiff freigeschaltet." end | ||
if s:match("[%.%!%?]$") or t:find(" ") then return s end | if s:match("[%.%!%?]$") or t:find(" ") then return s end | ||
return "Freischaltung: " .. s | return "Freischaltung: " .. s | ||
end | end | ||
-- | -- Awakening-Desc: NUR englische Keys (wie in HeroData) | ||
local function awakeningDesc(raw_value, override) | local function awakeningDesc(raw_value, override) | ||
if not isempty(override) then return override end | if not isempty(override) then return override end | ||
| Zeile 113: | Zeile 111: | ||
end | end | ||
-- | -- Avatar/Skins → TabberNeue | ||
local function buildAvatar(name, linkto, defaultFile, skinsTable | local function buildAvatar(name, linkto, defaultFile, skinsTable) | ||
if type(skinsTable) ~= "table" or next(skinsTable) == nil then | if type(skinsTable) ~= "table" or next(skinsTable) == nil then | ||
return fileWikitext(defaultFile, { class="kr-hc-avatar-img", link=linkto }) | return fileWikitext(defaultFile, { class="kr-hc-avatar-img", link=linkto, lazy=true }) | ||
end | end | ||
local frame = mw.getCurrentFrame() | local frame = mw.getCurrentFrame() | ||
defaultLabel = | local defaultLabel = (loadI18n().ui and loadI18n().ui.default_skin) or "Standard" | ||
local panels = {} | local panels = {} | ||
local function push(label, file) | local function push(label, file) | ||
table.insert(panels, | |||
table.insert(panels, tostring(label) .. "=\n" .. | tostring(label) .. "=\n" .. | ||
fileWikitext(file, { class="kr-hc-avatar-img", link=linkto })) | fileWikitext(file, { class="kr-hc-avatar-img", link=linkto, lazy=true }) | ||
) | |||
end | end | ||
push(defaultLabel, defaultFile) | |||
if skinsTable[1] ~= nil then | if skinsTable[1] ~= nil then | ||
for _, v in ipairs(skinsTable) do | for _, v in ipairs(skinsTable) do | ||
| Zeile 164: | Zeile 161: | ||
-- ======= RENDER ======= | -- ======= RENDER ======= | ||
function p.render(frame) | function p.render(frame) | ||
local args | local args = getArgs(frame) | ||
local name | local name = pick(args.name, args[1]) or "" | ||
local heroes = select(1, loadHeroes()) | local heroes = select(1, loadHeroes()) | ||
local data | local data = (heroes and (heroes[name] or heroes[mw.ustring.gsub(name or "", "_", " ")])) or {} | ||
local showLabels = norm(args.show_labels) == "yes" or norm(args.show_labels) == "1" | local showLabels = norm(args.show_labels) == "yes" or norm(args.show_labels) == "1" | ||
-- UI-Labels & Guardian-Flag | -- UI-Labels & Guardian-Flag | ||
local UI | local UI = (loadI18n().ui or {}) | ||
local entity | local entity = norm(pick(args.entity, data.entity)) | ||
local isGuardian = (entity == "guardian") | local isGuardian = (entity == "guardian") | ||
or norm(pick(args.guardian, data.guardian)) == "1" | or norm(pick(args.guardian, data.guardian)) == "1" | ||
| Zeile 191: | Zeile 188: | ||
local specialization = tr("specialization", specializationRaw) | local specialization = tr("specialization", specializationRaw) | ||
local resource = tr("resource", resourceRaw) | local resource = tr("resource", resourceRaw) | ||
local unlock_at = pick(args.unlock_at, data.unlock_at) | local unlock_at = pick(args.unlock_at, data.unlock_at) | ||
local awakening_bonusRaw = pick( | local awakening_bonusRaw = pick( | ||
args.awakening_bonus, data.awakening_bonus, | args.awakening_bonus, data.awakening_bonus, -- Held | ||
args.guardian_aura, data.guardian_aura, | args.guardian_aura, data.guardian_aura, -- Wächter | ||
data.aura | data.aura | ||
) | ) | ||
| Zeile 201: | Zeile 199: | ||
local awakening_desc = awakeningDesc(awakening_bonusRaw, pick(args.awakening_desc, data.awakening_desc)) | local awakening_desc = awakeningDesc(awakening_bonusRaw, pick(args.awakening_desc, data.awakening_desc)) | ||
local CFGmod | local CFGmod = loadConfig() | ||
local | local icon_map = CFGmod.icon_map | ||
local attackstyle_icon = (icon_map.attackstyle or {})[norm(attackstyleRaw)] | |||
local specialization_icon = (icon_map.specialization or {})[norm(specializationRaw)] | |||
local attackstyle_icon | local resource_icon = (icon_map.resource or {})[norm(resourceRaw)] | ||
local specialization_icon | local awakening_bonus_icon= (icon_map.awakening_bonus or {})[norm(awakening_bonusRaw)] | ||
local resource_icon | |||
local awakening_bonus_icon = ( | |||
-- Bilder/Links | -- Bilder/Links | ||
local linkto | local linkto = pick(args.linkto, name) | ||
local avatar = pick(args.avatar, (not isempty(name) and (name .. ".png")) or nil) | |||
local avatar = pick( | |||
-- Root | -- Root | ||
local root = mw.html.create("div"):addClass("kr-hero-card") | local root = mw.html.create("div"):addClass("kr-hero-card") | ||
root:addClass(isGuardian and "kr-layout-guardian" or "kr-layout-hero") | |||
if showLabels then root:addClass("kr-show-labels") end | if showLabels then root:addClass("kr-show-labels") end | ||
-- Avatar (links) | -- Avatar (links) | ||
local | local avatarDiv = root:tag("div"):addClass("kr-hc-avatar") | ||
avatarDiv:wikitext( buildAvatar(name, linkto, avatar, data.skins) ) | |||
-- Content (rechts) | -- Content (rechts) | ||
local content = root:tag("div"):addClass("kr-hc-content") | local content = root:tag("div"):addClass("kr-hc-content") | ||
-- Header-Block: Titel + Unlock-Zeile (danach explizite Trennlinie) | |||
local header = content:tag("div"):addClass("kr-hc-header") | |||
if not isempty(name) then | if not isempty(name) then | ||
header:tag("h2"):addClass("kr-hc-name"):wikitext(string.format("[[%s|%s]]", linkto, name)) | |||
end | |||
local unlock_text = unlock_text_de(unlock_at) | |||
if unlock_text then | |||
header:tag("div"):addClass("kr-hc-unlockline"):wikitext("🔒 " .. unlock_text) | |||
end | end | ||
content:tag("div"):addClass("kr-hc-sep") -- Trennlinie NACH Unlock | |||
-- Basics | -- Basics 2×2 | ||
local basics = content:tag("div"):addClass("kr-hc-basics2") | local basics = content:tag("div"):addClass("kr-hc-basics2") | ||
local function pair(label, value, icon) | local function pair(label, value, icon) | ||
| Zeile 246: | Zeile 240: | ||
it:tag("span"):addClass("label"):wikitext(label) | it:tag("span"):addClass("label"):wikitext(label) | ||
local v = it:tag("span"):addClass("value"):wikitext(value) | local v = it:tag("span"):addClass("value"):wikitext(value) | ||
if icon then v:tag("span"):addClass("value-icon"):wikitext(fileWikitext(icon, {size="24x24px", link=""})) end | if icon then v:tag("span"):addClass("value-icon"):wikitext(fileWikitext(icon, {size="24x24px", link="", lazy=true})) end | ||
end | end | ||
pair(L.class, class, nil) | pair(L.class, class, nil) | ||
| Zeile 253: | Zeile 247: | ||
pair("Ressource:", resource, resource_icon) | pair("Ressource:", resource, resource_icon) | ||
-- ===== | -- ===== Layout: Hero (2×2 Quadranten) | ||
if isGuardian then | if not isGuardian then | ||
local quad = content:tag("div"):addClass("kr-hero-quad") | |||
local | |||
-- | -- Q1: Hauptattribute (einspaltig, 3 Zeilen) | ||
local | do | ||
local q = quad:tag("div"):addClass("kr-quad") | |||
q:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute") | |||
local mains = q:tag("div"):addClass("kr-hc-main-attrs") | |||
local heroMain = type(data.main)=="table" and data.main or {} | |||
for i, key in ipairs(CFGmod.main_order) do | |||
local meta = CFGmod.main_meta[key] | |||
pill(mains, meta.label, pick(args["main"..i.."_val"], heroMain[key]), | |||
pick(args["main"..i.."_icon"], meta.icon), showLabels, | |||
pick(args["main"..i.."_desc"], meta.desc)) | |||
end | end | ||
end | end | ||
-- | -- Q2: Fortschrittsattribute (2×2) | ||
local | do | ||
local q = quad:tag("div"):addClass("kr-quad") | |||
q:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute") | |||
local advs = q:tag("div"):addClass("kr-hc-adv-attrs") | |||
local heroAdv = type(data.adv)=="table" and data.adv or {} | |||
for i, key in ipairs(CFGmod.adv_order) do | |||
local meta = CFGmod.adv_meta[key] | |||
adv( | adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]), | ||
pick(args["adv".. | pick(args["adv"..i.."_icon"], meta.icon), showLabels, | ||
pick(args["adv".. | pick(args["adv"..i.."_desc"], meta.desc)) | ||
end | end | ||
end | end | ||
-- | -- Q3: Fähigkeiten NUR wenn vorhanden | ||
local | local hasAbilities = type(data.abilities)=="table" and next(data.abilities) ~= nil | ||
if hasAbilities then | |||
local q = quad:tag("div"):addClass("kr-quad") | |||
q:tag("h3"):addClass("kr-hc-h"):wikitext("Fähigkeiten") | |||
q:tag("div"):addClass("kr-hc-abilities"):wikitext("") -- TODO: später befüllen | |||
end | |||
-- Q4: Erweckungsbonus (volle Breite) | |||
if not isempty(awakening_bonus) then | if not isempty(awakening_bonus) then | ||
local box = | local q = quad:tag("div"):addClass("kr-quad") | ||
q:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening) | |||
local box = q:tag("div"):addClass("kr-hc-awakening kr-tip") | |||
addTooltip(box, awakening_bonus, awakening_desc) | addTooltip(box, awakening_bonus, awakening_desc) | ||
if awakening_bonus_icon then | if awakening_bonus_icon then | ||
box:tag("div"):addClass("kr-hc-adv-icon") | box:tag("div"):addClass("kr-hc-adv-icon") | ||
:wikitext(fileWikitext(awakening_bonus_icon, {size="64x64px", link=""})) | :wikitext(fileWikitext(awakening_bonus_icon, {size="64x64px", link="", lazy=true})) | ||
end | end | ||
local main = box:tag("div"):addClass("kr-hc-adv-main") | local main = box:tag("div"):addClass("kr-hc-adv-main") | ||
local lab | local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(L.awakening) | ||
if not showLabels then lab:addClass("is-hidden") end | if not showLabels then lab:addClass("is-hidden") end | ||
main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus) | main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus) | ||
end | end | ||
-- ===== Layout: Guardian (3 Spalten) | |||
else | else | ||
local g = content:tag("div"):addClass("kr-g3") | |||
local | |||
-- | -- Spalte 1: Hauptattribute | ||
local | do | ||
local col = g:tag("div"):addClass("kr-quad") | |||
col:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute") | |||
local mains = col:tag("div"):addClass("kr-hc-main-attrs") | |||
local heroMain = type(data.main)=="table" and data.main or {} | |||
for i, key in ipairs(CFGmod.main_order) do | |||
local meta = CFGmod.main_meta[key] | |||
pill(mains, meta.label, pick(args["main"..i.."_val"], heroMain[key]), | |||
pick(args["main"..i.."_icon"], meta.icon), showLabels, | |||
pick(args["main"..i.."_desc"], meta.desc)) | |||
end | |||
end | end | ||
-- | -- Spalte 2: Fortschritt (1 Spalte) | ||
local | do | ||
local col = g:tag("div"):addClass("kr-quad") | |||
col:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute") | |||
local advs = col:tag("div"):addClass("kr-hc-adv-attrs") | |||
local heroAdv = type(data.adv)=="table" and data.adv or {} | |||
for i, key in ipairs(CFGmod.adv_order) do | |||
local meta = CFGmod.adv_meta[key] | |||
adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]), | |||
pick(args["adv"..i.."_icon"], meta.icon), showLabels, | |||
pick(args["adv"..i.."_desc"], meta.desc)) | |||
end | |||
end | end | ||
-- | -- Spalte 3: Wächteraura | ||
if not isempty(awakening_bonus) then | if not isempty(awakening_bonus) then | ||
local box = | local col = g:tag("div"):addClass("kr-quad") | ||
col:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening) | |||
local box = col:tag("div"):addClass("kr-hc-awakening kr-tip") | |||
addTooltip(box, awakening_bonus, awakening_desc) | addTooltip(box, awakening_bonus, awakening_desc) | ||
if awakening_bonus_icon then | if awakening_bonus_icon then | ||
box:tag("div"):addClass("kr-hc-adv-icon") | box:tag("div"):addClass("kr-hc-adv-icon") | ||
:wikitext(fileWikitext(awakening_bonus_icon, {size="64x64px", link=""})) | :wikitext(fileWikitext(awakening_bonus_icon, {size="64x64px", link="", lazy=true})) | ||
end | end | ||
local main = box:tag("div"):addClass("kr-hc-adv-main") | local main = box:tag("div"):addClass("kr-hc-adv-main") | ||
local lab | local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(L.awakening) | ||
if not showLabels then lab:addClass("is-hidden") end | if not showLabels then lab:addClass("is-hidden") end | ||
main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus) | main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus) | ||
Version vom 9. Oktober 2025, 18:23 Uhr
Die Dokumentation für dieses Modul kann unter Modul:HeroCard/Doku erstellt werden
-- Modul:HeroCard
local p = {}
-- ===== Helpers =====
local function isempty(v) return v == nil or v == "" end
local function pick(...) local n=select('#',...); for i=1,n do local v=select(i,...); if not isempty(v) then return v end end end
local function norm(s) if isempty(s) then return "" end return mw.ustring.lower(mw.text.trim(s)):gsub("%s+"," ") end
local function getArgs(frame)
local args, f1, f2 = {}, frame.args or {}, (frame:getParent() and frame:getParent().args) or {}
for k,v in pairs(f1) do if not isempty(v) then args[k]=v end end
for k,v in pairs(f2) do if not isempty(v) then args[k]=v end end
return args
end
-- Lazy load der ausgelagerten Module
local CFG, I18N
local function loadConfig()
if CFG then return CFG end
for _,t in ipairs{ "Modul:HeroConfig", "Module:HeroConfig" } do
local ok, mod = pcall(require, t); if ok and type(mod)=="table" then CFG=mod; return CFG end
end
error("HeroConfig not found")
end
local function loadI18n()
if I18N then return I18N end
for _,t in ipairs{ "Modul:HeroI18n", "Module:HeroI18n" } do
local ok, mod = pcall(require, t); if ok and type(mod)=="table" then I18N=mod; return I18N end
end
I18N = { i18n = {}, aw_desc = {}, ui = {} }; return I18N
end
-- HeroData (de/en)
local function loadHeroes()
for _,t in ipairs{ "Modul:HeroData", "Module:HeroData" } do
local ok, mod = pcall(require, t)
if ok and type(mod)=="table" and type(mod.heroes)=="table" then
return mod.heroes, {ok=true, from=t}
end
end
return nil, {ok=false}
end
local function fileWikitext(filename, opts)
if isempty(filename) then return "" end
local parts = { ("Datei:%s"):format(filename) }
if opts and opts.size then table.insert(parts, opts.size) end
if opts and opts.link then table.insert(parts, "link="..opts.link) end
if opts and opts.class then table.insert(parts, "class="..opts.class) end
if opts and opts.lazy then table.insert(parts, "loading=lazy") end
return string.format("[[%s]]", table.concat(parts, "|"))
end
-- Übersetzen per HeroI18n
local function tr(kind, value)
if isempty(value) then return value end
local map = loadI18n().i18n[kind] or {}
return map[norm(value)] or value
end
-- Tooltip-HTML
local function addTooltip(box, title, desc)
if (isempty(title) and isempty(desc)) then return end
local tip = box:tag("span"):addClass("kr-tipbox")
if not isempty(title) then tip:tag("span"):addClass("kr-tip-title"):wikitext(title) end
if not (isempty(title) or isempty(desc)) then tip:tag("span"):addClass("kr-tip-sep") end
if not isempty(desc) then tip:tag("span"):addClass("kr-tip-desc"):wikitext(desc) end
end
-- Kleine Bausteine
local function pill(parent, label, val, icon, showLabels, desc)
if isempty(val) then return end
local box = parent:tag("div"):addClass("kr-hc-pill kr-tip")
local left = box:tag("div"):addClass("kr-hc-pill-icon")
if not isempty(icon) then left:wikitext(fileWikitext(icon, {size="36x36px", link="", lazy=true})) end
local text = box:tag("div"):addClass("kr-hc-pill-text")
local lab = text:tag("div"):addClass("kr-hc-pill-label"):wikitext(label or "")
if not showLabels then lab:addClass("is-hidden") end
text:tag("div"):addClass("kr-hc-pill-value"):wikitext(val)
addTooltip(box, label, desc)
end
local function adv(parent, label, val, icon, showLabels, desc)
if isempty(val) then return end
local box = parent:tag("div"):addClass("kr-hc-adv kr-tip")
local left = box:tag("div"):addClass("kr-hc-adv-icon")
if not isempty(icon) then left:wikitext(fileWikitext(icon, {size="36x36px", link="", lazy=true})) end
local main = box:tag("div"):addClass("kr-hc-adv-main")
local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(label or "")
if not showLabels then lab:addClass("is-hidden") end
main:tag("div"):addClass("kr-hc-adv-value"):wikitext(val)
addTooltip(box, label, desc)
end
-- Unlock-Text
local function unlock_text_de(s)
if isempty(s) then return nil end
local t = norm(s)
local n = t:match("stage%s*(%d+)"); if n then return "Freischaltung bei Abschnitt: " .. n end
if t:match("pirate%s*ship") then return "Wird auf dem Piratenschiff freigeschaltet." end
if s:match("[%.%!%?]$") or t:find(" ") then return s end
return "Freischaltung: " .. s
end
-- Awakening-Desc: NUR englische Keys (wie in HeroData)
local function awakeningDesc(raw_value, override)
if not isempty(override) then return override end
if isempty(raw_value) then return "Bonus, der beim Erwachen freigeschaltet wird." end
local map = loadI18n().aw_desc or {}
return map[norm(raw_value)] or "Bonus, der beim Erwachen freigeschaltet wird."
end
-- Avatar/Skins → TabberNeue
local function buildAvatar(name, linkto, defaultFile, skinsTable)
if type(skinsTable) ~= "table" or next(skinsTable) == nil then
return fileWikitext(defaultFile, { class="kr-hc-avatar-img", link=linkto, lazy=true })
end
local frame = mw.getCurrentFrame()
local defaultLabel = (loadI18n().ui and loadI18n().ui.default_skin) or "Standard"
local panels = {}
local function push(label, file)
table.insert(panels,
tostring(label) .. "=\n" ..
fileWikitext(file, { class="kr-hc-avatar-img", link=linkto, lazy=true })
)
end
push(defaultLabel, defaultFile)
if skinsTable[1] ~= nil then
for _, v in ipairs(skinsTable) do
if type(v) == "table" then
local label = v.label or v[1]
local file = v.file or (name .. "_" .. (v.filename or label) .. ".png")
push(label, file)
else
local label = tostring(v)
push(label, name .. "_" .. label .. ".png")
end
end
else
local tmp = {}
for k, v in pairs(skinsTable) do
if type(v) == "table" then
local label = v.label or v[1] or tostring(k)
local file = v.file or (name .. "_" .. (v.filename or label) .. ".png")
table.insert(tmp, {label=label, file=file})
else
local label = tostring(v)
table.insert(tmp, {label=label, file=(name .. "_" .. label .. ".png")})
end
end
table.sort(tmp, function(a,b) return tostring(a.label) < tostring(b.label) end)
for _,it in ipairs(tmp) do push(it.label, it.file) end
end
local content = table.concat(panels, "\n|-|\n")
return frame:extensionTag('tabber', content)
end
-- ======= RENDER =======
function p.render(frame)
local args = getArgs(frame)
local name = pick(args.name, args[1]) or ""
local heroes = select(1, loadHeroes())
local data = (heroes and (heroes[name] or heroes[mw.ustring.gsub(name or "", "_", " ")])) or {}
local showLabels = norm(args.show_labels) == "yes" or norm(args.show_labels) == "1"
-- UI-Labels & Guardian-Flag
local UI = (loadI18n().ui or {})
local entity = norm(pick(args.entity, data.entity))
local isGuardian = (entity == "guardian")
or norm(pick(args.guardian, data.guardian)) == "1"
or pick(args.is_guardian, data.is_guardian) == true
local labelsHero = (UI.labels and UI.labels.hero) or { class="Klasse:", awakening="Erweckungsbonus" }
local labelsGuardian = (UI.labels and UI.labels.guardian) or { class="Typ:", awakening="Wächteraura" }
local L = isGuardian and labelsGuardian or labelsHero
-- Daten + Übersetzungen
local class = tr("class", pick(args.class, data.class))
local attackstyleRaw = pick(args.attackstyle, data.attackstyle)
local specializationRaw = pick(args.specialization, data.specialization)
local resourceRaw = pick(args.resource, data.resource)
local attackstyle = tr("attackstyle", attackstyleRaw)
local specialization = tr("specialization", specializationRaw)
local resource = tr("resource", resourceRaw)
local unlock_at = pick(args.unlock_at, data.unlock_at)
local awakening_bonusRaw = pick(
args.awakening_bonus, data.awakening_bonus, -- Held
args.guardian_aura, data.guardian_aura, -- Wächter
data.aura
)
local awakening_bonus = tr("awakening_bonus", awakening_bonusRaw)
local awakening_desc = awakeningDesc(awakening_bonusRaw, pick(args.awakening_desc, data.awakening_desc))
local CFGmod = loadConfig()
local icon_map = CFGmod.icon_map
local attackstyle_icon = (icon_map.attackstyle or {})[norm(attackstyleRaw)]
local specialization_icon = (icon_map.specialization or {})[norm(specializationRaw)]
local resource_icon = (icon_map.resource or {})[norm(resourceRaw)]
local awakening_bonus_icon= (icon_map.awakening_bonus or {})[norm(awakening_bonusRaw)]
-- Bilder/Links
local linkto = pick(args.linkto, name)
local avatar = pick(args.avatar, (not isempty(name) and (name .. ".png")) or nil)
-- Root
local root = mw.html.create("div"):addClass("kr-hero-card")
root:addClass(isGuardian and "kr-layout-guardian" or "kr-layout-hero")
if showLabels then root:addClass("kr-show-labels") end
-- Avatar (links)
local avatarDiv = root:tag("div"):addClass("kr-hc-avatar")
avatarDiv:wikitext( buildAvatar(name, linkto, avatar, data.skins) )
-- Content (rechts)
local content = root:tag("div"):addClass("kr-hc-content")
-- Header-Block: Titel + Unlock-Zeile (danach explizite Trennlinie)
local header = content:tag("div"):addClass("kr-hc-header")
if not isempty(name) then
header:tag("h2"):addClass("kr-hc-name"):wikitext(string.format("[[%s|%s]]", linkto, name))
end
local unlock_text = unlock_text_de(unlock_at)
if unlock_text then
header:tag("div"):addClass("kr-hc-unlockline"):wikitext("🔒 " .. unlock_text)
end
content:tag("div"):addClass("kr-hc-sep") -- Trennlinie NACH Unlock
-- Basics 2×2
local basics = content:tag("div"):addClass("kr-hc-basics2")
local function pair(label, value, icon)
if isempty(value) then return end
local it = basics:tag("div"):addClass("kr-hc-basic")
it:tag("span"):addClass("label"):wikitext(label)
local v = it:tag("span"):addClass("value"):wikitext(value)
if icon then v:tag("span"):addClass("value-icon"):wikitext(fileWikitext(icon, {size="24x24px", link="", lazy=true})) end
end
pair(L.class, class, nil)
pair("Spezialisierung:", specialization, specialization_icon)
pair("Angriffsart:", attackstyle, attackstyle_icon)
pair("Ressource:", resource, resource_icon)
-- ===== Layout: Hero (2×2 Quadranten)
if not isGuardian then
local quad = content:tag("div"):addClass("kr-hero-quad")
-- Q1: Hauptattribute (einspaltig, 3 Zeilen)
do
local q = quad:tag("div"):addClass("kr-quad")
q:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
local mains = q:tag("div"):addClass("kr-hc-main-attrs")
local heroMain = type(data.main)=="table" and data.main or {}
for i, key in ipairs(CFGmod.main_order) do
local meta = CFGmod.main_meta[key]
pill(mains, meta.label, pick(args["main"..i.."_val"], heroMain[key]),
pick(args["main"..i.."_icon"], meta.icon), showLabels,
pick(args["main"..i.."_desc"], meta.desc))
end
end
-- Q2: Fortschrittsattribute (2×2)
do
local q = quad:tag("div"):addClass("kr-quad")
q:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
local advs = q:tag("div"):addClass("kr-hc-adv-attrs")
local heroAdv = type(data.adv)=="table" and data.adv or {}
for i, key in ipairs(CFGmod.adv_order) do
local meta = CFGmod.adv_meta[key]
adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]),
pick(args["adv"..i.."_icon"], meta.icon), showLabels,
pick(args["adv"..i.."_desc"], meta.desc))
end
end
-- Q3: Fähigkeiten NUR wenn vorhanden
local hasAbilities = type(data.abilities)=="table" and next(data.abilities) ~= nil
if hasAbilities then
local q = quad:tag("div"):addClass("kr-quad")
q:tag("h3"):addClass("kr-hc-h"):wikitext("Fähigkeiten")
q:tag("div"):addClass("kr-hc-abilities"):wikitext("") -- TODO: später befüllen
end
-- Q4: Erweckungsbonus (volle Breite)
if not isempty(awakening_bonus) then
local q = quad:tag("div"):addClass("kr-quad")
q:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening)
local box = q:tag("div"):addClass("kr-hc-awakening kr-tip")
addTooltip(box, awakening_bonus, awakening_desc)
if awakening_bonus_icon then
box:tag("div"):addClass("kr-hc-adv-icon")
:wikitext(fileWikitext(awakening_bonus_icon, {size="64x64px", link="", lazy=true}))
end
local main = box:tag("div"):addClass("kr-hc-adv-main")
local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(L.awakening)
if not showLabels then lab:addClass("is-hidden") end
main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus)
end
-- ===== Layout: Guardian (3 Spalten)
else
local g = content:tag("div"):addClass("kr-g3")
-- Spalte 1: Hauptattribute
do
local col = g:tag("div"):addClass("kr-quad")
col:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
local mains = col:tag("div"):addClass("kr-hc-main-attrs")
local heroMain = type(data.main)=="table" and data.main or {}
for i, key in ipairs(CFGmod.main_order) do
local meta = CFGmod.main_meta[key]
pill(mains, meta.label, pick(args["main"..i.."_val"], heroMain[key]),
pick(args["main"..i.."_icon"], meta.icon), showLabels,
pick(args["main"..i.."_desc"], meta.desc))
end
end
-- Spalte 2: Fortschritt (1 Spalte)
do
local col = g:tag("div"):addClass("kr-quad")
col:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
local advs = col:tag("div"):addClass("kr-hc-adv-attrs")
local heroAdv = type(data.adv)=="table" and data.adv or {}
for i, key in ipairs(CFGmod.adv_order) do
local meta = CFGmod.adv_meta[key]
adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]),
pick(args["adv"..i.."_icon"], meta.icon), showLabels,
pick(args["adv"..i.."_desc"], meta.desc))
end
end
-- Spalte 3: Wächteraura
if not isempty(awakening_bonus) then
local col = g:tag("div"):addClass("kr-quad")
col:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening)
local box = col:tag("div"):addClass("kr-hc-awakening kr-tip")
addTooltip(box, awakening_bonus, awakening_desc)
if awakening_bonus_icon then
box:tag("div"):addClass("kr-hc-adv-icon")
:wikitext(fileWikitext(awakening_bonus_icon, {size="64x64px", link="", lazy=true}))
end
local main = box:tag("div"):addClass("kr-hc-adv-main")
local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(L.awakening)
if not showLabels then lab:addClass("is-hidden") end
main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus)
end
end
return tostring(root)
end
return p