Zum Inhalt springen


Modul:HeroCard: Unterschied zwischen den Versionen

Aus Firestone Idle RPG Wiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
 
(19 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 14: Zeile 14:
end
end


-- Lazy load der ausgelagerten Module
-- Lazy load
local CFG, I18N
local CFG, I18N
local function loadConfig()
local function loadConfig()
Zeile 28: Zeile 28:
     local ok, mod = pcall(require, t); if ok and type(mod)=="table" then I18N=mod; return I18N end
     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)
-- HeroData
local function loadHeroes()
local function loadHeroes()
   for _,t in ipairs{ "Modul:HeroData", "Module:HeroData" } do
   for _,t in ipairs{ "Modul:HeroData", "Module:HeroData" } do
     local ok, mod = pcall(require, t)
     local ok, mod = pcall(require, t)
     if ok and type(mod)=="table" and type(mod.heroes)=="table" then
     if ok and type(mod)=="table" and type(mod.heroes)=="table" then
       return mod.heroes, {ok=true, from=t}
       return mod.heroes
     end
     end
   end
   end
   return nil, {ok=false}
   return nil
end
end


Zeile 51: Zeile 51:
end
end


-- Übersetzen per HeroI18n
-- Übersetzen (HeroI18n.i18n[kind][key_en])
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 58: Zeile 58:
end
end


-- Tooltip-HTML
-- Tooltip-Text (aw_desc[key_en])
local function addTooltip(box, title, desc)
local function aw_desc(key_en)
   if (isempty(title) and isempty(desc)) then return end
   if isempty(key_en) then return nil end
   local tip = box:tag("span"):addClass("kr-tipbox")
   return (loadI18n().aw_desc or {})[norm(key_en)]
  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
end


-- Kleine Bausteine
-- Unlock-Text über I18n
local function pill(parent, label, val, icon, showLabels, desc)
local function unlock_text(ui, raw)
  if isempty(val) then return end
   if isempty(raw) then return nil end
  local box = parent:tag("div"):addClass("kr-hc-pill kr-tip")
   local t = norm(raw)
  local left = box:tag("div"):addClass("kr-hc-pill-icon")
   local n = t:match("stage%s*(%d+)")
  if not isempty(icon) then left:wikitext(fileWikitext(icon, {size="36x36px", link="", lazy=true})) end
  if n and ui.unlock and ui.unlock.at_stage then
  local text = box:tag("div"):addClass("kr-hc-pill-text")
    return (ui.unlock.at_stage:gsub("%$1", n))
  local lab = text:tag("div"):addClass("kr-hc-pill-label"):wikitext(label or "")
  end
  if not showLabels then lab:addClass("is-hidden") end
   if t:match("pirate%s*ship") and ui.unlock and ui.unlock.pirate_ship then
  text:tag("div"):addClass("kr-hc-pill-value"):wikitext(val)
    return ui.unlock.pirate_ship
  addTooltip(box, label, desc)
   end
end
   return raw
 
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
end


-- Default-Avatar
local function defaultAvatarFile(name, data, args)
local function defaultAvatarFile(name, data, args)
  -- Reihenfolge: |avatar=…  >  data.default_avatar  >  default_skin  >  Name.png
   if not isempty(args.avatar) then return args.avatar end
   if not isempty(args.avatar) then return args.avatar end
   if not isempty(data.default_avatar) then return data.default_avatar end
   if not isempty(data.default_avatar) then return data.default_avatar end
Zeile 122: Zeile 90:


-- Avatar/Skins → TabberNeue
-- Avatar/Skins → TabberNeue
local function buildAvatar(name, linkto, defaultFile, skinsTable)
local function buildAvatar(name, linkto, defaultFile, skinsTable, opts)
  opts = opts or {}
  local isGuardian = opts.isGuardian == true
 
   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, lazy=true })
     return fileWikitext(defaultFile, { class="kr-hc-avatar-img", link=linkto, lazy=true })
   end
   end
   local frame = mw.getCurrentFrame()
   local frame = mw.getCurrentFrame()
   local defaultLabel = (loadI18n().ui and loadI18n().ui.default_skin) or "Standard"
   local defaultLabel = (loadI18n().ui and loadI18n().ui.default_skin) or ""


   local panels = {}
   local panels = {}
Zeile 137: Zeile 108:
   end
   end


   push(defaultLabel, defaultFile)
   if isGuardian then
    push("E1", defaultFile)
  else
    push(defaultLabel, defaultFile)
  end
 
   if skinsTable[1] ~= nil then
   if skinsTable[1] ~= nil then
    local idx = 1
     for _, v in ipairs(skinsTable) do
     for _, v in ipairs(skinsTable) do
      local label, file
       if type(v) == "table" then
       if type(v) == "table" then
         local label = v.label or v[1]
         label = v.label or v[1]
         local file  = v.file  or (name .. "_" .. (v.filename or label) .. ".png")
         file  = v.file  or (name .. "_" .. (v.filename or label) .. ".png")
        push(label, file)
       else
       else
         local label = tostring(v)
         label = tostring(v)
         push(label, name .. "_" .. label .. ".png")
         file  = name .. "_" .. label .. ".png"
       end
       end
      idx = idx + 1
      push(isGuardian and ("E"..idx) or label, file)
     end
     end
   else
   else
     local tmp = {}
     local tmp, idx = {}, 1
     for k, v in pairs(skinsTable) do
     for k, v in pairs(skinsTable) do
      local label, file
       if type(v) == "table" then
       if type(v) == "table" then
         local label = v.label or v[1] or tostring(k)
         label = v.label or v[1] or tostring(k)
         local file  = v.file  or (name .. "_" .. (v.filename or label) .. ".png")
         file  = v.file  or (name .. "_" .. (v.filename or label) .. ".png")
        table.insert(tmp, {label=label, file=file})
       else
       else
         local label = tostring(v)
         label = tostring(v)
         table.insert(tmp, {label=label, file=(name .. "_" .. label .. ".png")})
         file = name .. "_" .. label .. ".png"
       end
       end
      table.insert(tmp, {label=label, file=file})
     end
     end
     table.sort(tmp, function(a,b) return tostring(a.label) < tostring(b.label) 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
     for _,it in ipairs(tmp) do
      idx = idx + 1
      push(isGuardian and ("E"..idx) or it.label, it.file)
    end
   end
   end


   local content = table.concat(panels, "\n|-|\n")
   local content = table.concat(panels, "\n|-|\n")
   return frame:extensionTag('tabber', content)
   return frame:extensionTag('tabber', content)
end
-- Profile-Auswahl
local function pickProfile(cfg, entity)
  local profiles = cfg.profiles or {}
  local base = profiles.default or {}
  local over = profiles[entity] or {}
  local P = {}
  P.main_order = over.main_order or base.main_order or {}
  P.main_meta  = over.main_meta  or base.main_meta  or {}
  P.adv_order  = over.adv_order  or base.adv_order  or {}
  P.adv_meta  = over.adv_meta  or base.adv_meta  or {}
  -- Guardian: Chaos
  P.chaos_main_order = over.chaos_main_order or {}
  P.chaos_main_meta  = over.chaos_main_meta  or {}
  P.chaos_adv_order  = over.chaos_adv_order  or {}
  P.chaos_adv_meta  = over.chaos_adv_meta  or {}
  return P
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: Hauptattribute-Kachel
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
-- Kleine Bausteine: Fortschritts-Kachel
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
-- 3er-Grid Renderer
local function render_grid(opts)
  local profile, data, L, UI, showLabels, entity = opts.profile, opts.data, opts.L, opts.UI, opts.showLabels, opts.entity
  local mode = opts.mode or "default" -- "default" | "chaos"
  local abilityLabel = (UI.labels and UI.labels.wm and UI.labels.wm.ability) or ""
  local H_main = (UI.headers and UI.headers.main) or ""
  local H_adv  = (UI.headers and UI.headers.adv)  or ""
  local grid = mw.html.create("div"):addClass("kr-g3")
  -- Spalte 1: Hauptattribute
  do
    local col = grid:tag("div"):addClass("kr-quad")
    col:tag("h3"):addClass("kr-hc-h"):wikitext(H_main)
    local mains = col:tag("div"):addClass("kr-hc-main-attrs")
    local src = (mode=="chaos") and ((type(data.chaos)=="table" and (data.chaos.main or {})) or {}) or (data.main or {})
    local order = (mode=="chaos") and profile.chaos_main_order or profile.main_order
    local metaT = (mode=="chaos") and profile.chaos_main_meta  or profile.main_meta
    for _, key in ipairs(order) do
      local meta = metaT[key] or { label = key }
      local val  = src[key]
      if not isempty(val) then
        pill(mains, meta.label, val, meta.icon, showLabels, meta.desc)
      end
    end
  end
  -- Spalte 2: Fortschrittsattribute
  do
    local col = grid:tag("div"):addClass("kr-quad")
    col:tag("h3"):addClass("kr-hc-h"):wikitext(H_adv)
    local advs = col:tag("div"):addClass("kr-hc-adv-attrs")
    local src = (mode=="chaos") and ((type(data.chaos)=="table" and (data.chaos.adv or {})) or {}) or (data.adv or {})
    local order = (mode=="chaos") and profile.chaos_adv_order or profile.adv_order
    local metaT = (mode=="chaos") and profile.chaos_adv_meta  or profile.adv_meta
    for _, key in ipairs(order) do
      local meta = metaT[key] or { label = key }
      local val  = src[key]
      if not isempty(val) then
        adv(advs, meta.label, val, meta.icon, showLabels, meta.desc)
      end
    end
  end
  -- Spalte 3: Awakening / Aura / Milestone + (WM) Fähigkeit
  do
    local col = grid:tag("div"):addClass("kr-quad")
    col:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening)
    local id_en, display_val
    if mode=="chaos" and entity=="guardian" then
      id_en      = type(data.chaos)=="table" and data.chaos.aura_id or nil
      display_val= type(data.chaos)=="table" and data.chaos.aura_value or nil
    else
      if entity=="guardian" then
        id_en      = data.aura_id
        display_val = data.aura_value
      elseif entity=="wm" then
        id_en = data.milestone_id
      else
        id_en = data.awakening_id
      end
    end
    local icon_map = ((opts.icon_map or {}).awakening_bonus) or {}
    local icon_file= icon_map[norm(id_en)]
    local title    = tr("awakening_bonus", id_en)
    local desc    = aw_desc(id_en)
    if not (isempty(title) and isempty(display_val)) then
      local box = col:tag("div"):addClass("kr-hc-awakening kr-hc-adv kr-tip")
      if not (isempty(title) and isempty(desc)) then
        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
      if icon_file then
        box:tag("div"):addClass("kr-hc-adv-icon")
          :wikitext(fileWikitext(icon_file, {size="64x64px", link="", lazy=true}))
      end
      local main = box:tag("div")
      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(not isempty(display_val) and display_val or title or "")
    end
    -- WM: zweite Überschrift + Fähigkeit-Kachel ohne Icon/Tooltip
    if entity=="wm" and not isempty(data.ability) then
      col:tag("h3"):addClass("kr-hc-h"):wikitext(abilityLabel)
      local b2 = col:tag("div"):addClass("kr-hc-awakening kr-hc-adv kr-no-icon")
      local m2 = b2:tag("div")
      local lab2 = m2:tag("div"):addClass("kr-hc-adv-label"):wikitext(abilityLabel)
      if not showLabels then lab2:addClass("is-hidden") end
      m2:tag("div"):addClass("kr-hc-adv-value"):wikitext(data.ability)
    end
  end
  return tostring(grid)
end
end


-- ======= 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 = 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 UI = loadI18n().ui 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
   -- Entity (nur eine Quelle)
  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")
  if entity=="" then entity = "hero" end
      or norm(pick(args.guardian, data.guardian)) == "1"
   local isGuardian = (entity=="guardian")
       or pick(args.is_guardian, data.is_guardian) == true
  local isWM        = (entity=="wm")
  local isMercenary = (entity=="mercenary")
  local isGod       = (entity=="god")


   local labelsHero    = (UI.labels and UI.labels.hero)     or { class="Klasse:",  awakening="Erweckungsbonus" }
  -- Root
   local labelsGuardian = (UI.labels and UI.labels.guardian) or { class="Typ:",      awakening="Wächteraura"     }
   local root = mw.html.create("div"):addClass("kr-hero-card kr-layout")
  local L = isGuardian and labelsGuardian or labelsHero
   root:addClass(isGuardian and "is-guardian" or isWM and "is-wm" or isMercenary and "is-mercenary" or isGod and "is-god" or "is-hero")
 
   if showLabels then root:addClass("kr-show-labels") end
  -- 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(
  -- Basics-Labels: Defaults + optionale Overrides pro Entity
     args.awakening_bonus, data.awakening_bonus,   -- Held
  local Bdef = UI.basics or {}
     args.guardian_aura,   data.guardian_aura,     -- Wächter
   local Bovr = (UI.basics_override and UI.basics_override[entity]) or {}
     data.aura
  local B = {
  )
     class          = pick(Bovr.class,         Bdef.class),
  local awakening_bonus = tr("awakening_bonus", awakening_bonusRaw)
     specialization = pick(Bovr.specialization, Bdef.specialization),
  local awakening_desc  = awakeningDesc(awakening_bonusRaw, pick(args.awakening_desc, data.awakening_desc))
     attackstyle    = pick(Bovr.attackstyle,   Bdef.attackstyle),
    resource      = pick(Bovr.resource,       Bdef.resource),
  }


   local CFGmod = loadConfig()
   -- Dritte-Spalte-Labels (Awakening/Aura/WM): Default + optionale Overrides pro Entity
   local icon_map = CFGmod.icon_map
   local Ldef  = (UI.labels and UI.labels.default) or {}
  local attackstyle_icon    = (icon_map.attackstyle    or {})[norm(attackstyleRaw)]
   local Lover = (UI.labels and UI.labels[entity])  or {}
   local specialization_icon = (icon_map.specialization or {})[norm(specializationRaw)]
   local L = {
   local resource_icon      = (icon_map.resource      or {})[norm(resourceRaw)]
    awakening = pick(Lover.awakening, Ldef.awakening),
   local awakening_bonus_icon= (icon_map.awakening_bonus or {})[norm(awakening_bonusRaw)]
    ability   = pick(Lover.ability,  Ldef.ability), -- nur für WM relevant
  }


   -- Bilder/Links
   local class          = tr("class",          data.class)
   local linkto = pick(args.linkto, name)
   local attackstyle    = tr("attackstyle",    data.attackstyle)
   local avatar = pick(args.avatar, (not isempty(name) and (name .. ".png")) or nil)
   local specialization = tr("specialization", data.specialization)
  local resource      = tr("resource",      data.resource)


   -- Root
   -- Config
   local root = mw.html.create("div"):addClass("kr-hero-card")
   local CFGmod  = loadConfig()
   root:addClass(isGuardian and "kr-layout-guardian" or "kr-layout-hero")
   local icon_map = CFGmod.icon_map or {}
   if showLabels then root:addClass("kr-show-labels") end
   local profile  = pickProfile(CFGmod, entity)


   -- Avatar (links)
   -- Avatar
  local linkto      = (isWM and "") or pick(args.linkto, name)
  local avatar_file = defaultAvatarFile(name, data, args)
   local avatarDiv = root:tag("div"):addClass("kr-hc-avatar")
   local avatarDiv = root:tag("div"):addClass("kr-hc-avatar")
   avatarDiv:wikitext( buildAvatar(name, linkto, avatar, data.skins) )
   avatarDiv:wikitext( buildAvatar(name, linkto, avatar_file, data.skins, { isGuardian = isGuardian }) )


   -- Content (rechts)
   -- Content
   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)
   -- Header
   local header = content:tag("div"):addClass("kr-hc-header")
   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))
     local h = header:tag("h2"):addClass("kr-hc-name")
    if isWM then
      h:wikitext(name) -- WM keine Linkseite
    else
      local titleTarget = pick(args.linkto, name)
      h:wikitext(string.format("[[%s|%s]]", titleTarget, name))
    end
   end
   end
   local unlock_text = unlock_text_de(unlock_at)
   local unlock_text = unlock_text(UI, data.unlock_at)
   if unlock_text then
   if unlock_text and unlock_text~="" then
     header:tag("div"):addClass("kr-hc-unlockline"):wikitext("🔒 " .. unlock_text)
     header:tag("div"):addClass("kr-hc-unlockline"):wikitext("🔒 " .. unlock_text)
   end
   end
   content:tag("div"):addClass("kr-hc-sep") -- Trennlinie NACH Unlock
   content:tag("div"):addClass("kr-hc-sep")


   -- Basics 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)
     if isempty(value) then return end
     if isempty(value) or isempty(label) then return end
     local it = basics:tag("div"):addClass("kr-hc-basic")
     local it = basics:tag("div"):addClass("kr-hc-basic")
     it:tag("span"):addClass("label"):wikitext(label)
     it:tag("span"):addClass("label"):wikitext(label)
     local v = it:tag("span"):addClass("value"):wikitext(value)
     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
   end
   pair(L.class, class, nil)
   pair(B.class,         class)
   pair("Spezialisierung:", specialization, specialization_icon)
   pair(B.specialization, specialization)
   pair("Angriffsart:",    attackstyle,    attackstyle_icon)
   pair(B.attackstyle,    attackstyle)
   pair("Ressource:",      resource,      resource_icon)
   pair(B.resource,      resource)


  -- ===== Layout: Hero (2×2 Quadranten)
  if not isGuardian then
    local quad = content:tag("div"):addClass("kr-hero-quad")


    -- Q1: Hauptattribute (einspaltig, 3 Zeilen)
  -- Haupt-Grid (Default)
    do
  local grid_html = render_grid{
      local q = quad:tag("div"):addClass("kr-quad")
    profile = profile, data = data, args = args, UI = UI,
      q:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
    L = L, showLabels = showLabels, entity = entity,
      local mains = q:tag("div"):addClass("kr-hc-main-attrs")
    icon_map = icon_map, mode = "default",
      local heroMain = type(data.main)=="table" and data.main or {}
    abilityLabel = (L.ability),
      for i, key in ipairs(CFGmod.main_order) do
  }
        local meta  = CFGmod.main_meta[key]
  content:wikitext(grid_html)
        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)
  -- Chaos-Grid (nur Guardian und nur wenn Werte vorhanden)
    do
  if isGuardian and type(data.chaos)=="table" then
      local q = quad:tag("div"):addClass("kr-quad")
    local hasChaos = false
      q:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
    if profile.chaos_main_order and #profile.chaos_main_order>0 then
      local advs = q:tag("div"):addClass("kr-hc-adv-attrs")
       local m = data.chaos.main or {}
       local heroAdv = type(data.adv)=="table" and data.adv or {}
       for _,k in ipairs(profile.chaos_main_order) do if not isempty(m[k]) then hasChaos=true break end end
       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
 
     if not hasChaos and profile.chaos_adv_order and #profile.chaos_adv_order>0 then
    -- Q3: Fähigkeiten-Quadrant (immer Platzhalter, ohne Überschrift)
       local a = data.chaos.adv or {}
     do
       for _,k in ipairs(profile.chaos_adv_order) do if not isempty(a[k]) then hasChaos=true break end end
       local q = quad:tag("div"):addClass("kr-quad")
       q:tag("div"):addClass("kr-hc-abilities-placeholder"):wikitext("")
     end
     end
 
     if not hasChaos and (not isempty(data.chaos.aura_id) or not isempty(data.chaos.aura_value)) then
 
       hasChaos = true
     -- 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-hc-adv 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
     end


     -- Spalte 2: Fortschritt (1 Spalte)
     if hasChaos then
    do
       local chaosTitle = (UI.headers and UI.headers.chaos) or ""
       local col = g:tag("div"):addClass("kr-quad")
       if chaosTitle~="" then content:tag("h2"):wikitext(chaosTitle) end
       col:tag("h3"):addClass("kr-hc-h"):wikitext("Fortschrittsattribute")
       content:tag("div"):addClass("kr-hc-sep")
       local advs = col:tag("div"):addClass("kr-hc-adv-attrs")
       local chaos_html = render_grid{
       local heroAdv = type(data.adv)=="table" and data.adv or {}
         profile = profile, data = data, L = L, UI = UI,
      for i, key in ipairs(CFGmod.adv_order) do
        showLabels = showLabels, entity = entity,
         local meta = CFGmod.adv_meta[key]
         icon_map = icon_map, mode = "chaos",
        adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]),
       }
            pick(args["adv"..i.."_icon"], meta.icon), showLabels,
       content:wikitext(chaos_html)
            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-hc-adv 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
   end
   end
Zeile 364: Zeile 444:
end
end


p.main = p.render
return p
return p

Aktuelle Version vom 17. Oktober 2025, 08:46 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
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
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
    end
  end
  return nil
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
  return string.format("[[%s]]", table.concat(parts, "|"))
end

-- Übersetzen (HeroI18n.i18n[kind][key_en])
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-Text (aw_desc[key_en])
local function aw_desc(key_en)
  if isempty(key_en) then return nil end
  return (loadI18n().aw_desc or {})[norm(key_en)]
end

-- Unlock-Text über I18n
local function unlock_text(ui, raw)
  if isempty(raw) then return nil end
  local t = norm(raw)
  local n = t:match("stage%s*(%d+)")
  if n and ui.unlock and ui.unlock.at_stage then
    return (ui.unlock.at_stage:gsub("%$1", n))
  end
  if t:match("pirate%s*ship") and ui.unlock and ui.unlock.pirate_ship then
    return ui.unlock.pirate_ship
  end
  return raw
end

-- Default-Avatar
local function defaultAvatarFile(name, data, args)
  if not isempty(args.avatar) then return args.avatar end
  if not isempty(data.default_avatar) then return data.default_avatar end
  if not isempty(data.default_skin) then
    return string.format("%s_%s.png", name, data.default_skin)
  end
  if not isempty(name) then return name .. ".png" end
  return nil
end

-- Avatar/Skins → TabberNeue
local function buildAvatar(name, linkto, defaultFile, skinsTable, opts)
  opts = opts or {}
  local isGuardian = opts.isGuardian == true

  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 ""

  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

  if isGuardian then
    push("E1", defaultFile)
  else
    push(defaultLabel, defaultFile)
  end

  if skinsTable[1] ~= nil then
    local idx = 1
    for _, v in ipairs(skinsTable) do
      local label, file
      if type(v) == "table" then
        label = v.label or v[1]
        file  = v.file  or (name .. "_" .. (v.filename or label) .. ".png")
      else
        label = tostring(v)
        file  = name .. "_" .. label .. ".png"
      end
      idx = idx + 1
      push(isGuardian and ("E"..idx) or label, file)
    end
  else
    local tmp, idx = {}, 1
    for k, v in pairs(skinsTable) do
      local label, file
      if type(v) == "table" then
        label = v.label or v[1] or tostring(k)
        file  = v.file  or (name .. "_" .. (v.filename or label) .. ".png")
      else
        label = tostring(v)
        file  = name .. "_" .. label .. ".png"
      end
      table.insert(tmp, {label=label, file=file})
    end
    table.sort(tmp, function(a,b) return tostring(a.label) < tostring(b.label) end)
    for _,it in ipairs(tmp) do
      idx = idx + 1
      push(isGuardian and ("E"..idx) or it.label, it.file)
    end
  end

  local content = table.concat(panels, "\n|-|\n")
  return frame:extensionTag('tabber', content)
end

-- Profile-Auswahl
local function pickProfile(cfg, entity)
  local profiles = cfg.profiles or {}
  local base = profiles.default or {}
  local over = profiles[entity] or {}

  local P = {}
  P.main_order = over.main_order or base.main_order or {}
  P.main_meta  = over.main_meta  or base.main_meta  or {}
  P.adv_order  = over.adv_order  or base.adv_order  or {}
  P.adv_meta   = over.adv_meta   or base.adv_meta   or {}
  -- Guardian: Chaos
  P.chaos_main_order = over.chaos_main_order or {}
  P.chaos_main_meta  = over.chaos_main_meta  or {}
  P.chaos_adv_order  = over.chaos_adv_order  or {}
  P.chaos_adv_meta   = over.chaos_adv_meta   or {}
  return P
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: Hauptattribute-Kachel
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

-- Kleine Bausteine: Fortschritts-Kachel
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

-- 3er-Grid Renderer
local function render_grid(opts)
  local profile, data, L, UI, showLabels, entity = opts.profile, opts.data, opts.L, opts.UI, opts.showLabels, opts.entity
  local mode = opts.mode or "default" -- "default" | "chaos"
  local abilityLabel = (UI.labels and UI.labels.wm and UI.labels.wm.ability) or ""

  local H_main = (UI.headers and UI.headers.main) or ""
  local H_adv  = (UI.headers and UI.headers.adv)  or ""

  local grid = mw.html.create("div"):addClass("kr-g3")

  -- Spalte 1: Hauptattribute
  do
    local col = grid:tag("div"):addClass("kr-quad")
    col:tag("h3"):addClass("kr-hc-h"):wikitext(H_main)
    local mains = col:tag("div"):addClass("kr-hc-main-attrs")
    local src = (mode=="chaos") and ((type(data.chaos)=="table" and (data.chaos.main or {})) or {}) or (data.main or {})
    local order = (mode=="chaos") and profile.chaos_main_order or profile.main_order
    local metaT = (mode=="chaos") and profile.chaos_main_meta  or profile.main_meta
    for _, key in ipairs(order) do
      local meta = metaT[key] or { label = key }
      local val  = src[key]
      if not isempty(val) then
        pill(mains, meta.label, val, meta.icon, showLabels, meta.desc)
      end
    end
  end

  -- Spalte 2: Fortschrittsattribute
  do
    local col = grid:tag("div"):addClass("kr-quad")
    col:tag("h3"):addClass("kr-hc-h"):wikitext(H_adv)
    local advs = col:tag("div"):addClass("kr-hc-adv-attrs")
    local src = (mode=="chaos") and ((type(data.chaos)=="table" and (data.chaos.adv or {})) or {}) or (data.adv or {})
    local order = (mode=="chaos") and profile.chaos_adv_order or profile.adv_order
    local metaT = (mode=="chaos") and profile.chaos_adv_meta  or profile.adv_meta
    for _, key in ipairs(order) do
      local meta = metaT[key] or { label = key }
      local val  = src[key]
      if not isempty(val) then
        adv(advs, meta.label, val, meta.icon, showLabels, meta.desc)
      end
    end
  end

  -- Spalte 3: Awakening / Aura / Milestone + (WM) Fähigkeit
  do
    local col = grid:tag("div"):addClass("kr-quad")
    col:tag("h3"):addClass("kr-hc-h"):wikitext(L.awakening)

    local id_en, display_val
    if mode=="chaos" and entity=="guardian" then
      id_en      = type(data.chaos)=="table" and data.chaos.aura_id or nil
      display_val= type(data.chaos)=="table" and data.chaos.aura_value or nil
    else
      if entity=="guardian" then
        id_en       = data.aura_id
        display_val = data.aura_value
      elseif entity=="wm" then
        id_en = data.milestone_id
      else
        id_en = data.awakening_id
      end
    end

    local icon_map = ((opts.icon_map or {}).awakening_bonus) or {}
    local icon_file= icon_map[norm(id_en)]
    local title    = tr("awakening_bonus", id_en)
    local desc     = aw_desc(id_en)

    if not (isempty(title) and isempty(display_val)) then
      local box = col:tag("div"):addClass("kr-hc-awakening kr-hc-adv kr-tip")
      if not (isempty(title) and isempty(desc)) then
        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
      if icon_file then
        box:tag("div"):addClass("kr-hc-adv-icon")
           :wikitext(fileWikitext(icon_file, {size="64x64px", link="", lazy=true}))
      end
      local main = box:tag("div")
      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(not isempty(display_val) and display_val or title or "")
    end

    -- WM: zweite Überschrift + Fähigkeit-Kachel ohne Icon/Tooltip
    if entity=="wm" and not isempty(data.ability) then
      col:tag("h3"):addClass("kr-hc-h"):wikitext(abilityLabel)
      local b2 = col:tag("div"):addClass("kr-hc-awakening kr-hc-adv kr-no-icon")
      local m2 = b2:tag("div")
      local lab2 = m2:tag("div"):addClass("kr-hc-adv-label"):wikitext(abilityLabel)
      if not showLabels then lab2:addClass("is-hidden") end
      m2:tag("div"):addClass("kr-hc-adv-value"):wikitext(data.ability)
    end
  end

  return tostring(grid)
end

-- ======= RENDER =======
function p.render(frame)
  local args  = getArgs(frame)
  local name  = pick(args.name, args[1]) or ""

  local heroes = loadHeroes()
  local data   = (heroes and (heroes[name] or heroes[mw.ustring.gsub(name or "", "_", " ")])) or {}

  local UI = loadI18n().ui or {}
  local showLabels = norm(args.show_labels) == "yes" or norm(args.show_labels) == "1"

  -- Entity (nur eine Quelle)
  local entity = norm(pick(args.entity, data.entity))
  if entity=="" then entity = "hero" end
  local isGuardian  = (entity=="guardian")
  local isWM        = (entity=="wm")
  local isMercenary = (entity=="mercenary")
  local isGod       = (entity=="god")

  -- Root
  local root = mw.html.create("div"):addClass("kr-hero-card kr-layout")
  root:addClass(isGuardian and "is-guardian" or isWM and "is-wm" or isMercenary and "is-mercenary" or isGod and "is-god" or "is-hero")
  if showLabels then root:addClass("kr-show-labels") end

  -- Basics-Labels: Defaults + optionale Overrides pro Entity
  local Bdef = UI.basics or {}
  local Bovr = (UI.basics_override and UI.basics_override[entity]) or {}
  local B = {
    class          = pick(Bovr.class,          Bdef.class),
    specialization = pick(Bovr.specialization, Bdef.specialization),
    attackstyle    = pick(Bovr.attackstyle,    Bdef.attackstyle),
    resource       = pick(Bovr.resource,       Bdef.resource),
  }

  -- Dritte-Spalte-Labels (Awakening/Aura/WM): Default + optionale Overrides pro Entity
  local Ldef  = (UI.labels and UI.labels.default) or {}
  local Lover = (UI.labels and UI.labels[entity])  or {}
  local L = {
    awakening = pick(Lover.awakening, Ldef.awakening),
    ability   = pick(Lover.ability,   Ldef.ability), -- nur für WM relevant
  }

  local class          = tr("class",          data.class)
  local attackstyle    = tr("attackstyle",    data.attackstyle)
  local specialization = tr("specialization", data.specialization)
  local resource       = tr("resource",       data.resource)

  -- Config
  local CFGmod   = loadConfig()
  local icon_map = CFGmod.icon_map or {}
  local profile  = pickProfile(CFGmod, entity)

  -- Avatar
  local linkto      = (isWM and "") or pick(args.linkto, name)
  local avatar_file = defaultAvatarFile(name, data, args)
  local avatarDiv = root:tag("div"):addClass("kr-hc-avatar")
  avatarDiv:wikitext( buildAvatar(name, linkto, avatar_file, data.skins, { isGuardian = isGuardian }) )

  -- Content
  local content = root:tag("div"):addClass("kr-hc-content")

  -- Header
  local header = content:tag("div"):addClass("kr-hc-header")
  if not isempty(name) then
    local h = header:tag("h2"):addClass("kr-hc-name")
    if isWM then
      h:wikitext(name) -- WM keine Linkseite
    else
      local titleTarget = pick(args.linkto, name)
      h:wikitext(string.format("[[%s|%s]]", titleTarget, name))
    end
  end
  local unlock_text = unlock_text(UI, data.unlock_at)
  if unlock_text and unlock_text~="" then
    header:tag("div"):addClass("kr-hc-unlockline"):wikitext("🔒 " .. unlock_text)
  end
  content:tag("div"):addClass("kr-hc-sep")

  -- Basics 2×2
  local basics = content:tag("div"):addClass("kr-hc-basics2")
  local function pair(label, value)
    if isempty(value) or isempty(label) then return end
    local it = basics:tag("div"):addClass("kr-hc-basic")
    it:tag("span"):addClass("label"):wikitext(label)
    it:tag("span"):addClass("value"):wikitext(value)
  end
  pair(B.class,          class)
  pair(B.specialization, specialization)
  pair(B.attackstyle,    attackstyle)
  pair(B.resource,       resource)


  -- Haupt-Grid (Default)
  local grid_html = render_grid{
    profile = profile, data = data, args = args, UI = UI,
    L = L, showLabels = showLabels, entity = entity,
    icon_map = icon_map, mode = "default",
    abilityLabel = (L.ability),
  }
  content:wikitext(grid_html)

  -- Chaos-Grid (nur Guardian und nur wenn Werte vorhanden)
  if isGuardian and type(data.chaos)=="table" then
    local hasChaos = false
    if profile.chaos_main_order and #profile.chaos_main_order>0 then
      local m = data.chaos.main or {}
      for _,k in ipairs(profile.chaos_main_order) do if not isempty(m[k]) then hasChaos=true break end end
    end
    if not hasChaos and profile.chaos_adv_order and #profile.chaos_adv_order>0 then
      local a = data.chaos.adv or {}
      for _,k in ipairs(profile.chaos_adv_order) do if not isempty(a[k]) then hasChaos=true break end end
    end
    if not hasChaos and (not isempty(data.chaos.aura_id) or not isempty(data.chaos.aura_value)) then
      hasChaos = true
    end

    if hasChaos then
      local chaosTitle = (UI.headers and UI.headers.chaos) or ""
      if chaosTitle~="" then content:tag("h2"):wikitext(chaosTitle) end
      content:tag("div"):addClass("kr-hc-sep")
      local chaos_html = render_grid{
        profile = profile, data = data, L = L, UI = UI,
        showLabels = showLabels, entity = entity,
        icon_map = icon_map, mode = "chaos",
      }
      content:wikitext(chaos_html)
    end
  end

  return tostring(root)
end

p.main = p.render
return p