Zum Inhalt springen


Modul:HeroCard: Unterschied zwischen den Versionen

Aus Firestone Idle RPG Wiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 14: Zeile 14:
end
end


-- ===== Lazy load: Config / I18n =====
-- 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
    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
    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) =====
-- 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


-- ===== MediaWiki-Bildmarkup =====
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


-- ===== i18n-Mapping =====
-- Ü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


-- ===== Tooltips =====
-- 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 =====
-- 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


-- ===== Freischaltungs-Text =====
-- 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 freigeschaltet auf dem Piratenschiff." 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
   if s:match("[%.%!%?]$") or t:find(" ") then return s end
   return "Freischaltung: " .. s
   return "Freischaltung: " .. s
end
end


-- ===== Awakening-Descriptions (englische Keys) =====
-- 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 (Single oder Tabber) =====
-- Avatar/Skins → TabberNeue
local function buildAvatar(name, linkto, defaultFile, skinsTable, defaultLabel)
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 = defaultLabel or (loadI18n().ui and loadI18n().ui.default_skin) or "Standard"
   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)
    if file == defaultFile then return end
     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


   table.insert(panels, defaultLabel .. "=\n" ..
   push(defaultLabel, defaultFile)
    fileWikitext(defaultFile, { class="kr-hc-avatar-img", link=linkto }))
 
   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 = getArgs(frame)
   local args = getArgs(frame)
   local name = pick(args.name, args[1]) or ""
   local name = pick(args.name, args[1]) or ""


   local heroes = select(1, loadHeroes())
   local heroes = select(1, loadHeroes())
   local data   = (heroes and (heroes[name] or heroes[mw.ustring.gsub(name or "", "_", " ")])) or {}
   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     = (loadI18n().ui or {})
   local UI = (loadI18n().ui or {})
   local entity = norm(pick(args.entity, data.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   = loadConfig()
   local CFGmod = loadConfig()
   local iconmap  = CFGmod.icon_map or {}
   local icon_map = CFGmod.icon_map
  local advGuard = CFGmod.adv_order_guardian or {"precision","bonus_damage"}
   local attackstyle_icon   = (icon_map.attackstyle   or {})[norm(attackstyleRaw)]
 
   local specialization_icon = (icon_map.specialization or {})[norm(specializationRaw)]
   local attackstyle_icon     = (iconmap.attackstyle     or {})[norm(attackstyleRaw)]
   local resource_icon       = (icon_map.resource       or {})[norm(resourceRaw)]
   local specialization_icon = (iconmap.specialization or {})[norm(specializationRaw)]
   local awakening_bonus_icon= (icon_map.awakening_bonus or {})[norm(awakening_bonusRaw)]
   local resource_icon       = (iconmap.resource       or {})[norm(resourceRaw)]
   local awakening_bonus_icon = (iconmap.awakening_bonus or {})[norm(awakening_bonusRaw)]


   -- Bilder/Links – Default-Avatar beachten
   -- Bilder/Links
   local linkto       = pick(args.linkto, name)
   local linkto = pick(args.linkto, name)
  local defaultSkin  = pick(args.default_skin, data.default_skin)
   local avatar = pick(args.avatar, (not isempty(name) and (name .. ".png")) or nil)
   local avatar = pick(
    args.avatar, data.avatar,
    (not isempty(defaultSkin) and (name .. "_" .. defaultSkin .. ".png")) or
    ((not isempty(name)) and (name .. ".png")) or nil
  )
  local defaultTabLabel = pick(args.default_skin_label, data.default_skin_label, (UI.default_skin or "Standard"))


   -- 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
  if isGuardian then root:addClass("kr-layout-guardian") else root:addClass("kr-layout-hero") end


   -- Avatar (links)
   -- Avatar (links)
   local avatarCol = root:tag("div"):addClass("kr-hc-avatar")
   local avatarDiv = root:tag("div"):addClass("kr-hc-avatar")
   avatarCol:wikitext( buildAvatar(name, linkto, avatar, data.skins, defaultTabLabel) )
   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
     content:tag("h2"):addClass("kr-hc-name"):wikitext(string.format("[[%s|%s]]", linkto, name))
     header:tag("h2"):addClass("kr-hc-name"):wikitext(string.format("[[%s|%s]]", linkto, name))
    local ut = unlock_text_de(unlock_at)
  end
    if not isempty(ut) then
  local unlock_text = unlock_text_de(unlock_at)
      content:tag("div"):addClass("kr-hc-unlockline"):wikitext("🔒 " .. ut)
  if unlock_text then
    end
    header:tag("div"):addClass("kr-hc-unlockline"):wikitext("🔒 " .. unlock_text)
   end
   end
  content:tag("div"):addClass("kr-hc-sep")  -- Trennlinie NACH Unlock


   -- Basics-Zeile (2×2)
   -- 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)


   -- ===== Layouts =====
   -- ===== Layout: Hero (2×2 Quadranten)
   if isGuardian then
   if not isGuardian then
    -- 3 Spalten: Haupt | Fortschritt | Aura
     local quad = content:tag("div"):addClass("kr-hero-quad")
     local grid = content:tag("div"):addClass("kr-g3")


     -- Spalte 1: Hauptattribute
     -- Q1: Hauptattribute (einspaltig, 3 Zeilen)
     local c1 = grid:tag("div"):addClass("kr-col")
     do
    c1:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
      local q = quad:tag("div"):addClass("kr-quad")
    local mwrap = c1:tag("div"):addClass("kr-hc-main-attrs")
      q:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
    local heroMain = type(data.main)=="table" and data.main or {}
      local mains = q:tag("div"):addClass("kr-hc-main-attrs")
    for i, key in ipairs(CFGmod.main_order) do
      local heroMain = type(data.main)=="table" and data.main or {}
      local meta = CFGmod.main_meta[key]
      for i, key in ipairs(CFGmod.main_order) do
      local val  = pick(args["main"..i.."_val"], heroMain[key])
        local meta = CFGmod.main_meta[key]
      if not isempty(val) then
        pill(mains, meta.label, pick(args["main"..i.."_val"], heroMain[key]),
        pill(mwrap, meta.label, val, pick(args["main"..i.."_icon"], meta.icon), showLabels, pick(args["main"..i.."_desc"], meta.desc))
            pick(args["main"..i.."_icon"], meta.icon), showLabels,
            pick(args["main"..i.."_desc"], meta.desc))
       end
       end
     end
     end


     -- Spalte 2: Fortschrittsattribute (guardian subset)
     -- Q2: Fortschrittsattribute (2×2)
     local c2 = grid:tag("div"):addClass("kr-col")
     do
    c2:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
      local q = quad:tag("div"):addClass("kr-quad")
    local awrap = c2:tag("div"):addClass("kr-hc-adv-attrs kr-adv-guardian")
      q:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
    local heroAdv = type(data.adv)=="table" and data.adv or {}
      local advs = q:tag("div"):addClass("kr-hc-adv-attrs")
    for idx, key in ipairs(advGuard) do
      local heroAdv = type(data.adv)=="table" and data.adv or {}
      local meta = CFGmod.adv_meta[key]
      for i, key in ipairs(CFGmod.adv_order) do
      if meta then
        local meta = CFGmod.adv_meta[key]
         adv(awrap, meta.label, pick(args["adv"..idx.."_val"], heroAdv[key]),
         adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]),
             pick(args["adv"..idx.."_icon"], meta.icon), showLabels,
             pick(args["adv"..i.."_icon"], meta.icon), showLabels,
             pick(args["adv"..idx.."_desc"], meta.desc))
             pick(args["adv"..i.."_desc"], meta.desc))
       end
       end
     end
     end


     -- Spalte 3: Wächteraura
     -- Q3: Fähigkeiten NUR wenn vorhanden
     local c3 = grid:tag("div"):addClass("kr-col")
     local hasAbilities = type(data.abilities)=="table" and next(data.abilities) ~= nil
    c3:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening)
    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 = c3:tag("div"):addClass("kr-hc-adv kr-tip")
      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 = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(L.awakening)
       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
    -- 2×2 Quadranten (unten links KEINE Fähigkeiten mehr, wenn leer → gar nicht rendern)
     local g = content:tag("div"):addClass("kr-g3")
     local quad = content:tag("div"):addClass("kr-hero-quad")


     -- TL: Hauptattribute
     -- Spalte 1: Hauptattribute
     local q1 = quad:tag("div"):addClass("kr-quad")
     do
    q1:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
      local col = g:tag("div"):addClass("kr-quad")
    local mwrap = q1:tag("div"):addClass("kr-hc-main-attrs")
      col:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
    local heroMain = type(data.main)=="table" and data.main or {}
      local mains = col:tag("div"):addClass("kr-hc-main-attrs")
    for i, key in ipairs(CFGmod.main_order) do
      local heroMain = type(data.main)=="table" and data.main or {}
      local meta = CFGmod.main_meta[key]
      for i, key in ipairs(CFGmod.main_order) do
      pill(mwrap, meta.label, pick(args["main"..i.."_val"], heroMain[key]),
        local meta = CFGmod.main_meta[key]
          pick(args["main"..i.."_icon"], meta.icon), showLabels,
        pill(mains, meta.label, pick(args["main"..i.."_val"], heroMain[key]),
          pick(args["main"..i.."_desc"], meta.desc))
            pick(args["main"..i.."_icon"], meta.icon), showLabels,
            pick(args["main"..i.."_desc"], meta.desc))
      end
     end
     end


     -- TR: Fortschrittsattribute
     -- Spalte 2: Fortschritt (1 Spalte)
     local q2 = quad:tag("div"):addClass("kr-quad")
     do
    q2:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
      local col = g:tag("div"):addClass("kr-quad")
    local awrap = q2:tag("div"):addClass("kr-hc-adv-attrs")
      col:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
    local heroAdv = type(data.adv)=="table" and data.adv or {}
      local advs = col:tag("div"):addClass("kr-hc-adv-attrs")
    for i, key in ipairs(CFGmod.adv_order) do
      local heroAdv = type(data.adv)=="table" and data.adv or {}
      local meta = CFGmod.adv_meta[key]
      for i, key in ipairs(CFGmod.adv_order) do
      adv(awrap, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]),
        local meta = CFGmod.adv_meta[key]
          pick(args["adv"..i.."_icon"], meta.icon), showLabels,
        adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]),
          pick(args["adv"..i.."_desc"], meta.desc))
            pick(args["adv"..i.."_icon"], meta.icon), showLabels,
            pick(args["adv"..i.."_desc"], meta.desc))
      end
     end
     end


     -- BR: Erweckungsbonus
     -- Spalte 3: Wächteraura
    local q4 = quad:tag("div"):addClass("kr-quad")
    q4:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening)
     if not isempty(awakening_bonus) then
     if not isempty(awakening_bonus) then
       local box = q4:tag("div"):addClass("kr-hc-adv kr-tip")
      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 = main:tag("div"):addClass("kr-hc-adv-label"):wikitext(L.awakening)
       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