Toggle menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

Module:Item

From Net 7 Portal Wiki

Module:Item/doc

This module implements the template {{Item/GenerateHeader}}, see the documentation there.


The above doc is transcluded from Module:Item/doc. (edit | history)
Add categories to the /doc subpage, not here. Subpages of this template.


local getArgs
local yesno = require('Module:Yesno')
local error = require('Module:Vrix').error
require('strict')

--------------------------------------------------------------------------------
-- Helper functions
--------------------------------------------------------------------------------

local function _args(frame)
    if not getArgs then
        getArgs = require('Module:Arguments').getArgs
    end
    local args = getArgs(frame, {
        trim = true,
        removeBlanks = true,
        wrappers = {
            'Template:Item/GenerateHeader',
        },
        parentOnly = true
    })
    return args
end

local function _dynamic_category_image(template, template_args)
    return mw.getCurrentFrame():expandTemplate{ title = template, args = template_args }
end

local function _file_link(image_title, link_contents)
    return mw.getCurrentFrame():expandTemplate{ title = 'CategoryImageLink', args = { image_title .. ".png", link_contents } }
end

local function _file_link_caption(image_title, link_contents, caption)
    -- we don't bother with this anymore, just reuse _file_link
    return _file_link(image_title, link_contents)
end

local function _firstToUpper(str)
    return (str:gsub("^%l", string.upper))
end

local function _titleCaseHelper(first, rest)
   return first:upper()..rest:lower()
end

local function _titleCase(str)
    -- Add extra characters to the pattern if you need to. _ and ' are
    --  found in the middle of identifiers and English words.
    -- We must also put %w_' into [%w_'] to make it handle normal stuff
    -- and extra stuff the same.
    -- This also turns hex numbers into, eg. 0Xa7d4
    return (str:gsub("(%a)([%w_']*)", _titleCaseHelper))
end

--------------------------------------------------------------------------------
-- Item class definition
--------------------------------------------------------------------------------

local Item = {}
Item.__index = Item

function Item.new(args)
    local obj = {}
    -- The category image and link block, if any (most are still hard-coded
    -- directly on item pages across the wiki, so the only ones that show up here
    -- are by category where all pages of that category have migrated to this
    -- module.
    obj.header = ""
    -- This is the brief summary of the item, e.g. "Level X Item"
    obj.brief = ""
    -- This is the group of categories we will add to the page for this item.
    obj.category = ""
    obj.level = args.item_level

    obj.type = args.item_type
    if obj.type then
        obj.type = obj.type:lower()
    end

    obj.subtitle = args.item_subtitle

    obj.agrippa = args.item_agrippa

    obj.component = args.component_type
    if obj.component then
        obj.component = obj.component:lower()
    end

    -- for an Ammunition Item itself
    obj.ammo = args.ammo_type
    if obj.ammo then
        obj.ammo = obj.ammo:lower()
    end

    -- for an Ammunition Item itself, only valid if obj.ammo is set
    obj.ammo_damage = args.ammo_damage_type
    if obj.ammo_damage then
        obj.ammo_damage = obj.ammo_damage:lower()
    end

    obj.raw = args.raw_type
    if obj.raw then
        obj.raw = obj.raw:lower()
    end

    obj.prospecting_node = args.prospecting_node_type
    if obj.prospecting_node then
        obj.prospecting_node = obj.prospecting_node:lower()
    end

    obj.refined = args.refined_type
    if obj.refined then
        obj.refined = obj.refined:lower()
    end

    obj.mission = args.mission
    obj.mission_related = args.mission_related
    obj.mission_reward = args.mission_reward

    -- for Missile Launcher and Projectile Launcher Item's which may support
    -- multiple damage types and therefore be placed in multiple categories
    -- accordingly
    obj.ammo_rows = args.ammo_rows
    if obj.ammo_rows then
        obj.ammo_rows = obj.ammo_rows:lower()
    end

    obj.vendor_buy_rows = args.vendor_buy_rows
    obj.vendor_sell_rows = args.vendor_sell_rows
    obj.vendor_buy_sell_rows = args.vendor_buy_sell_rows

    obj.drop_rows = args.drop_rows

    obj.prospect_rows = args.prospect_rows

    if obj.level ~= nil then
        obj.brief = obj.brief .. "Level " .. obj.level
    end

    if obj.type == nil then
        obj.brief = obj.brief .. " (" .. error{message="item_type not specified"} .. ")"
    else
        -- Component
        if obj.type:find("component") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Component-L" .. obj.level .. "]]"
                if obj.agrippa ~= nil then
                    obj.category = obj.category .. "[[Category:Agrippa Component-L" .. obj.level .. "]]"
                    obj.header = obj.header .. _dynamic_category_image('CategoryAgrippaComponent', { obj.level })
                else
                    obj.header = obj.header .. _dynamic_category_image('CategoryComponent', { obj.level })
                end
            end
            obj.category = obj.category .. "[[Category:Component]]"

            if obj.component == nil then
                obj.brief = obj.brief .. " (" .. error{message="component_type not specified"} .. ")"
            else
                if obj.component:find("shell") then
                    obj.category = obj.category .. "[[Category:Component-Ammo-Missile&ShellCasings]]"
                    obj.brief = obj.brief .. " Shell Casing"
                end

                if obj.component:find("propellant") then
                    obj.category = obj.category .. "[[Category:Component-Ammo-Propellants]]"
                    obj.brief = obj.brief .. " Propellant"
                end

                if obj.component:find("slug") then
                    obj.category = obj.category .. "[[Category:Component-Ammo-Slugs&Rails]]"
                    obj.brief = obj.brief .. " Ammunition Slug"
                end

                if obj.component:find("warhead") then
                    obj.category = obj.category .. "[[Category:Component-Ammo-Warheads]]"
                    obj.brief = obj.brief .. " Warhead"
                end

                if obj.component:find("computer") then
                    obj.category = obj.category .. "[[Category:Component-Computer-Computers]]"
                    obj.brief = obj.brief .. " Computer"
                end

                if obj.component:find("electronic") then
                    obj.category = obj.category .. "[[Category:Component-Computer-Electronics]]"
                    obj.brief = obj.brief .. " Electronic Item"
                end

                if obj.component:find("software") then
                    obj.category = obj.category .. "[[Category:Component-Computer-Software]]"
                    obj.brief = obj.brief .. " Software"
                end

                if obj.component:find("drone") then
                    obj.category = obj.category .. "[[Category:Component-Forged-DroneFrames]]"
                    obj.brief = obj.brief .. " Drone Frame"
                end

                if obj.component:find("engine") then
                    obj.category = obj.category .. "[[Category:Component-Forged-EngineFrames]]"
                    obj.brief = obj.brief .. " Engine Frame"
                end

                if obj.component:find("mount") then
                    obj.category = obj.category .. "[[Category:Component-Forged-Mounts]]"
                    obj.brief = obj.brief .. " Mount"
                end

                if obj.component:find("casing") then
                    obj.category = obj.category .. "[[Category:Component-Forged-ShieldCasings]]"
                    obj.brief = obj.brief .. " Casing"
                end

                if obj.component:find("other") then
                    obj.category = obj.category .. "[[Category:Component-Other]]"
                    obj.brief = obj.brief .. " Other"
                end

                if obj.component:find("generator") then
                    obj.category = obj.category .. "[[Category:Component-Power-Generators]]"
                    obj.brief = obj.brief .. " Power Generator"
                end

                if obj.component:find("converter") then
                    obj.category = obj.category .. "[[Category:Component-Power-PowerConverters]]"
                    obj.brief = obj.brief .. " Power Converter"
                end

                if obj.component:find("core") then
                    obj.category = obj.category .. "[[Category:Component-Power-PowerCores]]"
                    obj.brief = obj.brief .. " Power Core"
                end

                if obj.component:find("coupling") then
                    obj.category = obj.category .. "[[Category:Component-Power-PowerCouplings]]"
                    obj.brief = obj.brief .. " Power Coupling"
                end

                if obj.component:find("feeder") then
                    obj.category = obj.category .. "[[Category:Component-WepComps-AmmoLoadersFeeder]]"
                    obj.brief = obj.brief .. " Ammunition Feeder"
                end

                if obj.component:find("barrel") then
                    obj.category = obj.category .. "[[Category:Component-WepComps-Barrel&LaunchTubes]]"
                    obj.brief = obj.brief .. " Weapon Barrel"
                end

                if obj.component:find("mechanism") then
                    obj.category = obj.category .. "[[Category:Component-WepComps-FiringMechanisms]]"
                    obj.brief = obj.brief .. " Firing Mechanism"
                end

                if obj.component:find("optic") then
                    obj.category = obj.category .. "[[Category:Component-WepComps-Optics]]"
                    obj.brief = obj.brief .. " Beam Optic"
                end
            end -- if obj.component == nil else
        end -- if obj.type:find("component") then

        -- Ammo
        if obj.type:find("ammo") then
            obj.brief = obj.brief .. " Ammo"

            if obj.ammo == nil then
                obj.brief = obj.brief .. " (" .. error{message="ammo_type not specified"} .. ")"
            else
                local ammo_type_found=nil

                if obj.ammo:find("missile") then
                    if obj.ammo_damage == nil then
                        obj.brief = obj.brief .. " (" .. error{message="ammo_damage_type not specified"} .. ")"
                    else
                        if obj.ammo_damage:find("explosive") then
                            obj.category = obj.category .. "[[Category:Ammo-Missile-Explosive]]"
                        end
                        if obj.ammo_damage:find("emp") then
                            obj.category = obj.category .. "[[Category:Ammo-Missile-EMP]]"
                        end
                        if obj.ammo_damage:find("plasma") then
                            obj.category = obj.category .. "[[Category:Ammo-Missile-Plasma]]"
                        end
                        if obj.ammo_damage:find("chemical") then
                            obj.category = obj.category .. "[[Category:Ammo-Missile-Chemical]]"
                        end
                        if obj.ammo_damage:find("impact") then
                            obj.category = obj.category .. "[[Category:Ammo-Missile-Impact]]"
                        end
                    end -- if obj.ammo_damage == nil else
                    if obj.level ~= nil then
                        obj.category = obj.category .. "[[Category:Ammo-Missile-L" .. obj.level .. "]]"
                        obj.header = obj.header .. _dynamic_category_image('CategoryAmmoMissile', { obj.level })
                    end
                    obj.category = obj.category .. "[[Category:Ammo-Missile]]"
                    ammo_type_found=1
                end -- if obj.ammo:find("missile") then
                if obj.ammo:find("projectile") then
                    if obj.ammo_damage == nil then
                        obj.brief = obj.brief .. " (" .. error{message="ammo_damage_type not specified"} .. ")"
                    else
                        if obj.ammo_damage:find("impact") then
                            obj.category = obj.category .. "[[Category:Ammo-Projectile-Impact]]"
                        end
                        if obj.ammo_damage:find("plasma") then
                            obj.category = obj.category .. "[[Category:Ammo-Projectile-Plasma]]"
                        end
                        if obj.ammo_damage:find("chemical") then
                            obj.category = obj.category .. "[[Category:Ammo-Projectile-Chemical]]"
                        end
                        if obj.ammo_damage:find("explosive") then
                            obj.category = obj.category .. "[[Category:Ammo-Projectile-Explosive]]"
                        end
                        if obj.ammo_damage:find("energy") then
                            obj.category = obj.category .. "[[Category:Ammo-Projectile-Energy]]"
                        end
                    end -- if obj.ammo_damage == nil else
                    if obj.level ~= nil then
                        obj.category = obj.category .. "[[Category:Ammo-Projectile-L" .. obj.level .. "]]"
                        obj.header = obj.header .. _dynamic_category_image('CategoryAmmoProjectile', { obj.level })
                    end
                    obj.category = obj.category .. "[[Category:Ammo-Projectile]]"
                    ammo_type_found=1
                end -- if obj.ammo:find("projectile") then

                if ammo_type_found == nil then
                    obj.brief = obj.brief .. " (" .. error{message="invalid ammo_type, unable to identify ammo type"} .. ")"
                end
            end -- if obj.ammo == nil else
            obj.category = obj.category .. "[[Category:Ammo]]"
        end -- if obj.type:find("ammo") then

        -- Ancient Artifact
        if obj.type:find("artifact") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Ancient Artifact-L" .. obj.level .. "]]"
            end
            obj.category = obj.category .. "[[Category:Ancient Artifact]]"
            obj.type = "device," .. obj.type
            -- obj.brief for Ancient Artifact is simply "Device"
            if obj.level ~= nil then
                obj.header = obj.header .. mw.getCurrentFrame():expandTemplate{ title = 'Subcat', args = { _dynamic_category_image('CategoryDevice', { obj.level }), _file_link("AA", ":Category:Ancient Artifact") } }
            end
        end

        -- Device
        if obj.type:find("device") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Device-L" .. obj.level .. "]]"
                if not obj.type:find("artifact") then
                    -- don't add the category image again for AA's
                    obj.header = obj.header .. _dynamic_category_image('CategoryDevice', { obj.level })
                end
            end
            obj.category = obj.category .. "[[Category:Device]]"
            obj.brief = obj.brief .. " Device"
        end

        -- Engine
        if obj.type:find("engine") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Engine-L" .. obj.level .. "]]"
                obj.header = obj.header .. _dynamic_category_image('CategoryEngine', { obj.level })
            end
            obj.category = obj.category .. "[[Category:Engine]]"
            obj.brief = obj.brief .. " Engine"
        end

        -- EMP Beam Weapon
        if obj.type:find("emp") then
            obj.category = obj.category .. "[[Category:Beam-EMP]]"
            obj.type = "beam,weapon," .. obj.type
            obj.brief = obj.brief .. " EMP"
        end

        -- Energy Beam Weapon
        if obj.type:find("energy") then
            obj.category = obj.category .. "[[Category:Beam-Energy]]"
            obj.type = "beam,weapon," .. obj.type
            obj.brief = obj.brief .. " Energy"
        end

        -- Impact Beam Weapon (this exists solely for the very odd Agrippa Technology Examination - Build Weapons)
        if obj.type:find("impact") then
            obj.category = obj.category .. "[[Category:Beam-Impact]]"
            obj.type = "beam,weapon," .. obj.type
            obj.brief = obj.brief .. " Impact"
        end

        -- Plasma Beam Weapon
        if obj.type:find("plasma") then
            obj.category = obj.category .. "[[Category:Beam-Plasma]]"
            obj.type = "beam,weapon," .. obj.type
            obj.brief = obj.brief .. " Plasma"
        end

        -- Beam Weapon
        if obj.type:find("beam") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Beam-L" .. obj.level .. "]]"
                obj.header = obj.header .. _dynamic_category_image('CategoryBeam', { obj.level })
            end
            obj.category = obj.category .. "[[Category:Beam]]"
            obj.type = "weapon," .. obj.type
            obj.brief = obj.brief .. " Beam Weapon"
        end

        -- Missile Launcher
        if obj.type:find("missile") then
            if obj.ammo_rows == nil then
                obj.brief = obj.brief .. " (" .. error{message="ammo_rows not specified"} .. ")"
            else
                local damage_type_found=nil

                if obj.ammo_rows:find("||<center>explosive") then
                    obj.category = obj.category .. "[[Category:Missile Launcher-Explosive]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>emp") then
                    obj.category = obj.category .. "[[Category:Missile Launcher-EMP]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>plasma") then
                    obj.category = obj.category .. "[[Category:Missile Launcher-Plasma]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>chemical") then
                    obj.category = obj.category .. "[[Category:Missile Launcher-Chemical]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>impact") then
                    obj.category = obj.category .. "[[Category:Missile Launcher-Impact]]"
                    damage_type_found=1
                end

                if damage_type_found == nil then
                    obj.brief = obj.brief .. " (" .. error{message="invalid ammo_rows, unable to identify ammo damage types"} .. ")"
                end
            end -- if obj.ammo_rows == nil else
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Missile Launcher-L" .. obj.level .. "]]"
                obj.header = obj.header .. _dynamic_category_image('CategoryMissileLauncher', { obj.level })
            end
            obj.category = obj.category .. "[[Category:Missile Launcher]]"
            obj.type = "weapon," .. obj.type
            obj.brief = obj.brief .. " Missile Launcher"
        end

        -- Projectile Launcher
        if obj.type:find("projectile") then
            if obj.ammo_rows == nil then
                obj.brief = obj.brief .. " (" .. error{message="ammo_rows not specified"} .. ")"
            else
                local damage_type_found=nil

                if obj.ammo_rows:find("||<center>impact") then
                    obj.category = obj.category .. "[[Category:Projectile Launcher-Impact]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>plasma") then
                    obj.category = obj.category .. "[[Category:Projectile Launcher-Plasma]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>chemical") then
                    obj.category = obj.category .. "[[Category:Projectile Launcher-Chemical]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>explosive") then
                    obj.category = obj.category .. "[[Category:Projectile Launcher-Explosive]]"
                    damage_type_found=1
                end
                if obj.ammo_rows:find("||<center>energy") then
                    obj.category = obj.category .. "[[Category:Projectile Launcher-Energy]]"
                    damage_type_found=1
                end

                if damage_type_found == nil then
                    obj.brief = obj.brief .. " (" .. error{message="invalid ammo_rows, unable to identify ammo damage types"} .. ")"
                end
            end -- if obj.ammo_rows == nil else
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Projectile Launcher-L" .. obj.level .. "]]"
                obj.header = obj.header .. _dynamic_category_image('CategoryProjectileLauncher', { obj.level })
            end
            obj.category = obj.category .. "[[Category:Projectile Launcher]]"
            obj.type = "weapon," .. obj.type
            obj.brief = obj.brief .. " Projectile Launcher"
        end

        -- Reactor
        if obj.type:find("reactor") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Reactor-L" .. obj.level .. "]]"
                obj.header = obj.header .. _dynamic_category_image('CategoryReactor', { obj.level })
            end
            obj.category = obj.category .. "[[Category:Reactor]]"
            obj.brief = obj.brief .. " Reactor"
        end

        -- Shield
        if obj.type:find("shield") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Shield-L" .. obj.level .. "]]"
                obj.header = obj.header .. _dynamic_category_image('CategoryShield', { obj.level })
            end
            obj.category = obj.category .. "[[Category:Shield]]"
            obj.brief = obj.brief .. " Shield"
        end

        -- Weapon
        if obj.type:find("weapon") then
            obj.category = obj.category .. "[[Category:Weapon]]"
            -- obj.brief for weapons is handled by weapon type (because Beam's include "Weapon" and others don't)
        end

        -- Item
        if obj.type:find("item") or obj.type:find("other") then
            obj.category = obj.category .. "[[Category:Item-Other]]"
            obj.brief = obj.brief .. " Other"
            obj.header = obj.header .. mw.getCurrentFrame():expandTemplate{ title = 'CategoryItem-Other' }
        end

        -- No known use / Junk
        if obj.type:find("junk") or obj.type:find("no known use") then
            obj.category = obj.category .. "[[Category:Item-No known use]]"
            obj.brief = obj.brief .. " Other"
            obj.header = obj.header .. _file_link("Item-NoKnownUse", ":Category:Item-No known use|List of Items with No known use")
        end

        -- Trade Good
        if obj.type:find("trade") then
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Trade Good-L" .. obj.level .. "]]"
            end
            obj.category = obj.category .. "[[Category:Trade Good]]"
            obj.brief = obj.brief .. " Trade Good"
            obj.header = obj.header .. _file_link("Trade", ":Category:Trade Good|List of Trade Goods")
        end

        -- Raw Resource
        if obj.type:find("raw") then
            obj.header = obj.header .. _file_link("Rsrc-Raw", "List of Ores")
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Ore-L" .. obj.level .. "]]"
            end
            obj.category = obj.category .. "[[Category:Ore]]"

            if obj.raw ~= nil then
                local raw_types = { "alloy", "catalyst", "conductor", "core metal", "gas", "gemstone",
                                    "hydrocarbon", "magnetic", "metal", "optic gem", "radioactive", "silicate" }
                for _, raw_type in ipairs(raw_types) do
                    if obj.raw:find(raw_type) then
                        obj.category = obj.category .. "[[Category:" .. _titleCase(raw_type) .. "]]"
                        obj.brief = obj.brief .. " " .. _titleCase(raw_type)
                    end
                end
            end -- if obj.raw ~= nil then
        end -- if obj.type:find("raw") then

        -- Refined Resource
        if obj.type:find("refined") then
            obj.header = obj.header .. _file_link_caption("Rsrc-Refined", ":Category:Refined Resource", "List of Refined Resources")
            if obj.level ~= nil then
                obj.category = obj.category .. "[[Category:Refined Resource-L" .. obj.level .. "]]"
            end
            obj.category = obj.category .. "[[Category:Refined Resource]]"
            if obj.refined ~= nil then
                local refined_types = { "alloy", "catalyst", "conductor", "core metal", "gas", "gemstone",
                                        "hydrocarbon", "magnetic", "metal", "optic gem", "other", "radioactive", "silicate" }
                for _, refined_type in ipairs(refined_types) do
                    if obj.refined:find(refined_type) then
                        obj.category = obj.category .. "[[Category:" .. _titleCase(refined_type) .. "]]"
                        obj.brief = obj.brief .. " " .. _titleCase(refined_type)
                    end
                end
            end -- if obj.refined ~= nil then
        end -- if obj.type:find("refined") then

        -- Computer (Terminal Controller and Terminal Override)
        if obj.type:find("computer") then
            obj.category = obj.category .. "[[Category:Computer-Terminal]]"
            obj.brief = obj.brief .. " Computer"
            obj.header = obj.header .. _file_link("TermComp", ":Category:Computer-Terminal|List of Terminal Computers")
        end

        -- If the item can be found in prospecting nodes add it to the appropriate category for the type of node it's found in
        if obj.prospecting_node ~= nil then
            local prospecting_node_types = { "rock", "hydrocarbon", "crystal", "glowing", "gas", "hulk" }
            for _, prospecting_node_type in ipairs(prospecting_node_types) do
                if obj.prospecting_node:find(prospecting_node_type) then
                    obj.category = obj.category .. "[[Category:Prospecting Node-" .. _firstToUpper(prospecting_node_type) .. "]]"
                end
            end
        end -- if obj.prospecting_node ~= nil then
    end -- if obj.type == nil then

    if obj.level == nil then
        obj.brief = obj.brief .. " (" .. error{message="item_level not specified"} .. ")"
    end

    if obj.subtitle ~= nil then
        obj.brief = obj.brief .. " " .. obj.subtitle
    end

    if obj.mission ~= nil then
        obj.category = obj.category .. "[[Category:Mission Reward]][[Category:Meta/Mission Reward (Deprecated)]]"
    end

    if obj.mission_related ~= nil then
        obj.category = obj.category .. "[[Category:Mission Related]]"
    end

    if obj.mission_reward ~= nil then
        obj.category = obj.category .. "[[Category:Mission Reward]]"
    end

    if obj.vendor_buy_sell_rows ~= nil or obj.vendor_sell_rows ~= nil then
        -- nothing at the moment; category for items sold by specific vendors?
    end

    if obj.vendor_buy_sell_rows ~= nil or obj.vendor_buy_rows ~= nil then
        -- nothing at the moment; category for items bought by specific vendors?
    end

    if obj.drop_rows ~= nil then
        obj.category = obj.category .. "[[Category:Drop Pages]]"
    end

    if obj.prospect_rows ~= nil then
        obj.category = obj.category .. "[[Category:Prospectable]]"
    end

    if obj.agrippa ~= nil then
        obj.category = obj.category .. "[[Category:Agrippa Technology]]"
    end

    return setmetatable(obj, Item)
end

function Item:export()
    return tostring(mw.html.create()
        :wikitext(self.header .. self.category .. "\n==== " .. self.brief .. " ====\n" .. mw.text.nowiki(''))
    )
end

--------------------------------------------------------------------------------
-- Exports
--------------------------------------------------------------------------------

local p, mt = {}, {}

function p._exportClasses()
    -- For testing.
    return {
        Item = Item
    }
end

function p.main(k, args)
    local item = Item.new(args)
    return item:export()
end

function mt.__index(t, k)
    return function (frame)
        return t.main(k, _args(frame))
    end
end

return setmetatable(p, mt)