Module:Mod: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
>Illviljan
(Quering for all subobjects at the same time instead of one query per mod.)
m (88 revisions imported)
 
(57 intermediate revisions by 7 users not shown)
Line 1: Line 1:
--
-------------------------------------------------------------------------------
-- Module for mod related templates
--  
--
--                           Module:Mod
--  
-- This module implements Template:Mod
-------------------------------------------------------------------------------
 
require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
 
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Mod')
 
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 m_item = require('Module:Item2')


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 = 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 sets properties
        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


    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 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 111: Line 83:
--
--


-- 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', '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', '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 = 'Mod 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,
            },
            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=13},
        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=12},
                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
    --
    -- Validate single value properties and set them
    m_cargo.store_mapped_args{
        tpl_args = tpl_args,
        table_map = mod_map.main,
     }
     }
      
      
     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
        m_cargo.store({
            _table = 'mod_stats',
            id = stat_data.id,
            min = stat_data.min,
            max = stat_data.max,
        })
    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
    })
      
      
     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
       
     })
        g_args['tags'] = tags
   
         properties = {}
        properties['Has tag'] = table.concat(tags, ';')
         properties['+sep'] = ';'
        util.smw.set(g_frame, properties)
    end
   
    -- Validate % set the stat subobjects
     util.args.stats(g_args, {frame=g_frame})
      
      
     -- Validate & set spawn weight subobjects
     -- Validate and store mod sell values
     local i = 0
     local i = 0
    local id = nil
    local value = nil
    repeat
        i = i + 1
        id = {
            tag = string.format('spawn_weight%s_tag', i),
            value = string.format('spawn_weight%s_value', i),
        }
   
        value = {
            tag = g_args[id.tag],
            value = g_args[id.value],
        }
       
        if value.tag ~= nil and value.value ~= nil then
            properties = {}
            properties['Is tag number'] = i
            properties['Has tag'] = value.tag
            properties['Has spawn weight'] = h.validate.number({min=0})(id.value)
           
            util.smw.subobject(g_frame, string.format('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 subobjects
    i = 0
    id = nil
    value = nil
    repeat
        i = i + 1
        id = {
            tag = string.format('generation_weight%s_tag', i),
            value = string.format('generation_weight%s_value', i),
        }
   
        value = {
            tag = g_args[id.tag],
            value = g_args[id.value],
        }
       
        if value.tag ~= nil and value.value ~= nil then
            properties = {}
            properties['Is tag number'] = i
            properties['Has tag'] = value.tag
            properties['Has generation weight'] = h.validate.number({min=0})(id.value)
           
           
            util.smw.subobject(g_frame, string.format('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
   
    -- Validate & set mod sell values
    i = 0
     local names = {}
     local names = {}
     local sell_prices = {}
     local sell_prices = {}
     repeat  
     repeat  
         i = i + 1
         i = i + 1
         id = {
          
             name = string.format('sell_price%s_name', i),
        local id = {}
            amount = string.format('sell_price%s_amount', i),
        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 = {
            value[key] = data.func(tpl_args[id[key]])
            name = g_args[id.name],
         end
            amount = tonumber(g_args[id.amount]),
         }
          
          
         if value.name == nil and value.amount == nil then
         if value.name == nil and value.amount == nil then
Line 293: Line 345:
         elseif value.name ~= nil and value.amount ~= nil then
         elseif value.name ~= nil and value.amount ~= nil then
             if names[value.name] then
             if names[value.name] then
                 error('Do not specify a sell price item name multiple times. Instead adjust the amount')
                 error(i18n.errors.sell_price_duplicate_name)
             else
             else
                 names[value.name] = true
                 names[value.name] = true
             end
             end
           
 
             properties = {}
             local cargo_data = {
             properties['Has sell price item name'] = value.name
                _table = mod_map.mod_sell_prices.table,
            properties['Has sell price amount'] = value.amount
            }
              
             for key, data in pairs(mod_map.mod_sell_prices.fields) do
             util.smw.subobject(g_frame, 'sell price ' .. value.name, properties)
                cargo_data[data.field] = value[key]
             end
             m_cargo.store(cargo_data)
              
              
             sell_prices[#sell_prices+1] = value
             sell_prices[#sell_prices+1] = value
         else
         else
             error ('Both ' .. id.name .. ' and ' .. id.amount .. ' must be specified')
             error (string.format(i18n.errors.sell_price_missing_arguments, id.name, id.amount))
         end
         end
          
          
     until value == nil
     until value == nil
    -- Attach to tables
    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'}
      
      
     --
     --
Line 316: Line 377:
      
      
     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 344: Line 401:
             :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 360: Line 406:
     tbl = container:tag('table')
     tbl = container:tag('table')
     tbl
     tbl
         :attr('class', 'wikitable sortable')
         :addClass('wikitable sortable')
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :attr('colspan', 4)
                 :attr('colspan', 4)
                 :wikitext('Stats')
                 :wikitext(i18n.data_sheet.stats)
                 :done()
                 :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 414: Line 460:
     tbl = container:tag('table')
     tbl = container:tag('table')
     tbl
     tbl
         :attr('class', 'wikitable sortable')
         :addClass('wikitable sortable')
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :attr('colspan', 3)
                 :attr('colspan', 3)
                 :wikitext('Spawn Weights')
                 :wikitext(i18n.data_sheet.spawn_weights)
                 :done()
                 :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.weight)
                 :done()
                 :done()
             :done()
             :done()
Line 435: Line 481:
          
          
     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 464: Line 510:
     tbl = container:tag('table')
     tbl = container:tag('table')
     tbl
     tbl
         :attr('class', 'wikitable sortable')
         :addClass('wikitable sortable')
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :attr('colspan', 3)
                 :attr('colspan', 3)
                 :wikitext('Generation Weights')
                 :wikitext(i18n.data_sheet.generation_weights)
                 :done()
                 :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.weight)
                 :done()
                 :done()
             :done()
             :done()
Line 489: Line 535:
         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 513: Line 559:
     tbl = container:tag('table')
     tbl = container:tag('table')
     tbl
     tbl
         :attr('class', 'wikitable sortable')
         :addClass('wikitable sortable')
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :attr('colspan', 2)
                 :attr('colspan', 2)
                 :wikitext('Modifier sell price')
                 :wikitext(i18n.data_sheet.sell_price)
                 :done()
                 :done()
             :done()
             :done()
         :tag('tr')
         :tag('tr')
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has sell price amount|#]]')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('[[Property:Has sell price item name|Item]]')
                 :wikitext(i18n.data_sheet.item)
                 :done()
                 :done()
             :done()
             :done()
Line 537: Line 583:
                     :done()
                     :done()
                 :tag('td')
                 :tag('td')
                     :wikitext(m_item.item_link{item_name_exact=value.name})
                     :wikitext(string.format('[[%s]]', value.name))
                     :done()
                     :done()
                 :done()
                 :done()
Line 544: Line 590:
     -- Generic messages on the page
     -- Generic messages on the page
      
      
     out = {}
     local out = {}
      
      
     if mw.ustring.find(g_args['id'], '_') then
     if mw.ustring.find(tpl_args['id'], '_') then
         out[#out+1] = g_frame:expandTemplate{ title = 'Incorrect title', args = { title=g_args['id'] } } .. '\n\n\n'
         out[#out+1] = mw.getCurrentFrame():expandTemplate{ title = 'Incorrect title', args = { title=tpl_args['id'] } } .. '\n\n\n'
     end
     end
      
      
     if g_args['name'] then
     if tpl_args['name'] then
         out[#out+1] = string.format("'''%s''' is the internal id of modifier '''%s'''.\n", g_args['id'], g_args['name'])
         out[#out+1] = string.format(i18n.sections.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", g_args['id'], g_args['name'])
         out[#out+1] = string.format(i18n.sections.intro_unnamed_id, tpl_args['id'])
     end
     end
   
    -- Categories
   
    cats = {'Mods'}
   
    -- Done -> output
   
    return tostring(container) .. util.misc.add_category(cats) .. '\n' .. table.concat(out)
end


--
     -- Item usage
-- Template: SMW query mods
     local items = m_cargo.query(
--
         {'item_mods'},
 
         {'item_mods._pageName=page'},
function p.query_mods(frame)
         {
     -- Args
             where = string.format(
     g_args = getArgs(frame, {
                 'item_mods.id = "%s"',
        parentFirst = true
                tpl_args['id']
    })
             )
    g_frame = util.misc.get_frame(frame)
         }
   
     )
    g_args.colspan = 4
     if #items > 0 then
   
         local html = mw.html.create()
    local conditions = {}
            :tag('h2')
    conditions[#conditions+1] = 'concept'
                :wikitext(i18n.sections.items)
    if g_args.tag then
                :done()
         conditions[#conditions+1] = string.format('[[Has subobject::<q>[[-Has subobject::+]] [[Has spawn weight::>>0]] [[Has tag::%s]]</q>]]', g_args.tag)
             :tag('p')
    end
                 :wikitext(i18n.sections.used_by_items)
   
    g_args.header_level = g_args.header_level or 2
   
    -- Fields
    local fields = {}
    fields[#fields+1] = '?Is mod'
    fields[#fields+1] = '?Has name'
    fields[#fields+1] = '?Has level requirement'
    fields[#fields+1] = '?Has mod group'
    fields[#fields+1] = '?Has stat text'
    -- parameters
    local parameters = {}
    parameters.sort = 'Has mod group, '
    parameters.limit = 1000 -- lets see
   
    local data = {}
    data.header = {
        prefix = 'Prefix',
         suffix = 'Suffix',
    }
   
    local out = {}
    for _, v in ipairs({'prefix', 'suffix'}) do
        out[#out+1] = string.format('<h%i>%s</h%i>', g_args.header_level, data.header[v], g_args.header_level)
        conditions[1] = string.format('[[Concept:Spawnable named %s item mods]]', v)
       
        local query
        local results
        --
        -- Query tags
        --
        query = {}
        query[#query+1] = string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' '))
        query[#query+1] = '[[Has tag::+]]'
        query[#query+1] = '[[Has spawn weight::+]]'
         --query[#query+1] = '[[Has spawn weight::>>0]]'
        query[#query+1] = '?Has tag'
        query[#query+1] = '?Has spawn weight#' -- need native number
        query.limit = 1000
        query.offset = 0
        -- Tag order is very important
        query.sort = ', Is tag number'
       
        local tags = {}
        -- this works because lua only considers nil to be false >_>
        while query.offset do
             results = util.smw.query(query, g_frame)
            query.offset = query.offset + #results
            -- terminates the while if enough reuslts have been fetched
            if query.offset % 1000 ~= 0 then
                query.offset = nil
            end
           
            for _, row in ipairs(results) do
                local page, _ = string.gsub(row[1], '#_[%x]+', '')
                if tags[page] == nil then
                    tags[page] = {}
                end
               
                local text
                if tonumber(row['Has spawn weight']) > 0 then
                    text = '[[File:Yes.png|yes|link=]]'
                 else
                    text = '[[File:No.png|no|link=]]'
                end
               
                tags[page][#tags[page]+1] = string.format('%s %s', row['Has tag'], text)
            end
        end
       
        --
        -- Query mods
        --
        query = {}
        for _, v in ipairs(conditions) do
            query[#query+1] = v
        end
        for _, v in ipairs(fields) do
            query[#query+1] = v
        end
        for k, v in pairs(parameters) do
            query[k] = v
        end
       
        results = util.smw.query(query, g_frame)
       
        local last = ''
        local tbl = ''
        for _, row in ipairs(results) do
            local current = string.gsub(row['Is mod'], '%d+.*', '%%d.*')
             if string.match(last, current) then
                h.format_mod(tbl, row, tags[row[1]])
            else
                out[#out+1] = tostring(tbl)
                tbl = h.create_header(row)
                h.format_mod(tbl, row, tags[row[1]])
            end
            last = row['Is mod']
         end
       
        -- add the last table
        out[#out+1] = tostring(tbl)
    end
   
    return table.concat(out, '')
end
 
--
-- Template: SMW mod table
--
 
-- =p.mod_list{'Strength1', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'ColdCritMultiplier', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'MapMultipleExilesMap2Tier', 'asdasda', 'Area yields 15% more Items<br>8% increased Rarity of Items found in this Area<br>Area is inhabited by 2 additional Rogue Exiles<br>Extra monsters ignore rarity bias (Hidden)<br>+14% Monster pack size', userparam='extra_rows=1, type=map, effect_rowid=2'}
 
function p.mod_list(frame)
    local types = {'map', 'jewel'}
   
    -- Args
     g_args = getArgs(frame, {
        parentFirst = true
    })
     g_frame = util.misc.get_frame(frame)
   
    --
    local args = util.string.split_args(g_args.userparam, {sep=', '})
    g_args.userparam = args
   
    args.extra_rows = (tonumber(args.extra_rows) or 0)
    if args.show_tags == nil then
         args.show_tags = true
    else
        args.show_tags = util.cast.boolean(args.show_tags)
    end
    args.effect_rowid = (tonumber(args.effect_rowid) or 0) + 1
 
    tr = mw.html.create('tr')
    tr
        :tag('td')
            :attr('data-sort-value', g_args[2] or g_args[1])
            :wikitext(string.format('[[%s|%s]]', g_args[1], g_args[2] or g_args[1]))
            :done()
   
    local i = 2
    local row_max = i + args.extra_rows
    local text
    while i < row_max do
        i = i + 1
        text = g_args[i] or ''
        text = table.concat(mw.text.split(text, ';', true), '<br>')
       
        if args.type == 'map' and i == args.effect_rowid then
            text = mw.text.split(text, '<br>', true)
           
            local map = {
                '%d+%% increased Quantity of Items found in this Area',
                '%d+%% increased Rarity of Items found in this Area',
                '%+%d+%% Monster pack size',
            }
            out = {}
           
            local valid
            for k, v in pairs(text) do
                valid = true
                for _, pattern in ipairs(map) do
                    if mw.ustring.find(v, pattern) ~= nil then
                        valid = false
                        break
                    end
                end
               
                if valid then
                    table.insert(out, v)
                end
            end
           
            text = table.concat(out, '<br>')
        end
           
        tr
             :tag('td')
                 :wikitext(text)
                 :done()
                 :done()
    end
         out[#out+1] = tostring(html)
   
        out[#out+1] = h.item_table{
   
             q_tables = 'items',
    local query
             q_where = string.format(
    local result
                'items._pageName IN ("%s")',
   
                table.concat(m_util.table.column(items, 'page'), '","')
    if args.type == 'map' then
             ),
         query = {
             q_orderBy = 'items.name ASC',
            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%-%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 = {
            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
     -- 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.item_sell_price(frame)
-- ----------------------------------------------------------------------------
-- Query and sum the vendor prices for an item.
-- Exported functions
-- 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 = util.misc.get_frame(frame)


-- Only the explicit modifiers are counted when vendors calculates the price.
local p = {}
local condition = string.format('[[%s]]', g_args['page'])
local query_item_mods = {
condition,
'?Has explicit mod ids',
'?Has rarity',
}
local results_query_item_mods = util.smw.query(query_item_mods, g_frame)
local item_mods = 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 = 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 = 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.find_mod_domain(input)
p.table_main = m_cargo.declare_factory{data=mod_map.main}
-- Find the mod domain based on the item class.
p.table_mod_stats = m_cargo.declare_factory{data=mod_map.mod_stats}
local out = input
p.table_mod_spawn_weights = m_cargo.declare_factory{data=mod_map.mod_spawn_weights}
local mod_domains = game.constants.mod.domains
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}
for i,_ in ipairs(out) do
out[i]['Has mod domain'] = 1
for j, row in pairs(mod_domains) do
if out[i]['Has item class']:gsub('Map', 'Area'):match(mod_domains[j]['short_upper']) then -- This may need updating if an area item class doesn't have 'Map' in the string, or if the mod domain descriptions doesn't match the item class.  
out[i]['Has mod domain'] = j
end
end
out[i]['Has mod domain text'] = mod_domains[out[i]['Has mod domain']]['short_lower']
end
-- out[1]['Has mod domain'] = 1
-- out[1]['Has mod domain text'] = 'item'
return out
end


function p.get_item_tags(frame)
--
-- This function queries for the tags for a specific item.
-- Template:Mod
--  
-- Args
p.mod = m_util.misc.invoker_factory(_mod, {
local g_args = getArgs(frame, {
     wrappers = 'Template:Mod',
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)
for i,_ in ipairs(results) do
results[i]['Has tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ') -- Remove unnecessary symbols.
function table.reverse(a) -- Reverse order. Item tags are often sorted from lowest to highest priority. Therefore reversing order should lead to finding a match faster most of the time.
local res = {}
for i = #a, 1, -1 do
res[#res+1] = a[i]
end
return res
end
results[i]['Has tags'] = table.concat(table.reverse(util.string.split(results[i]['Has tags'], ', ')), ', ')
end
results = p.find_mod_domain(results)
return results
end
 
function p.header(str)
-- Replace specific numbers with a generic #.
local s = table.concat(util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
s = table.concat(util.string.split(s, '<br>'), ', ')
 
  return s
end
 
function p.drop_down_table(frame)
-- 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
-- Add a better solution for properties inside subobjects, for extra_properties. Note that properties already queried for should not be added with this solution.
-- 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'}
 
 
-- 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}[1]
local item_tags = {}
if g_args.item_tags ~= nil then
item_tags = util.string.split(g_args.item_tags, ', ')
else
item_tags = util.string.split(get_item_tags['Has tags'], ', ')
end
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 tag::%s]] </q>]]', table.concat(item_tags, ' || '))
-- Fields
local all_fields = {
1,
'Is mod',
'Has name',
'Has level requirement',
'Has mod group',
'Has mod type',
'Has stat text',
}
local extra_properties = {}
if g_args.extra_properties ~= nil then
extra_properties = util.string.split(g_args.extra_properties, ', ')
local a = #all_fields
for _,v in ipairs(extra_properties) do
table.insert(all_fields, a+1, v)
end
end
local fields = {}
for _,v in ipairs(all_fields) do
fields[#fields+1] = string.format('?%s', v)
end
-- Parameters
local parameters = {
sort = 'Has mod group, Has mod type, Has level requirement',
-- limit = 1000, -- lets see
offset = 0,
}
      
local data = {}
data = {
[1] = {
header = 'Prefix',
generation_type = 'prefix',
condition = string.format('[[Concept:Spawnable named prefix %s mods]]', get_item_tags['Has mod domain text']),
},
[2] = {
header = 'Suffix',
generation_type = 'suffix',
condition = string.format('[[Concept:Spawnable named suffix %s mods]]', get_item_tags['Has mod domain text']),
},
[3] = {
header = 'Corrupted',
generation_type = 'corrupted',
condition = string.format('[[Concept:Spawnable corrupted %s mods]]', get_item_tags['Has mod domain text']),
},
-- [4] = {
-- header = 'Forsaken masters',
-- generation_type = 'master',
-- 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 = {}
local item_mods = {}
local tableIndex = -1
for i_type,_ in ipairs(data) do
local generation_type = data[i_type]['generation_type']
conditions[1] = data[i_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
if results[generation_type] == nil then
results[generation_type] = {}
end
repeat
local result = util.smw.query(query, g_frame)
query.offset = query.offset + #result[#result] -- Possible error source if only one mod is missing.
for _,v in ipairs(result) do
results[generation_type][#results[generation_type]+1] = v
end
        until #result[#result] < 1000
item_mods[generation_type] = {}
local container = mw.html.create('div')
:attr('style', 'vertical-align:top; display:inline-block;')
if #results[generation_type] > 0 then
 
query_mod_tags = {
string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' ')),
'?Has tag#',
'?Has spawn weight#',
'?Is tag number#',
offset = 0,
}
local results_mod_tags = {}
local mod_data_subobject = {}
repeat -- Query again and add the remaining results.
results_mod_tags[#results_mod_tags+1] = util.smw.query(query_mod_tags, g_frame)
query_mod_tags.offset = query_mod_tags.offset + #results_mod_tags[#results_mod_tags]
for _,v in ipairs(results_mod_tags[#results_mod_tags]) do
 
local pagename = util.string.split(v[1], '#')[1]
if mod_data_subobject[pagename] == nil then
mod_data_subobject[pagename] = {}
end
local subobjectname = v[1]
mod_data_subobject[pagename][subobjectname] = v
end
until #results_mod_tags[#results_mod_tags] < 1000
local last
for i, v in ipairs(results[generation_type]) do -- Loop through all the results from the smw query.
local pagename = results[generation_type][i][1]
local j = 0
local tag_match_stop
repeat -- Loop through the mod tags until a match is found.
j = j+1
local subobjectname = string.format('%s#%s_%s', pagename, 'spawn_weight', j)
local mod_tag = mod_data_subobject[pagename][subobjectname]['Has tag']
local mod_tag_weight = tonumber(mod_data_subobject[pagename][subobjectname]['Has spawn weight'])
local y = 0
local tag_match_add
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 or -1) >= 0)) or (mod_data_subobject[pagename][subobjectname] == 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 tag_match_add then
for q,w in pairs( mod_data_subobject[pagename][subobjectname]) do -- Complement the useful properties for the subobject.
results[generation_type][i][q] = w
end
local a = #item_mods[generation_type]
item_mods[generation_type][a+1] = {}
for _,property in ipairs(all_fields) do -- Filtered item modifier table:
item_mods[generation_type][a+1][property] = results[generation_type][i][property]
end
end
until tag_match_stop
end
--
-- Display in table:
--
local headers = container
headers
:tag('h3')
:wikitext(string.format('%s', data[i_type]['header']))
:done()
:done()
local last = ''
for _, mod_properties in ipairs(item_mods[generation_type]) do
if mod_properties['Has mod group'] ~= last then
local count = {}
for _,n in ipairs(item_mods[generation_type]) do -- Check if there are multiple mod types in the same mod group:
if n['Has mod group'] == mod_properties['Has mod group'] then
count[n['Has mod type']] = 1
end
end
local number_of_mod_types = 0
for _ in pairs(count) do
number_of_mod_types = number_of_mod_types + 1
end
if number_of_mod_types > 1 then
tbl_caption = string.format('%s',g_frame:expandTemplate{title = 'c', args = {'mod', 'Mod group: ' .. mod_properties['Has mod group']}})
else
local mod_appl = 'Global' -- Assume the mod is global if local isn't specified in the mod id.
if mod_properties['Is mod']:find('Local') ~= nil then
mod_appl = 'Local'
end
tbl_caption = string.format('%s (%s)', g_frame:expandTemplate{title = 'c', args = {'mod', p.header(mod_properties['Has stat text'])}}, mod_appl)
end
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_properties)
:wikitext(tbl_caption)
:done()
:done()
end
local mod_name = mod_properties['Has name']
if  mod_name == '' then
mod_name = mod_properties['Is mod']
end
local td = mw.html.create('td')
if extra_properties ~= nil then
for _, extra_property in ipairs(extra_properties) do
td
:attr('width', '*')
:wikitext(string.format('%s:&nbsp;%s ', extra_property, mod_properties[extra_property]))
:done()
end
end
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]]', mod_properties[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;'), mod_properties['Has level requirement']))
:done()
:tag('td')
:attr('width', '*')
:wikitext(string.format('%s', g_frame:expandTemplate{title = 'c', args = {'mod', mod_properties['Has stat text']:gsub('<br>', ', ')}}))
:done()
:node(td)
:done()
:done()
last = mod_properties['Has mod group']
end
end
out[#out+1] = tostring(container)
    end
return table.concat(out,'')
end


return p
return p

Latest revision as of 20:25, 25 September 2024

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('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')

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

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 = 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 sets properties
    --
    -- tpl_args: argument table to work with
    -- args:
    --   prefix - input prefix for parsing the arguments from tpl_args
    --   table_map - cargo table map

    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 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', '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', '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 = 'Mod 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,
            },
            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
    --

    -- Validate single value properties and set them
    m_cargo.store_mapped_args{
        tpl_args = tpl_args,
        table_map = mod_map.main,
    }
    
    -- Validate and store stats
    m_util.args.stats(tpl_args)
    for _, stat_data in pairs(tpl_args.stats) do
        m_cargo.store({
            _table = 'mod_stats', 
            id = stat_data.id,
            min = stat_data.min,
            max = stat_data.max,
        })
    end
    
    -- Validate and store spawn weights
    h.set_weights(tpl_args, {
        prefix = 'spawn_weight',
        table_map = mod_map.mod_spawn_weights
    })
    
    -- Validate and store generation weights
    h.set_weights(tpl_args, {
        prefix = 'generation_weight',
        table_map = mod_map.mod_generation_weights
    })
    
    -- 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
            m_cargo.store(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

    -- Attach to tables
    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'}
    
    --
    -- 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('tr')
            :tag('th')
                :attr('colspan', 4)
                :wikitext(i18n.data_sheet.stats)
                :done()
            :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('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext(i18n.data_sheet.spawn_weights)
                :done()
            :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.weight)
                :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('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext(i18n.data_sheet.generation_weights)
                :done()
            :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.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
        :addClass('wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 2)
                :wikitext(i18n.data_sheet.sell_price)
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext(i18n.data_sheet.ordinal)
                :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

    -- 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{
            q_tables = 'items',
            q_where = string.format(
                'items._pageName IN ("%s")',
                table.concat(m_util.table.column(items, 'page'), '","')
            ),
            q_orderBy = 'items.name ASC',
        }
    end
    
    -- Categories
    
    local cats = {i18n.categories.mods}
    
    -- 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