Zum Inhalt springen


Modul:HeroCard: Unterschied zwischen den Versionen

Aus Firestone Idle RPG Wiki
Keine Bearbeitungszusammenfassung
Keine Bearbeitungszusammenfassung
Zeile 1: Zeile 1:
-- Modul:HeroCard
local p = {}
local p = {}


-- ======= CONFIG / DEFAULTS =======
-- ===== Helpers =====
local CFG = {
local function isempty(v) return v == nil or v == "" end
  main_order = { "damage", "hp", "armor" },
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
  main_meta = {
local function norm(s) if isempty(s) then return "" end return mw.ustring.lower(mw.text.trim(s)):gsub("%s+"," ") end
    damage = { label = "Schaden",      icon = "AttackIcon.png",
              desc  = "Dies bezeichnet die Menge an Gesundheit, die Gegner durch deine Treffer verlieren." },
    hp    = { label = "Gesundheit",  icon = "HealthIcon.png",
              desc  = "Wie viel Schaden du einstecken kannst, bevor du besiegt wirst." },
    armor  = { label = "Rüstung",     icon = "ArmorIcon.png",
              desc  = "Reduziert den erlittenen Schaden." },
  },
  adv_order = { "precision", "bonus_damage", "speed", "dodge" },
  adv_meta = {
    precision    = { label = "Krit.-Chance",        icon = "CriticalChanceIcon.png",
                    desc  = "Wahrscheinlichkeit, mit einem Angriff einen kritischen Treffer zu landen." },
    bonus_damage = { label = "Kritischer Schaden",  icon = "CriticalDamageIcon.png",
                    desc  = "Zusätzlicher Schaden, der bei kritischen Treffern verursacht wird." },
    speed        = { label = "Angriffsgeschwindigkeit", icon = "AttackSpeedIcon.png",
                    desc  = "Wie schnell der Held angreift (kleiner ist schneller)." },
    dodge        = { label = "Ausweichen",          icon = "DodgeIcon.png",
                    desc  = "Chance, einem gegnerischen Angriff vollständig auszuweichen." },
  },
 
  -- Icon-Mapping (deine Schreibweisen, UNVERÄNDERT)
  icon_map = {
    attackstyle = {
      melee = "MeleeIcon.png",
      spellcaster = "SpellcasterIcon.png",
      ranged = "RangedIcon.png",
    },
    specialization = {
      healer = "HealerIcon.png",
      damage = "DamageIcon.png",
      tank  = "TankIcon.png",
    },
    resource = {
      mana  = "ManaIcon.png",
      rage  = "RageIcon.png",
      energy = "EnergyIcon.png",
    },
    awakening_bonus = {
      ["raining gold"]        = "RainingGold.png",
      ["all main attributes"] = "AllAttributes.png",
      prestigious            = "Prestigious.png",
    }
  },
 
  -- DE-Übersetzung der Werte aus HeroData (UNVERÄNDERT außer Tipp-Korrekturen)
  i18n = {
    class = {
      warrior="Krieger", paladin="Paladin", rouge="Schurke",
      mage="Magier", druid="Druide", ranger="Waldläufer"
    },
    attackstyle = {
      melee="Nahkampf", ranged="Fernkampf", spellcaster="Zauberwirker"
    },
    specialization = { healer="Heiler", damage="Schaden", tank="Tank" },
    resource = { mana="Mana", rage="Wut", energy="Energie" },
    awakening_bonus = {
      ["raining gold"]="Goldregen",
      ["all main attributes"]="Alle Hauptattribute",
      prestigious="Prestigevoll"
    }
  }
}


-- ======= 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 (v == nil or v == "") then return v end
  end
  return nil
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 function getArgs(frame)
   local args, f1, f2 = {}, frame.args or {}, (frame:getParent() and frame:getParent().args) or {}
   local args, f1, f2 = {}, frame.args or {}, (frame:getParent() and frame:getParent().args) or {}
Zeile 87: Zeile 13:
   return args
   return args
end
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 = {} }; return I18N
end
-- HeroData (de/en)
local function loadHeroes()
local function loadHeroes()
  local tried, lastErr = {}, nil
   for _,t in ipairs{ "Modul:HeroData", "Module:HeroData" } do
   for _,title in ipairs{ "Modul:HeroData", "Module:HeroData" } do
     local ok, mod = pcall(require, t)
     local ok, mod = pcall(require, title)
     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, { ok=true, from=title }
    else
      table.insert(tried, title); lastErr = mod
     end
     end
   end
   end
   return nil, { ok=false, tried=tried, err=lastErr }
   return nil, {ok=false}
end
end
local function fileWikitext(filename, opts)
local function fileWikitext(filename, opts)
   if isempty(filename) then return "" end
   if isempty(filename) then return "" end
   local parts = { ("Datei:%s"):format(filename) }
   local parts = { ("Datei:%s"):format(filename) }
   if opts and opts.size  then table.insert(parts, opts.size) end
   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.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
   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
local function mapIcon(kind, value, override)
 
  if not isempty(override) then return override end
-- Übersetzen per HeroI18n
  local t = CFG.icon_map[kind] or {}
  return t[norm(value)]
end
local function tr(kind, value)
local function tr(kind, value)
   if isempty(value) then return value end
   if isempty(value) then return value end
   local m = CFG.i18n[kind] or {}
   local map = loadI18n().i18n[kind] or {}
   return m[norm(value)] or value
   return map[norm(value)] or value
end
end


-- Tooltip-Inhalt als echtes HTML (für Titel + Trenner + Beschreibung)
-- Tooltip-HTML (Dark-Box; Pfeil wird per CSS entfernt)
local function addTooltip(box, title, desc)
local function addTooltip(box, title, desc)
   if (title == nil or title == "") and (desc == nil or desc == "") then return end
   if (isempty(title) and isempty(desc)) then return end
   local tip = box:tag("span"):addClass("kr-tipbox")
   local tip = box:tag("span"):addClass("kr-tipbox")
   if title and title ~= "" then tip:tag("span"):addClass("kr-tip-title"):wikitext(title) end
   if not isempty(title) then tip:tag("span"):addClass("kr-tip-title"):wikitext(title) end
   if (title and title ~= "") and (desc and desc ~= "") then
   if not (isempty(title) or isempty(desc)) then tip:tag("span"):addClass("kr-tip-sep") end
    tip:tag("span"):addClass("kr-tip-sep")
   if not isempty(descthen tip:tag("span"):addClass("kr-tip-desc"):wikitext(desc) end
  end
   if desc and desc ~= "" then tip:tag("span"):addClass("kr-tip-desc"):wikitext(desc) end
end
end


-- Pill: Icon 36px, **kein** title-Attribut; Tooltip via addTooltip(...)
-- Kleine Bausteine
local function pill(htmlParent, label, val, icon, showLabels, desc)
local function pill(parent, label, val, icon, showLabels, desc)
   if val == nil or val == "" then return end
   if isempty(val) then return end
   local box = htmlParent: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 icon and icon ~= "" then left:wikitext(fileWikitext(icon, { size="36x36px", link="", lazy=true })) 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 "")
   if not showLabels then lab:addClass("is-hidden") end
   if not showLabels then lab:addClass("is-hidden") end
   text:tag("div"):addClass("kr-hc-pill-value"):wikitext(val)
   text:tag("div"):addClass("kr-hc-pill-value"):wikitext(val)
   addTooltip(box, label, desc)
   addTooltip(box, label, desc)
end
end


-- Adv: Icon 36px, Tooltip via addTooltip(...)
local function adv(parent, label, val, icon, showLabels, desc)
local function adv(htmlParent, label, val, icon, showLabels, desc)
   if isempty(val) then return end
   if val == nil or val == "" then return end
   local box = parent:tag("div"):addClass("kr-hc-adv kr-tip")
   local box = htmlParent: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 icon and icon ~= "" then left:wikitext(fileWikitext(icon, { size="36x36px", link="", lazy=true })) 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 "")
   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(val)
   main:tag("div"):addClass("kr-hc-adv-value"):wikitext(val)
   addTooltip(box, label, desc)
   addTooltip(box, label, desc)
end
end


 
-- Unlock-Text
-- Freischaltung → (dein Text, leicht erweitert)
local function unlock_text_de(s)
local function unlock_text_de(s)
   if s == nil or s == "" then return nil end
   if isempty(s) then return nil end
   local t = mw.ustring.lower(mw.text.trim(s)):gsub("%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 auf dem Piratenschiff freigeschaltet." end
   if t:match("pirate%s*ship") then return "Wird auf dem Piratenschiff freigeschaltet." end
  -- Vollständige eigene Sätze einfach übernehmen
   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


-- Vordefinierte Beschreibungen pro Awakening-Typ (DE + EN Schlüssel)
-- Awakening-Desc: NUR englische Keys (wie in HeroData)
local AW_DESC = {
  ["raining gold"]          = "Erhöht deinen Goldgewinn aus Kämpfen und Belohnungen.",
  ["all main attributes"]    = "Erhöht dauerhaft Schaden, Gesundheit und Rüstung.",
  ["prestigious"]            = "Verbessert Prestige-Belohnungen und Effizienz beim Prestigen.",
}
 
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
   if isempty(raw_value) then return "Bonus, der beim Erwachen freigeschaltet wird." end
   if isempty(raw_value) then return "Bonus, der beim Erwachen freigeschaltet wird." end
  -- erst auf Basis des Rohwerts, dann auf Basis der DE-Übersetzung versuchen
   local map = loadI18n().aw_desc or {}
   local k1 = norm(raw_value)
   return map[norm(raw_value)] or "Bonus, der beim Erwachen freigeschaltet wird."
   local k2 = norm(tr("awakening_bonus", raw_value) or raw_value)
  return AW_DESC[k1] or AW_DESC[k2] or "Bonus, der beim Erwachen freigeschaltet wird."
end
end


-- ======= RENDER =======
-- ======= RENDER =======
Zeile 196: Zeile 116:
   local name = pick(args.name, args[1]) or ""
   local name = pick(args.name, args[1]) or ""


   local heroes, info = 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 debugWanted = norm(args.debug) == "1" or norm(args.debug) == "yes"
   local showLabels = norm(args.show_labels) == "yes" or norm(args.show_labels) == "1"
   local showLabels = norm(args.show_labels) == "1" or norm(args.show_labels) == "yes"


   -- Basics (DE)
   -- Daten + Übersetzungen
   local class            = tr("class",          pick(args.class,          data.class))
   local class            = tr("class",          pick(args.class,          data.class))
   local attackstyleRaw    = pick(args.attackstyle,    data.attackstyle)
   local attackstyleRaw    = pick(args.attackstyle,    data.attackstyle)
Zeile 211: Zeile 130:
   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(args.awakening_bonus, data.awakening_bonus)
   local awakening_bonusRaw= pick(args.awakening_bonus, data.awakening_bonus)
   local awakening_bonus  = tr("awakening_bonus", awakening_bonusRaw)
   local awakening_bonus  = tr("awakening_bonus", awakening_bonusRaw)
   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))


   -- Icons
   local CFGmod = loadConfig()
   local attackstyle_icon    = mapIcon("attackstyle",   attackstyleRaw,    pick(args.attackstyle_icon,    data.attackstyle_icon))
  local icon_map = CFGmod.icon_map
   local specialization_icon = mapIcon("specialization", specializationRaw, pick(args.specialization_icon, data.specialization_icon))
 
   local resource_icon      = mapIcon("resource",       resourceRaw,      pick(args.resource_icon,      data.resource_icon))
   local attackstyle_icon    = (icon_map.attackstyle    or {})[norm(attackstyleRaw)]
   local awakening_bonus_icon= mapIcon("awakening_bonus",awakening_bonusRaw,pick(args.awakening_bonus_icon,data.awakening_bonus_icon))
   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
   -- Bilder/Links
Zeile 225: Zeile 147:
   local avatar = pick(args.avatar, (not isempty(name) and (name .. ".png")) or nil)
   local avatar = pick(args.avatar, (not isempty(name) and (name .. ".png")) or nil)


  -- Root
   local root = mw.html.create("div"):addClass("kr-hero-card")
   local root = mw.html.create("div"):addClass("kr-hero-card")
   if showLabels then root:addClass("kr-show-labels") end
   if showLabels then root:addClass("kr-show-labels") end


  -- Avatar
   root:tag("div"):addClass("kr-hc-avatar")
   root:tag("div"):addClass("kr-hc-avatar")
       :wikitext(fileWikitext(avatar, { class="kr-hc-avatar-img", link=linkto, lazy=true }))
       :wikitext(fileWikitext(avatar, { class="kr-hc-avatar-img", link=linkto, lazy=true }))


  -- Content
   local content = root:tag("div"):addClass("kr-hc-content")
   local content = root:tag("div"):addClass("kr-hc-content")
   if not isempty(name) then
   if not isempty(name) then
     local h = content:tag("h2"):addClass("kr-hc-name")
     local h = content:tag("h2"):addClass("kr-hc-name")
     local unlock_text = unlock_text_de(unlock_at)
     local unlock_text = unlock_text_de(unlock_at)
     h:wikitext(string.format("[[%s|%s]]%s",
     h:wikitext(string.format("[[%s|%s]]%s", linkto, name,
      linkto, name,
       unlock_text and (" <span class=\"kr-hc-unlock\">(" .. unlock_text .. ")</span>") or ""))
       unlock_text and (" <span class=\"kr-hc-unlock\">(" .. unlock_text .. ")</span>") or ""
    ))
  end
 
  if (not info or not info.ok) and debugWanted then
    content:tag("div"):addClass("kr-hc-debug"):wikitext("<strong>HeroData not loaded.</strong>")
   end
   end


   -- Basics 2×2
   -- Basics 2×2
   local basicsGrid = 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)
     if isempty(value) then return end
     if isempty(value) then return end
     local it = basicsGrid: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")
     local v = it:tag("span"):addClass("value"):wikitext(value)
    v:wikitext(value)
     if icon then v:tag("span"):addClass("value-icon"):wikitext(fileWikitext(icon, {size="24x24px", link="", lazy=true})) end
     if not isempty(icon) then
      v:tag("span"):addClass("value-icon")
        :wikitext(fileWikitext(icon, { size="24x24px", link="", lazy=true }))
    end
   end
   end
   pair("Klasse:",          class,          nil)
   pair("Klasse:",          class,          nil)
Zeile 265: Zeile 176:
   pair("Ressource:",      resource,      resource_icon)
   pair("Ressource:",      resource,      resource_icon)


   -- Hauptattribute (mit Tooltip-Texten)
   -- Hauptattribute
   content:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
   content:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
   local mains   = content:tag("div"):addClass("kr-hc-main-attrs")
   local mains = content:tag("div"):addClass("kr-hc-main-attrs")
   local heroMain= type(data.main) == "table" and data.main or {}
   local heroMain = type(data.main)=="table" and data.main or {}
   for i, key in ipairs(CFG.main_order) do
   for i, key in ipairs(CFGmod.main_order) do
     local meta  = CFG.main_meta[key]
     local meta  = CFGmod.main_meta[key]
     local val  = pick(args["main"..i.."_val"], heroMain[key])
     pill(mains, meta.label, pick(args["main"..i.."_val"], heroMain[key]),
    local icon  = pick(args["main"..i.."_icon"], meta.icon)
        pick(args["main"..i.."_icon"], meta.icon), showLabels,
    local desc  = pick(args["main"..i.."_desc"], meta.desc)
        pick(args["main"..i.."_desc"], meta.desc))
    pill(mains, meta.label, val, icon, showLabels, desc)
   end
   end


   -- Erweiterte Attribute
   -- Erweiterte Attribute
   content:tag("h3"):addClass("kr-hc-h"):wikitext("Erweiterte Attribute")
   content:tag("h3"):addClass("kr-hc-h"):wikitext("Erweiterte Attribute")
   local advs   = content:tag("div"):addClass("kr-hc-adv-attrs")
   local advs = content:tag("div"):addClass("kr-hc-adv-attrs")
   local heroAdv= type(data.adv) == "table" and data.adv or {}
   local heroAdv = type(data.adv)=="table" and data.adv or {}
   for i, key in ipairs(CFG.adv_order) do
   for i, key in ipairs(CFGmod.adv_order) do
     local meta = CFG.adv_meta[key]
     local meta = CFGmod.adv_meta[key]
     local val  = pick(args["adv"..i.."_val"], heroAdv[key])
     adv(advs, meta.label, pick(args["adv"..i.."_val"], heroAdv[key]),
    local icon  = pick(args["adv"..i.."_icon"], meta.icon)
        pick(args["adv"..i.."_icon"], meta.icon), showLabels,
    local desc  = pick(args["adv"..i.."_desc"], meta.desc)
        pick(args["adv"..i.."_desc"], meta.desc))
    adv(advs, meta.label, val, icon, showLabels, desc)
   end
   end
   for i = #CFG.adv_order + 1, 8 do
   for i=#CFGmod.adv_order+1,8 do
     local val = args["adv"..i.."_val"]
     local val=args["adv"..i.."_val"]; if isempty(val) then break end
    if isempty(val) then break end
     adv(advs, args["adv"..i.."_label"] or "", val, args["adv"..i.."_icon"], showLabels, args["adv"..i.."_desc"])
     adv(advs, args["adv"..i.."_label"] or "", val, args["adv"..i.."_icon"], showLabels, args["adv"..i.."_desc"])
   end
   end


   -- ===== Erweckungsbonus (Icon 64px) MIT Tooltip =====
   -- Erweckungsbonus – Tooltip-Titel = übersetzter Bonusname
   if not isempty(awakening_bonus) then
   if not isempty(awakening_bonus) then
     content:tag("h3"):addClass("kr-hc-h"):wikitext("Erweckungsbonus")
     content:tag("h3"):addClass("kr-hc-h"):wikitext("Erweckungsbonus")
     local awk = content:tag("div"):addClass("kr-hc-awakening")
     local awk = content:tag("div"):addClass("kr-hc-awakening")
     local box = awk:tag("div"):addClass("kr-hc-adv kr-tip")
     local box = awk:tag("div"):addClass("kr-hc-adv kr-tip")
 
     addTooltip(box, awakening_bonus, awakening_desc)
    -- Tooltip (Titel + auto-Beschreibung)
     if awakening_bonus_icon then
     addTooltip(box, "Erweckungsbonus", awakening_desc)
 
     if not isempty(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="", lazy=true }))
         :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("Erweckungsbonus")
     local lab = main:tag("div"):addClass("kr-hc-adv-label"):wikitext("Erweckungsbonus")
    main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus)
    -- Labels standardmäßig ausgeblendet
     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)
  end
  if debugWanted then
    content:tag("pre"):addClass("kr-hc-debug")
      :wikitext(mw.text.nowiki(mw.dumpObject({name=name, data=data, module=info})))
   end
   end


   return tostring(root)
   return tostring(root)
end
function p.selftest()
  local heroes, info = loadHeroes()
  local out = mw.html.create("div")
  if not (info and info.ok) then
    out:tag("p"):wikitext("HeroData NOT loaded.")
    return tostring(out)
  end
  out:tag("p"):wikitext("HeroData loaded from: " .. info.from)
  local ul = out:tag("ul"); for n,_ in pairs(heroes) do ul:tag("li"):wikitext(n) end
  return tostring(out)
end
end


return p
return p

Version vom 9. Oktober 2025, 12:55 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 = {} }; 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 (Dark-Box; Pfeil wird per CSS entfernt)
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

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

  -- 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)
  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")
  if showLabels then root:addClass("kr-show-labels") end

  root:tag("div"):addClass("kr-hc-avatar")
      :wikitext(fileWikitext(avatar, { class="kr-hc-avatar-img", link=linkto, lazy=true }))

  local content = root:tag("div"):addClass("kr-hc-content")
  if not isempty(name) then
    local h = content:tag("h2"):addClass("kr-hc-name")
    local unlock_text = unlock_text_de(unlock_at)
    h:wikitext(string.format("[[%s|%s]]%s", linkto, name,
      unlock_text and (" <span class=\"kr-hc-unlock\">(" .. unlock_text .. ")</span>") or ""))
  end

  -- 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("Klasse:",          class,          nil)
  pair("Spezialisierung:", specialization, specialization_icon)
  pair("Angriffsart:",     attackstyle,    attackstyle_icon)
  pair("Ressource:",       resource,       resource_icon)

  -- Hauptattribute
  content:tag("h3"):addClass("kr-hc-h"):wikitext("Hauptattribute")
  local mains = content: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

  -- Erweiterte Attribute
  content:tag("h3"):addClass("kr-hc-h"):wikitext("Erweiterte Attribute")
  local advs = content: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
  for i=#CFGmod.adv_order+1,8 do
    local val=args["adv"..i.."_val"]; if isempty(val) then break end
    adv(advs, args["adv"..i.."_label"] or "", val, args["adv"..i.."_icon"], showLabels, args["adv"..i.."_desc"])
  end

  -- Erweckungsbonus – Tooltip-Titel = übersetzter Bonusname
  if not isempty(awakening_bonus) then
    content:tag("h3"):addClass("kr-hc-h"):wikitext("Erweckungsbonus")
    local awk = content:tag("div"):addClass("kr-hc-awakening")
    local box = awk:tag("div"):addClass("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("Erweckungsbonus")
    main:tag("div"):addClass("kr-hc-adv-value"):wikitext(awakening_bonus)
    -- Labels standardmäßig ausgeblendet
    if not showLabels then lab:addClass("is-hidden") end
  end

  return tostring(root)
end

return p