Jump to content

Module:ItemCategoryTable: Difference between revisions

From Apogea Wiki
Dane (talk | contribs)
Fix: use container field for slots (via update-page on MediaWiki MCP Server)
Dane (talk | contribs)
No edit summary
 
(6 intermediate revisions by the same user not shown)
Line 1: Line 1:
local p = {}
local p = {}
local ItemConfig = require('Module:ItemConfig')


-- Parent categories and their child types
-- Query items by type from Cargo (type is a List field, requires HOLDS)
local parentCategories = {
    ["Accessories"] = {"Ring", "Necklace"},
    ["Weapons"] = {"Sword", "Large Sword", "Dagger", "Knife", "Axe", "Large Axe", "Bow", "Staff", "Orb"},
    ["Shields"] = {"Small Shield", "Large Shield"},
    ["Armor"] = {"Heavy Chest", "Heavy Legs", "Heavy Helmet", "Heavy Boots", "Light Chest", "Light Legs", "Light Helmet", "Light Boots", "Light Mask", "Light Neck"},
    ["Heavy Armor"] = {"Heavy Chest", "Heavy Legs", "Heavy Helmet", "Heavy Boots"},
    ["Light Armor"] = {"Light Chest", "Light Legs", "Light Helmet", "Light Boots", "Light Mask", "Light Neck"},
    ["Food"] = {"Cooked Food", "Edible Food", "Raw Food", "Special Food"},
    ["Consumables"] = {"Potions", "Drinks", "Cooked Food", "Edible Food", "Raw Food", "Special Food"},
    ["Materials"] = {"Book Products", "Cloth Products", "Cooking Items", "Desert Products", "Forge Products", "Grave Products", "Holy Products", "Monster Products", "North Products", "Plains Products", "Swamp Products", "Flowers"},
    ["Regional Products"] = {"Desert Products", "North Products", "Plains Products", "Swamp Products"},
    ["Ammunition"] = {"Arrows", "Conjured Arrows"},
    ["Equipment"] = {"Ring", "Necklace", "Gloves", "Sword", "Large Sword", "Dagger", "Knife", "Axe", "Large Axe", "Bow", "Staff", "Orb", "Small Shield", "Large Shield", "Heavy Chest", "Heavy Legs", "Heavy Helmet", "Heavy Boots", "Light Chest", "Light Legs", "Light Helmet", "Light Boots", "Light Mask", "Light Neck", "Container", "Tools"},
}
 
-- Query items by type from Cargo
local function getItemsByType(itemType)
local function getItemsByType(itemType)
     local tables = 'Items'
     local tables = 'Items'
     local fields = 'name,sprite,type,weight,damage,armor,defense,health,mana,ability,magic,rng,move_speed,attack_speed,health_regen,mana_regen,container,size,rarity'
     local fields = ItemConfig.cargoFields.itemsTable
     local args = {
     local args = {
         where = 'type LIKE "%' .. itemType .. '%"',
         where = 'type HOLDS "' .. itemType .. '"',
         orderBy = 'name',
         orderBy = 'name',
         limit = 500
         limit = 500
     }
     }
   
 
     return mw.ext.cargo.query(tables, fields, args)
     return mw.ext.cargo.query(tables, fields, args)
end
end
Line 33: Line 18:
local function getItemsByTypes(types)
local function getItemsByTypes(types)
     local tables = 'Items'
     local tables = 'Items'
     local fields = 'name,sprite,type,weight,damage,armor,defense,health,mana,ability,magic,rng,move_speed,attack_speed,health_regen,mana_regen,container,size,rarity'
     local fields = ItemConfig.cargoFields.itemsTable
   
 
     -- Build OR conditions for each type
     -- Build OR conditions for each type (using HOLDS for List field)
     local conditions = {}
     local conditions = {}
     for _, t in ipairs(types) do
     for _, t in ipairs(types) do
         table.insert(conditions, 'type LIKE "%' .. t .. '%"')
         table.insert(conditions, 'type HOLDS "' .. t .. '"')
     end
     end
   
 
     local args = {
     local args = {
         where = table.concat(conditions, ' OR '),
         where = table.concat(conditions, ' OR '),
Line 46: Line 31:
         limit = 500
         limit = 500
     }
     }
   
 
     return mw.ext.cargo.query(tables, fields, args)
     return mw.ext.cargo.query(tables, fields, args)
end
end


-- Get the singular type name from a plural category name
-- Query all items
local function singularize(plural)
local function getAllItems()
     local mapping = {
     local tables = 'Items'
        ["Rings"] = "Ring",
    local fields = ItemConfig.cargoFields.itemsTable
        ["Necklaces"] = "Necklace",
    local args = {
        ["Staves"] = "Staff",
         orderBy = 'name',
        ["Knives"] = "Knife",
         limit = 500
        ["Axes"] = "Axe",
        ["Large Axes"] = "Large Axe",
        ["Swords"] = "Sword",
        ["Large Swords"] = "Large Sword",
        ["Regular Swords"] = "Regular Sword",
        ["Daggers"] = "Dagger",
        ["Bows"] = "Bow",
        ["Orbs"] = "Orb",
        ["Small Shields"] = "Small Shield",
        ["Large Shields"] = "Large Shield",
        ["Gloves"] = "Gloves",
        ["Containers"] = "Container",
        ["Tools"] = "Tools",
        ["Heavy Helmets"] = "Heavy Helmet",
        ["Light Helmets"] = "Light Helmet",
        ["Light Masks"] = "Light Mask",
        ["Heavy Boots"] = "Heavy Boots",
        ["Light Boots"] = "Light Boots",
        ["Heavy Legs"] = "Heavy Legs",
        ["Light Legs"] = "Light Legs",
        ["Heavy Chest Armor"] = "Heavy Chest",
        ["Light Chest Armor"] = "Light Chest",
        ["Light Neck Armor"] = "Light Neck",
        ["Potions"] = "Potions",
        ["Drinks"] = "Drinks",
        ["Arrows"] = "Arrows",
        ["Conjured Arrows"] = "Conjured Arrows",
        ["Books"] = "Book",
        ["Cooked Food"] = "Cooked Food",
        ["Edible Food"] = "Edible Food",
        ["Raw Food"] = "Raw Food",
        ["Special Food"] = "Special Food",
        ["Light Sources"] = "Light Sources",
        ["Flowers"] = "Flowers",
        ["Currency"] = "Currency",
        ["Book Products"] = "Book Products",
        ["Cloth Products"] = "Cloth Products",
        ["Cooking Items"] = "Cooking Items",
        ["Desert Products"] = "Desert Products",
        ["Forge Products"] = "Forge Products",
         ["Grave Products"] = "Grave Products",
        ["Holy Products"] = "Holy Products",
        ["Monster Products"] = "Monster Products",
        ["North Products"] = "North Products",
        ["Plains Products"] = "Plains Products",
         ["Swamp Products"] = "Swamp Products",
     }
     }
    return mapping[plural] or plural:gsub("s$", "")
end


-- Get the plural/category name from a type
    return mw.ext.cargo.query(tables, fields, args)
local function pluralize(singular)
    local mapping = {
        ["Ring"] = "Rings",
        ["Necklace"] = "Necklaces",
        ["Staff"] = "Staves",
        ["Knife"] = "Knives",
        ["Axe"] = "Axes",
        ["Large Axe"] = "Large Axes",
        ["Sword"] = "Swords",
        ["Large Sword"] = "Large Swords",
        ["Dagger"] = "Daggers",
        ["Bow"] = "Bows",
        ["Orb"] = "Orbs",
        ["Small Shield"] = "Small Shields",
        ["Large Shield"] = "Large Shields",
        ["Gloves"] = "Gloves",
        ["Container"] = "Containers",
        ["Tools"] = "Tools",
        ["Heavy Helmet"] = "Heavy Helmets",
        ["Light Helmet"] = "Light Helmets",
        ["Light Mask"] = "Light Masks",
        ["Heavy Boots"] = "Heavy Boots",
        ["Light Boots"] = "Light Boots",
        ["Heavy Legs"] = "Heavy Legs",
        ["Light Legs"] = "Light Legs",
        ["Heavy Chest"] = "Heavy Chest Armor",
        ["Light Chest"] = "Light Chest Armor",
        ["Light Neck"] = "Light Neck Armor",
        ["Arrows"] = "Arrows",
        ["Conjured Arrows"] = "Conjured Arrows",
        ["Potions"] = "Potions",
        ["Drinks"] = "Drinks",
        ["Cooked Food"] = "Cooked Food",
        ["Edible Food"] = "Edible Food",
        ["Raw Food"] = "Raw Food",
        ["Special Food"] = "Special Food",
        ["Book Products"] = "Book Products",
        ["Cloth Products"] = "Cloth Products",
        ["Cooking Items"] = "Cooking Items",
        ["Desert Products"] = "Desert Products",
        ["Forge Products"] = "Forge Products",
        ["Grave Products"] = "Grave Products",
        ["Holy Products"] = "Holy Products",
        ["Monster Products"] = "Monster Products",
        ["North Products"] = "North Products",
        ["Plains Products"] = "Plains Products",
        ["Swamp Products"] = "Swamp Products",
        ["Flowers"] = "Flowers",
        ["Book"] = "Books",
        ["Light Sources"] = "Light Sources",
        ["Currency"] = "Currency",
    }
    return mapping[singular] or singular
end
end


Line 186: Line 70:
           statNum(item.attack_speed)
           statNum(item.attack_speed)
end
end
-- Sort column mapping
local sortColumns = {
    name = function(item) return item.name or item._pageName or "" end,
    type = function(item) return item.type or "" end,
    slots = function(item) return statNum(item.container) end,
    container = function(item) return statNum(item.container) end,
    weight = function(item) return statNum(item.weight) end,
    damage = function(item) return statNum(item.damage) end,
    armor = function(item) return statNum(item.armor) end,
    defense = function(item) return statNum(item.defense) end,
    health = function(item) return statNum(item.health) end,
    mana = function(item) return statNum(item.mana) end,
    ability = function(item) return statNum(item.ability) end,
    magic = function(item) return statNum(item.magic) end,
    rng = function(item) return statNum(item.rng) end,
    move_speed = function(item) return statNum(item.move_speed) end,
    attack_speed = function(item) return statNum(item.attack_speed) end,
    power = function(item) return item._power end,
    total = function(item) return item._power end
}


function p.render(frame)
function p.render(frame)
     local args = frame:getParent().args
     local args = frame.args
     local categoryName = args[1] or args.category or mw.title.getCurrentTitle().text:gsub("^Category:", "")
     local categoryName = args[1] or args.category or mw.title.getCurrentTitle().text:gsub("^Category:", "")
      
     local sortBy = args.sort or args.sortby
     -- Check if this is a parent category
    local sortDir = args.dir or args.sortdir or "desc"
     local isParent = parentCategories[categoryName] ~= nil
 
     -- Check if this is a parent category or all items
    local isAllItems = (categoryName == "Items")
     local isParent = ItemConfig.isParentCategory(categoryName) or isAllItems
     local isContainers = (categoryName == "Containers")
     local isContainers = (categoryName == "Containers")
     local items
     local items
      
 
     if isParent then
     if isAllItems then
         items = getItemsByTypes(parentCategories[categoryName])
        items = getAllItems()
     elseif ItemConfig.isParentCategory(categoryName) then
         items = getItemsByTypes(ItemConfig.getChildTypes(categoryName))
     else
     else
         local itemType = singularize(categoryName)
         local itemType = ItemConfig.singularize(categoryName)
         items = getItemsByType(itemType)
         items = getItemsByType(itemType)
     end
     end
   
 
     if not items or #items == 0 then
     if not items or #items == 0 then
         return '<p class="mw-empty">No items found.</p>'
         return '<p class="mw-empty">No items found.</p>'
     end
     end
   
 
     -- Calculate power for each item and sort
     -- Calculate power for each item
     for _, item in ipairs(items) do
     for _, item in ipairs(items) do
         item._power = calcPower(item)
         item._power = calcPower(item)
     end
     end
   
 
     -- Sort by slots for containers, otherwise by power
     -- Determine sort column
     if isContainers then
    local sortFunc = nil
         table.sort(items, function(a, b) return statNum(a.container) > statNum(b.container) end)
     if sortBy and sortColumns[sortBy] then
        sortFunc = sortColumns[sortBy]
    elseif isContainers then
         sortFunc = sortColumns.slots
     else
     else
         table.sort(items, function(a, b) return a._power > b._power end)
         sortFunc = sortColumns.power
     end
     end
      
 
     -- Sort items
    local ascending = (sortDir == "asc")
    table.sort(items, function(a, b)
        local valA = sortFunc(a)
        local valB = sortFunc(b)
        if ascending then
            return valA < valB
        else
            return valA > valB
        end
    end)
 
     -- Build sortable table
     -- Build sortable table
     local tbl = mw.html.create('table')
     local tbl = mw.html.create('table')
Line 224: Line 149:
       :addClass('sortable')
       :addClass('sortable')
       :css('width', '100%')
       :css('width', '100%')
   
 
     -- Header row
     -- Header row
     local header = tbl:tag('tr')
     local header = tbl:tag('tr')
Line 250: Line 175:
     header:tag('th'):attr('title', 'Attack Speed'):wikitext('AS')
     header:tag('th'):attr('title', 'Attack Speed'):wikitext('AS')
     header:tag('th'):attr('title', 'Total Stats'):wikitext('Total')
     header:tag('th'):attr('title', 'Total Stats'):wikitext('Total')
   
 
     -- Data rows
     -- Data rows
     for _, item in ipairs(items) do
     for _, item in ipairs(items) do
         local row = tbl:tag('tr')
         local row = tbl:tag('tr')
       
 
         -- Icon column
         -- Icon column
         local iconCell = row:tag('td')
         local iconCell = row:tag('td')
             :css('width', '64px')
             :css('width', '64px')
             :css('text-align', 'center')
             :css('text-align', 'center')
         local sprite = item.sprite or item.name
         local sprite = item.sprite or item.name or "Unknown"
         iconCell:wikitext(frame:preprocess('{{Sprite|' .. sprite .. '|x2}}'))
         if sprite and sprite ~= "" then
          
            iconCell:wikitext(frame:preprocess('{{Sprite|' .. sprite .. '|x2}}'))
         end
 
         -- Item name column
         -- Item name column
         row:tag('td'):wikitext('[[' .. item.name .. ']]')
        local itemName = item.name or "Unknown"
       
         row:tag('td'):wikitext('[[' .. itemName .. ']]')
 
         -- Type column (for parent categories)
         -- Type column (for parent categories)
         if isParent then
         if isParent then
             local typeCategory = pluralize(item.type or "")
             local typeCategory = ItemConfig.pluralize(item.type or "")
             row:tag('td'):wikitext('[[:Category:' .. typeCategory .. '|' .. (item.type or "") .. ']]')
             row:tag('td'):wikitext('[[:Category:' .. typeCategory .. '|' .. (item.type or "") .. ']]')
         end
         end
       
 
         -- Slots column (for containers)
         -- Slots column (for containers)
         if isContainers then
         if isContainers then
             row:tag('td'):css('text-align', 'right'):wikitext(stat(item.container))
             row:tag('td'):css('text-align', 'right'):wikitext(stat(item.container))
         end
         end
       
 
         -- Stats
         -- Stats
         row:tag('td'):css('text-align', 'right'):wikitext(stat(item.weight))
         row:tag('td'):css('text-align', 'right'):wikitext(stat(item.weight))
Line 290: Line 218:
         row:tag('td'):css('text-align', 'right'):wikitext(item._power)
         row:tag('td'):css('text-align', 'right'):wikitext(item._power)
     end
     end
   
 
     return tostring(tbl)
     return tostring(tbl)
end
end


return p
return p

Latest revision as of 21:15, 1 February 2026

Documentation for this module may be created at Module:ItemCategoryTable/doc

local p = {}
local ItemConfig = require('Module:ItemConfig')

-- Query items by type from Cargo (type is a List field, requires HOLDS)
local function getItemsByType(itemType)
    local tables = 'Items'
    local fields = ItemConfig.cargoFields.itemsTable
    local args = {
        where = 'type HOLDS "' .. itemType .. '"',
        orderBy = 'name',
        limit = 500
    }

    return mw.ext.cargo.query(tables, fields, args)
end

-- Query items by multiple types
local function getItemsByTypes(types)
    local tables = 'Items'
    local fields = ItemConfig.cargoFields.itemsTable

    -- Build OR conditions for each type (using HOLDS for List field)
    local conditions = {}
    for _, t in ipairs(types) do
        table.insert(conditions, 'type HOLDS "' .. t .. '"')
    end

    local args = {
        where = table.concat(conditions, ' OR '),
        orderBy = 'name',
        limit = 500
    }

    return mw.ext.cargo.query(tables, fields, args)
end

-- Query all items
local function getAllItems()
    local tables = 'Items'
    local fields = ItemConfig.cargoFields.itemsTable
    local args = {
        orderBy = 'name',
        limit = 500
    }

    return mw.ext.cargo.query(tables, fields, args)
end

-- Get numeric value from stat (0 if empty)
local function statNum(value)
    if value and value ~= "" then
        return tonumber(value) or 0
    end
    return 0
end

-- Format stat value (return 0 if empty)
local function stat(value)
    if value and value ~= "" then
        return value
    end
    return "0"
end

-- Calculate total stat power for an item
local function calcPower(item)
    return statNum(item.damage) + statNum(item.armor) + statNum(item.defense) +
           statNum(item.health) + statNum(item.mana) + statNum(item.ability) +
           statNum(item.magic) + statNum(item.rng) + statNum(item.move_speed) +
           statNum(item.attack_speed)
end

-- Sort column mapping
local sortColumns = {
    name = function(item) return item.name or item._pageName or "" end,
    type = function(item) return item.type or "" end,
    slots = function(item) return statNum(item.container) end,
    container = function(item) return statNum(item.container) end,
    weight = function(item) return statNum(item.weight) end,
    damage = function(item) return statNum(item.damage) end,
    armor = function(item) return statNum(item.armor) end,
    defense = function(item) return statNum(item.defense) end,
    health = function(item) return statNum(item.health) end,
    mana = function(item) return statNum(item.mana) end,
    ability = function(item) return statNum(item.ability) end,
    magic = function(item) return statNum(item.magic) end,
    rng = function(item) return statNum(item.rng) end,
    move_speed = function(item) return statNum(item.move_speed) end,
    attack_speed = function(item) return statNum(item.attack_speed) end,
    power = function(item) return item._power end,
    total = function(item) return item._power end
}

function p.render(frame)
    local args = frame.args
    local categoryName = args[1] or args.category or mw.title.getCurrentTitle().text:gsub("^Category:", "")
    local sortBy = args.sort or args.sortby
    local sortDir = args.dir or args.sortdir or "desc"

    -- Check if this is a parent category or all items
    local isAllItems = (categoryName == "Items")
    local isParent = ItemConfig.isParentCategory(categoryName) or isAllItems
    local isContainers = (categoryName == "Containers")
    local items

    if isAllItems then
        items = getAllItems()
    elseif ItemConfig.isParentCategory(categoryName) then
        items = getItemsByTypes(ItemConfig.getChildTypes(categoryName))
    else
        local itemType = ItemConfig.singularize(categoryName)
        items = getItemsByType(itemType)
    end

    if not items or #items == 0 then
        return '<p class="mw-empty">No items found.</p>'
    end

    -- Calculate power for each item
    for _, item in ipairs(items) do
        item._power = calcPower(item)
    end

    -- Determine sort column
    local sortFunc = nil
    if sortBy and sortColumns[sortBy] then
        sortFunc = sortColumns[sortBy]
    elseif isContainers then
        sortFunc = sortColumns.slots
    else
        sortFunc = sortColumns.power
    end

    -- Sort items
    local ascending = (sortDir == "asc")
    table.sort(items, function(a, b)
        local valA = sortFunc(a)
        local valB = sortFunc(b)
        if ascending then
            return valA < valB
        else
            return valA > valB
        end
    end)

    -- Build sortable table
    local tbl = mw.html.create('table')
    tbl:addClass('wikitable')
       :addClass('sortable')
       :css('width', '100%')

    -- Header row
    local header = tbl:tag('tr')
    header:tag('th')
        :css('width', '64px')
        :addClass('unsortable')
        :wikitext('')
    header:tag('th'):wikitext('Item')
    if isParent then
        header:tag('th'):wikitext('Type')
    end
    if isContainers then
        header:tag('th'):attr('title', 'Container Slots'):wikitext('Slots')
    end
    header:tag('th'):attr('title', 'Weight'):wikitext('WGT')
    header:tag('th'):attr('title', 'Damage'):wikitext('DMG')
    header:tag('th'):attr('title', 'Armor'):wikitext('ARM')
    header:tag('th'):attr('title', 'Defense'):wikitext('DEF')
    header:tag('th'):attr('title', 'Health'):wikitext('HP')
    header:tag('th'):attr('title', 'Mana'):wikitext('MP')
    header:tag('th'):attr('title', 'Ability'):wikitext('ABL')
    header:tag('th'):attr('title', 'Magic'):wikitext('MAG')
    header:tag('th'):attr('title', 'Range'):wikitext('RNG')
    header:tag('th'):attr('title', 'Move Speed'):wikitext('MS')
    header:tag('th'):attr('title', 'Attack Speed'):wikitext('AS')
    header:tag('th'):attr('title', 'Total Stats'):wikitext('Total')

    -- Data rows
    for _, item in ipairs(items) do
        local row = tbl:tag('tr')

        -- Icon column
        local iconCell = row:tag('td')
            :css('width', '64px')
            :css('text-align', 'center')
        local sprite = item.sprite or item.name or "Unknown"
        if sprite and sprite ~= "" then
            iconCell:wikitext(frame:preprocess('{{Sprite|' .. sprite .. '|x2}}'))
        end

        -- Item name column
        local itemName = item.name or "Unknown"
        row:tag('td'):wikitext('[[' .. itemName .. ']]')

        -- Type column (for parent categories)
        if isParent then
            local typeCategory = ItemConfig.pluralize(item.type or "")
            row:tag('td'):wikitext('[[:Category:' .. typeCategory .. '|' .. (item.type or "") .. ']]')
        end

        -- Slots column (for containers)
        if isContainers then
            row:tag('td'):css('text-align', 'right'):wikitext(stat(item.container))
        end

        -- Stats
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.weight))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.damage))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.armor))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.defense))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.health))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.mana))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.ability))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.magic))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.rng))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.move_speed))
        row:tag('td'):css('text-align', 'right'):wikitext(stat(item.attack_speed))
        row:tag('td'):css('text-align', 'right'):wikitext(item._power)
    end

    return tostring(tbl)
end

return p