Module:Mod: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
>Illviljan
(Tags on items don't have any priority order anymore, to fix issue with bow mods. Tags on modifiers still have.)
(No game modes in poe2 yet)
 
(80 intermediate revisions by 8 users not shown)
Line 1: Line 1:
--
-------------------------------------------------------------------------------
-- Module for mod related templates
--  
--
--                           Module:Mod
--  
-- This module implements Template:Mod
-------------------------------------------------------------------------------
 
require('strict')
local m_util = require('Module:Util')
 
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Mod')
 
local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo')
local m_game = use_sandbox and mw.loadData('Module:Game/sandbox') or mw.loadData('Module:Game')
 
-- Lazy loading
local f_item_table -- require('Module:Item table').item_table


local util = require('Module:Util')
-- The cfg table contains all localisable strings and configuration, to make it
local getArgs = require('Module:Arguments').getArgs
-- easier to port this module to another wiki.
local game = require('Module:Game')
local cfg = use_sandbox and mw.loadData('Module:Mod/config/sandbox') or mw.loadData('Module:Mod/config')


local p = {}
local i18n = cfg.i18n


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


local h = {}
local h = {}


-- Validate single value properties and set them
-- Lazy loading for Module:Item table
 
function h.item_table(args)
h.validate = {}
     if not f_item_table then
 
         f_item_table = use_sandbox and require('Module:Item table/sandbox').item_table or require('Module:Item table').item_table
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
    return f_item_table(args)
end
end


function h.validate.number (args)
function h.set_weights(tpl_args, args)
     return function (arg)
     -- Parses a weighted pair of lists and stores cargo data
        g_args[arg] = util.cast.number(g_args[arg], args)
    --
        return g_args[arg]
    -- tpl_args: argument table to work with
     end
    -- args:
end
    --  prefix - input prefix for parsing the arguments from tpl_args
     --  table_map - cargo table map
    --  store - whether to store the Cargo data


    args = args or {}
    for i=1, math.huge do -- repeat until no more weights are found
        local prefix = args.prefix .. i
        local params = {
            tag = string.format('%s_tag', prefix),
            value = string.format('%s_value', prefix),
        }
        local tag = tpl_args[params.tag]
        local value = tpl_args[params.value]
        if tag == nil and value == nil then
            break
        end
        if tag == nil or value == nil then
            error(string.format(i18n.errors.invalid_weight, params.tag, params.value))
        end
        value = m_util.cast.number(value, {min = 0})


function h.handle_mapped_property_args (map)
        -- Store to cargo table unless tag = default and value = 0
    local properties = {}
         if args.store and (tag ~= 'default' or value ~= 0) then
   
             m_cargo.store({
    for _, data in ipairs(map) do
                _table = args.table_map.table,
         if data.func ~= nil then
                [args.table_map.fields.ordinal.field] = i,
             data.func(data.name)
                [args.table_map.fields.tag.field] = tag,
        end
                [args.table_map.fields.value.field] = value,
       
            })
        if data.property ~= nil then
            properties[data.property] = g_args[data.name]
         end
         end
     end
     end
   
    g_frame:callParserFunction('#set:', properties)
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


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
Line 110: Line 84:
--
--


-- 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"}
local mod_map = {
function p.mod(frame)
    main = {
    -- Get args
        table = 'mods',
    g_args = getArgs(frame, {
        display_order = {'id', 'name', 'mod_groups', 'mod_type', 'domain', 'generation_type', --[['game_mode',--]] 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
        parentFirst = true
        order = {'id', 'name', 'mod_groups', 'mod_type', 'domain', 'generation_type', --[['game_mode',--]] 'required_level', 'stat_text', 'stat_text_raw', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
    })
        fields = {
    g_frame = util.misc.get_frame(frame)
            id = {
   
                name = 'id',
    --
                field = 'id',
    -- Validation & semantic properties
                type = 'String',
    --
                wikitext = i18n.data_sheet.id,
   
                func = function (tpl_args, value)
    -- Validate single value properties and set them
                    -- Validate that the id is unique
   
                    local results = m_cargo.query(
    local map = {
                        {'mods'},
        {
                        {'mods._pageName'},
             name = 'id',
                        {
             property = 'Is mod',
                            where = string.format(
             wikitext = 'Mod Id',
                                'mods.id = "%s" AND mods._pageName != "%s"',
                                value,
                                m_cargo.addslashes(mw.title.getCurrentTitle().prefixedText)
                            )
                        }
                    )
                    if #results > 0 then
                        error(string.format(i18n.errors.duplicate_mod_id, results[1]['mods._pageName']))
                    end
                    return value
                end
            },
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                wikitext = i18n.data_sheet.name,
            },
            mod_groups = {
                name = 'mod_groups',
                field = 'mod_groups',
                type = 'List (,) of String',
                wikitext = i18n.data_sheet.mod_groups,
                display = function (value)
                    return table.concat(value, ', ')
                end,
                default = {},
            },
            mod_type = {
                name = 'mod_type',
                field = 'mod_type',
                type = 'String',
                wikitext = i18n.data_sheet.mod_type,
            },
            domain = {
                name = 'domain',
                field = 'domain',
                type = 'Integer',
                wikitext = i18n.data_sheet.domain,
                display = function (value)
                    return string.format(i18n.data_sheet.domain_fmt, m_game.constants.mod.domains[value]['short_upper'], value)
                end,
            },
            generation_type = {
                name = 'generation_type',
                field = 'generation_type',
                type = 'Integer',
                wikitext = i18n.data_sheet.generation_type,
                display = function (value)
                    return string.format(i18n.data_sheet.generation_type_fmt, m_game.constants.mod.generation_types[value]['short_upper'], value)
                end,
            },
            --[[game_mode = {
                name = 'game_mode',
                field = 'game_mode',
                type = 'Integer',
                wikitext = i18n.data_sheet.game_modes,
                display = function (value)
                    if value == nil then
                        return value
                    end
                    value = m_util.cast.number(value)
                    local modes = {}
                    for k, m in ipairs(m_game.modes) do
                        if value == 0 or value == k then
                            table.insert(modes, m.short_upper)
                        end
                    end
                    return table.concat(modes, ', ')
                end,
            },--]]
            required_level = {
                name = 'required_level',
                field = 'required_level',
                type = 'Integer',
                wikitext = i18n.data_sheet.required_level,
            },
            stat_text = {
                name = 'stat_text',
                field = 'stat_text',
                type = 'Text',
                wikitext = i18n.data_sheet.stat_text,
            },
            stat_text_raw = {
                name = nil,
                field = 'stat_text_raw',
                type = 'Text',
                func = function (tpl_args, value)
                    if tpl_args.stat_text then
                        -- Strip wikilinks and html, but keep any line break tags
                        value = m_util.string.strip_wikilinks(tpl_args.stat_text)
                        value = mw.ustring.gsub(value, '<br */?>', '�')
                        value = m_util.string.strip_html(value)
                        value = mw.ustring.gsub(value, '�', '<br>')
                    end
                    return value
                end
            },
            granted_buff_id = {
                name = 'granted_buff_id',
                field = 'granted_buff_id',
                type = 'String',
                wikitext = i18n.data_sheet.granted_buff_id,
            },
            granted_buff_value = {
                name = 'granted_buff_value',
                field = 'granted_buff_value',
                type = 'Integer',
                wikitext = i18n.data_sheet.granted_buff_value,
            },
             granted_skill = {
                name = 'granted_skill',
                field = 'granted_skill',
                type = 'String',
                wikitext = i18n.data_sheet.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.data_sheet.tier_text,
            },
         },
         },
         {
    },
             name = 'name',
    mod_stats = {
             property = 'Has name',
        table = 'mod_stats',
             wikitext = 'Name',
         fields = {
             id = {
                field = 'id',
                type = 'String',
             },
            min = {
                field = 'min',
                type = 'Integer',
            },
             max = {
                field = 'max',
                type = 'Integer',
            },
         },
         },
         {
    },
             name = 'mod_group',
    mod_spawn_weights = {
             property = 'Has mod group',
        table = 'mod_spawn_weights',
             wikitext = 'Group',
         fields = {
             ordinal = {
                field = 'ordinal',
                type = 'Integer',
             },
            tag = {
                field = 'tag',
                type = 'String',
            },
             value = {
                field = 'value',
                type = 'Integer',
            },
         },
         },
         {
    },
             name = 'mod_type',
    mod_generation_weights = {
             property = 'Has mod type',
        table = 'mod_generation_weights',
             wikitext = 'Mod type',
         fields = {
             ordinal = {
                field = 'ordinal',
                type = 'Integer',
            },
             tag = {
                field = 'tag',
                type = 'String',
             },
            value = {
                field = 'value',
                type = 'Integer',
            },
         },
         },
        {
    },
            name = 'domain',
    mod_sell_prices = {
            property = 'Has mod domain',
        table = 'mod_sell_prices',
            func = h.validate.number{min=1, max=11},
        order = {'name', 'amount'},
            wikitext = 'Mod domain',
         fields = {
            display = function (value)
             name = {
                return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')'
                name = 'name',
            end,
                field = 'name',
        },
                type = 'String',
         {
                func = function (value) return value end,
             name = 'generation_type',
             },
            property = 'Has mod generation type',
             amount = {
            func = h.validate.number{min=1, max=10},
                name = 'amount',
            wikitext = 'Generation type',
                field = 'amount',
            display = function (value)
                type = 'Integer',
                return game.constants.mod.generation_types[value]['short_upper'] .. ' (Id: ' .. value .. ')'
                func = tonumber,
            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',
         },
         },
    },
}
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _mod(tpl_args)
    -- p.mod{id = "LocalIncreasedPhysicalDamagePercentUniqueOneHandSword2", name = "", mod_groups = "LocalPhysicalDamagePercent, Dexterity", 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"}
   
    --
    -- Validate and store
    --
    --local store = tpl_args.game_mode ~= nil
    local store = true
    -- Validate and store mod data
    m_cargo.store_mapped_args{
        tpl_args = tpl_args,
        table_map = mod_map.main,
        rtr = not store,
     }
     }
      
      
     h.handle_mapped_property_args(map)
     -- Validate and store stats
    m_util.args.stats(tpl_args)
    for _, stat_data in pairs(tpl_args.stats) do
        if store then
            m_cargo.store({
                _table = 'mod_stats',
                id = stat_data.id,
                min = stat_data.min,
                max = stat_data.max,
            })
        end
    end
      
      
     -- Validate & set multi value property
     -- Validate and store spawn weights
    h.set_weights(tpl_args, {
        prefix = 'spawn_weight',
        table_map = mod_map.mod_spawn_weights,
        store = store,
    })
      
      
     if g_args['tags'] == nil then
     -- Validate and store generation weights
        g_args['tags'] = {}
    h.set_weights(tpl_args, {
    else
         prefix = 'generation_weight',
         local tags = mw.text.split(g_args['tags'], ', ')
         table_map = mod_map.mod_generation_weights,
          
         store = store,
         g_args['tags'] = tags
    })
      
      
        properties = {}
     -- Validate and store mod sell values
        properties['Has tag'] = table.concat(tags, ';')
     local i = 0
        properties['+sep'] = ';'
     local names = {}
        g_frame:callParserFunction('#set:', properties)
     local sell_prices = {}
    end
     repeat
   
         i = i + 1
     -- 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 = {
        local id = {}
             id = g_args[id.id],
         local value = {}
             min = g_args[id.min],
        for key, data in pairs(mod_map.mod_sell_prices.fields) do
            max = g_args[id.max],
             id[key] = string.format('%s%s_%s', 'sell_price', i, data.name)
         }
             value[key] = data.func(tpl_args[id[key]])
         end
          
          
         if not (value.id == nil and value.min == nil and value.max == nil) then
         if value.name == nil and value.amount == nil then
             local onenil = nil
            value = nil
            for _, k in ipairs({'id', 'min', 'max'}) do
        elseif value.name ~= nil and value.amount ~= nil then
                 if value[k] == nil then
             if names[value.name] then
                    onenil = k
                error(i18n.errors.sell_price_duplicate_name)
                    break
            else
                end
                 names[value.name] = true
             end
             end


             if onenil ~= nil then
             local cargo_data = {
                 error('"' .. id[onenil] .. '" is not set')
                 _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
            if store then
                m_cargo.store(cargo_data)
             end
             end
              
              
             properties = {}
             sell_prices[#sell_prices+1] = value
            properties['Is stat number'] = i
        else
            properties['Has stat id'] = value.id
             error(string.format(i18n.errors.sell_price_missing_arguments, id.name, id.amount))
             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
    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
     until value == nil
            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
     -- Attach to tables
        i = i + 1
    if store then
         id = {
         mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mods/attach'}
            tag = string.format('generation_weight%s_tag', i),
         mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod stats/attach'}
            value = string.format('generation_weight%s_value', i),
         mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod spawn weights/attach'}
        }
        mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod generation weights/attach'}
   
         mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod sell prices/attach'}
         value = {
    end
            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
      
      
     --
     --
Line 313: Line 409:
      
      
     local container = mw.html.create('div')
     local container = mw.html.create('div')
    container
         :addClass('modbox')
         :attr('class', 'modbox')
      
      
     -- core stats
     -- core stats
      
      
     local tbl = container:tag('table')
     local tbl = container:tag('table')
    tbl
         :addClass('wikitable')
         :attr('class', 'wikitable')
      
      
     for _, data in ipairs(map) do
     for _, key in ipairs(mod_map.main.display_order) do
         local text
        local data = mod_map.main.fields[key]
         if data.display == nil then
         local text = tpl_args[key]
            text = g_args[data.name]
         if type(data.display) == 'function' then
        else
             text = data.display(text)
             text = data.display(g_args[data.name])
         end
         end
       
         tbl
         tbl
             :tag('tr')
             :tag('tr')
                 :tag('th')
                 :tag('th')
                     :wikitext(string.format('[[Property:%s|%s]]', data.property, data.wikitext))
                     :wikitext(data.wikitext)
                     :done()
                     :done()
                 :tag('td')
                 :tag('td')
Line 341: Line 433:
             :done()
             :done()
     end
     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
     -- stat table
Line 357: Line 438:
     tbl = container:tag('table')
     tbl = container:tag('table')
     tbl
     tbl
         :attr('class', 'wikitable sortable')
         :addClass('wikitable sortable')
         :tag('tr')
         :tag('caption')
             :tag('th')
             :wikitext(i18n.data_sheet.stats)
                :attr('colspan', 4)
                :wikitext('Stats')
                :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Is stat number|#]]')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has stat id|Stat Id]]')
                 :wikitext(i18n.data_sheet.stat_id)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has minimum stat value|Minimum]]')
                 :wikitext(i18n.data_sheet.min)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has maximum stat value|Maximum]]')
                 :wikitext(i18n.data_sheet.max)
                 :done()
                 :done()
             :done()
             :done()
         :done()
         :done()
          
          
     for i=1, 5 do
     for i=1, #tpl_args.stats do
         local value = {
         local value = {
             id = g_args['stat' .. i .. '_id'],
             id = tpl_args['stat' .. i .. '_id'],
             min = g_args['stat' .. i .. '_min'],
             min = tpl_args['stat' .. i .. '_min'],
             max = g_args['stat' .. i .. '_max'],
             max = tpl_args['stat' .. i .. '_max'],
         }
         }
          
          
Line 411: Line 489:
     tbl = container:tag('table')
     tbl = container:tag('table')
     tbl
     tbl
         :attr('class', 'wikitable sortable')
         :addClass('wikitable sortable')
         :tag('tr')
         :tag('caption')
             :tag('th')
             :wikitext(i18n.data_sheet.spawn_weights)
                :attr('colspan', 3)
                :wikitext('Spawn Weights')
                :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Is tag number|#]]')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has tag|Tag]]')
                 :wikitext(i18n.data_sheet.tag)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has spawn weight|Weight]]')
                 :wikitext(i18n.data_sheet.value)
                 :done()
                 :done()
             :done()
             :done()
Line 432: Line 507:
          
          
     i = 0
     i = 0
     value = nil
     local value = nil
     repeat
     repeat
         i = i + 1
         i = i + 1
         value = {
         value = {
             tag = g_args[string.format('spawn_weight%s_tag', i)],
             tag = tpl_args[string.format('spawn_weight%s_tag', i)],
             value = g_args[string.format('spawn_weight%s_value', i)],
             value = tpl_args[string.format('spawn_weight%s_value', i)],
         }
         }
          
          
Line 461: Line 536:
     tbl = container:tag('table')
     tbl = container:tag('table')
     tbl
     tbl
         :attr('class', 'wikitable sortable')
         :addClass('wikitable sortable')
         :tag('tr')
         :tag('caption')
             :tag('th')
             :wikitext(i18n.data_sheet.generation_weights)
                :attr('colspan', 3)
                :wikitext('Generation Weights')
                :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Is tag number|#]]')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has tag|Tag]]')
                 :wikitext(i18n.data_sheet.tag)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has generation weight|Weight]]')
                 :wikitext(i18n.data_sheet.value)
                 :done()
                 :done()
             :done()
             :done()
Line 486: Line 558:
         i = i + 1
         i = i + 1
         value = {
         value = {
             tag = g_args[string.format('generation_weight%s_tag', i)],
             tag = tpl_args[string.format('generation_weight%s_tag', i)],
             value = g_args[string.format('generation_weight%s_value', i)],
             value = tpl_args[string.format('generation_weight%s_value', i)],
         }
         }
          
          
Line 507: Line 579:
     until value.tag == nil
     until value.tag == nil
      
      
     -- Generic messages on the page
     -- Sell prices
    tbl = container:tag('table')
    tbl
        :addClass('wikitable sortable')
        :tag('caption')
            :wikitext(i18n.data_sheet.sell_price)
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.data_sheet.amount)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.item)
                :done()
            :done()
        :done()
      
      
     out = {}
     for i, value in ipairs(sell_prices) do
   
        tbl
    if mw.ustring.find(g_args['id'], '_') then
            :tag('tr')
        out[#out+1] = g_frame:expandTemplate{ title = 'Incorrect title', args = { title=g_args['id'] } } .. '\n\n\n'
                :tag('td')
                    :wikitext(value.amount)
                    :done()
                :tag('td')
                    :wikitext(string.format('[[%s]]', value.name))
                    :done()
                :done()
     end
     end
      
      
     if g_args['name'] then
     -- Generic messages on the page
        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
     local out = {}
   
    cats = {'Mods'}
   
    -- Done -> output
      
      
     return tostring(container) .. util.misc.add_category(cats) .. '\n' .. table.concat(out)
     if mw.ustring.find(tpl_args['id'], '_') then
end
         out[#out+1] = mw.getCurrentFrame():expandTemplate{ title = 'Incorrect title', args = { title=tpl_args['id'] } } .. '\n\n\n'
 
--
-- 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
     end
      
      
     g_args.header_level = g_args.header_level or 2
     if tpl_args['name'] then
   
         out[#out+1] = string.format(i18n.sections.intro_named_id, tpl_args['id'], tpl_args['name'])
    -- 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
     else
         args.show_tags = util.cast.boolean(args.show_tags)
         out[#out+1] = string.format(i18n.sections.intro_unnamed_id, tpl_args['id'])
     end
     end
     args.effect_rowid = (tonumber(args.effect_rowid) or 0) + 1
     out[#out+1] = '\n'


     tr = mw.html.create('tr')
     -- Item usage
    tr
    local items = m_cargo.query(
         :tag('td')
         {'item_mods'},
            :attr('data-sort-value', g_args[2] or g_args[1])
        {'item_mods._pageName=page'},
             :wikitext(string.format('[[%s|%s]]', g_args[1], g_args[2] or g_args[1]))
        {
             :done()
             where = string.format(
   
                'item_mods.id = "%s"',
     local i = 2
                tpl_args['id']
     local row_max = i + args.extra_rows
             )
    local text
        }
    while i < row_max do
     )
        i = i + 1
     if #items > 0 then
        text = g_args[i] or ''
        local html = mw.html.create()
        text = table.concat(mw.text.split(text, ';', true), '<br>')
             :tag('h2')
       
                 :wikitext(i18n.sections.items)
        if args.type == 'map' and i == args.effect_rowid then
                 :done()
             text = mw.text.split(text, '<br>', true)
             :tag('p')
           
                 :wikitext(i18n.sections.used_by_items)
            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()
                 :done()
    end
        out[#out+1] = tostring(html)
   
         out[#out+1] = h.item_table{
   
             where = string.format(
    local query
                'items._pageName IN ("%s")',
    local result
                table.concat(m_util.table.column(items, 'page'), '","')
   
             ),
    if args.type == 'map' then
             orderBy = 'items.name ASC',
         query = {
             debug = tpl_args.debug,
             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
     end
      
      
     local tags
     -- Categories
    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 cats = {i18n.categories.mods}
        local jewels = {
    --[[if tpl_args.game_mode == nil then
            dex=0,
        cats[#cats+1] = i18n.categories.no_game_mode
            str=0,
    end--]]
            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
     -- Done -> output
        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)
     return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)
end
end


function p.get_item_tags(frame)
-- ----------------------------------------------------------------------------
-- This function queries for the tags for a specific item.
-- Exported functions
-- ----------------------------------------------------------------------------
-- 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)


results[1]['Has tags'] = results[1]['Has tags']:gsub('(<MANY>)', ', ') -- Remove unnecessary symbols.
local p = {}
-- function table.reverse(a) -- Reverse order
-- 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)
p.table_main = m_cargo.declare_factory{data=mod_map.main}
-- Replace numbers with #
p.table_mod_stats = m_cargo.declare_factory{data=mod_map.mod_stats}
s = table.concat(util.string.split(str, '%(%d+%.*%d* to %d+%.*%d*%)'), '#')
p.table_mod_spawn_weights = m_cargo.declare_factory{data=mod_map.mod_spawn_weights}
s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
p.table_mod_generation_weights = m_cargo.declare_factory{data=mod_map.mod_generation_weights}
s = s:gsub('<br>', ', ') -- s:gsub('%[%[(.-)%]%]', '%1'):gsub('(%a*)%|', ''):gsub('<br>', ', ')
p.table_mod_sell_prices = m_cargo.declare_factory{data=mod_map.mod_sell_prices}
 
  return s
end


function p.drop_down_table(frame)
--
-- Misses forsaken masters currently
-- Template:Mod
--  
-- Weapons
p.mod = m_util.misc.invoker_factory(_mod, {
-- p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
     wrappers = 'Template:Mod',
 
})
-- 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 = 'One-handed maces'}
-- p.drop_down_table{item = 'Driftwood Sceptre', header = 'Sceptres'}
-- p.drop_down_table{item = 'Rusted Sword', header = 'One-handed swords'}
-- p.drop_down_table{item = 'Rusted Spike', header = 'Thrusting Swords'}
-- p.drop_down_table{item = 'Driftwood Wand', header = 'Wands'}
-- 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 = 'Driftwood Maul', header = 'Two Handed Maces'}
-- p.drop_down_table{item = 'Fishing Rod', header = 'Fishing Rods'}
-- p.drop_down_table{item = 'Gnarled Branch', header = 'Staves'}
-- p.drop_down_table{item = 'Corroded Blade', header = 'Two Handed Swords'}
-- 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 = 'Evasion/energy shield body armours'}
-- p.drop_down_table{item = 'Sacrificial Garb', header = 'Armour/evasion/energy shield 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 = 'Evasion/energy shield 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 = 'Evasion/energy shield 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 = 'Evasion/energy shield shields'}
 
-- Args
    local g_args = getArgs(frame, {
        parentFirst = true
    })
    local g_frame = util.misc.get_frame(frame)
   
local get_item_tags = p.get_item_tags{g_args.item}
local item_tags = {}
item_tags = util.string.split(get_item_tags['Has tags'], ', ')
local header = g_args['header']
if header == nil then
header = table.concat(item_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(item_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]]',
},
corrupted = {
header = 'Corrupted',
condition = '[[Concept:Spawnable corrupted 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_item_tags[1]}})
local results = {}
     for i_type, generation_type in ipairs({'prefix', 'suffix', 'corrupted'}) 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_mod_tags = {}
local mod_tags_weighting = {}
local last
for i, v in ipairs(results[generation_type]) do -- Loop through all the results from the smw query.
-- Query mod_tags
local pagename = results[generation_type][i][1]
query_mod_tags = {
string.format('[[-Has subobject::%s]]', pagename),
'?Has tag',
'?Has spawn weight',
'?Is tag number',
sort='Is tag number',
}
results_mod_tags[i] = util.smw.query(query_mod_tags, g_frame)
local j = 0
local tag_match_stop
repeat -- Loop through the mod tags until a match is found.
j = j+1
mod_tag = results_mod_tags[i][j]['Has tag']
local mod_tag_weight = results_mod_tags[i][j]['Has spawn weight']:gsub(',', '')
mod_tag_weight = tonumber(mod_tag_weight)
local y = 0
local tag_match_display
repeat -- Loop through the item tags until it matches the mod tag and the mod tag has a value.
y = y+1
tag_match_stop = (mod_tag == item_tags[y]) and (mod_tag_weight >= 0)
tag_match_display = (mod_tag == item_tags[y]) and (mod_tag_weight > 0)
until tag_match_stop or y == #item_tags
if tag_match_display 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
local mod_name = results[generation_type][i]['Has name']
if  mod_name == '' then
mod_name = results[generation_type][i]['Is mod']
end
tbl
:tag('tr')
:tag('td')
:attr('width', '150')
:wikitext(string.format('[[%s|%s]]', results[generation_type][i][1], 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;'), 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 tag_match_stop or j == #results_mod_tags[i]
end
out[#out+1] = tostring(container)
    end
return table.concat(out,'')
end


return p
return p

Latest revision as of 17:05, 7 December 2025

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


Lua logo

This module depends on the following other modules:

Module for handling for modifiers with Cargo support.

List of currently implemented templates

-------------------------------------------------------------------------------
-- 
--                            Module:Mod
-- 
-- This module implements Template:Mod
-------------------------------------------------------------------------------

require('strict')
local m_util = require('Module:Util')

-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Mod')

local m_cargo = use_sandbox and require('Module:Cargo/sandbox') or require('Module:Cargo')
local m_game = use_sandbox and mw.loadData('Module:Game/sandbox') or mw.loadData('Module:Game')

-- Lazy loading
local f_item_table -- require('Module:Item table').item_table

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Mod/config/sandbox') or mw.loadData('Module:Mod/config')

local i18n = cfg.i18n

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

local h = {}

-- Lazy loading for Module:Item table
function h.item_table(args)
    if not f_item_table then
        f_item_table = use_sandbox and require('Module:Item table/sandbox').item_table or require('Module:Item table').item_table
    end
    return f_item_table(args)
end

function h.set_weights(tpl_args, args)
    -- Parses a weighted pair of lists and stores cargo data
    --
    -- tpl_args: argument table to work with
    -- args:
    --   prefix - input prefix for parsing the arguments from tpl_args
    --   table_map - cargo table map
    --   store - whether to store the Cargo data

    args = args or {}
    for i=1, math.huge do -- repeat until no more weights are found
        local prefix = args.prefix .. i
        local params = {
            tag = string.format('%s_tag', prefix),
            value = string.format('%s_value', prefix),
        }
        local tag = tpl_args[params.tag]
        local value = tpl_args[params.value]
        if tag == nil and value == nil then
            break
        end
        if tag == nil or value == nil then
            error(string.format(i18n.errors.invalid_weight, params.tag, params.value))
        end
        value = m_util.cast.number(value, {min = 0})

        -- Store to cargo table unless tag = default and value = 0
        if args.store and (tag ~= 'default' or value ~= 0) then
            m_cargo.store({
                _table = args.table_map.table,
                [args.table_map.fields.ordinal.field] = i,
                [args.table_map.fields.tag.field] = tag,
                [args.table_map.fields.value.field] = value,
            })
        end
    end
end

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

--
-- Template: Mod
--

local mod_map = {
    main = {
        table = 'mods',
        display_order = {'id', 'name', 'mod_groups', 'mod_type', 'domain', 'generation_type', --[['game_mode',--]] 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
        order = {'id', 'name', 'mod_groups', 'mod_type', 'domain', 'generation_type', --[['game_mode',--]] '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.data_sheet.id,
                func = function (tpl_args, value)
                    -- Validate that the id is unique
                    local results = m_cargo.query(
                        {'mods'},
                        {'mods._pageName'},
                        {
                            where = string.format(
                                'mods.id = "%s" AND mods._pageName != "%s"',
                                value,
                                m_cargo.addslashes(mw.title.getCurrentTitle().prefixedText)
                            )
                        }
                    )
                    if #results > 0 then
                        error(string.format(i18n.errors.duplicate_mod_id, results[1]['mods._pageName']))
                    end
                    return value
                end
            },
            name = {
                name = 'name',
                field = 'name',
                type = 'String',
                wikitext = i18n.data_sheet.name,
            },
            mod_groups = {
                name = 'mod_groups',
                field = 'mod_groups',
                type = 'List (,) of String',
                wikitext = i18n.data_sheet.mod_groups,
                display = function (value)
                    return table.concat(value, ', ')
                end,
                default = {},
            },
            mod_type = {
                name = 'mod_type',
                field = 'mod_type',
                type = 'String',
                wikitext = i18n.data_sheet.mod_type,
            },
            domain = {
                name = 'domain',
                field = 'domain',
                type = 'Integer',
                wikitext = i18n.data_sheet.domain,
                display = function (value)
                    return string.format(i18n.data_sheet.domain_fmt, m_game.constants.mod.domains[value]['short_upper'], value)
                end,
            },
            generation_type = {
                name = 'generation_type',
                field = 'generation_type',
                type = 'Integer',
                wikitext = i18n.data_sheet.generation_type,
                display = function (value)
                    return string.format(i18n.data_sheet.generation_type_fmt, m_game.constants.mod.generation_types[value]['short_upper'], value)
                end,
            },
            --[[game_mode = {
                name = 'game_mode',
                field = 'game_mode',
                type = 'Integer',
                wikitext = i18n.data_sheet.game_modes,
                display = function (value)
                    if value == nil then
                        return value
                    end
                    value = m_util.cast.number(value)
                    local modes = {}
                    for k, m in ipairs(m_game.modes) do
                        if value == 0 or value == k then
                            table.insert(modes, m.short_upper)
                        end
                    end
                    return table.concat(modes, ', ')
                end,
            },--]]
            required_level = {
                name = 'required_level',
                field = 'required_level',
                type = 'Integer',
                wikitext = i18n.data_sheet.required_level,
            },
            stat_text = {
                name = 'stat_text',
                field = 'stat_text',
                type = 'Text',
                wikitext = i18n.data_sheet.stat_text,
            },
            stat_text_raw = {
                name = nil,
                field = 'stat_text_raw',
                type = 'Text',
                func = function (tpl_args, value)
                    if tpl_args.stat_text then
                        -- Strip wikilinks and html, but keep any line break tags
                        value = m_util.string.strip_wikilinks(tpl_args.stat_text)
                        value = mw.ustring.gsub(value, '<br */?>', '�')
                        value = m_util.string.strip_html(value)
                        value = mw.ustring.gsub(value, '�', '<br>')
                    end
                    return value
                end
            },
            granted_buff_id = {
                name = 'granted_buff_id',
                field = 'granted_buff_id',
                type = 'String',
                wikitext = i18n.data_sheet.granted_buff_id,
            },
            granted_buff_value = {
                name = 'granted_buff_value',
                field = 'granted_buff_value',
                type = 'Integer',
                wikitext = i18n.data_sheet.granted_buff_value,
            },
            granted_skill = {
                name = 'granted_skill',
                field = 'granted_skill',
                type = 'String',
                wikitext = i18n.data_sheet.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.data_sheet.tier_text,
            },
        },
    },
    mod_stats = {
        table = 'mod_stats',
        fields = {
            id = {
                field = 'id',
                type = 'String',
            },
            min = {
                field = 'min',
                type = 'Integer',
            },
            max = {
                field = 'max',
                type = 'Integer',
            },
        },
    },
    mod_spawn_weights = {
        table = 'mod_spawn_weights',
        fields = {
            ordinal = {
                field = 'ordinal',
                type = 'Integer',
            },
            tag = {
                field = 'tag',
                type = 'String',
            },
            value = {
                field = 'value',
                type = 'Integer',
            },
        },
    },
    mod_generation_weights = {
        table = 'mod_generation_weights',
        fields = {
            ordinal = {
                field = 'ordinal',
                type = 'Integer',
            },
            tag = {
                field = 'tag',
                type = 'String',
            },
            value = {
                field = 'value',
                type = 'Integer',
            },
        },
    },
    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,
            },
        },
    },
}

-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------

local function _mod(tpl_args)
    -- p.mod{id = "LocalIncreasedPhysicalDamagePercentUniqueOneHandSword2", name = "", mod_groups = "LocalPhysicalDamagePercent, Dexterity", 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"}
    
    --
    -- Validate and store
    --

    --local store = tpl_args.game_mode ~= nil
    local store = true

    -- Validate and store mod data
    m_cargo.store_mapped_args{
        tpl_args = tpl_args,
        table_map = mod_map.main,
        rtr = not store,
    }
    
    -- Validate and store stats
    m_util.args.stats(tpl_args)
    for _, stat_data in pairs(tpl_args.stats) do
        if store then
            m_cargo.store({
                _table = 'mod_stats', 
                id = stat_data.id,
                min = stat_data.min,
                max = stat_data.max,
            })
        end
    end
    
    -- Validate and store spawn weights
    h.set_weights(tpl_args, {
        prefix = 'spawn_weight',
        table_map = mod_map.mod_spawn_weights,
        store = store,
    })
    
    -- Validate and store generation weights
    h.set_weights(tpl_args, {
        prefix = 'generation_weight',
        table_map = mod_map.mod_generation_weights,
        store = store,
    })
    
    -- Validate and store mod sell values
    local i = 0
    local names = {}
    local sell_prices = {}
    repeat 
        i = i + 1
        
        local id = {}
        local 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
            if store then
                m_cargo.store(cargo_data)
            end
            
            sell_prices[#sell_prices+1] = value
        else
            error(string.format(i18n.errors.sell_price_missing_arguments, id.name, id.amount))
        end
        
    until value == nil

    -- Attach to tables
    if store then
        mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mods/attach'}
        mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod stats/attach'}
        mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod spawn weights/attach'}
        mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod generation weights/attach'}
        mw.getCurrentFrame():expandTemplate{title = 'Template:Mod/cargo/mod sell prices/attach'}
    end
    
    --
    -- Display
    --
    
    local container = mw.html.create('div')
        :addClass('modbox')
    
    -- core stats
    
    local tbl = container:tag('table')
        :addClass('wikitable')
    
    for _, key in ipairs(mod_map.main.display_order) do
        local data = mod_map.main.fields[key]
        local text = tpl_args[key]
        if type(data.display) == 'function' then
            text = data.display(text)
        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
        :addClass('wikitable sortable')
        :tag('caption')
            :wikitext(i18n.data_sheet.stats)
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.data_sheet.ordinal)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.stat_id)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.min)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.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
        :addClass('wikitable sortable')
        :tag('caption')
            :wikitext(i18n.data_sheet.spawn_weights)
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.data_sheet.ordinal)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.tag)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.value)
                :done()
            :done()
        :done()
        
    i = 0
    local 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
        :addClass('wikitable sortable')
        :tag('caption')
            :wikitext(i18n.data_sheet.generation_weights)
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.data_sheet.ordinal)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.tag)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.value)
                :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
        :addClass('wikitable sortable')
        :tag('caption')
            :wikitext(i18n.data_sheet.sell_price)
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.data_sheet.amount)
                :done()
            :tag('th')
                :wikitext(i18n.data_sheet.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
    
    local out = {}
    
    if mw.ustring.find(tpl_args['id'], '_') then
        out[#out+1] = mw.getCurrentFrame():expandTemplate{ title = 'Incorrect title', args = { title=tpl_args['id'] } } .. '\n\n\n'
    end
    
    if tpl_args['name'] then
        out[#out+1] = string.format(i18n.sections.intro_named_id, tpl_args['id'], tpl_args['name'])
    else
        out[#out+1] = string.format(i18n.sections.intro_unnamed_id, tpl_args['id'])
    end
    out[#out+1] = '\n'

    -- Item usage
    local items = m_cargo.query(
        {'item_mods'},
        {'item_mods._pageName=page'},
        {
            where = string.format(
                'item_mods.id = "%s"',
                tpl_args['id']
            )
        }
    )
    if #items > 0 then
        local html = mw.html.create()
            :tag('h2')
                :wikitext(i18n.sections.items)
                :done()
            :tag('p')
                :wikitext(i18n.sections.used_by_items)
                :done()
        out[#out+1] = tostring(html)
        out[#out+1] = h.item_table{
            where = string.format(
                'items._pageName IN ("%s")',
                table.concat(m_util.table.column(items, 'page'), '","')
            ),
            orderBy = 'items.name ASC',
            debug = tpl_args.debug,
        }
    end
    
    -- Categories
    
    local cats = {i18n.categories.mods}
    --[[if tpl_args.game_mode == nil then
        cats[#cats+1] = i18n.categories.no_game_mode
    end--]]
    
    -- Done -> output
    
    return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)
end

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

p.table_main = m_cargo.declare_factory{data=mod_map.main}
p.table_mod_stats = m_cargo.declare_factory{data=mod_map.mod_stats}
p.table_mod_spawn_weights = m_cargo.declare_factory{data=mod_map.mod_spawn_weights}
p.table_mod_generation_weights = m_cargo.declare_factory{data=mod_map.mod_generation_weights}
p.table_mod_sell_prices = m_cargo.declare_factory{data=mod_map.mod_sell_prices}

--
-- Template:Mod
-- 
p.mod = m_util.misc.invoker_factory(_mod, {
    wrappers = 'Template:Mod',
})

return p