Module:PadUtils: Difference between revisions
Appearance
GlifterPad (talk | contribs) No edit summary |
GlifterPad (talk | contribs) No edit summary |
||
| (17 intermediate revisions by the same user not shown) | |||
| Line 13: | Line 13: | ||
end | end | ||
-- | -- Accept any of: | ||
-- "Floor Name" | -- "Floor Name" | ||
-- "Floor_Target|Floor Label" | -- "Floor_Target|Floor Label" | ||
| Line 20: | Line 19: | ||
-- "[[Floor_Target|Floor Label]]" | -- "[[Floor_Target|Floor Label]]" | ||
local function parseItem(item) | local function parseItem(item) | ||
local inner = item:match("^%[%[(.*)%]%]$") | local inner = item:match("^%[%[(.*)%]%]$") | ||
if inner then item = inner end | if inner then item = inner end | ||
local t, l = item:match("^(.-)|(.*)$") | local t, l = item:match("^(.-)|(.*)$") | ||
local target = mw.text.trim(t or item) | local target = mw.text.trim(t or item) | ||
| Line 30: | Line 27: | ||
end | end | ||
-- | -- Always normalize to "base/floor" for the link target. | ||
-- If | -- If target already starts with "base/", strip that prefix first. | ||
local function normalizeTarget(base, target) | local function normalizeTarget(base, target) | ||
target = mw.text.trim(target) | target = mw.text.trim(target) | ||
local withSlash = base .. "/" | local withSlash = base .. "/" | ||
if target:sub(1, #withSlash) == withSlash then | if target:sub(1, #withSlash) == withSlash then | ||
target = target:sub(#withSlash + 1) | target = target:sub(#withSlash + 1) | ||
end | |||
return base .. "/" .. target | |||
end | |||
-- Module:PadUtils (append this function) | |||
local p = {} | |||
-- ... (keep your other helpers / functions above) | |||
-- Build a nested list of all dungeons and their floors from Cargo. | |||
-- Optional args: | |||
-- type : filter by Type (e.g., "Daily") | |||
-- ol : "1"/"true" to use <ol> for dungeons; floors are always <ul> | |||
-- order : "name" or "id" (defaults to name) | |||
-- Build a nested list of all dungeons and their floors from Cargo. | |||
-- Args (all optional): | |||
-- type : filter by Type (e.g., "Daily") | |||
-- ol : "1"/"true" -> numbered dungeons; floors stay bulleted | |||
-- order : "name" (default) or "id" -- top-level and floor order | |||
-- showcount : "1"/"true" -> append (N) after each dungeon name | |||
-- limit : max rows to fetch (default 5000) | |||
-- Renders: | |||
-- <a href="/wiki/Dungeon">Dungeon</a> | |||
-- <ul> | |||
-- <li><a href="/wiki/Dungeon/Floor">Floor</a></li> | |||
-- ... | |||
-- </ul> | |||
-- (repeats for each dungeon) | |||
-- Build a list of floor links under the current dungeon (or a given base). | |||
-- Usage from template/page: | |||
-- {{#invoke:PadUtils|dungeonFloors | base={{FULLPAGENAME}} | floors=Floor A;Floor B }} | |||
function p.dungeonFloors(frame) | |||
local args = frame.args | |||
local base = args.base and mw.text.trim(args.base) or mw.title.getCurrentTitle().fullText | |||
local floors = args.floors or "" | |||
local ordered = (args.ol == "1" or args.ol == "true") | |||
local before = args.before or "" | |||
local after = args.after or "" | |||
-- split on semicolons | |||
local list = {} | |||
for part in mw.text.gsplit(floors, ";", true) do | |||
part = mw.text.trim(part) | |||
if part ~= "" then | |||
-- allow [[Target|Label]] or Target|Label or plain Name | |||
local inner = part:match("^%[%[(.*)%]%]$") or part | |||
local t, l = inner:match("^(.-)|(.*)$") | |||
local target = mw.text.trim(t or inner) | |||
local label = mw.text.trim(l or inner) | |||
-- if target already starts with "base/", strip it; always link as base/target | |||
local withSlash = base .. "/" | |||
if target:sub(1, #withSlash) == withSlash then | |||
target = target:sub(#withSlash + 1) | |||
end | |||
local fullTarget = base .. "/" .. target | |||
local titleObj = mw.title.new(fullTarget) | |||
local linkTarget = titleObj and titleObj.prefixedText or fullTarget | |||
local bullet = ordered and "#" or "*" | |||
table.insert(list, string.format("%s [[%s|%s%s%s]]", bullet, linkTarget, before, label, after)) | |||
end | |||
end | |||
return frame:preprocess(table.concat(list, "\n")) | |||
end | |||
-- List all dungeon floors that drop a given item. | |||
-- Usage on the item page: | |||
local p = {} | |||
local cargo = mw.ext.cargo | |||
function p.dropLocations(frame) | |||
local item = frame.args.item | |||
if not item or item == '' then return '' end | |||
local results = cargo.query( | |||
'dungeon_floors', | |||
'Dungeon, Name', | |||
{ | |||
where = string.format('Drops HOLDS "%s"', item), | |||
orderBy = 'Dungeon, Name', | |||
limit = 5000 | |||
} | |||
) | |||
local out = {} | |||
for _, row in ipairs(results) do | |||
local dungeon = row.Dungeon or '' | |||
local name = row.Name or '' | |||
-- Strip any existing brackets in Dungeon (like [[Thursday Dungeon]]) | |||
dungeon = dungeon:gsub('%[%[', ''):gsub('%]%]', '') | |||
-- Link directly to Dungeon/Floor | |||
table.insert(out, string.format( | |||
'* [[%s/%s|%s]]', | |||
dungeon, name, name | |||
)) | |||
end | end | ||
-- | return table.concat(out, '\n') | ||
local | end | ||
return | |||
-- Group drop locations by Dungeon (with header, dungeon name plain text, floors linked) | |||
-- Usage: {{#invoke:PadUtils|dropLocationsGrouped|item=Dragon Seed}} | |||
function p.dropLocationsGrouped(frame) | |||
local item = mw.text.trim(frame.args.item or mw.title.getCurrentTitle().text or "") | |||
if item == "" then return "=== Dungeon Drop ===\nNone" end | |||
local function stripLinks(s) | |||
s = mw.text.trim(s or "") | |||
local inner = s:match("^%[%[(.*)%]%]$") | |||
return mw.text.trim(inner or s) | |||
end | |||
-- Cargo query: find all floors that drop the item | |||
local rows = mw.ext.cargo.query( | |||
'dungeon_floors', | |||
'Dungeon, Name', | |||
{ | |||
where = string.format('Drops HOLDS "%s"', item:gsub('"','""')), | |||
orderBy = 'Dungeon, Name', | |||
limit = 5000 | |||
} | |||
) | |||
-- If no results, just show header + "None" | |||
if not rows or #rows == 0 then | |||
return "=== Dungeon Drop ===\nNone" | |||
end | |||
-- Group results by dungeon | |||
local groups, order = {}, {} | |||
for _, r in ipairs(rows) do | |||
local rawDungeon = stripLinks(r.Dungeon) | |||
local t = mw.title.new(rawDungeon) | |||
local label = t and t.text or (rawDungeon:gsub("_", " ")) | |||
local key = label | |||
if not groups[key] then | |||
groups[key] = { label = label, floors = {} } | |||
table.insert(order, key) | |||
end | |||
table.insert(groups[key].floors, stripLinks(r.Name)) | |||
end | |||
-- Sort groups + floors alphabetically | |||
table.sort(order) | |||
for _, k in ipairs(order) do table.sort(groups[k].floors) end | |||
-- Build output: header → dungeon plain text → bullet list of floor links | |||
local out = { "=== Dungeon Drop ===" } | |||
for i, k in ipairs(order) do | |||
local g = groups[k] | |||
table.insert(out, g.label) | |||
for _, fname in ipairs(g.floors) do | |||
local target = g.label .. '/' .. fname | |||
table.insert(out, '* [[' .. target .. '|' .. fname .. ']]') | |||
end | |||
if i < #order then table.insert(out, '') end -- spacing between groups | |||
end | |||
return frame:preprocess(table.concat(out, '\n')) | |||
end | |||
-- Build a dungeon → floors nested list | |||
-- Usage: {{#invoke:PadUtils|dungeonIndex}} or {{#invoke:PadUtils|dungeonIndex|type=Daily}} | |||
function p.dungeonIndex(frame) | |||
local args = frame.args | |||
local where = nil | |||
if args.type and args.type ~= "" then | |||
where = string.format("Type = '%s'", tostring(args.type):gsub("'", "''")) | |||
end | |||
local rows = mw.ext.cargo.query( | |||
'dungeon_floors', | |||
'Dungeon, Name', | |||
{ where = where, orderBy = 'Dungeon, Name', limit = 5000 } | |||
) | |||
if not rows or #rows == 0 then | |||
return "''No results''" | |||
end | |||
-- helper: strip [[...]] and normalize underscores/spaces | |||
local function stripLinks(s) | |||
s = mw.text.trim(s or "") | |||
local inner = s:match("^%[%[(.*)%]%]$") | |||
return mw.text.trim(inner or s) | |||
end | |||
-- normalize dungeon (stable key + display) | |||
local function normDungeon(raw) | |||
local s = stripLinks(raw) | |||
local t = mw.title.new(s) | |||
if t then | |||
return t.prefixedText, t.prefixedText, t.text -- key, linkTarget, label | |||
else | |||
local label = s:gsub("_", " ") | |||
return label, s, label | |||
end | |||
end | |||
-- group by dungeon | |||
local groups, order = {}, {} | |||
for _, r in ipairs(rows) do | |||
local key, linkTarget, label = normDungeon(r.Dungeon) | |||
if not groups[key] then | |||
groups[key] = { link = linkTarget, label = label, floors = {} } | |||
table.insert(order, key) | |||
end | |||
table.insert(groups[key].floors, stripLinks(r.Name)) | |||
end | |||
table.sort(order, function(a,b) return groups[a].label < groups[b].label end) | |||
for _, k in ipairs(order) do table.sort(groups[k].floors) end | |||
-- build wikitext: clickable dungeon header + bullet list of clickable floors | |||
local out = {} | |||
for i, k in ipairs(order) do | |||
local g = groups[k] | |||
table.insert(out, string.format('[[%s|%s]]', g.link, g.label)) | |||
for _, fname in ipairs(g.floors) do | |||
local target = g.link .. '/' .. fname | |||
table.insert(out, '* [[' .. target .. '|' .. fname .. ']]') | |||
end | |||
if i < #order then table.insert(out, '') end | |||
end | |||
return frame:preprocess(table.concat(out, '\n')) | |||
end | end | ||
function p.dungeonFloors(frame) | function p.dungeonFloors(frame) | ||
local args = frame.args | local args = frame.args | ||
local base = args.base and mw.text.trim(args.base) or mw.title.getCurrentTitle().fullText | local base = args.base and mw.text.trim(args.base) or mw.title.getCurrentTitle().fullText | ||
local floors = split(args.floors or "", ";") | local floors = split(args.floors or "", ";") | ||
local | local ordered = (args.ol == "1" or args.ol == "true") | ||
local before = args.before or "" | local before = args.before or "" | ||
local after = args.after or "" | local after = args.after or "" | ||
| Line 66: | Line 277: | ||
if #floors == 0 then return "" end | if #floors == 0 then return "" end | ||
local | local lines = {} | ||
for _, raw in ipairs(floors) do | for _, raw in ipairs(floors) do | ||
local target, label = parseItem(raw) | local target, label = parseItem(raw) | ||
local titleText = normalizeTarget(base, target) | local titleText = normalizeTarget(base, target) | ||
local titleObj = mw.title.new(titleText) | |||
local | local linkTarget = titleObj and titleObj.prefixedText or titleText -- <-- fixed | ||
local bullet = ordered and "#" or "*" | |||
local | table.insert(lines, string.format("%s [[%s|%s%s%s]]", bullet, linkTarget, before, label, after)) | ||
table.insert( | |||
end | end | ||
return | return frame:preprocess(table.concat(lines, "\n")) | ||
end | end | ||
return p | return p | ||
Latest revision as of 21:02, 22 August 2025
Documentation for this module may be created at Module:PadUtils/doc
-- Module:PadUtils
local p = {}
-- split "A;B;C" safely (ignores empty pieces)
local function split(text, delim)
local out = {}
if not text or text == "" then return out end
for part in mw.text.gsplit(text, delim, true) do
part = mw.text.trim(part)
if part ~= "" then table.insert(out, part) end
end
return out
end
-- Accept any of:
-- "Floor Name"
-- "Floor_Target|Floor Label"
-- "[[Dungeon/Floor_Target|Floor Label]]"
-- "[[Floor_Target|Floor Label]]"
local function parseItem(item)
local inner = item:match("^%[%[(.*)%]%]$")
if inner then item = inner end
local t, l = item:match("^(.-)|(.*)$")
local target = mw.text.trim(t or item)
local label = mw.text.trim(l or item)
return target, label
end
-- Always normalize to "base/floor" for the link target.
-- If target already starts with "base/", strip that prefix first.
local function normalizeTarget(base, target)
target = mw.text.trim(target)
local withSlash = base .. "/"
if target:sub(1, #withSlash) == withSlash then
target = target:sub(#withSlash + 1)
end
return base .. "/" .. target
end
-- Module:PadUtils (append this function)
local p = {}
-- ... (keep your other helpers / functions above)
-- Build a nested list of all dungeons and their floors from Cargo.
-- Optional args:
-- type : filter by Type (e.g., "Daily")
-- ol : "1"/"true" to use <ol> for dungeons; floors are always <ul>
-- order : "name" or "id" (defaults to name)
-- Build a nested list of all dungeons and their floors from Cargo.
-- Args (all optional):
-- type : filter by Type (e.g., "Daily")
-- ol : "1"/"true" -> numbered dungeons; floors stay bulleted
-- order : "name" (default) or "id" -- top-level and floor order
-- showcount : "1"/"true" -> append (N) after each dungeon name
-- limit : max rows to fetch (default 5000)
-- Renders:
-- <a href="/wiki/Dungeon">Dungeon</a>
-- <ul>
-- <li><a href="/wiki/Dungeon/Floor">Floor</a></li>
-- ...
-- </ul>
-- (repeats for each dungeon)
-- Build a list of floor links under the current dungeon (or a given base).
-- Usage from template/page:
-- {{#invoke:PadUtils|dungeonFloors | base={{FULLPAGENAME}} | floors=Floor A;Floor B }}
function p.dungeonFloors(frame)
local args = frame.args
local base = args.base and mw.text.trim(args.base) or mw.title.getCurrentTitle().fullText
local floors = args.floors or ""
local ordered = (args.ol == "1" or args.ol == "true")
local before = args.before or ""
local after = args.after or ""
-- split on semicolons
local list = {}
for part in mw.text.gsplit(floors, ";", true) do
part = mw.text.trim(part)
if part ~= "" then
-- allow [[Target|Label]] or Target|Label or plain Name
local inner = part:match("^%[%[(.*)%]%]$") or part
local t, l = inner:match("^(.-)|(.*)$")
local target = mw.text.trim(t or inner)
local label = mw.text.trim(l or inner)
-- if target already starts with "base/", strip it; always link as base/target
local withSlash = base .. "/"
if target:sub(1, #withSlash) == withSlash then
target = target:sub(#withSlash + 1)
end
local fullTarget = base .. "/" .. target
local titleObj = mw.title.new(fullTarget)
local linkTarget = titleObj and titleObj.prefixedText or fullTarget
local bullet = ordered and "#" or "*"
table.insert(list, string.format("%s [[%s|%s%s%s]]", bullet, linkTarget, before, label, after))
end
end
return frame:preprocess(table.concat(list, "\n"))
end
-- List all dungeon floors that drop a given item.
-- Usage on the item page:
local p = {}
local cargo = mw.ext.cargo
function p.dropLocations(frame)
local item = frame.args.item
if not item or item == '' then return '' end
local results = cargo.query(
'dungeon_floors',
'Dungeon, Name',
{
where = string.format('Drops HOLDS "%s"', item),
orderBy = 'Dungeon, Name',
limit = 5000
}
)
local out = {}
for _, row in ipairs(results) do
local dungeon = row.Dungeon or ''
local name = row.Name or ''
-- Strip any existing brackets in Dungeon (like [[Thursday Dungeon]])
dungeon = dungeon:gsub('%[%[', ''):gsub('%]%]', '')
-- Link directly to Dungeon/Floor
table.insert(out, string.format(
'* [[%s/%s|%s]]',
dungeon, name, name
))
end
return table.concat(out, '\n')
end
-- Group drop locations by Dungeon (with header, dungeon name plain text, floors linked)
-- Usage: {{#invoke:PadUtils|dropLocationsGrouped|item=Dragon Seed}}
function p.dropLocationsGrouped(frame)
local item = mw.text.trim(frame.args.item or mw.title.getCurrentTitle().text or "")
if item == "" then return "=== Dungeon Drop ===\nNone" end
local function stripLinks(s)
s = mw.text.trim(s or "")
local inner = s:match("^%[%[(.*)%]%]$")
return mw.text.trim(inner or s)
end
-- Cargo query: find all floors that drop the item
local rows = mw.ext.cargo.query(
'dungeon_floors',
'Dungeon, Name',
{
where = string.format('Drops HOLDS "%s"', item:gsub('"','""')),
orderBy = 'Dungeon, Name',
limit = 5000
}
)
-- If no results, just show header + "None"
if not rows or #rows == 0 then
return "=== Dungeon Drop ===\nNone"
end
-- Group results by dungeon
local groups, order = {}, {}
for _, r in ipairs(rows) do
local rawDungeon = stripLinks(r.Dungeon)
local t = mw.title.new(rawDungeon)
local label = t and t.text or (rawDungeon:gsub("_", " "))
local key = label
if not groups[key] then
groups[key] = { label = label, floors = {} }
table.insert(order, key)
end
table.insert(groups[key].floors, stripLinks(r.Name))
end
-- Sort groups + floors alphabetically
table.sort(order)
for _, k in ipairs(order) do table.sort(groups[k].floors) end
-- Build output: header → dungeon plain text → bullet list of floor links
local out = { "=== Dungeon Drop ===" }
for i, k in ipairs(order) do
local g = groups[k]
table.insert(out, g.label)
for _, fname in ipairs(g.floors) do
local target = g.label .. '/' .. fname
table.insert(out, '* [[' .. target .. '|' .. fname .. ']]')
end
if i < #order then table.insert(out, '') end -- spacing between groups
end
return frame:preprocess(table.concat(out, '\n'))
end
-- Build a dungeon → floors nested list
-- Usage: {{#invoke:PadUtils|dungeonIndex}} or {{#invoke:PadUtils|dungeonIndex|type=Daily}}
function p.dungeonIndex(frame)
local args = frame.args
local where = nil
if args.type and args.type ~= "" then
where = string.format("Type = '%s'", tostring(args.type):gsub("'", "''"))
end
local rows = mw.ext.cargo.query(
'dungeon_floors',
'Dungeon, Name',
{ where = where, orderBy = 'Dungeon, Name', limit = 5000 }
)
if not rows or #rows == 0 then
return "''No results''"
end
-- helper: strip [[...]] and normalize underscores/spaces
local function stripLinks(s)
s = mw.text.trim(s or "")
local inner = s:match("^%[%[(.*)%]%]$")
return mw.text.trim(inner or s)
end
-- normalize dungeon (stable key + display)
local function normDungeon(raw)
local s = stripLinks(raw)
local t = mw.title.new(s)
if t then
return t.prefixedText, t.prefixedText, t.text -- key, linkTarget, label
else
local label = s:gsub("_", " ")
return label, s, label
end
end
-- group by dungeon
local groups, order = {}, {}
for _, r in ipairs(rows) do
local key, linkTarget, label = normDungeon(r.Dungeon)
if not groups[key] then
groups[key] = { link = linkTarget, label = label, floors = {} }
table.insert(order, key)
end
table.insert(groups[key].floors, stripLinks(r.Name))
end
table.sort(order, function(a,b) return groups[a].label < groups[b].label end)
for _, k in ipairs(order) do table.sort(groups[k].floors) end
-- build wikitext: clickable dungeon header + bullet list of clickable floors
local out = {}
for i, k in ipairs(order) do
local g = groups[k]
table.insert(out, string.format('[[%s|%s]]', g.link, g.label))
for _, fname in ipairs(g.floors) do
local target = g.link .. '/' .. fname
table.insert(out, '* [[' .. target .. '|' .. fname .. ']]')
end
if i < #order then table.insert(out, '') end
end
return frame:preprocess(table.concat(out, '\n'))
end
function p.dungeonFloors(frame)
local args = frame.args
local base = args.base and mw.text.trim(args.base) or mw.title.getCurrentTitle().fullText
local floors = split(args.floors or "", ";")
local ordered = (args.ol == "1" or args.ol == "true")
local before = args.before or ""
local after = args.after or ""
if #floors == 0 then return "" end
local lines = {}
for _, raw in ipairs(floors) do
local target, label = parseItem(raw)
local titleText = normalizeTarget(base, target)
local titleObj = mw.title.new(titleText)
local linkTarget = titleObj and titleObj.prefixedText or titleText -- <-- fixed
local bullet = ordered and "#" or "*"
table.insert(lines, string.format("%s [[%s|%s%s%s]]", bullet, linkTarget, before, label, after))
end
return frame:preprocess(table.concat(lines, "\n"))
end
return p