Zum Inhalt springen


Modul:HeroCard: Unterschied zwischen den Versionen

Aus Firestone Idle RPG Wiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 295: Zeile 295:
   return tostring(grid)
   return tostring(grid)
end
end


-- ======= RENDER =======
-- ======= RENDER =======
Zeile 327: Zeile 326:
   if showLabels then root:addClass("kr-show-labels") end
   if showLabels then root:addClass("kr-show-labels") end


  -- Labels für UI
   -- Labels für UI
   -- Labels für UI
   local labelsHero    = (UI.labels and UI.labels.hero)    or { class="Klasse:",  awakening="Erweckungsbonus" }
   local labelsHero    = (UI.labels and UI.labels.hero)    or { class="Klasse:",  awakening="Erweckungsbonus" }
Zeile 335: Zeile 333:
   local L = isGuardian and labelsGuardian or labelsHero
   local L = isGuardian and labelsGuardian or labelsHero
   if isWM then
   if isWM then
    -- bei WM: Hauptkachel = Meilenstein-Effekt
     L = { class = L.class or (labelsHero.class or "Klasse:"), awakening = labelsWM.awakening }
     L = { class = L.class or (labelsHero.class or "Klasse:"), awakening = labelsWM.awakening }
   end
   end
Zeile 358: Zeile 355:
   local icon_map = (CFGmod.icon_map or {})
   local icon_map = (CFGmod.icon_map or {})
   local awakening_icon = (icon_map.awakening_bonus or {})[norm(awakening_raw)]
   local awakening_icon = (icon_map.awakening_bonus or {})[norm(awakening_raw)]
  -- Profil jetzt EINMAL bestimmen (fix für nil-Profile)
  local profile = pickProfile(CFGmod, entity)


   -- Avatar
   -- Avatar
Zeile 373: Zeile 373:
     local h = header:tag("h2"):addClass("kr-hc-name")
     local h = header:tag("h2"):addClass("kr-hc-name")
     if isWM then
     if isWM then
      -- Kriegsmaschine: nur Text, KEIN Link
       h:wikitext(name) -- WM: kein Link
       h:wikitext(name)
     else
     else
      -- alle anderen: verlinken (|linkto= kann Ziel überschreiben)
       local titleTarget = pick(args.linkto, name)
       local titleTarget = pick(args.linkto, name)
       h:wikitext(string.format("[[%s|%s]]", titleTarget, name))
       h:wikitext(string.format("[[%s|%s]]", titleTarget, name))
Zeile 404: Zeile 402:
       showLabels = showLabels, isWM = isWM,
       showLabels = showLabels, isWM = isWM,
       abilityLabel = ((UI.labels and UI.labels.wm and UI.labels.wm.ability) or "Fähigkeit"),
       abilityLabel = ((UI.labels and UI.labels.wm and UI.labels.wm.ability) or "Fähigkeit"),
       awakening_bonus = awakening_bonus, awakening_desc = awakening_desc,
      awakening_raw = awakening_raw,
       awakening_icon = (not isWM and awakening_bonus_icon or awakening_bonus_icon), -- bei WM wieder Icon zeigen
       awakening_bonus = awakening_name,
      awakening_desc = awakening_desc,
       awakening_icon = awakening_icon, -- WM zeigt Icon wieder an
      icon_map = icon_map,
       mode = "default",
       mode = "default",
     }
     }
Zeile 414: Zeile 415:
   -- Wächter: Default-Grid
   -- Wächter: Default-Grid
   local default_html = render_grid{
   local default_html = render_grid{
     profile = pickProfile(CFGmod, entity), data = data, args = args, L = L,
     profile = profile, data = data, args = args, L = L,
     showLabels = showLabels, isWM = false,
     showLabels = showLabels, isWM = false,
     awakening_raw = awakening_raw,
     awakening_raw = awakening_raw,
Zeile 428: Zeile 429:
   local hasChaos = false
   local hasChaos = false
   local chaos = (type(data.chaos)=="table" and data.chaos) or {}
   local chaos = (type(data.chaos)=="table" and data.chaos) or {}
  -- main
   if profile.chaos_main_order and #profile.chaos_main_order > 0 then
  local prof = pickProfile(CFGmod, entity)
   if prof.chaos_main_order and #prof.chaos_main_order > 0 then
     local src = chaos.main or chaos
     local src = chaos.main or chaos
     for _,k in ipairs(prof.chaos_main_order) do
     for _,k in ipairs(profile.chaos_main_order) do
       if not isempty(pick(args["chaos_"..k], src and src[k])) then hasChaos = true break end
       if not isempty(pick(args["chaos_"..k], src and src[k])) then hasChaos = true break end
     end
     end
   end
   end
  -- adv
   if (not hasChaos) and profile.chaos_adv_order and #profile.chaos_adv_order > 0 then
   if (not hasChaos) and prof.chaos_adv_order and #prof.chaos_adv_order > 0 then
     local src = chaos.adv or chaos
     local src = chaos.adv or chaos
     for _,k in ipairs(prof.chaos_adv_order) do
     for _,k in ipairs(profile.chaos_adv_order) do
       if not isempty(pick(args["chaos_"..k], src and src[k])) then hasChaos = true break end
       if not isempty(pick(args["chaos_"..k], src and src[k])) then hasChaos = true break end
     end
     end
   end
   end
  -- aura
   if (not hasChaos) and (not isempty(pick(args.chaos_guardian_aura, args.chaos_aura, chaos.aura))) then
   if (not hasChaos) and (not isempty(pick(args.chaos_aura_id, chaos.aura_id)) or not isempty(pick(args.chaos_aura_value, chaos.aura_value, args.chaos_aura, chaos.aura))) then
     hasChaos = true
     hasChaos = true
   end
   end
Zeile 452: Zeile 449:
     content:tag("div"):addClass("kr-hc-sep")
     content:tag("div"):addClass("kr-hc-sep")
     local chaos_html = render_grid{
     local chaos_html = render_grid{
       profile = prof, data = data, args = args, L = L,
       profile = profile, data = data, args = args, L = L,
       showLabels = showLabels, isWM = false,
       showLabels = showLabels, isWM = false,
      -- Default-Werte für Fallbacks (Titel/Desc), falls chaos_* fehlt
       awakening_raw = awakening_raw,
       awakening_raw = awakening_raw,
       awakening_bonus = awakening_name,
       awakening_bonus = awakening_name,
       awakening_desc = awakening_desc,
       awakening_desc = awakening_desc,
      awakening_icon = awakening_icon,
       icon_map = icon_map,
       icon_map = icon_map,
       mode = "chaos",
       mode = "chaos",

Version vom 16. Oktober 2025, 00:01 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
  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 (engl. Keys)
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

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 (nur für Avatar)
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 "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

  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
      if type(v) == "table" then
        local label = v.label or v[1]
        local file  = v.file  or (name .. "_" .. (v.filename or label) .. ".png")
        idx = idx + 1
        local tabLabel = isGuardian and ("E" .. idx) or label
        push(tabLabel, file)
      else
        local label = tostring(v)
        idx = idx + 1
        local file = name .. "_" .. label .. ".png"
        local tabLabel = isGuardian and ("E" .. idx) or label
        push(tabLabel, file)
      end
    end
  else
    local tmp, idx = {}, 1
    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
      idx = idx + 1
      local tabLabel = isGuardian and ("E" .. idx) or it.label
      push(tabLabel, it.file)
    end
  end

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

-- ===== Profile-Auswahl aus HeroConfig =====
local function pickProfile(cfg, entity)
  local profiles = cfg.profiles or {}
  local base = profiles.default or {}

  local key = "default"
  if entity == "guardian" then key = "guardian"
  elseif entity == "wm" then key = "wm"
  end

  local over = profiles[key] 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-Rift
  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

-- ===== Hilfsrenderer für das 3er-Grid =====
local function render_grid(opts)
  local profile, data, args, L, showLabels, isWM = opts.profile, opts.data, opts.args, opts.L, opts.showLabels, opts.isWM
  local abilityLabel = opts.abilityLabel or "Fähigkeit"
  local mode = opts.mode or "default" -- "default" | "chaos"

  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("Hauptattribute")
    local mains = col:tag("div"):addClass("kr-hc-main-attrs")
    local src = (mode=="chaos")
      and ((type(data.chaos)=="table" and (data.chaos.main or data.chaos)) or (data.chaos_main or {}))
      or (type(data.main)=="table" and 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 i, key in ipairs(order) do
      local meta = metaT[key] or { label = key }
      local val  = (mode=="chaos") and pick(args["chaos_"..key], src[key]) or pick(args["main"..i.."_val"], src[key])
      pill(mains, meta.label, val, meta.icon, showLabels, meta.desc)
    end
  end

  -- -------- Spalte 2: Fortschrittsattribute --------
  do
    local col = grid: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 src = (mode=="chaos")
      and ((type(data.chaos)=="table" and (data.chaos.adv or data.chaos)) or (data.chaos_adv or {}))
      or (type(data.adv)=="table" and 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 i, key in ipairs(order) do
      local meta = metaT[key] or { label = key }
      local val  = (mode=="chaos") and pick(args["chaos_"..key], src[key]) or pick(args["adv"..i.."_val"], src[key])
      adv(advs, meta.label, val, meta.icon, showLabels, meta.desc)
    end
  end

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

    local aura_val = (mode=="chaos")
      and pick(args.chaos_guardian_aura, args.chaos_aura, (type(opts.data.chaos)=="table" and opts.data.chaos.aura), opts.awakening_bonus)
      or opts.awakening_bonus

    if not isempty(aura_val) then
      -- Hauptkachel: immer MIT Icon & Tooltip (auch bei WM)
      local box = col:tag("div"):addClass("kr-hc-awakening kr-hc-adv kr-tip")
      addTooltip(box, aura_val, opts.awakening_desc)
      if opts.awakening_icon then
        box:tag("div"):addClass("kr-hc-adv-icon")
           :wikitext(fileWikitext(opts.awakening_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(aura_val)
    end

    -- Zusätzliche Kachel NUR für WM: "Fähigkeit" ohne Icon/Tooltip
    if isWM then
      local ability = pick(args.ability, data.ability, data.skill, data.wm_ability)
      if not isempty(ability) then
        local b2 = col:tag("div"):addClass("kr-hc-adv kr-no-icon")
        local m2 = b2:tag("div"):addClass("kr-hc-adv-main")
        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(ability)
      end
    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 = 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 & Entity
  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
  if isGuardian then entity = "guardian" end
  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")
  if isGuardian then root:addClass("is-guardian")
  elseif isWM then root:addClass("is-wm")
  elseif isMercenary then root:addClass("is-mercenary")
  elseif isGod then root:addClass("is-god")
  else root:addClass("is-hero") end
  if showLabels then root:addClass("kr-show-labels") end

  -- Labels für UI
  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 labelsWM       = (UI.labels and UI.labels.wm)       or { awakening="Meilenstein-Effekt", ability="Fähigkeit" }

  local L = isGuardian and labelsGuardian or labelsHero
  if isWM then
    L = { class = L.class or (labelsHero.class or "Klasse:"), awakening = labelsWM.awakening }
  end

  -- Basics (ohne Icons)
  local class          = tr("class",          pick(args.class,          data.class))
  local attackstyle    = tr("attackstyle",    pick(args.attackstyle,    data.attackstyle))
  local specialization = tr("specialization", pick(args.specialization, data.specialization))
  local resource       = tr("resource",       pick(args.resource,       data.resource))
  local unlock_at      = pick(args.unlock_at, data.unlock_at)

  -- Awakening (Default)
  local awakening_raw = pick(
    args.awakening_bonus, data.awakening_bonus,
    args.guardian_aura,   data.guardian_aura,
    data.aura
  )
  local awakening_name = tr("awakening_bonus", awakening_raw)
  local awakening_desc = awakeningDesc(awakening_raw, pick(args.awakening_desc, data.awakening_desc))

  local CFGmod   = loadConfig()
  local icon_map = (CFGmod.icon_map or {})
  local awakening_icon = (icon_map.awakening_bonus or {})[norm(awakening_raw)]

  -- Profil jetzt EINMAL bestimmen (fix für nil-Profile)
  local profile = pickProfile(CFGmod, entity)

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

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

  -- Header + Unlock
  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: kein Link
    else
      local titleTarget = pick(args.linkto, name)
      h:wikitext(string.format("[[%s|%s]]", titleTarget, name))
    end
  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")

  -- Basics 2×2
  local basics = content:tag("div"):addClass("kr-hc-basics2")
  local function pair(label, value)
    if isempty(value) 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(L.class, class)
  pair("Spezialisierung:", specialization)
  pair("Angriffsart:",     attackstyle)
  pair("Ressource:",       resource)

  -- ===== Inhalte
  if not isGuardian then
    local grid_html = render_grid{
      profile = profile, data = data, args = args, L = L,
      showLabels = showLabels, isWM = isWM,
      abilityLabel = ((UI.labels and UI.labels.wm and UI.labels.wm.ability) or "Fähigkeit"),
      awakening_raw = awakening_raw,
      awakening_bonus = awakening_name,
      awakening_desc = awakening_desc,
      awakening_icon = awakening_icon, -- WM zeigt Icon wieder an
      icon_map = icon_map,
      mode = "default",
    }
    content:wikitext(grid_html)
    return tostring(root)
  end

  -- Wächter: Default-Grid
  local default_html = render_grid{
    profile = profile, data = data, args = args, L = L,
    showLabels = showLabels, isWM = false,
    awakening_raw = awakening_raw,
    awakening_bonus = awakening_name,
    awakening_desc = awakening_desc,
    awakening_icon = awakening_icon,
    icon_map = icon_map,
    mode = "default",
  }
  content:wikitext(default_html)

  -- Wächter: Chaos-Grid (falls vorhanden)
  local hasChaos = false
  local chaos = (type(data.chaos)=="table" and data.chaos) or {}
  if profile.chaos_main_order and #profile.chaos_main_order > 0 then
    local src = chaos.main or chaos
    for _,k in ipairs(profile.chaos_main_order) do
      if not isempty(pick(args["chaos_"..k], src and src[k])) then hasChaos = true break end
    end
  end
  if (not hasChaos) and profile.chaos_adv_order and #profile.chaos_adv_order > 0 then
    local src = chaos.adv or chaos
    for _,k in ipairs(profile.chaos_adv_order) do
      if not isempty(pick(args["chaos_"..k], src and src[k])) then hasChaos = true break end
    end
  end
  if (not hasChaos) and (not isempty(pick(args.chaos_guardian_aura, args.chaos_aura, chaos.aura))) then
    hasChaos = true
  end

  if hasChaos then
    content:tag("h2"):wikitext("Chaos-Attribute")
    content:tag("div"):addClass("kr-hc-sep")
    local chaos_html = render_grid{
      profile = profile, data = data, args = args, L = L,
      showLabels = showLabels, isWM = false,
      awakening_raw = awakening_raw,
      awakening_bonus = awakening_name,
      awakening_desc = awakening_desc,
      awakening_icon = awakening_icon,
      icon_map = icon_map,
      mode = "chaos",
    }
    content:wikitext(chaos_html)
  end

  return tostring(root)
end

-- Alias
p.main = p.render
return p