Modul:Firestone
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