Module:Mod: Difference between revisions

From Path of Exile Wiki
Jump to navigation Jump to search
>Illviljan
mNo edit summary
>Illviljan
No edit summary
Line 857: Line 857:
end
end


--
function p.get_tags(frame)
-- Template:
-- This function queries for the tags for a specific item.
--
 
function p.item_affix_table(frame)
-- Args
-- Args
g_args = getArgs(frame, {
local g_args = getArgs(frame, {
parentFirst = true
parentFirst = true
})
})
g_frame = util.misc.get_frame(frame)
local g_frame = util.misc.get_frame(frame)
-- This function adds specific tags first then tags that follows a pattern and lastly the generic default tag.
function add_tags(frame)
    -- Args
g_args = getArgs(frame, {
parentFirst = true
})
g_frame = util.misc.get_frame(frame)
 
local tag_spec = g_args.tag_prefix
local pattern = g_args.pattern
local tags = game.constants.tags
local header_prefix = g_args.header_prefix or ''
local tags_filtered = {}
local out = {}
local ignored = {}
if g_args.ignore ~= nil then
ignored = mw.text.split(g_args.ignore, ', ')
end
 
function titleCase( first, rest )
return first:upper()..rest:lower()
end
 
if pattern ~= nil then
for i = 1, #tags do
-- Add only the tags matching the string.
if string.match(tags[i]['full'], pattern) then
tags_filtered[#tags_filtered+1] = tags[i]['full']
end
-- Check that the latest added tag isn't on the ignore list.
for _, ignore in ipairs(ignored) do
if tags_filtered[#tags_filtered] == ignore  then
table.remove(tags_filtered, #tags_filtered)
end
end
end
for _,tag_i in ipairs(tags_filtered) do
if tag_i ~= pattern then
out[#out+1] = {
tags = table.concat({tag_spec, tag_i, pattern, 'default'}, ', '),
name = string.format('%s %s',
header_prefix or '',
tag_i:gsub('_', ' '):gsub(pattern, tag_spec)
):gsub('%s+', ' '):gsub("(%a)([%w_']*)", titleCase)
}
end
end
else
out[#out+1] = {
tags = table.concat({tag_spec, 'default'}, ', '),
name = header_prefix:gsub("(%a)([%w_']*)", titleCase)
}
end
return out
end
local out = {}
local item_name = g_args[1]
--
local query = {
local input = {
string.format('[[Has name::%s]]', item_name),
-- Weapons
'?Has tags',
add_tags{tag_prefix = 'axe', pattern = 'weapon', ignore = 'specific_weapon'},
'?Has base strength requirement',
add_tags{tag_prefix = 'bow', pattern = 'weapon', ignore = 'specific_weapon, one_hand_weapon'},
'?Has base intelligence requirement',
add_tags{tag_prefix = 'claw', pattern = 'weapon', ignore = 'specific_weapon, two_hand_weapon'},
'?Has base dexterity requirement',
add_tags{tag_prefix = 'dagger', pattern = 'weapon', ignore = 'specific_weapon, two_hand_weapon'},
'?Has item class'
add_tags{tag_prefix = 'mace', pattern = 'weapon', ignore = 'specific_weapon'},
add_tags{tag_prefix = 'sceptre', pattern = 'weapon', ignore = 'specific_weapon, two_hand_weapon'},
add_tags{tag_prefix = 'staff', pattern = 'weapon', ignore = 'specific_weapon, one_hand_weapon'},
add_tags{tag_prefix = 'sword', pattern = 'weapon', ignore = 'specific_weapon'},
add_tags{tag_prefix = 'sword', pattern = 'weapon', ignore = 'specific_weapon, two_hand_weapon', header_prefix = 'Thrusting'},
add_tags{tag_prefix = 'wand', pattern = 'weapon', ignore = 'specific_weapon, two_hand_weapon'},
{{tags = 'fishing_rod', name = 'Fishing Rod'}},
-- Accessories
add_tags{tag_prefix = 'amulet'},
add_tags{tag_prefix = 'belt'},
add_tags{tag_prefix = 'quiver'},
add_tags{tag_prefix = 'ring'},
-- Armour
add_tags{tag_prefix = 'body_armour',pattern = 'armour', ignore = 'body_armour'},
add_tags{tag_prefix = 'boots', pattern = 'armour', ignore = 'body_armour'},
add_tags{tag_prefix = 'gloves', pattern = 'armour', ignore = 'body_armour'},
add_tags{tag_prefix = 'helmet', pattern = 'armour', ignore = 'body_armour'},
}
}
 
local results = util.smw.query(query, g_frame)
-- local input = {
-- add_tags{tag_prefix = 'axe', pattern = 'weapon', ignore = 'specific_weapon'},
-- Remove uneccessary symbols and reverse the tag order.
-- add_tags{tag_prefix = 'bow', pattern = 'weapon', ignore = 'specific_weapon, one_hand_weapon'},
results[1]['Has tags'] = results[1]['Has tags']:gsub('(<MANY>)', ', ')
-- }
--
function table.reverse(a)
for _, tag_tbl in ipairs(input) do
local res = {}
for _, v in ipairs(tag_tbl) do
for i = #a, 1, -1 do
out[#out+1] = p.drop_down_table(v)
res[#res+1] = a[i]
end
end
return res
end
end
results[1]['Has tags'] = table.concat(table.reverse(util.string.split(results[1]['Has tags'], ', ')), ', ')
return results[1]
end


return table.concat(out, '\n\n')
function p.header(str)
-- Replace numbers with #
s = table.concat(util.string.split(str, '%(%d+%.*%d* to %d+%.*%d*%)'), '#')
s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
-- Remove links and <br>.
s = s:gsub('%[%[(.-)%]%]', '%1'):gsub('(%a*)%|', ''):gsub('<br>', ', ')
 
  return s
end
end


function p.drop_down_table(frame)
-- Misses forsaken masters currently
-- Weapons
-- p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
-- p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
-- p.drop_down_table{item = 'Crude Bow', header = 'Bows'}
-- p.drop_down_table{item = 'Nailed Fist', header = 'Claws'}
-- p.drop_down_table{item = 'Glass Shank', header = 'Daggers'}
-- p.drop_down_table{item = 'Driftwood Club', header = 'Generic One Handed Maces'}
-- p.drop_down_table{item = 'Driftwood Sceptre', header = 'Sceptres'}
-- p.drop_down_table{item = 'Driftwood Maul', header = 'Two Handed Maces'}
-- p.drop_down_table{item = 'Gnarled Branch', header = 'Staves'}
-- p.drop_down_table{item = 'Rusted Sword', header = 'Generic One Handed Swords'}
-- p.drop_down_table{item = 'Rusted Spike', header = 'Thrusting Swords'}
-- p.drop_down_table{item = 'Corroded Blade', header = 'Two Handed Swords'}
-- p.drop_down_table{item = 'Driftwood wand', header = 'Wands'}
-- p.drop_down_table{item = 'Fishing Rod', header = 'Fishing Rods'}
-- Accessories
-- p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
-- p.drop_down_table{item = 'Leather Belt', header = 'Belts'}
-- p.drop_down_table{item = 'Blunt Arrow Quiver', header = 'Quivers'}
-- p.drop_down_table{item = 'Coral Ring', header = 'Rings'}
-- Armour
-- p.drop_down_table{item = 'Plate Vest', header = 'Armour Body Armours'}
-- p.drop_down_table{item = 'Shabby Jerkin', header = 'Evasion Body Armours'}
-- p.drop_down_table{item = 'Simple Robe', header = 'Energy Shield Body Armours'}
-- p.drop_down_table{item = 'Scale Vest', header = 'Armour/Evasion Body Armours'}
-- p.drop_down_table{item = 'Chainmail Vest', header = 'Armour/Energy Shield Body Armours'}
-- p.drop_down_table{item = 'Padded Vest', header = 'Energy Shield/Evasion Body Armours'}
-- p.drop_down_table{item = 'Sacrificial Garb', header = 'Armour/Energy Shield/Evasion Body Armours'}
-- p.drop_down_table{item = 'Iron Greaves', header = 'Armour Boots'}
-- p.drop_down_table{item = 'Rawhide Boots', header = 'Evasion Boots'}
-- p.drop_down_table{item = 'Wool Shoes', header = 'Energy Shield Boots'}
-- p.drop_down_table{item = 'Leatherscale Boots', header = 'Armour/Evasion Boots'}
-- p.drop_down_table{item = 'Chain Boots', header = 'Armour/Energy Shield Boots'}
-- p.drop_down_table{item = 'Wrapped Boots', header = 'Energy Shield/Evasion Boots'}
-- p.drop_down_table{item = 'Golden Caligae', header = 'Demigod'}
-- p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour Gloves'}
-- p.drop_down_table{item = 'Rawhide Gloves', header = 'Evasion Gloves'}
-- p.drop_down_table{item = 'Wool Gloves', header = 'Energy Shield Gloves'}
-- p.drop_down_table{item = 'Fishscale Gauntlets', header = 'Armour/Evasion Gloves'}
-- p.drop_down_table{item = 'Chain Gloves', header = 'Armour/Energy Shield Gloves'}
-- p.drop_down_table{item = 'Wrapped Mitts', header = 'Energy Shield/Evasion Gloves'}
-- p.drop_down_table{item = 'Iron Hat', header = 'Armour Helmets'}
-- p.drop_down_table{item = 'Leather Cap', header = 'Evasion Helmets'}
-- p.drop_down_table{item = 'Vine Circlet', header = 'Energy Shield Helmets'}
-- p.drop_down_table{item = 'Battered Helm', header = 'Armour/Evasion Helmets'}
-- p.drop_down_table{item = 'Rusted Coif', header = 'Armour/Energy Shield Helmets'}
-- p.drop_down_table{item = 'Scare Mask', header = 'Energy Shield/Evasion Helmets'}
-- p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour Shields'}
-- p.drop_down_table{item = 'Goathide Buckler', header = 'Evasion Shields'}
-- p.drop_down_table{item = 'Twig Spirit Shield', header = 'Energy Shield Shields'}
-- p.drop_down_table{item = 'Rotted Round Shield', header = 'Armour/Evasion Shields'}
-- p.drop_down_table{item = 'Plank Kite Shield', header = 'Armour/Energy Shield Shields'}
-- p.drop_down_table{item = 'Spiked Bundle', header = 'Energy Shield/Evasion Shields'}


function p.drop_down_table(frame)
-- Args
-- Args
     g_args = getArgs(frame, {
     local g_args = getArgs(frame, {
         parentFirst = true
         parentFirst = true
     })
     })
     g_frame = util.misc.get_frame(frame)
     local g_frame = util.misc.get_frame(frame)
      
      
-- Items can have several tags affecting them.
local get_tags = p.get_tags{g_args.item}
-- Misses forsaken masters currently
local tags = {}
-- p.drop_down_table{tags = 'dagger, one_hand_weapon, weapon, default', name = 'Daggers'} -- name could be taken from module:game?
tags = util.string.split(get_tags['Has tags'], ', ')
-- p.drop_down_table{tags = 'sword, one_hand_weapon, weapon, default', name = 'One Hand Swords'}
-- p.drop_down_table{tags = 'sword, two_hand_weapon, weapon, default', name = 'Two Hand Swords'}
-- p.drop_down_table{tags = 'amulet, default', name = 'Amulets'}
-- p.drop_down_table{tags = 'ring, default', name = 'Rings'}
-- p.drop_down_table{tags = 'belt, default', name = 'Belts'}
-- p.drop_down_table{tags = 'quiver, default', name = 'Quivers'}
-- p.drop_down_table{tags = 'body_armour, int_armour, armour, default', name = 'Intelligence body armours'}
-- p.drop_down_table{tags = 'helmet, int_armour, armour, default', name = 'Intelligence helmets'}
-- p.drop_down_table{tags = 'gloves, int_armour, armour, default', name = 'Intelligence gloves'}
-- p.drop_down_table{tags = 'boots', int_armour, armour, default', name = 'Intelligence boots'}
g_args.tag = mw.text.split(g_args['tags'], ', ')
local header = g_args['header']
 
if header == nil then
-- local tags_condition = table.concat(g_args.tag, ' || ')
header = table.concat(tags, ', ')
end
-- Conditions
-- Conditions
    local conditions = {}
local conditions = {}
     conditions[#conditions+1] = 'concept'
     conditions[1] = 'concept' -- Reserve for Concepts.
     if g_args.tag then
     conditions[#conditions+1] = string.format('[[Has subobject::<q> [[Has spawn weight::>>0]] [[Has tag::%s]] </q>]]', table.concat(tags, ' || '))
        conditions[#conditions+1] = string.format('[[Has subobject::<q> [[Has spawn weight::>>0]] <q> [[Has tag::%s]] </q> </q>]]', table.concat(g_args.tag, ' || '))
    end
   
   
     -- Fields
     -- Fields
Line 1,025: Line 1,006:
      
      
     local data = {}
     local data = {}
     data.header = {
     data = {
         prefix = 'Prefix',
         prefix = {
         suffix = 'Suffix',
header = 'Prefix',
condition = '[[Concept:Spawnable named prefix item mods]]',
},
         suffix = {
header = 'Suffix',
condition = '[[Concept:Spawnable named suffix item mods]]',
},
master = {
header = 'Forsaken masters',
condition = '[[Concept:AAAAAAAAAAAAAAAAAAAA]]',
},
     }
     }
local header_item
    if g_args.name ~= nil then
header_item = g_args.name
else
header_item = table.concat(g_args.tag, ', ')
end
-- Define the output.
-- Define the output.
local out = {}
local out = {}
out[#out+1] = '<div class="mw-collapsible-collapse-all" style="cursor:pointer;">Collapse All</div> | <div class="mw-collapsible-expand-all" style="cursor:pointer;">Expand All</div> \n'


out[#out+1] = string.format('==%s== \n', header_item:gsub("^%l", string.upper):gsub("_", " "))
out[#out+1] = string.format('==%s== \n', header)
out[#out+1] = '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[Collapse All]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[Expand All]</div></div>'
out[#out+1] = string.format('The table below displays the available [[modifiers]] for [[item]]s such as %s.<br><br><br>', g_frame:expandTemplate{title = 'Item link', args = {get_tags[1]}})
local results ={}
local results = {}
     for _, val in ipairs({'prefix', 'suffix'}) do -- Loop through prefixes and suffixes.
     for i_type, generation_type in ipairs({'prefix', 'suffix'}) do -- Change to data eventually.
out[#out+1] = '<div style="vertical-align:top; display:inline-block;">'
out[#out+1] = string.format('\n===%s===\n',data.header[val])
        conditions[1] = string.format('[[Concept:Spawnable named %s item mods]]', val)
     
         --
         --
         -- Query mods
         -- Query mods
         --
         --
        query = {}
conditions[1] = data[generation_type]['condition']
local query = {}
         for _, v in ipairs(conditions) do
         for _, v in ipairs(conditions) do
             query[#query+1] = v
             query[#query+1] = v
Line 1,062: Line 1,044:
             query[k] = v
             query[k] = v
         end
         end
results[generation_type] = util.smw.query(query, g_frame)
          
          
        results[val] = util.smw.query(query, g_frame)
local container = mw.html.create('div')
       
:attr('style', 'vertical-align:top; display:inline-block;')
local headers = container
headers
:tag('h3')
:wikitext(string.format('%s', data[generation_type]['header']))
:done()
:done()
local results_tags = {}
local results_tags = {}
local tags_weighting = {}
local tags_weighting = {}
local tbl = {}
local last
local last
for i, v in ipairs(results[generation_type]) do -- Loop through all the results from the smw query.
for i, v in ipairs(results[val]) do -- Loop through all the results from the smw query.
-- Query tags
-- Query tags
local mod_id = results[val][i]['Is mod']:gsub('_','~') -- mod id and mod pagename can sometimes be different. This tag query method is similar to other functions, so this bug might be noticeable on other places as well?
local mod_id = results[generation_type][i]['Is mod']:gsub('_','~') -- mod id and {{PAGENAME}} can sometimes be different. This tag query method is similar to other functions, so this bug might be noticeable on other places as well?
query_tags = {
query_tags = {
string.format('[[-Has subobject::%s]]', mod_id),
string.format('[[-Has subobject::%s]]', mod_id),
Line 1,081: Line 1,070:
}
}
results_tags[i] = util.smw.query(query_tags, g_frame)  
results_tags[i] = util.smw.query(query_tags, g_frame)  
tags_weighting[i] = {}
tags_weighting[i] = {}
tags_weighting[i][1] = results_tags[i][1]
tags_weighting[i][1] = results_tags[i][1]
 
local tags_spawn = {}
for _, row in ipairs(results_tags[i]) do
for f, row in ipairs(results_tags[i]) do
local spawn_weight = row['Has spawn weight']:gsub(',', '') -- Remove thousand separator.
local spawn_weight = row['Has spawn weight']:gsub(',', '') -- Remove the thousand separator that the numbers may have.
tags_weighting[i][row['Has tag']] = tonumber(spawn_weight)
tags_weighting[i][row['Has tag']] = tonumber(spawn_weight)
end
end
local j = 0  
local j = 0  
repeat   
repeat   
j = j+1
j = j+1
if (tags_weighting[i][tags[j]] or -1) > 0 then  
if (tags_weighting[i][g_args.tag[j]] or -1) > 0 then
if results[generation_type][i]['Has mod type'] ~= last then -- If the current mod type is different to the last mod type, create a new table.
-- Display in a list, similar to poedb. Drop down table, with the varius tiers of mods in that mod type inside.
local mod_appl = 'Global' -- Assume the mod is global if local isn't specified in the mod id.
if results[val][i]['Has mod type'] ~= last then -- If the mod type is different to the last, create a new table.
if results[generation_type][i]['Is mod']:find('Local') ~= nil then  
if tbl[#tbl] ~= nil then --i ~= 1 then
tbl[#tbl+1] = '</table>'
end
tbl[#tbl+1] = '<table class="wikitable mw-collapsible mw-collapsed" style="text-align:left; line-height: 1.75em; width:800px; ">'
-- Search for "Local" in the mod id. IF it doesn't exist assume that the mod applies globally.
local mod_appl
if results[val][i]['Is mod']:find('Local') ~= nil then
mod_appl = 'Local'
mod_appl = 'Local'
else
mod_appl = 'Global'
end
end
local mod_header = results[val][i]['Has stat text']:gsub('%d', '#'):gsub('%[%[(.-)%]%]', '%1'):gsub('(%a*)%|', ''):gsub('<br>', ', ') -- Removes the links since they're annoying when expanding and collapsing a lot.
local tbl_caption = p.header(results[generation_type][i]['Has stat text'])
last = results[generation_type][i]['Has mod type']
tbl[#tbl+1] = string.format('<caption style="text-align:left; line-height: 1.0em;> %s (%s) &nbsp;</caption>', g_frame:expandTemplate{ title = 'c', args = { 'mod', mod_header} }, mod_appl)
tbl = container:tag('table')
tbl
last = results[val][i]['Has mod type']
:attr('class', 'mw-collapsible mw-collapsed')
:attr('style', 'text-align:left; line-height:1.60em; width:800px;') -- white-space:nowrap;
:tag('th')
:attr('style', 'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;')
:attr('colspan', '3')
:wikitext(string.format('%s (%s)', g_frame:expandTemplate{title = 'c', args = {'mod', tbl_caption}}, mod_appl))
:done()
:done()
end
end
 
tbl
--Display the bulk of the mods.
:tag('tr')
tbl[#tbl+1] = string.format('<tr><td> %s %s, %s ([[%s|%s]]) </td></tr>', game.level_requirement['short_upper'], results[val][i]['Has level requirement'], g_frame:expandTemplate{ title = 'c', args = { 'mod', results[val][i]['Has stat text']:gsub('<br>', ', ') } }, results[val][i][1], results[val][i]['Has name'])
:tag('td')
:attr('width', '150')
:wikitext(string.format('[[%s|%s]]', results[generation_type][i][1], results[generation_type][i]['Has name']:gsub('%s', '&nbsp;')))
:done()
:tag('td')
:attr('width', '1')
:wikitext(string.format('%s&nbsp;%s',game.level_requirement['short_upper']:gsub('%s', '&nbsp;'), results[generation_type][i]['Has level requirement']))
:done()
:tag('td')
:attr('width', '*')
:wikitext(string.format('%s', g_frame:expandTemplate{title = 'c', args = {'mod', results[generation_type][i]['Has stat text']:gsub('<br>', ', ')}}))
:done()
end
end
until (tags_weighting[i][g_args.tag[j]] or -1) >= 0 or j > #g_args.tag
until (tags_weighting[i][tags[j]] or -1) >= 0
end
end
tbl[#tbl+1] = '</table>' -- outro
out[#out+1] = tostring(container)
 
out[#out+1] = table.concat(tbl, '')
out[#out+1] = '</div>'
     end
     end
return table.concat(out, '')
return table.concat(out,'')
end
end


return p
return p

Revision as of 11:55, 20 August 2016

Module documentation[view] [edit] [history] [purge]


This module is used on 34000+ pages.

To avoid major disruption and server load, do not make unnecessary edits to this module. Test changes to this module first using its /sandbox and /testcases subpages . All of the changes can then be applied to this module in a single edit.

Consider discussing changes on the talk page or on Discord before implementing them.

Lua logo

This module depends on the following other modules:

Module for handling for modifiers with Cargo support.

List of currently implemented templates

de:Modul:Mod

--
-- Module for mod related templates
--

local util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local game = require('Module:Game')

local p = {}

-- ----------------------------------------------------------------------------
-- Utility / Helper functions
-- ----------------------------------------------------------------------------

local h = {}

-- Validate single value properties and set them

h.validate = {}

function h.validate.not_nil (args)
    return function (arg)
        if g_args[arg] == nil then
            error(string.format('%s must not be nil', arg))
        end
    end
end

function h.validate.number (args)
    return function (arg)
        g_args[arg] = util.cast.number(g_args[arg], args)
        return g_args[arg]
    end
end


function h.handle_mapped_property_args (map)
    local properties = {}
    
    for _, data in ipairs(map) do
        if data.func ~= nil then
            data.func(data.name)
        end
        
        if data.property ~= nil then
            properties[data.property] = g_args[data.name]
        end
    end
    
    g_frame:callParserFunction('#set:', properties)
end

function h.create_header(row)
    local stat = mw.html.create('span')
    local text, nsub = mw.ustring.gsub(row['Has stat text'], '%d+', '?')
    stat
        :attr('class', 'mod-table-header-stat')
        :wikitext(text)
        :done()
        
    local mgroup = mw.html.create('span')
    mgroup
        :attr('class', 'mod-table-header-modgroup')
        :wikitext(row['Has mod group'])
        :done()
        
    local tbl = mw.html.create('table')
    tbl
        :attr('class', 'wikitable mw-collapsible mw-collapsed mod-table') 
        :tag('tr')
            :tag('th')
                :attr('class', 'mod-table-header')
                :attr('colspan', g_args.colspan)
                :tag('span')
                    :attr('class', 'mod-table-header-container')
                    :wikitext(tostring(stat) .. tostring(mgroup))
                :done()
            :done()
    return tbl
end

function h.format_mod(tbl, row, tags)
    local tr = tbl:tag('tr')
    tr
        :tag('td')
            :wikitext(string.format('[[%s|%s]]', row[1], row['Has name']))
            :attr('class', 'mod-table-cell-name')
            :done()
        :tag('td')
            :wikitext(row['Has level requirement'])
            :attr('class', 'mod-table-cell-level')
            :done()
        :tag('td')
            :wikitext(row['Has stat text'])
            :attr('class', 'mod-table-cell-stat')
            :done()
        :tag('td')
            :wikitext(table.concat(tags, ', '))
            :attr('class', 'mod-table-cell-tags')
            :done()
end


-- ----------------------------------------------------------------------------
-- Templates
-- ----------------------------------------------------------------------------

--
-- Template: Mod
--

-- p.mod{id = "LocalIncreasedPhysicalDamagePercentUniqueOneHandSword2", name = "", mod_group = "LocalPhysicalDamagePercent", domain = "1", generation_type = "3", required_level = "1", mod_type = "LocalPhysicalDamagePercent", stat_text = "150% increased Physical Damage", stat1_id = "local_physical_damage_+%", stat1_min = "150", stat1_max = "150"}
function p.mod(frame)
    -- Get args
    g_args = getArgs(frame, {
        parentFirst = true
    })
    g_frame = util.misc.get_frame(frame)
    
    --
    -- Validation & semantic properties
    --
    
    -- Validate single value properties and set them
    
    local map = {
        {
            name = 'id',
            property = 'Is mod',
            wikitext = 'Mod Id',
        },
        {
            name = 'name',
            property = 'Has name',
            wikitext = 'Name',
        },
        {
            name = 'mod_group',
            property = 'Has mod group',
            wikitext = 'Group',
        },
        {
            name = 'mod_type',
            property = 'Has mod type',
            wikitext = 'Mod type',
        },
        {
            name = 'domain',
            property = 'Has mod domain',
            func = h.validate.number{min=1, max=11},
            wikitext = 'Mod domain',
            display = function (value)
                return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')'
            end,
        },
        {
            name = 'generation_type',
            property = 'Has mod generation type',
            func = h.validate.number{min=1, max=10},
            wikitext = 'Generation type',
            display = function (value)
                return game.constants.mod.generation_types[value]['short_upper'] .. ' (Id: ' .. value .. ')'
            end,
        },
        {
            name = 'required_level',
            property = 'Has level requirement',
            func = h.validate.number{min=0, max=100},
            wikitext = 'Req. level',
        },
        {
            name = 'stat_text',
            property = 'Has stat text',
            wikitext = 'Effect',
        },
        {
            name = 'granted_buff_id',
            property = 'Has granted buff id',
            wikitext = 'Granted Buff Id',
        },
        {
            name = 'granted_buff_value',
            property = 'Has granted buff value',
            wikitext = 'Granted Buff Value',
        },
        {
            name = 'granted_skill',
            property = 'Has granted skill id',
            wikitext = 'Granted Skill',
        },
    }
    
    h.handle_mapped_property_args(map)
    
    -- Validate & set multi value property
    
    if g_args['tags'] == nil then
        g_args['tags'] = {}
    else
        local tags = mw.text.split(g_args['tags'], ', ')
        
        g_args['tags'] = tags
    
        properties = {}
        properties['Has tag'] = table.concat(tags, ';')
        properties['+sep'] = ';'
        g_frame:callParserFunction('#set:', properties)
    end
    
    -- Validate % set the stat multi value property
    local i
    local id
    local value
    for i=1, 5 do
        local id = {
            id = 'stat' .. i .. '_id',
            min = 'stat' .. i .. '_min',
            max = 'stat' .. i .. '_max',
        }
        
        local value = {
            id = g_args[id.id],
            min = g_args[id.min],
            max = g_args[id.max],
        }
        
        if not (value.id == nil and value.min == nil and value.max == nil) then
            local onenil = nil
            for _, k in ipairs({'id', 'min', 'max'}) do
                if value[k] == nil then
                    onenil = k
                    break
                end
            end

            if onenil ~= nil then
                error('"' .. id[onenil] .. '" is not set')
            end
            
            properties = {}
            properties['Is stat number'] = i
            properties['Has stat id'] = value.id
            properties['Has minimum stat value'] = h.validate.number({})(id.min)
            properties['Has maximum stat value'] = h.validate.number({})(id.max)
            
            g_frame:callParserFunction(string.format('#subobject: stat %s', i), properties)
        end
    end
    
    -- Validate & set spawn weight multi value property
    i = 0
    id = nil
    value = nil

    repeat
        i = i + 1
        id = {
            tag = string.format('spawn_weight%s_tag', i),
            value = string.format('spawn_weight%s_value', i),
        }
    
        value = {
            tag = g_args[id.tag],
            value = g_args[id.value],
        }
        
        if value.tag ~= nil and value.value ~= nil then
            properties = {}
            properties['Is tag number'] = i
            properties['Has tag'] = value.tag
            properties['Has spawn weight'] = h.validate.number({min=0})(id.value)
            
            g_frame:callParserFunction(string.format('#subobject: spawn weight %s', i), properties)
            
        elseif not (value.tag == nil and value.value == nil) then
            error ('Both ' .. id.tag .. ' and ' .. id.value .. ' must be specified')
        end
    until value.tag == nil
    
    -- Validate & set generation weight multi value property
    i = 0
    id = nil
    value = nil

    repeat
        i = i + 1
        id = {
            tag = string.format('generation_weight%s_tag', i),
            value = string.format('generation_weight%s_value', i),
        }
    
        value = {
            tag = g_args[id.tag],
            value = g_args[id.value],
        }
        
        if value.tag ~= nil and value.value ~= nil then
            properties = {}
            properties['Is tag number'] = i
            properties['Has tag'] = value.tag
            properties['Has generation weight'] = h.validate.number({min=0})(id.value)
            
            g_frame:callParserFunction(string.format('#subobject: generation weight %s', i), properties)
            
        elseif not (value.tag == nil and value.value == nil) then
            error ('Both ' .. id.tag .. ' and ' .. id.value .. ' must be specified')
        end
    until value.tag == nil
    
    --
    -- Display
    --
    
    local container = mw.html.create('div')
    container
        :attr('class', 'modbox')
    
    -- core stats
    
    local tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable')
    
    for _, data in ipairs(map) do
        local text
        if data.display == nil then
            text = g_args[data.name]
        else
            text = data.display(g_args[data.name])
        end
        
        tbl
            :tag('tr')
                :tag('th')
                    :wikitext(string.format('[[Property:%s|%s]]', data.property, data.wikitext))
                    :done()
                :tag('td')
                    :wikitext(text)
                    :done()
                :done()
            :done()
    end
    
    tbl
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Has tag|Tags]]')
                :done()
            :tag('td')
                :wikitext(table.concat(g_args['tags'], ', '))
                :done()
            :done()
        :done()
    
    -- stat table
    
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 4)
                :wikitext('Stats')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Is stat number|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has stat id|Stat Id]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has minimum stat value|Minimum]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has maximum stat value|Maximum]]')
                :done()
            :done()
        :done()
        
    for i=1, 5 do
        local value = {
            id = g_args['stat' .. i .. '_id'],
            min = g_args['stat' .. i .. '_min'],
            max = g_args['stat' .. i .. '_max'],
        }
        
        if value.id then
            tbl
                :tag('tr')
                    :tag('td')
                        :wikitext(i)
                        :done()
                    :tag('td')
                        :wikitext(value.id)
                        :done()
                    :tag('td')
                        :wikitext(value.min)
                        :done()
                    :tag('td')
                        :wikitext(value.max)
                        :done()
                    :done()
                :done()
        end
    end
    
    -- spawn weight table
    
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext('Spawn Weights')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Is tag number|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has tag|Tag]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has spawn weight|Weight]]')
                :done()
            :done()
        :done()
        
    i = 0
    value = nil
    repeat
        i = i + 1
        value = {
            tag = g_args[string.format('spawn_weight%s_tag', i)],
            value = g_args[string.format('spawn_weight%s_value', i)],
        }
        
        if value.tag then
            tbl
                :tag('tr')
                    :tag('td')
                        :wikitext(i)
                        :done()
                    :tag('td')
                        :wikitext(value.tag)
                        :done()
                    :tag('td')
                        :wikitext(value.value)
                        :done()
                    :done()
                :done()
        end
    until value.tag == nil
    
    -- generation weight table
    
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext('Generation Weights')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Is tag number|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has tag|Tag]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has generation weight|Weight]]')
                :done()
            :done()
        :done()
    
    i = 0
    value = nil
    repeat
        i = i + 1
        value = {
            tag = g_args[string.format('generation_weight%s_tag', i)],
            value = g_args[string.format('generation_weight%s_value', i)],
        }
        
        if value.tag then
            tbl
                :tag('tr')
                    :tag('td')
                        :wikitext(i)
                        :done()
                    :tag('td')
                        :wikitext(value.tag)
                        :done()
                    :tag('td')
                        :wikitext(value.value)
                        :done()
                    :done()
                :done()
        end
    until value.tag == nil
    
    -- Generic messages on the page
    
    out = {}
    
    if mw.ustring.find(g_args['id'], '_') then
        out[#out+1] = g_frame:expandTemplate{ title = 'Incorrect title', args = { title=g_args['id'] } } .. '\n\n\n'
    end
    
    if g_args['name'] then
        out[#out+1] = string.format("'''%s''' is the internal id of modifier '''%s'''.\n", g_args['id'], g_args['name'])
    else
        out[#out+1] = string.format("'''%s''' is the internal id of an unnamed modifier.\n", g_args['id'], g_args['name'])
    end
    
    -- Categories
    
    cats = {'Mods'}
    
    -- Done -> output
    
    return tostring(container) .. util.misc.add_category(cats) .. '\n' .. table.concat(out) 
end

--
-- Template: SMW query mods
-- 

function p.query_mods(frame)
    -- Args
    g_args = getArgs(frame, {
        parentFirst = true
    })
    g_frame = util.misc.get_frame(frame)
    
    g_args.colspan = 4
    
    local conditions = {}
    conditions[#conditions+1] = 'concept'
    if g_args.tag then
        conditions[#conditions+1] = string.format('[[Has subobject::<q>[[-Has subobject::+]] [[Has spawn weight::>>0]] [[Has tag::%s]]</q>]]', g_args.tag)
    end
    
    g_args.header_level = g_args.header_level or 2
    
    -- Fields
    local fields = {}
    fields[#fields+1] = '?Is mod'
    fields[#fields+1] = '?Has name'
    fields[#fields+1] = '?Has level requirement'
    fields[#fields+1] = '?Has mod group'
    fields[#fields+1] = '?Has stat text'
    -- parameters
    local parameters = {}
    parameters.sort = 'Has mod group, '
    parameters.limit = 1000 -- lets see
    
    local data = {}
    data.header = {
        prefix = 'Prefix',
        suffix = 'Suffix',
    }
    
    local out = {}
    for _, v in ipairs({'prefix', 'suffix'}) do
        out[#out+1] = string.format('<h%i>%s</h%i>', g_args.header_level, data.header[v], g_args.header_level)
        conditions[1] = string.format('[[Concept:Spawnable named %s item mods]]', v)
        
        local query
        local results
        --
        -- Query tags
        --
        query = {}
        query[#query+1] = string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' '))
        query[#query+1] = '[[Has tag::+]]'
        query[#query+1] = '[[Has spawn weight::+]]'
        --query[#query+1] = '[[Has spawn weight::>>0]]'
        query[#query+1] = '?Has tag'
        query[#query+1] = '?Has spawn weight#' -- need native number
        query.limit = 1000
        query.offset = 0
        -- Tag order is very important
        query.sort = ', Is tag number'
        
        local tags = {}
        -- this works because lua only considers nil to be false >_>
        while query.offset do
            results = util.smw.query(query, g_frame)
            query.offset = query.offset + #results
            -- terminates the while if enough reuslts have been fetched
            if query.offset % 1000 ~= 0 then
                query.offset = nil
            end
            
            for _, row in ipairs(results) do
                local page, _ = string.gsub(row[1], '#_[%x]+', '')
                if tags[page] == nil then
                    tags[page] = {}
                end
                
                local text
                if tonumber(row['Has spawn weight']) > 0 then
                    text = '[[File:Yes.png|yes|link=]]'
                else
                    text = '[[File:No.png|no|link=]]'
                end
                
                tags[page][#tags[page]+1] = string.format('%s %s', row['Has tag'], text)
            end
        end
        
        --
        -- Query mods
        --
        query = {}
        for _, v in ipairs(conditions) do
            query[#query+1] = v
        end
        for _, v in ipairs(fields) do
            query[#query+1] = v
        end
        for k, v in pairs(parameters) do
            query[k] = v
        end
        
        results = util.smw.query(query, g_frame)
        
        local last = ''
        local tbl = ''
        for _, row in ipairs(results) do
            local current = string.gsub(row['Is mod'], '%d+.*', '%%d.*')
            if string.match(last, current) then
                h.format_mod(tbl, row, tags[row[1]])
            else
                out[#out+1] = tostring(tbl)
                tbl = h.create_header(row)
                h.format_mod(tbl, row, tags[row[1]])
            end
            last = row['Is mod']
        end
        
        -- add the last table
        out[#out+1] = tostring(tbl)
    end
    
    return table.concat(out, '')
end

--
-- Template: SMW mod table
-- 

-- =p.mod_list{'Strength1', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'ColdCritMultiplier', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'MapMultipleExilesMap2Tier', 'asdasda', 'Area yields 15% more Items<br>8% increased Rarity of Items found in this Area<br>Area is inhabited by 2 additional Rogue Exiles<br>Extra monsters ignore rarity bias (Hidden)<br>+14% Monster pack size', userparam='extra_rows=1, type=map, effect_rowid=2'}

function p.mod_list(frame)
    local types = {'map', 'jewel'}
    
    -- Args
    g_args = getArgs(frame, {
        parentFirst = true
    })
    g_frame = util.misc.get_frame(frame)
    
    --
    local args = util.string.split_args(g_args.userparam, {sep=', '})
    g_args.userparam = args
    
    args.extra_rows = (tonumber(args.extra_rows) or 0)
    if args.show_tags == nil then
        args.show_tags = true
    else
        args.show_tags = util.cast.boolean(args.show_tags)
    end
    args.effect_rowid = (tonumber(args.effect_rowid) or 0) + 1

    tr = mw.html.create('tr')
    tr
        :tag('td')
            :attr('data-sort-value', g_args[2] or g_args[1])
            :wikitext(string.format('[[%s|%s]]', g_args[1], g_args[2] or g_args[1]))
            :done()
    
    local i = 2
    local row_max = i + args.extra_rows
    local text
    while i < row_max do
        i = i + 1
        text = g_args[i] or ''
        text = table.concat(mw.text.split(text, ';', true), '<br>')
        
        if args.type == 'map' and i == args.effect_rowid then
            text = mw.text.split(text, '<br>', true)
            
            local map = {
                '%d+%% increased Quantity of Items found in this Area',
                '%d+%% increased Rarity of Items found in this Area',
                '%+%d+%% Monster pack size',
            }
            out = {}
            
            local valid
            for k, v in pairs(text) do
                valid = true
                for _, pattern in ipairs(map) do
                    if mw.ustring.find(v, pattern) ~= nil then
                        valid = false
                        break
                    end
                end
                
                if valid then
                    table.insert(out, v)
                end
            end
            
            text = table.concat(out, '<br>')
        end
            
        tr
            :tag('td')
                :wikitext(text)
                :done()
    end
    
    
    local query
    local result
    
    if args.type == 'map' then
        query = {
            string.format('[[-Has subobject::%s]]', g_args[1]),
            '[[Has stat id::+]]',
            '?Has stat id',
            '?Has minimum stat value',
            '?Has maximum stat value',
        }
        
        result = util.smw.query(query, g_frame)
        
        local stat_map = {
            ['map_item_drop_quantity_+%'] = {disp=0, sort=0},
            ['map_item_drop_rarity_+%'] = {disp=0, sort=0},
            ['map_pack_size_+%'] = {disp=0, sort=0},
        }
        
        local stat
        for _, row in ipairs(result) do
            stat = stat_map[row['Has stat id']]
            if stat ~= nil then
                stat.sort = (row['Has minimum stat value'] + row['Has maximum stat value']) / 2
                if row['Has minimum stat value'] ~= row['Has minimum stat value'] then
                    stat.disp = string.format('(%s to %s)', row['Has minimum stat value'], row['Has maximum stat value'])
                else
                    stat.disp = row['Has minimum stat value']
                end
            end
        end
        
        for _, k in ipairs({'map_item_drop_quantity_+%', 'map_item_drop_rarity_+%', 'map_pack_size_+%'}) do
            stat = stat_map[k]
            tr
                :tag('td')
                    :attr('data-sort-value', stat.sort)
                    :wikitext(stat.disp)
                    :done()
                :done()
        end
    end
    
    local tags
    if args.show_tags or args.type == 'jewel' then
        query = {
            string.format('[[-Has subobject::%s]]', g_args[1]),
            '[[Has tag::+]]',
            '?Has tag',
            '?Has spawn weight',
            sort='Is tag number',
        }

        tags = {}
        result = util.smw.query(query, g_frame)
    end
    
    if args.type == 'jewel' then
        local jewels = {
            dex=0,
            str=0,
            int=0,
            pris=0,
        }
        
        local cast_tbl = {
            not_dex={'str', 'int'},
            not_int={'str', 'dex'},
            not_str={'int', 'dex'},
            default={'str','int','dex','pris'},
        }
        
        local i = #result
        local row
        local cast
        while i > 0 do
            row = result[i]
            cast = cast_tbl[row['Has tag']]
            if cast ~= nil then
                for _, k in ipairs(cast) do
                    jewels[k] = row['Has spawn weight']
                end
            end
            
            i = i - 1
        end
        
        tr
            :tag('td')
                :attr('class', 'table-cell-dex')
                :wikitext(jewels.dex)
                :done()
            :tag('td')
                :attr('class', 'table-cell-int')
                :wikitext(jewels.int)
                :done()
            :tag('td')
                :attr('class', 'table-cell-str')
                :wikitext(jewels.str)
                :done()
            :tag('td')
                :attr('class', 'table-cell-prismatic')
                :wikitext(jewels.pris)
                :done()
            :done()
    end
    
    if args.show_tags then
        for _, row in ipairs(result) do
            tags[#tags+1] = string.format('%s %s', row['Has tag'], row['Has spawn weight'])
        end
        
        tr
            :tag('td')
                :wikitext(table.concat(tags, '<br>'))
                :done()
            :done()
    end
    
    return tostring(tr)
end

function p.get_tags(frame)
	-- This function queries for the tags for a specific item.
	
	-- Args
	local g_args = getArgs(frame, {
		parentFirst = true
	})
	local g_frame = util.misc.get_frame(frame)
	
	local item_name = g_args[1]
	
	local query = {
		string.format('[[Has name::%s]]', item_name),
		'?Has tags',
		'?Has base strength requirement',
		'?Has base intelligence requirement',
		'?Has base dexterity requirement',
		'?Has item class'
	}
	local results = util.smw.query(query, g_frame)
	
	-- Remove uneccessary symbols and reverse the tag order.
	results[1]['Has tags'] = results[1]['Has tags']:gsub('(<MANY>)', ', ')
	
	function table.reverse(a)
		local res = {}
		for i = #a, 1, -1 do
			res[#res+1] = a[i]
		end
		return res
	end
	
	results[1]['Has tags'] = table.concat(table.reverse(util.string.split(results[1]['Has tags'], ', ')), ', ')
		
	return results[1]
end

function p.header(str)
	-- Replace numbers with #
	s = table.concat(util.string.split(str, '%(%d+%.*%d* to %d+%.*%d*%)'), '#')
	s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
	-- Remove links and <br>.
	s = s:gsub('%[%[(.-)%]%]', '%1'):gsub('(%a*)%|', ''):gsub('<br>', ', ')
   
   return s
end

function p.drop_down_table(frame)
	-- Misses forsaken masters currently
	
	-- Weapons
	-- p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
	-- p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
	-- p.drop_down_table{item = 'Crude Bow', header = 'Bows'}
	-- p.drop_down_table{item = 'Nailed Fist', header = 'Claws'}
	-- p.drop_down_table{item = 'Glass Shank', header = 'Daggers'}
	-- p.drop_down_table{item = 'Driftwood Club', header = 'Generic One Handed Maces'}
	-- p.drop_down_table{item = 'Driftwood Sceptre', header = 'Sceptres'}
	-- p.drop_down_table{item = 'Driftwood Maul', header = 'Two Handed Maces'}
	-- p.drop_down_table{item = 'Gnarled Branch', header = 'Staves'}
	-- p.drop_down_table{item = 'Rusted Sword', header = 'Generic One Handed Swords'}
	-- p.drop_down_table{item = 'Rusted Spike', header = 'Thrusting Swords'}
	-- p.drop_down_table{item = 'Corroded Blade', header = 'Two Handed Swords'}
	-- p.drop_down_table{item = 'Driftwood wand', header = 'Wands'}
	-- p.drop_down_table{item = 'Fishing Rod', header = 'Fishing Rods'}
	
	-- Accessories
	-- p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
	-- p.drop_down_table{item = 'Leather Belt', header = 'Belts'}
	-- p.drop_down_table{item = 'Blunt Arrow Quiver', header = 'Quivers'}
	-- p.drop_down_table{item = 'Coral Ring', header = 'Rings'}
	
	-- Armour
	-- p.drop_down_table{item = 'Plate Vest', header = 'Armour Body Armours'}
	-- p.drop_down_table{item = 'Shabby Jerkin', header = 'Evasion Body Armours'}
	-- p.drop_down_table{item = 'Simple Robe', header = 'Energy Shield Body Armours'}
	-- p.drop_down_table{item = 'Scale Vest', header = 'Armour/Evasion Body Armours'}
	-- p.drop_down_table{item = 'Chainmail Vest', header = 'Armour/Energy Shield Body Armours'}
	-- p.drop_down_table{item = 'Padded Vest', header = 'Energy Shield/Evasion Body Armours'}
	-- p.drop_down_table{item = 'Sacrificial Garb', header = 'Armour/Energy Shield/Evasion Body Armours'}
	
	-- p.drop_down_table{item = 'Iron Greaves', header = 'Armour Boots'}
	-- p.drop_down_table{item = 'Rawhide Boots', header = 'Evasion Boots'}
	-- p.drop_down_table{item = 'Wool Shoes', header = 'Energy Shield Boots'}
	-- p.drop_down_table{item = 'Leatherscale Boots', header = 'Armour/Evasion Boots'}
	-- p.drop_down_table{item = 'Chain Boots', header = 'Armour/Energy Shield Boots'}
	-- p.drop_down_table{item = 'Wrapped Boots', header = 'Energy Shield/Evasion Boots'}
	-- p.drop_down_table{item = 'Golden Caligae', header = 'Demigod'}
	
	-- p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour Gloves'}
	-- p.drop_down_table{item = 'Rawhide Gloves', header = 'Evasion Gloves'}
	-- p.drop_down_table{item = 'Wool Gloves', header = 'Energy Shield Gloves'}
	-- p.drop_down_table{item = 'Fishscale Gauntlets', header = 'Armour/Evasion Gloves'}
	-- p.drop_down_table{item = 'Chain Gloves', header = 'Armour/Energy Shield Gloves'}
	-- p.drop_down_table{item = 'Wrapped Mitts', header = 'Energy Shield/Evasion Gloves'}
	
	-- p.drop_down_table{item = 'Iron Hat', header = 'Armour Helmets'}
	-- p.drop_down_table{item = 'Leather Cap', header = 'Evasion Helmets'}
	-- p.drop_down_table{item = 'Vine Circlet', header = 'Energy Shield Helmets'}
	-- p.drop_down_table{item = 'Battered Helm', header = 'Armour/Evasion Helmets'}
	-- p.drop_down_table{item = 'Rusted Coif', header = 'Armour/Energy Shield Helmets'}
	-- p.drop_down_table{item = 'Scare Mask', header = 'Energy Shield/Evasion Helmets'}
	
	-- p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour Shields'}
	-- p.drop_down_table{item = 'Goathide Buckler', header = 'Evasion Shields'}
	-- p.drop_down_table{item = 'Twig Spirit Shield', header = 'Energy Shield Shields'}
	-- p.drop_down_table{item = 'Rotted Round Shield', header = 'Armour/Evasion Shields'}
	-- p.drop_down_table{item = 'Plank Kite Shield', header = 'Armour/Energy Shield Shields'}
	-- p.drop_down_table{item = 'Spiked Bundle', header = 'Energy Shield/Evasion Shields'}

	-- Args
    local g_args = getArgs(frame, {
        parentFirst = true
    })
    local g_frame = util.misc.get_frame(frame)
    
	local get_tags = p.get_tags{g_args.item}
	local tags = {}
	tags = util.string.split(get_tags['Has tags'], ', ')
	
	
	
	local header = g_args['header']
	if header == nil then
		header = table.concat(tags, ', ')
	end
	
	-- Conditions
	local conditions = {}
    conditions[1] = 'concept' -- Reserve for Concepts.
    conditions[#conditions+1] = string.format('[[Has subobject::<q> [[Has spawn weight::>>0]] [[Has tag::%s]] </q>]]', table.concat(tags, ' || '))
 
    -- Fields
    local fields = {
		'?Is mod',
		'?Has name',
		'?Has level requirement',
		'?Has mod group',
		'?Has mod type',
		'?Has stat text'
	}

	-- Parameters
    local parameters = {
		sort = 'Has mod group, Has mod type, Has level requirement',
		limit = 1000 -- lets see
	}
    
    local data = {}
    data = {
        prefix = {
			header = 'Prefix',
			condition = '[[Concept:Spawnable named prefix item mods]]',
		},
        suffix = {
			header = 'Suffix',
			condition = '[[Concept:Spawnable named suffix item mods]]',
		},
		master = {
			header = 'Forsaken masters',
			condition = '[[Concept:AAAAAAAAAAAAAAAAAAAA]]',
		},
    }
	
	-- Define the output.
	local out = {}

	out[#out+1] = string.format('==%s== \n', header)
	out[#out+1] = '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[Collapse All]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[Expand All]</div></div>'	
	out[#out+1] = string.format('The table below displays the available [[modifiers]] for [[item]]s such as %s.<br><br><br>', g_frame:expandTemplate{title = 'Item link', args = {get_tags[1]}})
	
	local results = {}
    for i_type, generation_type in ipairs({'prefix', 'suffix'}) do -- Change to data eventually.
        --
        -- Query mods
        --
		conditions[1] = data[generation_type]['condition']
		local query = {}
        for _, v in ipairs(conditions) do
            query[#query+1] = v
        end
        for _, v in ipairs(fields) do
            query[#query+1] = v
        end
        for k, v in pairs(parameters) do
            query[k] = v
        end
		results[generation_type] = util.smw.query(query, g_frame)
        
		local container = mw.html.create('div')
			:attr('style', 'vertical-align:top; display:inline-block;')
		
		local headers = container 	
		headers
			:tag('h3')
				:wikitext(string.format('%s', data[generation_type]['header']))
				:done()
			:done()
		
		local results_tags = {}
		local tags_weighting = {}
		local last
		for i, v in ipairs(results[generation_type]) do -- Loop through all the results from the smw query.
			-- Query tags
			local mod_id = results[generation_type][i]['Is mod']:gsub('_','~') -- mod id and {{PAGENAME}} can sometimes be different. This tag query method is similar to other functions, so this bug might be noticeable on other places as well?
			query_tags = {
				string.format('[[-Has subobject::%s]]', mod_id),
				'?Has tag',
				'?Has spawn weight',
				'?Is tag number',
				sort='Is tag number',
			}
			results_tags[i] = util.smw.query(query_tags, g_frame) 
			
			tags_weighting[i] = {}
			tags_weighting[i][1] = results_tags[i][1]
			local tags_spawn = {}
			for f, row in ipairs(results_tags[i]) do
				local spawn_weight = row['Has spawn weight']:gsub(',', '') -- Remove the thousand separator that the numbers may have.
				tags_weighting[i][row['Has tag']] = tonumber(spawn_weight)
			end
			
			local j = 0 
			repeat  
				j = j+1
				if (tags_weighting[i][tags[j]] or -1) > 0 then 
					if results[generation_type][i]['Has mod type'] ~= last then -- If the current mod type is different to the last mod type, create a new table.
						local mod_appl = 'Global' -- Assume the mod is global if local isn't specified in the mod id.
						if results[generation_type][i]['Is mod']:find('Local') ~= nil then 
							mod_appl = 'Local'
						end
						
						local tbl_caption = p.header(results[generation_type][i]['Has stat text'])
						last = results[generation_type][i]['Has mod type']
						tbl = container:tag('table')
						tbl
							:attr('class', 'mw-collapsible mw-collapsed')
							:attr('style', 'text-align:left; line-height:1.60em; width:800px;') -- white-space:nowrap; 
							:tag('th')
								:attr('style', 'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;')
								:attr('colspan', '3')
								:wikitext(string.format('%s (%s)', g_frame:expandTemplate{title = 'c', args = {'mod', tbl_caption}}, mod_appl))
								:done()
							:done()	
					end
					tbl
						:tag('tr')
							:tag('td')
								:attr('width', '150')
								:wikitext(string.format('[[%s|%s]]', results[generation_type][i][1], results[generation_type][i]['Has name']:gsub('%s', '&nbsp;')))
							:done()
							:tag('td')
								:attr('width', '1')
								:wikitext(string.format('%s&nbsp;%s',game.level_requirement['short_upper']:gsub('%s', '&nbsp;'), results[generation_type][i]['Has level requirement']))
							:done()		
							:tag('td')
								:attr('width', '*')
								:wikitext(string.format('%s', g_frame:expandTemplate{title = 'c', args = {'mod', results[generation_type][i]['Has stat text']:gsub('<br>', ', ')}}))
							:done()			
							
				end
			until (tags_weighting[i][tags[j]] or -1) >= 0
		end
		out[#out+1] = tostring(container)
    end
	
	return	table.concat(out,'')
end

return p