Jump to content

Module:PadUtils: Difference between revisions

From PAD Wiki
No edit summary
No edit summary
 
(4 intermediate revisions by the same user not shown)
Line 36: Line 36:
     end
     end
     return base .. "/" .. target
     return base .. "/" .. target
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
end


Line 87: Line 62:
-- </ul>
-- </ul>
-- (repeats for each dungeon)
-- (repeats for each dungeon)
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
    -- strip [[...]] helper
    local function stripLinks(s)
        s = mw.text.trim(s or "")
        local inner = s:match("^%[%[(.*)%]%]$")
        return mw.text.trim(inner or s)
    end
    -- normalize a dungeon to a stable key + link target + display
    local function normalizeDungeon(raw)
        local s = stripLinks(raw)
        -- try to build a proper Title; this normalizes spaces/underscores
        local t = mw.title.new(s)
        if t then
            return t.prefixedText, t.prefixedText, t.text -- key, link target, display text
        else
            -- fallback: treat underscores/spaces as equivalent
            local display = (s:gsub("_", " "))
            return display, s, display
        end
    end
    -- group floors by normalized dungeon key
    local groups, order = {}, {}
    for _, r in ipairs(rows) do
        local key, linkTarget, display = normalizeDungeon(r.Dungeon)
        if not groups[key] then
            groups[key] = { link = linkTarget, label = display, 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)
    -- sort floors alphabetically within each dungeon
    for _, k in ipairs(order) do table.sort(groups[k].floors) end
    -- build wikitext: dungeon as link on its own line, floors as bullets
    local out = {}
    for _, 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
        table.insert(out, "") -- blank line between dungeons
    end
    return frame:preprocess(table.concat(out, "\n"))
end


-- Build a list of floor links under the current dungeon (or a given base).
-- Build a list of floor links under the current dungeon (or a given base).
Line 195: Line 104:
-- List all dungeon floors that drop a given item.
-- List all dungeon floors that drop a given item.
-- Usage on the item page:
-- Usage on the item page:
--  {{#invoke:PadUtils|dropLocations|item=Dragon Seed}}
local p = {}
local cargo = mw.ext.cargo
 
function p.dropLocations(frame)
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 "")
     local item = mw.text.trim(frame.args.item or mw.title.getCurrentTitle().text or "")
     if item == "" then return "None" end
     if item == "" then return "=== Dungeon Drop ===\nNone" end


     local function stripLinks(s)
     local function stripLinks(s)
Line 206: Line 151:
     end
     end


     -- Cargo expects HOLDS "value" (double quotes) for List fields.
     -- Cargo query: find all floors that drop the item
     -- We also include a LIKE fallback in case any rows were stored oddly.
     local rows = mw.ext.cargo.query(
    local itemEsc = item:gsub('"', '""')  -- escape double quotes
        'dungeon_floors',
    local where = string.format(
        'Dungeon, Name',
        '(Drops HOLDS "%s") OR (Drops LIKE "%%%s%%")',
        {
        itemEsc, item:gsub('%%', '%%%%'):gsub('"','""')
            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(
     local rows = mw.ext.cargo.query(
Line 219: Line 215:
         { where = where, orderBy = 'Dungeon, Name', limit = 5000 }
         { where = where, orderBy = 'Dungeon, Name', limit = 5000 }
     )
     )
    if not rows or #rows == 0 then
        return "''No results''"
    end


     if not rows or #rows == 0 then
     -- helper: strip [[...]] and normalize underscores/spaces
         return "None"
    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
     end


     local out = {}
    -- group by dungeon
     local groups, order = {}, {}
     for _, r in ipairs(rows) do
     for _, r in ipairs(rows) do
         local dungeon = stripLinks(r.Dungeon)
         local key, linkTarget, label = normDungeon(r.Dungeon)
         local floor  = stripLinks(r.Name)
         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


        -- Normalize to a proper internal title for linking
    table.sort(order, function(a,b) return groups[a].label < groups[b].label end)
        local dungeonTitle = mw.title.new(dungeon)
    for _, k in ipairs(order) do table.sort(groups[k].floors) end
        local dungeonLink  = (dungeonTitle and dungeonTitle.prefixedText) or dungeon


         local targetTitle  = mw.title.new(dungeonLink .. '/' .. floor)
    -- build wikitext: clickable dungeon header + bullet list of clickable floors
         local linkTarget  = (targetTitle and targetTitle.prefixedText) or (dungeonLink .. '/' .. floor)
    local out = {}
 
    for i, k in ipairs(order) do
        table.insert(out, '* [[' .. linkTarget .. '|' .. floor .. ']]')
         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
     end


Line 242: Line 267:
end
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
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