Jump to content

Module:ItemCategoryTable

From Apogea Wiki
Revision as of 02:54, 28 January 2026 by Dane (talk | contribs) (Add Type column for parent categories (via update-page on MediaWiki MCP Server))

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

local p = {}

-- Parent categories and their child types
local parentCategories = {
    ["Accessories"] = {"Ring", "Necklace", "Gloves"},
    ["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"},
}

-- Query items by type from Cargo
local function getItemsByType(itemType)
    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 args = {
        where = 'type LIKE "%' .. 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 = 'name,sprite,type,weight,damage,armor,defense,health,mana,ability,magic,rng,move_speed,attack_speed,health_regen,mana_regen,container,size,rarity'
    
    -- Build OR conditions for each type
    local conditions = {}
    for _, t in ipairs(types) do
        table.insert(conditions, 'type LIKE "%' .. t .. '%"')
    end
    
    local args = {
        where = table.concat(conditions, ' OR '),
        orderBy = 'name',
        limit = 500
    }
    
    return mw.ext.cargo.query(tables, fields, args)
end

-- Get the singular type name from a plural category name
local function singularize(plural)
    local mapping = {
        ["Rings"] = "Ring",
        ["Necklaces"] = "Necklace",
        ["Staves"] = "Staff",
        ["Knives"] = "Knife",
        ["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
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",
        ["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",
    }
    return mapping[singular] or singular
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

function p.render(frame)
    local args = frame:getParent().args
    local categoryName = args[1] or args.category or mw.title.getCurrentTitle().text:gsub("^Category:", "")
    
    -- Check if this is a parent category
    local isParent = parentCategories[categoryName] ~= nil
    local items
    
    if isParent then
        items = getItemsByTypes(parentCategories[categoryName])
    else
        local itemType = 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 and sort by it (descending)
    for _, item in ipairs(items) do
        item._power = calcPower(item)
    end
    table.sort(items, function(a, b) return a._power > b._power 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'):wikitext('Item')
    if isParent then
        header:tag('th'):wikitext('Type')
    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')
        
        -- Item name with sprite
        local nameCell = row:tag('td')
        local sprite = item.sprite or item.name
        nameCell:wikitext(frame:preprocess('{{Sprite|' .. sprite .. '|x2}}'))
        nameCell:wikitext(' [[' .. item.name .. ']]')
        
        -- Type column (for parent categories)
        if isParent then
            local typeCategory = pluralize(item.type or "")
            row:tag('td'):wikitext('[[:Category:' .. typeCategory .. '|' .. (item.type or "") .. ']]')
        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