Modul:Firestone: Unterschied zwischen den Versionen
Keine Bearbeitungszusammenfassung |
Keine Bearbeitungszusammenfassung |
||
| Zeile 4: | Zeile 4: | ||
-- ===== helpers ===== | -- ===== helpers ===== | ||
local function isempty(v) return v==nil or v=="" end | local function isempty(v) return v==nil or v=="" end | ||
local function norm(s) if s==nil then return "" end s=mw.text.trim(tostring(s)) | local function norm(s) | ||
if s==nil then return "" end | |||
s = mw.text.trim(tostring(s)) | |||
return mw.ustring.lower(s):gsub("%s+"," ") | |||
end | |||
local function getArgs(frame) | local function getArgs(frame) | ||
local a = {} | -- bevorzugt die Parameter der Vorlage (Elternframe) | ||
local a, p = {}, frame:getParent() | |||
local src = (p and p.args) or frame.args or {} | local src = (p and p.args) or frame.args or {} | ||
for k,v in pairs(src) do if v~=nil and v~="" then a[k]=mw.text.trim(tostring(v)) end end | for k,v in pairs(src) do if v~=nil and v~="" then a[k]=mw.text.trim(tostring(v)) end end | ||
return a | return a | ||
end | end | ||
local function pick_key(tbl, key) | local function pick_key(tbl, key) | ||
if type(tbl)~="table" then return nil end | if type(tbl)~="table" then return nil end | ||
if tbl[key]~=nil then return key end | if tbl[key]~=nil then return key end | ||
local k2 = tostring(key) | local k2 = tostring(key) | ||
local kn = norm(k2) | if tbl[k2]~=nil then return k2 end | ||
local kn = norm(k2) | |||
for k,_ in pairs(tbl) do if norm(k)==kn then return k end end | |||
return nil | return nil | ||
end | end | ||
local function deep_get(tbl, path) | local function deep_get(tbl, path) | ||
local cur = tbl | local cur = tbl | ||
| Zeile 28: | Zeile 37: | ||
return cur | return cur | ||
end | end | ||
local function fileWikitext(file, | |||
local function fileWikitext(file, opts) | |||
if isempty(file) then return "" end | if isempty(file) then return "" end | ||
local parts = {" | local parts = {"File:"..file} | ||
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.thumb then | |||
table.insert(parts, "thumb") | |||
if opts.caption and opts.caption~="" then table.insert(parts, opts.caption) end | |||
end | |||
return string.format("[[%s]]", table.concat(parts, "|")) | return string.format("[[%s]]", table.concat(parts, "|")) | ||
end | |||
local function heading(level, text) | |||
if isempty(text) then return "" end | |||
local marks = (level==3) and "===" or "==" | |||
return string.format("%s %s %s", marks, text, marks) | |||
end | end | ||
-- ===== lazy loads ===== | -- ===== lazy loads ===== | ||
local HEROES, EVENTS, I18N | local HEROES, EVENTS, I18N | ||
local function heroes() | local function heroes() | ||
if HEROES then return HEROES end | if HEROES then return HEROES end | ||
for _,t in ipairs{ "Module:HeroData","Modul:HeroData" } do | for _,t in ipairs{ "Module:HeroData","Modul:HeroData" } do | ||
local ok, | local ok, data = pcall(mw.loadData, t) | ||
local ok2, | if ok and type(data)=="table" and type(data.heroes)=="table" then HEROES=data.heroes; return HEROES end | ||
end | local ok2, mod = pcall(require, t) | ||
if ok2 and type(mod)=="table" and type(mod.heroes)=="table" then HEROES=mod.heroes; return HEROES end | |||
end | |||
HEROES = {}; return HEROES | |||
end | end | ||
local function events() | local function events() | ||
if EVENTS then return EVENTS end | if EVENTS then return EVENTS end | ||
for _,t in ipairs{ "Module:EventData","Modul:EventData" } do | for _,t in ipairs{ "Module:EventData","Modul:EventData" } do | ||
local ok, | local ok, data = pcall(mw.loadData, t) | ||
if ok and type(data)=="table" and type(data.events)=="table" then EVENTS=data.events; return EVENTS end | |||
end | end | ||
for _,t in ipairs{ "Module:EventData","Modul:EventData" } do | for _,t in ipairs{ "Module:EventData","Modul:EventData" } do | ||
local ok, | local ok, mod = pcall(require, t) | ||
end | if ok and type(mod)=="table" and type(mod.events)=="table" then EVENTS=mod.events; return EVENTS end | ||
end | |||
EVENTS = {}; return EVENTS | |||
end | end | ||
local function i18n() | local function i18n() | ||
if I18N then return I18N end | if I18N then return I18N end | ||
for _,t in ipairs{ "Module:HeroI18n","Modul:HeroI18n" } do | for _,t in ipairs{ "Module:HeroI18n","Modul:HeroI18n" } do | ||
local ok, | local ok, data = pcall(mw.loadData, t) | ||
local ok2, | if ok and type(data)=="table" then I18N=data; return I18N end | ||
local ok2, mod = pcall(require, t) | |||
if ok2 and type(mod)=="table" then I18N=mod; return I18N end | |||
end | end | ||
I18N={ i18n={}, events={names={},sections={},infobox={}, | I18N = { i18n={}, events={names={},sections={},infobox={},links={},units={}} } | ||
return I18N | |||
end | |||
-- ===== translations/formatters ===== | |||
local function tr(kind, val) | |||
if isempty(val) then return val end | |||
local map = (i18n().i18n or {})[kind] or {} | |||
return map[norm(val)] or val | |||
end | |||
local function tr_month(val) | |||
if isempty(val) then return val end | |||
return tr("month", val) | |||
end | |||
local function tr_event_name(evkey) | |||
local names = (i18n().events or {}).names or {} | |||
return names[norm(evkey)] or evkey | |||
end | end | ||
local function section_label(evkey, which) | local function section_label(evkey, which) | ||
local E=i18n().events or {} | local E = i18n().events or {} | ||
local ev = (E.sections and E.sections[norm(evkey)]) or {} | |||
local def= (E.sections and E.sections.default) or {} | |||
return (ev[which] or def[which] or "") | return (ev[which] or def[which] or "") | ||
end | end | ||
local function | |||
local function type_display(t) | |||
local map = ((i18n().events or {}).infobox or {}).type or {} | |||
return map[norm(t or "")] or (t or "") | |||
end | end | ||
local function type_link(t) | |||
local function | local L = ((i18n().events or {}).links or {}).type or {} | ||
local | local target = L[norm(t or "")] or "" | ||
local disp = type_display(t) | |||
if target~="" then | |||
return string.format("[[%s|%s]]", target, disp) | |||
else | |||
return disp | |||
end | end | ||
if | end | ||
local function fmt_duration(s) | |||
if isempty(s) then return s end | |||
local U = (i18n().events or {}).units or {} | |||
local singular = U.week_singular or "Woche" | |||
local plural = U.week_plural or "Wochen" | |||
-- "2 weeks" / "1 week" | |||
local n = s:match("^(%d+)%s+weeks?$") | |||
if n then | |||
local num = tonumber(n) or 0 | |||
return string.format("%d %s", num, (num==1 and singular or plural)) | |||
end | end | ||
return s | |||
end | |||
local function fmt_unlocks(v) | |||
-- "character_level_10" -> "Level 10" | |||
if isempty(v) then return v end | |||
local n = tostring(v):match("^character_level_(%d+)$") | |||
if n then | |||
local fmt = (((i18n().events or {}).infobox or {}).unlocks_format) or "Level $1" | |||
return (fmt:gsub("%$1", n)) | |||
end | end | ||
if | return v | ||
end | |||
-- ===== event helpers ===== | |||
local function resolve_event(key) | |||
local evs = events() | |||
local real = pick_key(evs, key) | |||
return real and evs[real] or nil | |||
end | |||
local function render_history(ev) | |||
local H = ev.history or {} | |||
if #H==0 then return "" end | |||
local lines = {} | |||
for _,h in ipairs(H) do | |||
local base = string.format("* %s: %s bis %s", tostring(h.year or ""), tostring(h.start or ""), tostring(h["end"] or "")) | |||
if h.alt_platform and h.alt_platform~="" then | |||
base = base .. string.format(" (%s auf Google Play)", tostring(h.alt_platform)) | |||
end | |||
table.insert(lines, base) | |||
end | end | ||
return table.concat(lines,"\n") | return table.concat(lines, "\n") | ||
end | end | ||
local function render_avatars_all(ev, evkey) | |||
local function | local AV = ev.avatars or {} | ||
local | -- Jahre sortieren | ||
local token_key=ev.token and ev.token.key or "" | local years = {} | ||
local out= | for y,_ in pairs(AV) do table.insert(years, y) end | ||
table.sort(years) | |||
local out = {} | |||
local token_key = ev.token and ev.token.key or "" | |||
local token_icon= ev.token and ev.token.icon or "" | |||
local tokens_lbl= section_label(evkey, "tokens") | |||
for _,year in ipairs(years) do | |||
local list = AV[year] | |||
table.insert(out, string.format("==== %d ====", year)) | |||
table.insert(out, '{| class="article-table" style="font-size: 14px;"') | |||
table.insert(out, '!colspan="2" | Avatar\n!Preis') | |||
for _,it in ipairs(list) do | |||
table.insert(out, "|-") | |||
table.insert(out, | |||
string.format("|[[File:%s|50px]] || %s || [[File:%s|25px]] %s %s", | |||
tostring(it.file or ""), | |||
tostring(it.title or ""), | |||
tostring(token_icon or ""), | |||
tostring(it.cost or 0), | |||
tokens_lbl | |||
) | |||
) | |||
end | |||
table.insert(out, "|}") | |||
table.insert(out, "") -- Leerzeile | |||
end | end | ||
return table.concat(out, "\n") | return table.concat(out, "\n") | ||
end | end | ||
local function | local function render_exchange(ev, evkey) | ||
local | local tok_icon = ev.token and ev.token.icon or "" | ||
for | local tokens = section_label(evkey, "tokens") | ||
table. | |||
local out={} | local rows = {} | ||
local function push_header() | |||
table.insert(rows, '{| class="article-table" style="font-size: 14px;"') | |||
table.insert(rows, "!Angebot\n!Preis\n!Limit") | |||
end | |||
local function push_row_offer(range, icon, title, price, limit) | |||
table.insert(rows, "|-") | |||
table.insert(rows, string.format("|%s: [[File:%s|30px]] 1 [[%s|%s]] || [[File:%s|25px]] %s %s || %s", | |||
range, icon, title, title, tok_icon, tostring(price or 0), tokens, tostring(limit or "") | |||
)) | |||
end | |||
local function push_row_currency(icon, amount, title, price, limit) | |||
table.insert(rows, "|-") | |||
table.insert(rows, string.format("|[[File:%s|30px]] %s [[%s|%s]] || [[File:%s|25px]] %s %s || %s", | |||
icon, tostring(amount or 0), title, title, tok_icon, tostring(price or 0), tokens, tostring(limit or "") | |||
)) | |||
end | |||
push_header() | |||
for _,it in ipairs(ev.exchange.chests_by_level or {}) do | |||
push_row_offer("Stufe "..it.range, it.icon, it.title, it.price, it.limit) | |||
end | |||
for _,it in ipairs(ev.exchange.chests_by_stars or {}) do | |||
push_row_offer("Sterne "..it.range, it.icon, it.title, it.price, it.limit) | |||
end | |||
for _,it in ipairs(ev.exchange.chests_by_oracle or {}) do | |||
push_row_offer("Orakel "..it.range, it.icon, it.title, it.price, it.limit) | |||
end | |||
for _,it in ipairs(ev.exchange.currencies or {}) do | |||
push_row_currency(it.icon, it.amount, it.title, it.price, it.limit) | |||
end | |||
table.insert(rows, "|}") | |||
-- Fußnote (optional) | |||
local gp_req = deep_get(ev, {"exchange","notes","gp_req"}) | |||
if gp_req and gp_req~="" then | |||
table.insert(rows, "*erfordert außerdem mehr als "..gp_req.." gesamte [[Gear Power|Ausrüstungsstärke]]") | |||
end | |||
return table.concat(rows, "\n") | |||
end | |||
local function render_offers(ev, kind, evkey) | |||
local list = (kind=="start") and ((ev.shop or {}).initial) or ((ev.shop or {}).after) | |||
if type(list)~="table" or #list==0 then return "" end | |||
local tokens = section_label(evkey, "tokens") | |||
if kind=="start" then | |||
-- 3 Spalten wie im Original | |||
local out = { | |||
'{| class="article-table" style="font-size: 14px;"', | |||
'! colspan="2" style="text-align:center" | '..(list[1].name or "")..'<br />[[File:'..(list[1].image or "")..'|x50px]]', | |||
'! colspan="2" style="text-align:center" | '..(list[2].name or "")..'<br />[[File:'..(list[2].image or "")..'|x50px]]', | |||
'! colspan="2" style="text-align:center" | '..(list[3].name or "")..'<br />[[File:'..(list[3].image or "")..'|x50px]]', | |||
'|-', | |||
'[[File:CurrencyPumpkin.png|30px]] || '..tostring(list[1].token or 0).." "..tokens, | |||
'[[File:CurrencyPumpkin.png|30px]] || '..tostring(list[2].token or 0).." "..tokens, | |||
'[[File:CurrencyPumpkin.png|30px]] || '..tostring(list[3].token or 0).." "..tokens, | |||
'|-', | |||
'Preis: || *', | |||
'Preis: || *', | |||
'Preis: || *', | |||
'|}', | |||
' * der Preis hängt von deiner Plattform ab', | |||
} | |||
return table.concat(out, "\n") | |||
else | |||
-- 4 Spalten wie im Original | |||
local headers = {} | |||
table.insert(headers, '{| class="article-table" style="font-size: 14px;"') | |||
table.insert(headers, "|-") | |||
for i=1,#list do | |||
local it=list[i] | |||
table.insert(headers, string.format('! colspan="2" style="text-align:center" | %s<br />[[File:%s|x50px]]', it.name or "", it.image or "")) | |||
end | |||
table.insert(headers, "|-") | |||
local prices = {} | |||
for i=1,#list do | |||
local it=list[i] | |||
table.insert(prices, string.format('[[File:CurrencyPumpkin.png|30px]] || %s %s', tostring(it.token or 0), tokens)) | |||
end | |||
local foot = {} | |||
for i=1,#list do table.insert(foot, "Preis: || *") end | |||
table.insert(headers, table.concat(prices, "\n")) | |||
table.insert(headers, "|-") | |||
table.insert(headers, table.concat(foot, "\n")) | |||
table.insert(headers, "|}") | |||
table.insert(headers, " * der Preis hängt von deiner Plattform ab") | |||
return table.concat(headers, "\n") | |||
end | end | ||
end | end | ||
local function render_file(evkey, which, ev) | |||
local function | ev = ev or {} | ||
local imgs = ev.images or {} | |||
local | local it = imgs[which] or {} | ||
local | local caption | ||
if which=="deco" then caption = section_label(evkey, "deco_caption") | |||
elseif which=="ex_shop" then caption = section_label(evkey, "hub") | |||
elseif which=="fullname" then caption = section_label(evkey, "fullname") | |||
end | end | ||
return | return fileWikitext(it.file, {thumb=true, caption=caption}) | ||
end | end | ||
-- ===== | -- ====== EVENT dispatcher ====== | ||
local function fn_event(args) | local function fn_event(args) | ||
local key=args[2]; if isempty(key) then return "" end | local key = args[2]; if isempty(key) then return "" end | ||
local ev=resolve_event(key); if not ev then return "" end | local ev = resolve_event(key); if not ev then return "" end | ||
local evkey = pick_key(events(), key) or key | |||
-- | local a3, a4 = args[3], args[4] | ||
if | |||
if isempty(a3) then return "" end | |||
return | -- Überschriften | ||
if a3=="h2" then | |||
return heading(2, section_label(evkey, a4 or "")) | |||
elseif a3=="h3" then | |||
return heading(3, section_label(evkey, a4 or "")) | |||
end | end | ||
if | |||
return | -- Bilder | ||
if a3=="file" then | |||
return render_file(evkey, a4 or "", ev) | |||
end | end | ||
if | |||
-- Links | |||
return | if a3=="link" and a4=="type" then | ||
return type_link(ev.type) | |||
end | |||
-- zusammengesetzte Renderings | |||
if a3=="history" then | |||
return render_history(ev) | |||
elseif a3=="avatars" then | |||
return render_avatars_all(ev, evkey) | |||
elseif a3=="exchange" then | |||
return render_exchange(ev, evkey) | |||
elseif a3=="offers" then | |||
local kind = args[4] -- "start" | "more" | |||
return render_offers(ev, kind, evkey) | |||
end | end | ||
-- | -- einfache Felder/Labels | ||
if a3=="banner" then | |||
return tostring(ev.banner or "") | |||
elseif a3=="type" then | |||
return type_display(ev.type) | |||
elseif a3=="month" or a3=="date" then | |||
return tr_month(ev.month or ev.date or "") | |||
elseif a3=="duration" then | |||
return fmt_duration(ev.duration) | |||
elseif a3=="unlocks_at" then | |||
return fmt_unlocks(ev.unlocks_at) | |||
elseif a3=="eventname" then | |||
return tr_event_name(evkey) | |||
end | |||
-- | -- Freiform-Schlüssel wie "character_level_10" | ||
do | |||
local maybe = fmt_unlocks(a3) | |||
if maybe ~= a3 then return maybe end | |||
local | |||
if | |||
end | end | ||
-- reine Label-Getter aus Sections (currencies, shopname, ex_shop, fullname, shortname, name …) | |||
local lbl = section_label(evkey, a3) | |||
if not isempty(lbl) then return lbl end | |||
-- Fallback auf tiefe Pfade: {{Firestone|Event|name|path|to|field}} | |||
local path, i = {}, 3 | |||
while args[i] do table.insert(path, args[i]); i=i+1 end | |||
local v = deep_get(ev, path) | local v = deep_get(ev, path) | ||
return tostring(v or "") | return tostring(v or "") | ||
end | end | ||
-- ===== | -- ===== main ===== | ||
function M.main(frame) | |||
local a = getArgs(frame) | |||
local | -- numerische Parameter durchreichen | ||
for i=1,12 do a[i] = a[i] and tostring(a[i]) or nil end | |||
local dom = a[1] and norm(a[1]) or "" | |||
local | |||
if dom=="event" then | |||
return fn_event(a) | |||
else | |||
return "" | |||
if dom=="event" | end | ||
end | end | ||
return M | return M | ||
Version vom 20. Oktober 2025, 04:32 Uhr
Die Dokumentation für dieses Modul kann unter Modul:Firestone/Doku erstellt werden
-- Modul:Firestone
local M = {}
-- ===== helpers =====
local function isempty(v) return v==nil or v=="" end
local function norm(s)
if s==nil then return "" end
s = mw.text.trim(tostring(s))
return mw.ustring.lower(s):gsub("%s+"," ")
end
local function getArgs(frame)
-- bevorzugt die Parameter der Vorlage (Elternframe)
local a, p = {}, frame:getParent()
local src = (p and p.args) or frame.args or {}
for k,v in pairs(src) do if v~=nil and v~="" then a[k]=mw.text.trim(tostring(v)) end end
return a
end
local function pick_key(tbl, key)
if type(tbl)~="table" then return nil end
if tbl[key]~=nil then return key end
local k2 = tostring(key)
if tbl[k2]~=nil then return k2 end
local kn = norm(k2)
for k,_ in pairs(tbl) do if norm(k)==kn then return k end end
return nil
end
local function deep_get(tbl, path)
local cur = tbl
for _,k in ipairs(path) do
if type(cur)~="table" then return nil end
local real = pick_key(cur, k); if not real then return nil end
cur = cur[real]
end
return cur
end
local function fileWikitext(file, opts)
if isempty(file) then return "" end
local parts = {"File:"..file}
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.thumb then
table.insert(parts, "thumb")
if opts.caption and opts.caption~="" then table.insert(parts, opts.caption) end
end
return string.format("[[%s]]", table.concat(parts, "|"))
end
local function heading(level, text)
if isempty(text) then return "" end
local marks = (level==3) and "===" or "=="
return string.format("%s %s %s", marks, text, marks)
end
-- ===== lazy loads =====
local HEROES, EVENTS, I18N
local function heroes()
if HEROES then return HEROES end
for _,t in ipairs{ "Module:HeroData","Modul:HeroData" } do
local ok, data = pcall(mw.loadData, t)
if ok and type(data)=="table" and type(data.heroes)=="table" then HEROES=data.heroes; return HEROES end
local ok2, mod = pcall(require, t)
if ok2 and type(mod)=="table" and type(mod.heroes)=="table" then HEROES=mod.heroes; return HEROES end
end
HEROES = {}; return HEROES
end
local function events()
if EVENTS then return EVENTS end
for _,t in ipairs{ "Module:EventData","Modul:EventData" } do
local ok, data = pcall(mw.loadData, t)
if ok and type(data)=="table" and type(data.events)=="table" then EVENTS=data.events; return EVENTS end
end
for _,t in ipairs{ "Module:EventData","Modul:EventData" } do
local ok, mod = pcall(require, t)
if ok and type(mod)=="table" and type(mod.events)=="table" then EVENTS=mod.events; return EVENTS end
end
EVENTS = {}; return EVENTS
end
local function i18n()
if I18N then return I18N end
for _,t in ipairs{ "Module:HeroI18n","Modul:HeroI18n" } do
local ok, data = pcall(mw.loadData, t)
if ok and type(data)=="table" then I18N=data; return I18N end
local ok2, mod = pcall(require, t)
if ok2 and type(mod)=="table" then I18N=mod; return I18N end
end
I18N = { i18n={}, events={names={},sections={},infobox={},links={},units={}} }
return I18N
end
-- ===== translations/formatters =====
local function tr(kind, val)
if isempty(val) then return val end
local map = (i18n().i18n or {})[kind] or {}
return map[norm(val)] or val
end
local function tr_month(val)
if isempty(val) then return val end
return tr("month", val)
end
local function tr_event_name(evkey)
local names = (i18n().events or {}).names or {}
return names[norm(evkey)] or evkey
end
local function section_label(evkey, which)
local E = i18n().events or {}
local ev = (E.sections and E.sections[norm(evkey)]) or {}
local def= (E.sections and E.sections.default) or {}
return (ev[which] or def[which] or "")
end
local function type_display(t)
local map = ((i18n().events or {}).infobox or {}).type or {}
return map[norm(t or "")] or (t or "")
end
local function type_link(t)
local L = ((i18n().events or {}).links or {}).type or {}
local target = L[norm(t or "")] or ""
local disp = type_display(t)
if target~="" then
return string.format("[[%s|%s]]", target, disp)
else
return disp
end
end
local function fmt_duration(s)
if isempty(s) then return s end
local U = (i18n().events or {}).units or {}
local singular = U.week_singular or "Woche"
local plural = U.week_plural or "Wochen"
-- "2 weeks" / "1 week"
local n = s:match("^(%d+)%s+weeks?$")
if n then
local num = tonumber(n) or 0
return string.format("%d %s", num, (num==1 and singular or plural))
end
return s
end
local function fmt_unlocks(v)
-- "character_level_10" -> "Level 10"
if isempty(v) then return v end
local n = tostring(v):match("^character_level_(%d+)$")
if n then
local fmt = (((i18n().events or {}).infobox or {}).unlocks_format) or "Level $1"
return (fmt:gsub("%$1", n))
end
return v
end
-- ===== event helpers =====
local function resolve_event(key)
local evs = events()
local real = pick_key(evs, key)
return real and evs[real] or nil
end
local function render_history(ev)
local H = ev.history or {}
if #H==0 then return "" end
local lines = {}
for _,h in ipairs(H) do
local base = string.format("* %s: %s bis %s", tostring(h.year or ""), tostring(h.start or ""), tostring(h["end"] or ""))
if h.alt_platform and h.alt_platform~="" then
base = base .. string.format(" (%s auf Google Play)", tostring(h.alt_platform))
end
table.insert(lines, base)
end
return table.concat(lines, "\n")
end
local function render_avatars_all(ev, evkey)
local AV = ev.avatars or {}
-- Jahre sortieren
local years = {}
for y,_ in pairs(AV) do table.insert(years, y) end
table.sort(years)
local out = {}
local token_key = ev.token and ev.token.key or ""
local token_icon= ev.token and ev.token.icon or ""
local tokens_lbl= section_label(evkey, "tokens")
for _,year in ipairs(years) do
local list = AV[year]
table.insert(out, string.format("==== %d ====", year))
table.insert(out, '{| class="article-table" style="font-size: 14px;"')
table.insert(out, '!colspan="2" | Avatar\n!Preis')
for _,it in ipairs(list) do
table.insert(out, "|-")
table.insert(out,
string.format("|[[File:%s|50px]] || %s || [[File:%s|25px]] %s %s",
tostring(it.file or ""),
tostring(it.title or ""),
tostring(token_icon or ""),
tostring(it.cost or 0),
tokens_lbl
)
)
end
table.insert(out, "|}")
table.insert(out, "") -- Leerzeile
end
return table.concat(out, "\n")
end
local function render_exchange(ev, evkey)
local tok_icon = ev.token and ev.token.icon or ""
local tokens = section_label(evkey, "tokens")
local rows = {}
local function push_header()
table.insert(rows, '{| class="article-table" style="font-size: 14px;"')
table.insert(rows, "!Angebot\n!Preis\n!Limit")
end
local function push_row_offer(range, icon, title, price, limit)
table.insert(rows, "|-")
table.insert(rows, string.format("|%s: [[File:%s|30px]] 1 [[%s|%s]] || [[File:%s|25px]] %s %s || %s",
range, icon, title, title, tok_icon, tostring(price or 0), tokens, tostring(limit or "")
))
end
local function push_row_currency(icon, amount, title, price, limit)
table.insert(rows, "|-")
table.insert(rows, string.format("|[[File:%s|30px]] %s [[%s|%s]] || [[File:%s|25px]] %s %s || %s",
icon, tostring(amount or 0), title, title, tok_icon, tostring(price or 0), tokens, tostring(limit or "")
))
end
push_header()
for _,it in ipairs(ev.exchange.chests_by_level or {}) do
push_row_offer("Stufe "..it.range, it.icon, it.title, it.price, it.limit)
end
for _,it in ipairs(ev.exchange.chests_by_stars or {}) do
push_row_offer("Sterne "..it.range, it.icon, it.title, it.price, it.limit)
end
for _,it in ipairs(ev.exchange.chests_by_oracle or {}) do
push_row_offer("Orakel "..it.range, it.icon, it.title, it.price, it.limit)
end
for _,it in ipairs(ev.exchange.currencies or {}) do
push_row_currency(it.icon, it.amount, it.title, it.price, it.limit)
end
table.insert(rows, "|}")
-- Fußnote (optional)
local gp_req = deep_get(ev, {"exchange","notes","gp_req"})
if gp_req and gp_req~="" then
table.insert(rows, "*erfordert außerdem mehr als "..gp_req.." gesamte [[Gear Power|Ausrüstungsstärke]]")
end
return table.concat(rows, "\n")
end
local function render_offers(ev, kind, evkey)
local list = (kind=="start") and ((ev.shop or {}).initial) or ((ev.shop or {}).after)
if type(list)~="table" or #list==0 then return "" end
local tokens = section_label(evkey, "tokens")
if kind=="start" then
-- 3 Spalten wie im Original
local out = {
'{| class="article-table" style="font-size: 14px;"',
'! colspan="2" style="text-align:center" | '..(list[1].name or "")..'<br />[[File:'..(list[1].image or "")..'|x50px]]',
'! colspan="2" style="text-align:center" | '..(list[2].name or "")..'<br />[[File:'..(list[2].image or "")..'|x50px]]',
'! colspan="2" style="text-align:center" | '..(list[3].name or "")..'<br />[[File:'..(list[3].image or "")..'|x50px]]',
'|-',
'[[File:CurrencyPumpkin.png|30px]] || '..tostring(list[1].token or 0).." "..tokens,
'[[File:CurrencyPumpkin.png|30px]] || '..tostring(list[2].token or 0).." "..tokens,
'[[File:CurrencyPumpkin.png|30px]] || '..tostring(list[3].token or 0).." "..tokens,
'|-',
'Preis: || *',
'Preis: || *',
'Preis: || *',
'|}',
' * der Preis hängt von deiner Plattform ab',
}
return table.concat(out, "\n")
else
-- 4 Spalten wie im Original
local headers = {}
table.insert(headers, '{| class="article-table" style="font-size: 14px;"')
table.insert(headers, "|-")
for i=1,#list do
local it=list[i]
table.insert(headers, string.format('! colspan="2" style="text-align:center" | %s<br />[[File:%s|x50px]]', it.name or "", it.image or ""))
end
table.insert(headers, "|-")
local prices = {}
for i=1,#list do
local it=list[i]
table.insert(prices, string.format('[[File:CurrencyPumpkin.png|30px]] || %s %s', tostring(it.token or 0), tokens))
end
local foot = {}
for i=1,#list do table.insert(foot, "Preis: || *") end
table.insert(headers, table.concat(prices, "\n"))
table.insert(headers, "|-")
table.insert(headers, table.concat(foot, "\n"))
table.insert(headers, "|}")
table.insert(headers, " * der Preis hängt von deiner Plattform ab")
return table.concat(headers, "\n")
end
end
local function render_file(evkey, which, ev)
ev = ev or {}
local imgs = ev.images or {}
local it = imgs[which] or {}
local caption
if which=="deco" then caption = section_label(evkey, "deco_caption")
elseif which=="ex_shop" then caption = section_label(evkey, "hub")
elseif which=="fullname" then caption = section_label(evkey, "fullname")
end
return fileWikitext(it.file, {thumb=true, caption=caption})
end
-- ====== EVENT dispatcher ======
local function fn_event(args)
local key = args[2]; if isempty(key) then return "" end
local ev = resolve_event(key); if not ev then return "" end
local evkey = pick_key(events(), key) or key
local a3, a4 = args[3], args[4]
if isempty(a3) then return "" end
-- Überschriften
if a3=="h2" then
return heading(2, section_label(evkey, a4 or ""))
elseif a3=="h3" then
return heading(3, section_label(evkey, a4 or ""))
end
-- Bilder
if a3=="file" then
return render_file(evkey, a4 or "", ev)
end
-- Links
if a3=="link" and a4=="type" then
return type_link(ev.type)
end
-- zusammengesetzte Renderings
if a3=="history" then
return render_history(ev)
elseif a3=="avatars" then
return render_avatars_all(ev, evkey)
elseif a3=="exchange" then
return render_exchange(ev, evkey)
elseif a3=="offers" then
local kind = args[4] -- "start" | "more"
return render_offers(ev, kind, evkey)
end
-- einfache Felder/Labels
if a3=="banner" then
return tostring(ev.banner or "")
elseif a3=="type" then
return type_display(ev.type)
elseif a3=="month" or a3=="date" then
return tr_month(ev.month or ev.date or "")
elseif a3=="duration" then
return fmt_duration(ev.duration)
elseif a3=="unlocks_at" then
return fmt_unlocks(ev.unlocks_at)
elseif a3=="eventname" then
return tr_event_name(evkey)
end
-- Freiform-Schlüssel wie "character_level_10"
do
local maybe = fmt_unlocks(a3)
if maybe ~= a3 then return maybe end
end
-- reine Label-Getter aus Sections (currencies, shopname, ex_shop, fullname, shortname, name …)
local lbl = section_label(evkey, a3)
if not isempty(lbl) then return lbl end
-- Fallback auf tiefe Pfade: {{Firestone|Event|name|path|to|field}}
local path, i = {}, 3
while args[i] do table.insert(path, args[i]); i=i+1 end
local v = deep_get(ev, path)
return tostring(v or "")
end
-- ===== main =====
function M.main(frame)
local a = getArgs(frame)
-- numerische Parameter durchreichen
for i=1,12 do a[i] = a[i] and tostring(a[i]) or nil end
local dom = a[1] and norm(a[1]) or ""
if dom=="event" then
return fn_event(a)
else
return ""
end
end
return M