Module:Mod/sandbox: Difference between revisions

From Path of Exile Wiki
Jump to navigation Jump to search
>Illviljan
mNo edit summary
>Vinifera7
No edit summary
Line 3: Line 3:
--
--


local getArgs = require('Module:Arguments').getArgs
local m_util = require('Module:Util')
local m_util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local m_cargo = require('Module:Cargo')
local game = require('Module:Game')
local m_game = require('Module:Game')
local f_item_link = require('Module:Item link').item_link
local f_item_link = require('Module:Item link').item_link
local cargo = mw.ext.cargo


local p = {}
local p = {}
Line 21: Line 20:


local i18n = {
local i18n = {
     args = {
     categories = {
         --
        mods = 'Mods',
         -- Mod template
    },
         --
   
    tooltips = {
         -- intro texts
        intro_named_id = "'''%s''' is the internal id of [[modifier]] '''%s'''.\n",
        intro_unnamed_id = "'''%s''' is the internal id of an unnamed [[modifier]].\n",
       
         -- core data
        id = 'Mod Id',
        name = 'Name',
        mod_group = 'Group',
        mod_type = 'Mod type',
        domain = 'Domain',
        domain_fmt = '%s (Id: %s)',
        generation_type = 'Generation type',
        generation_type_fmt = '%s (Id: %s)',
        required_level = 'Req. Level',
        stat_text = 'Effect',
        granted_buff_id = 'Granted Buff Id',
        granted_buff_value = 'Granted Buff Value',
        granted_skill = 'Granted Skill',
        tags = 'Tags',
        tier_text = 'Tier Text',
       
         -- shared
        ordinal = '#',
       
        -- stats
        stats = 'Stats',
        stat_id = 'Stat Id',
        min = 'Minimum',
        max = 'Maximum',
          
          
         -- main
         -- weights
         id = 'id',
         spawn_weights = 'Spawn weights',
         name = 'name',
         generation_weights = 'Generation weights',
         mod_group = 'mod_group',
         tag = 'Tag',
         mod_type = 'mod_type',
         weight = 'Weight',
        domain = 'domain',
        generation_type = 'generation_type',
        required_level = 'required_level',
        stat_text = 'stat_text',
        granted_buff_id = 'granted_buff_id',
        granted_buff_value = 'granted_buff_value',
        granted_skill = 'granted_skill',
        tags = 'tags',
          
          
         -- sell price
         -- sell price
         sell_price_prefix = 'sell_price',
         sell_price = 'Modifier sell price',
        item_name = 'name',
         item = 'Item',
         amount = 'amount',
     },
     },
      
      
Line 53: Line 73:
         sell_price_missing_argument = 'Both %s and %s must be specified',
         sell_price_missing_argument = 'Both %s and %s must be specified',
     },
     },
 
    drop_down_table = {
        collapse_all = 'Collapse all',
        expand_all = 'Expand all',
        table_intro = 'The table below displays the available [[modifiers]] for [[item]]s such as',
        prefix = 'Prefix',
        suffix = 'Suffix',
        corrupted = 'Corrupted'     
    },
}
}


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- m_utility / Helper functions
-- utility / Helper functions
-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------


local h = {}
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 (tpl_args, frame, value)
        return m_util.cast.number(value, args)
    end
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
function h.cargo_query(tpl_args)
    --[[
    Returns a Cargo query of all the results, even if there are more
    results than the maximum query limit. It also adds popular fields.
   
    tpl_args should include these keys:
    tpl_args.tables
    tpl_args.fields
    tpl_args.q_*
   
    ]]
   
    local tables = m_util.string.split(tpl_args.tables, ', ')
    local fields = m_util.string.split(tpl_args.fields, ', ')
   
    -- Parse query arguments
    local query = {
        -- Workaround: Fix duplicates but removes other rows as well.
        groupBy = tables[1] .. '._pageID',
        limit = 5000,
        offset = 0,
    }
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
            query[string.sub(key, 3)] = value
        end
    end
   
    -- Add commonly used fields:
    local fields_base = {
        '_pageNamespace',
        '_pageTitle',
        '_ID',
        '_rowID',
        '_pageID',
        '_pageName'
        -- '_value'
    }
    for _, tbl in ipairs(tables) do
        for _, fld in ipairs(fields_base) do
            fields[#fields+1] = string.format('%s.%s', tbl, fld)
        end
    end
   
    -- Query cargo table. If there are too many results then repeat,
    -- offset, re-query and add the remaining results:
    local results = {}     
    repeat
        local result = mw.ext.cargo.query(
            table.concat(tables, ', '),
            table.concat(fields, ', '),
            query
        )
        query.offset = query.offset + #result
        for _,v in ipairs(result) do
            results[#results + 1] = v
        end
    until #result < query.limit
   
    return results
end


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
Line 213: Line 93:
     main = {
     main = {
         table = 'mods',
         table = 'mods',
         order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags'},
         display_order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
         parse_order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'stat_text_raw', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags'},
         order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'stat_text_raw', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
         fields = {
         fields = {
             id = {
             id = {
                 name = i18n.args.id,
                 name = 'id',
                 field = i18n.args.id,
                 field = 'id',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Mod Id',
                 wikitext = i18n.tooltips.id,
             },
             },
             name = {
             name = {
                 name = i18n.args.name,
                 name = 'name',
                 field = i18n.args.name,
                 field = 'name',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Name',
                 wikitext = i18n.tooltips.name,
             },
             },
             mod_group = {
             mod_group = {
                 name = i18n.args.mod_group,
                 name = 'mod_group',
                 field = i18n.args.mod_group,
                 field = 'mod_group',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Group',
                 wikitext = i18n.tooltips.mod_group,
             },
             },
             mod_type = {
             mod_type = {
                 name = i18n.args.mod_type,
                 name = 'mod_type',
                 field = i18n.args.mod_type,
                 field = 'mod_type',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Mod type',
                 wikitext = i18n.tooltips.mod_type,
             },
             },
             domain = {
             domain = {
                 name = i18n.args.domain,
                 name = 'domain',
                 field = i18n.args.domain,
                 field = 'domain',
                 type = 'Integer',
                 type = 'Integer',
                func = h.validate.number{min=1, max=15},
                 wikitext = 'Mod domain',
                 wikitext = 'Mod domain',
                 display = function (value)
                 display = function (value)
                     return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')'
                     return string.format(i18n.tooltips.domain_fmt, m_game.constants.mod.domains[value]['short_upper'], value)
                 end,
                 end,
             },
             },
             generation_type = {
             generation_type = {
                 name = i18n.args.generation_type,
                 name = 'generation_type',
                 field = i18n.args.generation_type,
                 field = 'generation_type',
                 type = 'Integer',
                 type = 'Integer',
                 func = h.validate.number{min=1, max=12},
                 wikitext = i18n.tooltips.generation_type,
                wikitext = 'Generation type',
                 display = function (value)
                 display = function (value)
                     return game.constants.mod.generation_types[value]['short_upper'] .. ' (Id: ' .. value .. ')'
                     return string.format(i18n.tooltips.generation_type_fmt, m_game.constants.mod.generation_types[value]['short_upper'], value)
                 end,
                 end,
             },
             },
             required_level = {
             required_level = {
                 name = i18n.args.required_level,
                 name = 'required_level',
                 field = i18n.args.required_level,
                 field = 'required_level',
                 type = 'Integer',
                 type = 'Integer',
                 func = h.validate.number{min=0, max=100},
                 wikitext = i18n.tooltips.required_level,
                wikitext = 'Req. level',
             },
             },
             stat_text = {
             stat_text = {
                 name = i18n.args.stat_text,
                 name = 'stat_text',
                 field = i18n.args.stat_text,
                 field = 'stat_text',
                 type = 'Text',
                 type = 'Text',
                 wikitext = 'Effect',
                 wikitext = i18n.tooltips.stat_text,
             },
             },
             stat_text_raw = {
             stat_text_raw = {
Line 292: Line 169:
             },
             },
             granted_buff_id = {
             granted_buff_id = {
                 name = i18n.args.granted_buff_id,
                 name = 'granted_buff_id',
                 field = i18n.args.granted_buff_id,
                 field = 'granted_buff_id',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Granted Buff Id',
                 wikitext = i18n.tooltips.granted_buff_id,
             },
             },
             granted_buff_value = {
             granted_buff_value = {
                 name = i18n.args.granted_buff_value,
                 name = 'granted_buff_value',
                 field = i18n.args.granted_buff_value,
                 field = 'granted_buff_value',
                 type = 'Integer',
                 type = 'Integer',
                 wikitext = 'Granted Buff Value',
                 wikitext = i18n.tooltips.granted_buff_value,
             },
             },
             granted_skill = {
             granted_skill = {
                 name = i18n.args.granted_skill,
                 name = 'granted_skill',
                 field = i18n.args.granted_skill,
                 field = 'granted_skill',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Granted Skill',
                 wikitext = i18n.tooltips.granted_skill,
             },
             },
             tags = {
             tags = {
                 name = i18n.args.tags,
                 name = 'tags',
                 field = i18n.args.tags,
                 field = 'tags',
                 type = 'List (,) of String',
                 type = 'List (,) of String',
                 wikitext = 'Tags',  
                 wikitext = 'Tags',  
                func = function (tpl_args, frame, value)
                    if value == nil then
                        return {}
                    else
                        return m_util.string.split(value, ', ')
                    end
                end,
                func_smw = function(tpl_args, frame)
                    return table.concat(tpl_args.tags, ';') 
                end,
                func_cargo = function(tpl_args, frame)
                    return table.concat(tpl_args.tags, ',') 
                end,
                 display = function(value)
                 display = function(value)
                     return table.concat(value, ', ')   
                     return table.concat(value, ', ')   
                 end,                 
                 end,
                 default = {},
            },
            tier_text = {
                name = 'tier_text',
                field = 'tier_text',
                type = 'Text',
                wikitext = i18n.tooltips.tier_text,
             },
             },
         },
         },
Line 338: Line 209:
         fields = {
         fields = {
             name = {
             name = {
                 name = i18n.args.item_name,
                 name = 'name',
                 field = i18n.args.item_name,
                 field = 'name',
                 type = 'String',
                 type = 'String',
                 func = function (value) return value end,
                 func = function (value) return value end,
             },
             },
             amount = {
             amount = {
                 name = i18n.args.amount,
                 name = 'amount',
                 field = i18n.args.amount,
                 field = 'amount',
                 type = 'Integer',
                 type = 'Integer',
                 func = tonumber,
                 func = tonumber,
Line 353: Line 224:
}
}


p.table_main = m_util.cargo.declare_factory{data=mod_map.main}
p.table_main = m_cargo.declare_factory{data=mod_map.main}
p.table_mod_sell_prices = m_util.cargo.declare_factory{data=mod_map.mod_sell_prices}
p.table_mod_sell_prices = m_cargo.declare_factory{data=mod_map.mod_sell_prices}


function p.table_mod_stats(frame)
function p.table_mod_stats(frame)
     m_util.cargo.declare(frame, {
     m_cargo.declare(frame, {
         _table = 'mod_stats',
         _table = 'mod_stats',
         id = 'String',
         id = 'String',
Line 380: Line 251:
     -- Validate single value properties and set them
     -- Validate single value properties and set them
      
      
     local cargo_data = {
     m_util.args.from_cargo_map{
         _table = mod_map.main.table,
        tpl_args=tpl_args,
        frame=frame,
         table_map=mod_map.main,
     }
     }
   
    for _, key in pairs(mod_map.main.parse_order) do
        data = mod_map.main.fields[key]
        local value
        if data.func ~= nil then
            if data.name then
                value = data.func(tpl_args, frame, tpl_args[data.name])
            else
                value = data.func(tpl_args, frame)
            end
        else
            value = tpl_args[data.name]
        end
       
        tpl_args[key] = value
        if data.field ~= nil then
            if data.func_cargo then
                cargo_data[data.field] = data.func_cargo(tpl_args, frame)
            else
                cargo_data[data.field] = value
            end
        end
    end
    m_util.cargo.store(frame, cargo_data)
      
      
     -- Validate % set the stat subobjects
     -- Validate % set the stat subobjects
     m_util.args.stats(tpl_args, {frame=frame})
     m_util.args.stats(tpl_args, {frame=frame})
     for _, stat_data in pairs(tpl_args.stats) do
     for _, stat_data in pairs(tpl_args.stats) do
         m_util.cargo.store(frame, {
         m_cargo.store(frame, {
             _table = 'mod_stats',  
             _table = 'mod_stats',  
             id = stat_data.id,
             id = stat_data.id,
Line 441: Line 288:
         value = {}
         value = {}
         for key, data in pairs(mod_map.mod_sell_prices.fields) do
         for key, data in pairs(mod_map.mod_sell_prices.fields) do
             id[key] = string.format('%s%s_%s', i18n.args.sell_price_prefix, i, data.name)
             id[key] = string.format('%s%s_%s', 'sell_price', i, data.name)
             value[key] = data.func(tpl_args[id[key]])
             value[key] = data.func(tpl_args[id[key]])
         end
         end
Line 460: Line 307:
                 cargo_data[data.field] = value[key]
                 cargo_data[data.field] = value[key]
             end
             end
             m_util.cargo.store(frame, cargo_data)
             m_cargo.store(frame, cargo_data)
              
              
             sell_prices[#sell_prices+1] = value
             sell_prices[#sell_prices+1] = value
Line 483: Line 330:
         :attr('class', 'wikitable')
         :attr('class', 'wikitable')
      
      
     for _, key in ipairs(mod_map.main.order) do
     for _, key in ipairs(mod_map.main.display_order) do
         local data = mod_map.main.fields[key]
         local data = mod_map.main.fields[key]
         local text
         local text
Line 503: Line 350:
             :done()
             :done()
     end
     end
   
    tbl
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Has tag|Tags]]')
                :done()
            :tag('td')
                :wikitext(table.concat(tpl_args['tags'], ', '))
                :done()
            :done()
        :done()
      
      
     -- stat table
     -- stat table
Line 523: Line 359:
             :tag('th')
             :tag('th')
                 :attr('colspan', 4)
                 :attr('colspan', 4)
                 :wikitext('Stats')
                 :wikitext(i18n.tooltips.stats)
                 :done()
                 :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Is stat number|#]]')
                 :wikitext(i18n.tooltips.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has stat id|Stat Id]]')
                 :wikitext(i18n.tooltips.stat_id)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has minimum stat value|Minimum]]')
                 :wikitext(i18n.tooltips.min)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has maximum stat value|Maximum]]')
                 :wikitext(i18n.tooltips.max)
                 :done()
                 :done()
             :done()
             :done()
Line 577: Line 413:
             :tag('th')
             :tag('th')
                 :attr('colspan', 3)
                 :attr('colspan', 3)
                 :wikitext('Spawn Weights')
                 :wikitext(i18n.tooltips.spawn_weights)
                 :done()
                 :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Is tag number|#]]')
                 :wikitext(i18n.tooltips.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has tag|Tag]]')
                 :wikitext(i18n.tooltips.tag)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has spawn weight|Weight]]')
                 :wikitext(i18n.tooltips.weight)
                 :done()
                 :done()
             :done()
             :done()
Line 627: Line 463:
             :tag('th')
             :tag('th')
                 :attr('colspan', 3)
                 :attr('colspan', 3)
                 :wikitext('Generation Weights')
                 :wikitext(i18n.tooltips.generation_weights)
                 :done()
                 :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Is tag number|#]]')
                 :wikitext(i18n.tooltips.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has tag|Tag]]')
                 :wikitext(i18n.tooltips.tag)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has generation weight|Weight]]')
                 :wikitext(i18n.tooltips.weight)
                 :done()
                 :done()
             :done()
             :done()
Line 676: Line 512:
             :tag('th')
             :tag('th')
                 :attr('colspan', 2)
                 :attr('colspan', 2)
                 :wikitext('Modifier sell price')
                 :wikitext(i18n.tooltips.sell_price)
                 :done()
                 :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has sell price amount|#]]')
                 :wikitext(i18n.tooltips.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has sell price item name|Item]]')
                 :wikitext(i18n.tooltips.item)
                 :done()
                 :done()
             :done()
             :done()
Line 710: Line 546:
      
      
     if tpl_args['name'] then
     if tpl_args['name'] then
         out[#out+1] = string.format("'''%s''' is the internal id of modifier '''%s'''.\n", tpl_args['id'], tpl_args['name'])
   
         out[#out+1] = string.format(i18n.tooltips.intro_named_id, tpl_args['id'], tpl_args['name'])
     else
     else
         out[#out+1] = string.format("'''%s''' is the internal id of an unnamed modifier.\n", tpl_args['id'], tpl_args['name'])
         out[#out+1] = string.format(i18n.tooltips.intro_unnamed_id, tpl_args['id'])
     end
     end
      
      
     -- Categories
     -- Categories
      
      
     cats = {'Mods'}
     cats = {i18n.categories.mods}
      
      
     -- Done -> output
     -- Done -> output
      
      
     return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)  
     return tostring(container) .. m_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 = m_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 = m_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 = m_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 = m_util.misc.get_frame(frame)
   
    --
    local args = m_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 = m_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 = m_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%-%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 = m_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.item_sell_price(frame)
    --[[
    Query and sum the vendor prices for an item.
   
    Unidentified items won't currently show the correct vendor price.
    Not sure how that is specified, nor is it used at all.
    Expanding {{il}} seems to give a nil /n the first time a new command
    is run. Doesn't always happen.
   
    = p.item_sell_price{page="Voideye"}
    = p.item_sell_price{page="Pyre"}
    = p.item_sell_price{page="Vessel of Vinktar (Lightning Penetration)"}
    ]]
   
    -- Args
    local g_args = getArgs(frame, {
        parentFirst = true
    })
    local g_frame = m_util.misc.get_frame(frame)
    -- Only the explicit modifiers are counted when vendors calculates
    -- the price.
    local condition = string.format('[[%s]]', g_args['page'])
    local query_item_mods = {
        condition,
        '?Has explicit mod ids',
        '?Has rarity',
    }
    local results_query_item_mods = m_util.smw.query(
        query_item_mods,
        g_frame
    )
    local item_mods = m_util.string.split(
        results_query_item_mods[1]['Has explicit mod ids'],
        '<MANY>'
    )   
   
    -- If the item has a Normal rarity then the sell price would be a
    -- fixed price.
    if results_query_item_mods[1]['Has rarity'] == 'Normal' then
        local amount_normal = 1
        local currency_normal = 'Scroll Fragment'
        return string.format(
            '%s %s',
            amount_normal,
            g_frame:expandTemplate{
                title = 'il',
                args = {currency_normal, currency_normal .. 's'}
            }
        )
       
        -- return string.format('%s %s', amount_normal, currency_normal)
    end
   
    local mods_sell_price = {}
    for _, modid in ipairs(item_mods) do
        local query_mod_page = {
            string.format('[[Is mod::%s]]', modid),
        }
        local mod_page = m_util.smw.query(query_mod_page, g_frame)
       
        local query_mod_sell_price = {
            string.format('[[-Has subobject::%s]]', mod_page[1][1]),
            '?Has sell price amount#',
            '?Has sell price item name',
        }
        local results = m_util.smw.query(query_mod_sell_price, g_frame)
       
        for _, k in ipairs(results) do
            if k['Has sell price amount'] ~= '' then
                if mods_sell_price[k['Has sell price item name']] == nil then
                    mods_sell_price[k['Has sell price item name']] =
                        k['Has sell price amount']
                else
                    mods_sell_price[k['Has sell price item name']] =
                        k['Has sell price amount']
                      + mods_sell_price[k['Has sell price item name']]
                end
            end
        end
    end
   
    local out = {}
    for currency, amount in pairs(mods_sell_price) do
        out[#out+1] = string.format(
            '%s %s',
            amount,
            g_frame:expandTemplate{
                title = 'il',
                args = {currency, currency .. 's'}
            }
        )
    end
   
    return table.concat(out, ', ')
end   
   
function p.get_mod_domain(cargo_query)
    --[[
Gets the mod domain based on the item class.
    ]]
    local out = cargo_query
    local mod_domains = game.constants.mod.domains
   
    -- Set item class as key and the mod domain as value:
    local class_to_domain = {
        ['Life Flasks']=2,
        ['Mana Flasks']=2,
        ['Hybrid Flasks']=2,
        ['Utility Flasks']=2,
        ['Critical Utility Flasks']=2,
        ['Maps']=5,
        ['Jewel']=11,
        ['Leaguestones']=13,
        ['Abyss Jewel']=14,
    }
   
    for i,_ in ipairs(out) do
        -- Get the domain, if it's not defined in the table assume it's
        -- in the item domain.
        out[i]['items.domain'] = class_to_domain[out[i]['items.class']] or 1
       
        -- Convert the mod domain number to understandable text:
        out[i]['items.domain_text'] = mod_domains[out[i]['items.domain']]['short_lower']
    end
    return out
end
function p.get_item_tags(frame)
    --[[
    This function queries for the tags of a specific item.
    ]]
   
    -- Args
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)
   
    tpl_args.tables = 'items'
    tpl_args.fields = 'items.name, items.tags, items.class'
    local tbl = {
        {tpl_args.page, 'items._pageName = "%s"'},
        {tpl_args.item, 'items.name = "%s"'},
    }
    for _,v in ipairs(tbl) do
        if v[1] ~= nil then
            condition = string.format(v[2], v[1])
            break
        end
    end
    tpl_args.q_where = condition
    tpl_args.q_groupBy = 'items._pageID'
    tpl_args.q_orderBy = 'items.name'
    -- Query mods with cargo:
    results = h.cargo_query(tpl_args)
    -- -- SMW workaround, remove when module:item2 is ready:
    -- local tbl = {
        -- {tpl_args.page, '[[%s]]'},
        -- {tpl_args.item, '[[Has name::%s]]'},
    -- }
    -- for _,v in ipairs(tbl) do
        -- if v[1] ~= nil then
            -- condition = string.format(v[2], v[1])
            -- break
        -- end
    -- end
    -- local results = m_util.smw.query(
        -- {
            -- condition,
            -- '?Has name',
            -- '?Has tags',
            -- '?Has item class'
        -- },
        -- frame
    -- )
    -- for i,_ in ipairs(results) do
        -- results[i]['items.tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ')
        -- results[i]['items.class'] = results[i]['Has item class']
        -- results[i]['items.name'] = results[i]['Has name']
        -- results[i]['items._pageName'] = results[i][1]
    -- end
    -- --
   
   
    results = p.get_mod_domain(results)
    return results
end
function p.header(str)
    --[[
    This function replace specific numbers with a generic #.
    ]]
   
    local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
    s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
    s = table.concat(m_util.string.split(s, '<br>'), ', ')
 
  return s
end
function p.get_spawn_chance(frame)
    --[[
    Calculates the spawn chance of a set of mods that all have a
    spawn weight.
    ]]
    -- Args
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)
   
    local tbl = tpl_args['tbl']
    -- Probabilities affecting the result besides the spawn weight:
    local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1
   
    local N = 0
    for i,_ in ipairs(tbl) do
        -- Total number of outcomes.
        N = N + tbl[i]['spawn_weights.weight']
    end
   
    for i,_ in ipairs(tbl) do
        -- Number of ways it can happen:
        local n = tbl[i]['spawn_weights.weight']
        -- Truncated value:
        tbl[i]['spawn_weights.chance'] = string.format(
            "%0.2f%%",
            n/N * chance_multiplier*100
        )
    end
   
    return tbl
end
   
function p.drop_down_table(frame)
    --[[
    This function queries mods that can spawn on an item. It compares
    the item tags and the spawn weight tags. If there's a match and
    the spawn weight is larger than zero, then that mod is added to a
    drop down list.
   
    To Do
    * Misses forsaken masters currently.
    * Add a proper expand/collapse toggle for the entire header row so
        it reacts together with mw-collapsible.
    * Show Mod group in a better way perhaps:
        Mod group (expanded)
            # to Damage (Collapsed)
            3 to Damage
            5 to Damage
   
    Examples:
    Weapons
    p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
    p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
    Accessories
    p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
   
    Jewels
    p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
   
    Armour
    p.drop_down_table{item = 'Plate Vest', header = 'Armour body armours'}
    p.drop_down_table{item = 'Iron Greaves', header = 'Armour boots'}
    p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour gloves'}
    p.drop_down_table{item = 'Iron Hat', header = 'Armour helmets'}
    p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour shields'}
   
    p.drop_down_table{
        item = 'Fishing Rod',
        header = 'FISH PLEASE',
        item_tags = 'fishing_rod',
        extra_fields = 'Has spawn weight, Has spawn chance'
    }
   
    = p.drop_down_table{
        item = 'Fishing Rod',
        item_tags = 'axe, one_hand_weapon, onehand, weapon, default'
    }
    = p.drop_down_table{
        item = 'Vaal Blade',
    }
       
    ]]
    -- Get template args:
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)
   
    -- Get the items tags:
    local get_item_tags = p.get_item_tags(tpl_args)[1]
   
    -- For some reason cargo queried item tags, are not comma-space
    -- separated.
    local item_tags = {}
    if tpl_args.item_tags  ~= nil then
        item_tags = m_util.string.split(tpl_args.item_tags, ', ')
    else
        item_tags = m_util.string.split(get_item_tags['items.tags'], ',')
    end
    -- local item_tags = m_util.string.split(
        -- tpl_args.item_tags or get_item_tags['items.tags'],
        -- ', '
    -- )
   
    -- Create drop down lists in these sections and query in these
    -- generation types.
    local section = {}
    section = {
        [1] = {
            header = i18n.drop_down_table.prefix,
            generation_type = 1,
        },
        [2] = {
            header = i18n.drop_down_table.suffix,
            generation_type = 2,
        },
        [3] = {
            header = i18n.drop_down_table.corrupted,
            generation_type = 5,
            chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
        },
        -- [4] = {
            -- header = 'Forsaken masters',
            -- generation_type = 'master',   
        -- },
    }
     
     
    -- Introductory text:
    local out = {}
    out[#out+1] = string.format(
        '==%s== \n',
        tpl_args['header'] or table.concat(item_tags, ', ')
    )
    out[#out+1] = string.format(
        '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[%s]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[%s]</div></div>',
        i18n.drop_down_table.collapse_all,
        i18n.drop_down_table.expand_all
    )
    out[#out+1] = string.format('%s %s.<br><br><br>',
        i18n.drop_down_table.table_intro,
        f_item_link{page=get_item_tags['items._pageName']}
    )
   
    local item_mods = {}
    local tableIndex = -1       
    for _, sctn in ipairs(section) do
        local container = mw.html.create('div')
            :attr('style', 'vertical-align:top; display:inline-block;')
   
        -- Format the where condition:
        generation_type = sctn['generation_type']
        local where = {}
        for _, item_tag in ipairs(item_tags) do
            where[#where+1] = string.format(
            '(spawn_weights.tag="%s" AND mods.generation_type=%s AND mods.domain=%s)',
            item_tag,
            sctn['generation_type'],
            get_item_tags['items.domain']
        )
        end
       
        tpl_args.tables = 'mods, spawn_weights, mod_stats'
        tpl_args.fields = 'mods.name, mods.id, mods.required_level, mods.generation_type, mods.domain, mods.mod_group, mods.mod_type, mods.stat_text, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal'
        tpl_args.q_join = 'mods._pageID=spawn_weights._pageID, mods._pageID=mod_stats._pageID'
        tpl_args.q_where = table.concat(where, ' OR ')
        tpl_args.q_groupBy = 'mods._pageID, spawn_weights.tag, spawn_weights.weight'
        tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods._pageName, mods.required_level, spawn_weights.ordinal'
       
        local extra_fields = {}
        if tpl_args.extra_fields ~= nil then
            extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
            tpl_args.fields = string.format(
                '%s, %s',
                tpl_args.fields,
                table.concat(extra_fields, ', ')
            )
        end
       
        -- Query mods:
        results = h.cargo_query(tpl_args)
        -- Create own list for spawn weights and group by page name:
        local spawn_weights = {}
local results_unique = {}
local hash = {}
        for _,v in ipairs(results) do
            if spawn_weights[v['mods._pageName']] == nil then
                spawn_weights[v['mods._pageName']] = {}
            end
            local n = #spawn_weights[v['mods._pageName']] or 0
            spawn_weights[v['mods._pageName']][n+1] = v
            -- Get a sorted list that only has unique page names:
            if hash[v['mods._pageName']] ~= true then
                results_unique[#results_unique+1] = v
                hash[v['mods._pageName']] = true
            end
        end
       
        if #results_unique > 0 then
            item_mods[generation_type] = {}
                     
            -- Loop through all the modifiers from the concept pages:
            local last
            for _, v in ipairs(results_unique) do 
                local pagename = v['spawn_weights._pageName']
               
                -- Loop through all the modifier tags until they match
                -- the item tags:
                local j = 0
                local tag_match_stop
                repeat
                    j = j+1
                    local mod_tag = spawn_weights[pagename][j]['spawn_weights.tag']
                    local mod_tag_weight = tonumber(
                        spawn_weights[pagename][j]['spawn_weights.weight']
                    )
                    -- Loop through the item tags until it matches the
                    -- spawn weight tag and the mod tag has a value larger than
                    -- zero:
                    local y = 0
                    local tag_match_add = false
                    repeat   
                        y = y+1
                        tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (spawn_weights[pagename][j] == nil)
                        tag_match_add =  (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
                    until tag_match_stop or y == #item_tags
                   
                    -- If there's a match then save that mod and other
                    -- interesting information:
                    if tag_match_add then
                       
                        -- Assume that the mod is global then go through
                        -- all the stat ids and check if any of the
                        -- stats are local:
                        local mod_scope = 'Global'
                        for _, vv in ipairs(spawn_weights[pagename]) do
                            if vv['mod_stats.id']:find('.*local.*') ~= nil then
                                mod_scope = 'Local'
                            end
                        end
                       
                        -- Save the matching modifier tag:
                        local a = #item_mods[generation_type]
                        item_mods[generation_type][a+1] = spawn_weights[pagename][j]
                       
                        -- Save other interesting fields:
                        item_mods[generation_type][a+1]['mods.scope'] = mod_scope   
                        item_mods[generation_type][a+1]['spawn_weight.idx_match'] = j                       
item_mods[generation_type][a+1]['mods.add'] = tag_match_add   
item_mods[generation_type][a+1]['mods.stop'] = tag_match_stop   
                    end
                until tag_match_stop
            end
           
            -- If the user wants to see the spawn chance then do the
            -- calculations and save that result as well:
            if tpl_args.spawn_chance ~= nil then
                extra_fields[#extra_fields+1] = 'spawn_weights.chance'
                item_mods[generation_type] = p.get_spawn_chance{
                    tbl = item_mods[generation_type],
                    chance_multiplier = sctn['chance_multiplier']
                }
            end
            -- Create the drop down table with <table></table>:
            local headers = container
            headers
                :tag('h3')
                    :wikitext(string.format(
                        '%s',
                        sctn['header']
                        )
                    )
                    :done()
                :done()
               
            -- Loop through and add all matching mods to the <table>.
            local tbl, last
            for _, rows in ipairs(item_mods[generation_type]) do 
           
                -- If the last mod group is different to the current
                -- mod group then assume the mod isn't related and start
                -- a new drop down list:
                if rows['mods.mod_group'] ~= last then
                   
                    -- Check through all the mods and see if there are
                    -- multiple mod types within the same mod group:
                    local count = {}
                    for _, n in ipairs(item_mods[generation_type]) do
                       
                        -- If the mod has the same mod group, then add
                        -- the mod type to the counter. Only unique mod
                        -- types matter so the number is just a dummy
                        -- value:
                        if n['mods.mod_group'] == rows['mods.mod_group'] then
                            count[n['mods.mod_type']] = 1
                        end
                    end
                   
                    -- Calculate how many unique mod types with the
                    -- same mod group there are:
                    number_of_mod_types = 0
                    for _ in pairs(count) do
                        number_of_mod_types = number_of_mod_types + 1
                    end
                   
                    -- If there are multiple unique mod types with the
                    -- same mod group then change the style of the drop
                    -- down list to indicate it:
                    if number_of_mod_types > 1 then
                        tbl_caption = string.format(
                            '%s',
                            m_util.html.poe_color(
                                'mod',
                                'Mod group: ' .. rows['mods.mod_group']
                            )
                        )
                    else
                        tbl_caption = string.format(
                            '%s (%s)',
                            m_util.html.poe_color(
                                'mod',
                                p.header(rows['mods.stat_text'])
                            ),
                            rows['mods.scope']
                        )
                    end
                   
                    -- Add class and style to the <table>:
                    tableIndex = tableIndex+1
                    tbl = container:tag('table')
                    tbl
                        :attr('class', 'mw-collapsible mw-collapsed')
                        :attr('style',
                            'text-align:left; line-height:1.60em; width:810px;'
                        )
                        :tag('th')
                            :attr('class',
                                string.format(
                                    'mw-customtoggle-%s',
                                    tableIndex
                                )
                            )
                            :attr('style',
                                'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
                            )
                            :attr('colspan', '3' .. #extra_fields)
                            :wikitext(tbl_caption)
                            :done()
                        :done()   
                end
               
                -- If the mod has no name then use the mod id:
                local mod_name = rows['mods.name']
                if  mod_name == '' or mod_name == nil then
                    mod_name = rows['mods.id']
                end
               
                -- Check if there are any extra properties to show in
                -- the drop down list and then add a cell for that,
                -- add this node at the end of the table row:
                local td = mw.html.create('td')
                if extra_fields ~= nil then
                    for _, extra_field in ipairs(extra_fields) do
                        td
                            :attr('width', '*')
                            :wikitext(string.format(
                                '%s:&nbsp;%s ',
                                extra_field,
                                rows[extra_field]
                                )
                            )
                            :done()
                    end
                end
               
                -- Add a table row with the interesting properties that
                -- modifier has:
                tbl
                    :tag('tr')
                        :attr('class', 'mw-collapsible mw-collapsed')
                        :attr(
                            'id',
                            string.format(
                                'mw-customcollapsible-%s',
                                tableIndex
                            )
                        )
                        :tag('td')
                            :attr('width', '160')
                            :wikitext(
                                string.format(
                                    '&nbsp;&nbsp;&nbsp;[[%s|%s]]',
                                    rows['mods._pageName'],
                                    mod_name:gsub('%s', '&nbsp;')
                                )
                            )
                            :done()
                        :tag('td')
                            :attr('width', '1')
                            :wikitext(
                                string.format(
                                    '%s&nbsp;%s',
                                    game.level_requirement['short_upper']:gsub('%s', '&nbsp;'),
                                    rows['mods.required_level']
                                )
                            )
                            :done()       
                        :tag('td')
                            :attr('width', '*')
                            :wikitext(
                                string.format(
                                    '%s',
                                    m_util.html.poe_color(
                                        'mod',
                                        rows['mods.stat_text']:gsub('<br>', ', ')
                                    ) 
                                )
                            )
                            :done()
                        :node(td)
                        :done()
                    :done()
               
                -- Save the last mod group for later comparison:
                last = rows['mods.mod_group']
            end
        end
       
        out[#out+1] = tostring(container)
    end
   
    return table.concat(out,'')
end
end


return p
return p

Revision as of 04:02, 2 May 2021

This is the module sandbox page for Module:Mod (diff).

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


--
-- Module for mod related templates
--

local getArgs = require('Module:Arguments').getArgs
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
local m_game = require('Module:Game')
local f_item_link = require('Module:Item link').item_link

local p = {}

-- ----------------------------------------------------------------------------
-- Strings
-- ----------------------------------------------------------------------------
-- This section contains strings used by this module.
-- Add new strings here instead of in-code directly, this will help other
-- people to correct spelling mistakes easier and help with translation to
-- other PoE wikis.

local i18n = {
    categories = {
        mods = 'Mods',
    },
    
    tooltips = {
        -- intro texts
        intro_named_id = "'''%s''' is the internal id of [[modifier]] '''%s'''.\n",
        intro_unnamed_id = "'''%s''' is the internal id of an unnamed [[modifier]].\n",
        
        -- core data
        id = 'Mod Id',
        name = 'Name',
        mod_group = 'Group',
        mod_type = 'Mod type',
        domain = 'Domain',
        domain_fmt = '%s (Id: %s)',
        generation_type = 'Generation type',
        generation_type_fmt = '%s (Id: %s)',
        required_level = 'Req. Level',
        stat_text = 'Effect',
        granted_buff_id = 'Granted Buff Id',
        granted_buff_value = 'Granted Buff Value',
        granted_skill = 'Granted Skill',
        tags = 'Tags',
        tier_text = 'Tier Text',
        
        -- shared
        ordinal = '#',
        
        -- stats
        stats = 'Stats',
        stat_id = 'Stat Id',
        min = 'Minimum',
        max = 'Maximum',
        
        -- weights
        spawn_weights = 'Spawn weights',
        generation_weights = 'Generation weights',
        tag = 'Tag',
        weight = 'Weight',
        
        -- sell price
        sell_price = 'Modifier sell price',
        item = 'Item',
    },
    
    errors = {
        --
        -- Mod template
        --
        sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
        sell_price_missing_argument = 'Both %s and %s must be specified',
    },

}

-- ----------------------------------------------------------------------------
-- utility / Helper functions
-- ----------------------------------------------------------------------------

local h = {}

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

--
-- Template: Mod
--

local mod_map = {
    main = {
        table = 'mods',
        display_order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
        order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'stat_text_raw', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
        fields = {
            id = {
                name = 'id',
                field = 'id',
                type = 'String',
                wikitext = i18n.tooltips.id,
            },
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                wikitext = i18n.tooltips.name,
            },
            mod_group = {
                name = 'mod_group',
                field = 'mod_group',
                type = 'String',
                wikitext = i18n.tooltips.mod_group,
            },
            mod_type = {
                name = 'mod_type',
                field = 'mod_type',
                type = 'String',
                wikitext = i18n.tooltips.mod_type,
            },
            domain = {
                name = 'domain',
                field = 'domain',
                type = 'Integer',
                wikitext = 'Mod domain',
                display = function (value)
                    return string.format(i18n.tooltips.domain_fmt, m_game.constants.mod.domains[value]['short_upper'], value)
                end,
            },
            generation_type = {
                name = 'generation_type',
                field = 'generation_type',
                type = 'Integer',
                wikitext = i18n.tooltips.generation_type,
                display = function (value)
                    return string.format(i18n.tooltips.generation_type_fmt, m_game.constants.mod.generation_types[value]['short_upper'], value)
                end,
            },
            required_level = {
                name = 'required_level',
                field = 'required_level',
                type = 'Integer',
                wikitext = i18n.tooltips.required_level,
            },
            stat_text = {
                name = 'stat_text',
                field = 'stat_text',
                type = 'Text',
                wikitext = i18n.tooltips.stat_text,
            },
            stat_text_raw = {
                name = nil,
                field = 'stat_text_raw',
                type = 'Text',
                func = function(tpl_args, frame)
                    if tpl_args.stat_text then
                        tpl_args.stat_text_raw = string.gsub(
                            -- [[x]] -> x
                            string.gsub(
                                tpl_args.stat_text, '%[%[([^%]|]+)%]%]', '%1'
                            ), 
                            -- [[x|y]] -> y
                            '%[%[[^|]+|([^%]|]+)%]%]', '%1'
                        )
                    end
                    return tpl_args.stat_text_raw
                end
            },
            granted_buff_id = {
                name = 'granted_buff_id',
                field = 'granted_buff_id',
                type = 'String',
                wikitext = i18n.tooltips.granted_buff_id,
            },
            granted_buff_value = {
                name = 'granted_buff_value',
                field = 'granted_buff_value',
                type = 'Integer',
                wikitext = i18n.tooltips.granted_buff_value,
            },
            granted_skill = {
                name = 'granted_skill',
                field = 'granted_skill',
                type = 'String',
                wikitext = i18n.tooltips.granted_skill,
            },
            tags = {
                name = 'tags',
                field = 'tags',
                type = 'List (,) of String',
                wikitext = 'Tags', 
                display = function(value)
                    return table.concat(value, ', ')  
                end,
                default = {},
            },
            tier_text = {
                name = 'tier_text',
                field = 'tier_text',
                type = 'Text',
                wikitext = i18n.tooltips.tier_text,
            },
        },
    },
    mod_sell_prices = {
        table = 'mod_sell_prices',
        order = {'name', 'amount'},
        fields = {
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                func = function (value) return value end,
            },
            amount = {
                name = 'amount',
                field = 'amount',
                type = 'Integer',
                func = tonumber,
            },
        },
    },
}

p.table_main = m_cargo.declare_factory{data=mod_map.main}
p.table_mod_sell_prices = m_cargo.declare_factory{data=mod_map.mod_sell_prices}

function p.table_mod_stats(frame)
    m_cargo.declare(frame, {
        _table = 'mod_stats',
        id = 'String',
        min = 'Integer',
        max = 'Integer',
    })
end


-- 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
    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    --
    -- Validation & semantic properties
    --
    
    -- Validate single value properties and set them
    
    m_util.args.from_cargo_map{
        tpl_args=tpl_args,
        frame=frame,
        table_map=mod_map.main,
    }
    
    -- Validate % set the stat subobjects
    m_util.args.stats(tpl_args, {frame=frame})
    for _, stat_data in pairs(tpl_args.stats) do
        m_cargo.store(frame, {
            _table = 'mod_stats', 
            id = stat_data.id,
            min = stat_data.min,
            max = stat_data.max,
        })
    end
    
    -- Validate & set spawn weight subobjects
    m_util.args.spawn_weight_list(tpl_args, {
        frame=frame, 
    })
    
    -- Validate & set generation weight subobjects
    m_util.args.generation_weight_list(tpl_args, {
        frame=frame, 
    })
    
    -- Validate & set mod sell values
    i = 0
    local names = {}
    local sell_prices = {}
    repeat 
        i = i + 1
        
        local id = {}
        value = {}
        for key, data in pairs(mod_map.mod_sell_prices.fields) do
            id[key] = string.format('%s%s_%s', 'sell_price', i, data.name)
            value[key] = data.func(tpl_args[id[key]])
        end
        
        if value.name == nil and value.amount == nil then
            value = nil
        elseif value.name ~= nil and value.amount ~= nil then
            if names[value.name] then
                error(i18n.errors.sell_price_duplicate_name)
            else
                names[value.name] = true
            end

            local cargo_data = {
                _table = mod_map.mod_sell_prices.table,
            }
            for key, data in pairs(mod_map.mod_sell_prices.fields) do
                cargo_data[data.field] = value[key]
            end
            m_cargo.store(frame, cargo_data)
            
            sell_prices[#sell_prices+1] = value
        else
            error (string.format(i18n.errors.sell_price_missing_arguments, id.name, id.amount))
        end
        
    until value == nil
    
    --
    -- Display
    --
    
    local container = mw.html.create('div')
    container
        :attr('class', 'modbox')
    
    -- core stats
    
    local tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable')
    
    for _, key in ipairs(mod_map.main.display_order) do
        local data = mod_map.main.fields[key]
        local text
        if data.display == nil then
            text = tpl_args[key]
        else
            text = data.display(tpl_args[key])
        end
        
        tbl
            :tag('tr')
                :tag('th')
                    :wikitext(data.wikitext)
                    :done()
                :tag('td')
                    :wikitext(text)
                    :done()
                :done()
            :done()
    end
    
    -- stat table
    
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 4)
                :wikitext(i18n.tooltips.stats)
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.tooltips.ordinal)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.stat_id)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.min)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.max)
                :done()
            :done()
        :done()
        
    for i=1, #tpl_args.stats do
        local value = {
            id = tpl_args['stat' .. i .. '_id'],
            min = tpl_args['stat' .. i .. '_min'],
            max = tpl_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(i18n.tooltips.spawn_weights)
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.tooltips.ordinal)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.tag)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.weight)
                :done()
            :done()
        :done()
        
    i = 0
    value = nil
    repeat
        i = i + 1
        value = {
            tag = tpl_args[string.format('spawn_weight%s_tag', i)],
            value = tpl_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(i18n.tooltips.generation_weights)
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.tooltips.ordinal)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.tag)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.weight)
                :done()
            :done()
        :done()
    
    i = 0
    value = nil
    repeat
        i = i + 1
        value = {
            tag = tpl_args[string.format('generation_weight%s_tag', i)],
            value = tpl_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
    
    -- Sell prices
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 2)
                :wikitext(i18n.tooltips.sell_price)
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.tooltips.ordinal)
                :done()
            :tag('th')
                :wikitext(i18n.tooltips.item)
                :done()
            :done()
        :done()
    
    for i, value in ipairs(sell_prices) do
        tbl
            :tag('tr')
                :tag('td')
                    :wikitext(value.amount)
                    :done()
                :tag('td')
                    :wikitext(string.format('[[%s]]', value.name))
                    :done()
                :done()
    end
    
    -- Generic messages on the page
    
    out = {}
    
    if mw.ustring.find(tpl_args['id'], '_') then
        out[#out+1] = frame:expandTemplate{ title = 'Incorrect title', args = { title=tpl_args['id'] } } .. '\n\n\n'
    end
    
    if tpl_args['name'] then
    
        out[#out+1] = string.format(i18n.tooltips.intro_named_id, tpl_args['id'], tpl_args['name'])
    else
        out[#out+1] = string.format(i18n.tooltips.intro_unnamed_id, tpl_args['id'])
    end
    
    -- Categories
    
    cats = {i18n.categories.mods}
    
    -- Done -> output
    
    return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out) 
end

return p