Module:Mod: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
>OmegaK2
(remove property links)
m (88 revisions imported)
 
(27 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_util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local m_cargo = require('Module:Cargo')
local game = require('Module:Game')
local f_item_link = require('Module:Item link').item_link


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


local p = {}
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
-- Strings
-- 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')
-- This section contains strings used by this module.
-- Add new strings here instead of in-code directly, this will help other
-- people to correct spelling mistakes easier and help with translation to
-- other PoE wikis.


local i18n = {
local i18n = cfg.i18n
    args = {
        --
        -- Mod template
        --
       
        -- main
        id = 'id',
        name = 'name',
        mod_group = 'mod_group',
        mod_type = 'mod_type',
        domain = 'domain',
        generation_type = 'generation_type',
        required_level = 'required_level',
        stat_text = 'stat_text',
        granted_buff_id = 'granted_buff_id',
        granted_buff_value = 'granted_buff_value',
        granted_skill = 'granted_skill',
        tags = 'tags',
       
        -- sell price
        sell_price_prefix = 'sell_price',
        item_name = 'name',
        amount = 'amount',
    },
   
    errors = {
        --
        -- Mod template
        --
        sell_price_duplicate_name = 'Do not specify a sell price item name multiple times. Adjust the amount instead.',
        sell_price_missing_argument = 'Both %s and %s must be specified',
    },
    drop_down_table = {
        collapse_all = 'Collapse all',
        expand_all = 'Expand all',
        table_intro = 'The table below displays the available [[modifiers]] for [[item]]s such as',
        prefix = 'Prefix',
        suffix = 'Suffix',
        corrupted = 'Corrupted'     
    },
}


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- m_utility / Helper functions
-- 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
 
function h.validate.number (args)
    return function (tpl_args, frame, value)
        return m_util.cast.number(value, args)
     end
     end
    return f_item_table(args)
end
end


function h.create_header(row)
function h.set_weights(tpl_args, args)
    local stat = mw.html.create('span')
     -- Parses a weighted pair of lists and sets properties
    local text, nsub = mw.ustring.gsub(row['Has stat text'], '%d+', '?')
     --
     stat
     -- tpl_args: argument table to work with
        :attr('class', 'mod-table-header-stat')
     -- args:
        :wikitext(text)
    --   prefix - input prefix for parsing the arguments from tpl_args
        :done()
    --   table_map - cargo table map
       
     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)
     args = args or {}
     local tr = tbl:tag('tr')
     for i=1, math.huge do -- repeat until no more weights are found
     tr
         local prefix = args.prefix .. i
        :tag('td')
         local params = {
            :wikitext(string.format('[[%s|%s]]', row[1], row['Has name']))
             tag = string.format('%s_tag', prefix),
            :attr('class', 'mod-table-cell-name')
             value = string.format('%s_value', prefix),
            :done()
        }
         :tag('td')
        local tag = tpl_args[params.tag]
            :wikitext(row['Has level requirement'])
         local value = tpl_args[params.value]
            :attr('class', 'mod-table-cell-level')
         if tag == nil and value == nil then
            :done()
             break
         :tag('td')
             :wikitext(row['Has stat text'])
            :attr('class', 'mod-table-cell-stat')
            :done()
        :tag('td')
            :wikitext(table.concat(tags, ', '))
            :attr('class', 'mod-table-cell-tags')
             :done()
end
 
 
function h.cargo_query(tpl_args)
    --[[
    Returns a Cargo query of all the results, even if there are more
    results than the maximum query limit. It also adds popular fields.
   
    tpl_args should include these keys:
    tpl_args.tables
    tpl_args.fields
    tpl_args.q_*
   
    ]]
   
    local tables = m_util.string.split(tpl_args.tables, ', ')
    local fields = m_util.string.split(tpl_args.fields, ', ')
   
    -- Parse query arguments
    local query = {
        -- Workaround: Fix duplicates but removes other rows as well.  
         groupBy = tables[1] .. '._pageID',
         limit = 5000,
        offset = 0,
    }
    for key, value in pairs(tpl_args) do
        if string.sub(key, 0, 2) == 'q_' then
             query[string.sub(key, 3)] = value
         end
         end
    end
        if tag == nil or value == nil then
   
            error(string.format(i18n.errors.invalid_weight, params.tag, params.value))
    -- Add commonly used fields:
    local fields_base = {
        '_pageNamespace',
        '_pageTitle',
        '_ID',
        '_rowID',
        '_pageID',
        '_pageName'
        -- '_value'
    }
    for _, tbl in ipairs(tables) do
        for _, fld in ipairs(fields_base) do
            fields[#fields+1] = string.format('%s.%s', tbl, fld)
         end
         end
    end
         value = m_util.cast.number(value, {min = 0})
   
    -- Query cargo table. If there are too many results then repeat,
    -- offset, re-query and add the remaining results:
    local results = {}     
    repeat
         local result = mw.ext.cargo.query(
            table.concat(tables, ', '),
            table.concat(fields, ', '),
            query
        )
        query.offset = query.offset + #result


         for _,v in ipairs(result) do
         -- Store to cargo table unless tag = default and value = 0
            results[#results + 1] = v
        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
     until #result < query.limit
     end
   
    return results
end
end


Line 213: Line 86:
     main = {
     main = {
         table = 'mods',
         table = 'mods',
         order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags'},
         display_order = {'id', 'name', 'mod_groups', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags', 'tier_text'},
         parse_order = {'id', 'name', 'mod_group', 'mod_type', 'domain', 'generation_type', 'required_level', 'stat_text', 'stat_text_raw', 'granted_buff_id', 'granted_buff_value', 'granted_skill', 'tags'},
         order = {'id', 'name', 'mod_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 = {
         fields = {
             id = {
             id = {
                 name = i18n.args.id,
                 name = 'id',
                 field = i18n.args.id,
                 field = 'id',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Mod Id',
                 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 = i18n.args.name,
                 name = 'name',
                 field = i18n.args.name,
                 field = 'name',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Name',
                 wikitext = i18n.data_sheet.name,
             },
             },
             mod_group = {
             mod_groups = {
                 name = i18n.args.mod_group,
                 name = 'mod_groups',
                 field = i18n.args.mod_group,
                 field = 'mod_groups',
                 type = 'String',
                 type = 'List (,) of String',
                 wikitext = 'Group',
                 wikitext = i18n.data_sheet.mod_groups,
                display = function (value)
                    return table.concat(value, ', '
                end,
                default = {},
             },
             },
             mod_type = {
             mod_type = {
                 name = i18n.args.mod_type,
                 name = 'mod_type',
                 field = i18n.args.mod_type,
                 field = 'mod_type',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Mod type',
                 wikitext = i18n.data_sheet.mod_type,
             },
             },
             domain = {
             domain = {
                 name = i18n.args.domain,
                 name = 'domain',
                 field = i18n.args.domain,
                 field = 'domain',
                 type = 'Integer',
                 type = 'Integer',
                func = h.validate.number{min=1, max=15},
                 wikitext = 'Mod domain',
                 wikitext = 'Mod domain',
                 display = function (value)
                 display = function (value)
                     return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')'
                     return string.format(i18n.data_sheet.domain_fmt, m_game.constants.mod.domains[value]['short_upper'], value)
                 end,
                 end,
             },
             },
             generation_type = {
             generation_type = {
                 name = i18n.args.generation_type,
                 name = 'generation_type',
                 field = i18n.args.generation_type,
                 field = 'generation_type',
                 type = 'Integer',
                 type = 'Integer',
                 func = h.validate.number{min=1, max=12},
                 wikitext = i18n.data_sheet.generation_type,
                wikitext = 'Generation type',
                 display = function (value)
                 display = function (value)
                     return game.constants.mod.generation_types[value]['short_upper'] .. ' (Id: ' .. value .. ')'
                     return string.format(i18n.data_sheet.generation_type_fmt, m_game.constants.mod.generation_types[value]['short_upper'], value)
                 end,
                 end,
             },
             },
             required_level = {
             required_level = {
                 name = i18n.args.required_level,
                 name = 'required_level',
                 field = i18n.args.required_level,
                 field = 'required_level',
                 type = 'Integer',
                 type = 'Integer',
                 func = h.validate.number{min=0, max=100},
                 wikitext = i18n.data_sheet.required_level,
                wikitext = 'Req. level',
             },
             },
             stat_text = {
             stat_text = {
                 name = i18n.args.stat_text,
                 name = 'stat_text',
                 field = i18n.args.stat_text,
                 field = 'stat_text',
                 type = 'Text',
                 type = 'Text',
                 wikitext = 'Effect',
                 wikitext = i18n.data_sheet.stat_text,
             },
             },
             stat_text_raw = {
             stat_text_raw = {
Line 277: Line 169:
                 field = 'stat_text_raw',
                 field = 'stat_text_raw',
                 type = 'Text',
                 type = 'Text',
                 func = function(tpl_args, frame)
                 func = function (tpl_args, value)
                     if tpl_args.stat_text then
                     if tpl_args.stat_text then
                         tpl_args.stat_text_raw = string.gsub(
                         -- Strip wikilinks and html, but keep any line break tags
                            -- [[x]] -> x
                        value = m_util.string.strip_wikilinks(tpl_args.stat_text)
                            string.gsub(
                        value = mw.ustring.gsub(value, '<br */?>', '')
                                tpl_args.stat_text, '%[%[([^%]|]+)%]%]', '%1'
                        value = m_util.string.strip_html(value)
                            ),  
                        value = mw.ustring.gsub(value, '', '<br>')
                            -- [[x|y]] -> y
                            '%[%[[^|]+|([^%]|]+)%]%]', '%1'
                        )
                     end
                     end
                     return tpl_args.stat_text_raw
                     return value
                 end
                 end
             },
             },
             granted_buff_id = {
             granted_buff_id = {
                 name = i18n.args.granted_buff_id,
                 name = 'granted_buff_id',
                 field = i18n.args.granted_buff_id,
                 field = 'granted_buff_id',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Granted Buff Id',
                 wikitext = i18n.data_sheet.granted_buff_id,
             },
             },
             granted_buff_value = {
             granted_buff_value = {
                 name = i18n.args.granted_buff_value,
                 name = 'granted_buff_value',
                 field = i18n.args.granted_buff_value,
                 field = 'granted_buff_value',
                 type = 'Integer',
                 type = 'Integer',
                 wikitext = 'Granted Buff Value',
                 wikitext = i18n.data_sheet.granted_buff_value,
             },
             },
             granted_skill = {
             granted_skill = {
                 name = i18n.args.granted_skill,
                 name = 'granted_skill',
                 field = i18n.args.granted_skill,
                 field = 'granted_skill',
                 type = 'String',
                 type = 'String',
                 wikitext = 'Granted Skill',
                 wikitext = i18n.data_sheet.granted_skill,
             },
             },
             tags = {
             tags = {
                 name = i18n.args.tags,
                 name = 'tags',
                 field = i18n.args.tags,
                 field = 'tags',
                 type = 'List (,) of String',
                 type = 'List (,) of String',
                 wikitext = 'Tags',  
                 wikitext = 'Tags',  
                 func = function (tpl_args, frame, value)
                 display = function (value)
                     if value == nil then
                     return table.concat(value, ', ')
                        return {}
                    else
                        return m_util.string.split(value, ', ')
                    end
                 end,
                 end,
                 func_cargo = function(tpl_args, frame)
                 default = {},
                    return table.concat(tpl_args.tags, ','
            },
                 end,
            tier_text = {
                 display = function(value)
                name = 'tier_text',
                    return table.concat(value, ', '
                field = 'tier_text',
                 end,                 
                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',
             },
             },
         },
         },
Line 335: Line 272:
         fields = {
         fields = {
             name = {
             name = {
                 name = i18n.args.item_name,
                 name = 'name',
                 field = i18n.args.item_name,
                 field = 'name',
                 type = 'String',
                 type = 'String',
                 func = function (value) return value end,
                 func = function (value) return value end,
             },
             },
             amount = {
             amount = {
                 name = i18n.args.amount,
                 name = 'amount',
                 field = i18n.args.amount,
                 field = 'amount',
                 type = 'Integer',
                 type = 'Integer',
                 func = tonumber,
                 func = tonumber,
Line 350: Line 287:
}
}


p.table_main = m_util.cargo.declare_factory{data=mod_map.main}
-- ----------------------------------------------------------------------------
p.table_mod_sell_prices = m_util.cargo.declare_factory{data=mod_map.mod_sell_prices}
-- Main functions
-- ----------------------------------------------------------------------------


function p.table_mod_stats(frame)
local function _mod(tpl_args)
     m_util.cargo.declare(frame, {
     -- 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"}
        _table = 'mod_stats',
        id = 'String',
        min = 'Integer',
        max = 'Integer',
    })
end
 
 
-- p.mod{id = "LocalIncreasedPhysicalDamagePercentUniqueOneHandSword2", name = "", mod_group = "LocalPhysicalDamagePercent", domain = "1", generation_type = "3", required_level = "1", mod_type = "LocalPhysicalDamagePercent", stat_text = "150% increased Physical Damage", stat1_id = "local_physical_damage_+%", stat1_min = "150", stat1_max = "150"}
function p.mod(frame)
    -- Get args
    tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
      
      
     --
     --
     -- Validation & semantic properties
     -- Validate and store
     --
     --
   
 
     -- Validate single value properties and set them
     -- Validate single value properties and set them
      
     m_cargo.store_mapped_args{
    local cargo_data = {
        tpl_args = tpl_args,
         _table = mod_map.main.table,
         table_map = mod_map.main,
     }
     }
      
      
    for _, key in pairs(mod_map.main.parse_order) do
     -- Validate and store stats
        data = mod_map.main.fields[key]
     m_util.args.stats(tpl_args)
        local value
        if data.func ~= nil then
            if data.name then
                value = data.func(tpl_args, frame, tpl_args[data.name])
            else
                value = data.func(tpl_args, frame)
            end
        else
            value = tpl_args[data.name]
        end
       
        tpl_args[key] = value
 
        if data.field ~= nil then
            if data.func_cargo then
                cargo_data[data.field] = data.func_cargo(tpl_args, frame)
            else
                cargo_data[data.field] = value
            end
        end
    end
 
    m_util.cargo.store(frame, cargo_data)
   
     -- Validate % set the stat subobjects
     m_util.args.stats(tpl_args, {frame=frame})
     for _, stat_data in pairs(tpl_args.stats) do
     for _, stat_data in pairs(tpl_args.stats) do
         m_util.cargo.store(frame, {
         m_cargo.store({
             _table = 'mod_stats',  
             _table = 'mod_stats',  
             id = stat_data.id,
             id = stat_data.id,
Line 418: Line 315:
     end
     end
      
      
     -- Validate & set spawn weight subobjects
     -- Validate and store spawn weights
     m_util.args.spawn_weight_list(tpl_args, {
     h.set_weights(tpl_args, {
         frame=frame,  
         prefix = 'spawn_weight',
        table_map = mod_map.mod_spawn_weights
     })
     })
      
      
     -- Validate & set generation weight subobjects
     -- Validate and store generation weights
     m_util.args.generation_weight_list(tpl_args, {
     h.set_weights(tpl_args, {
         frame=frame,  
         prefix = 'generation_weight',
        table_map = mod_map.mod_generation_weights
     })
     })
      
      
     -- Validate & set mod sell values
     -- Validate and store mod sell values
     i = 0
     local i = 0
     local names = {}
     local names = {}
     local sell_prices = {}
     local sell_prices = {}
Line 436: Line 335:
          
          
         local id = {}
         local id = {}
         value = {}
         local value = {}
         for key, data in pairs(mod_map.mod_sell_prices.fields) do
         for key, data in pairs(mod_map.mod_sell_prices.fields) do
             id[key] = string.format('%s%s_%s', i18n.args.sell_price_prefix, i, data.name)
             id[key] = string.format('%s%s_%s', 'sell_price', i, data.name)
             value[key] = data.func(tpl_args[id[key]])
             value[key] = data.func(tpl_args[id[key]])
         end
         end
Line 457: Line 356:
                 cargo_data[data.field] = value[key]
                 cargo_data[data.field] = value[key]
             end
             end
             m_util.cargo.store(frame, cargo_data)
             m_cargo.store(cargo_data)
              
              
             sell_prices[#sell_prices+1] = value
             sell_prices[#sell_prices+1] = value
Line 465: Line 364:
          
          
     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 471: 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 _, key in ipairs(mod_map.main.order) do
     for _, key in ipairs(mod_map.main.display_order) do
         local data = mod_map.main.fields[key]
         local data = mod_map.main.fields[key]
         local text
         local text = tpl_args[key]
         if data.display == nil then
         if type(data.display) == 'function' then
            text = tpl_args[key]
             text = data.display(text)
        else
             text = data.display(tpl_args[key])
         end
         end
       
         tbl
         tbl
             :tag('tr')
             :tag('tr')
Line 500: Line 401:
             :done()
             :done()
     end
     end
   
    tbl
        :tag('tr')
            :tag('th')
                :wikitext('Tags')
                :done()
            :tag('td')
                :wikitext(table.concat(tpl_args['tags'], ', '))
                :done()
            :done()
        :done()
      
      
     -- stat table
     -- stat table
Line 516: 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('#')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Stat Id')
                 :wikitext(i18n.data_sheet.stat_id)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Min')
                 :wikitext(i18n.data_sheet.min)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Max')
                 :wikitext(i18n.data_sheet.max)
                 :done()
                 :done()
             :done()
             :done()
Line 570: 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('#')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Tag')
                 :wikitext(i18n.data_sheet.tag)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Weight')
                 :wikitext(i18n.data_sheet.weight)
                 :done()
                 :done()
             :done()
             :done()
Line 591: Line 481:
          
          
     i = 0
     i = 0
     value = nil
     local value = nil
     repeat
     repeat
         i = i + 1
         i = i + 1
Line 620: 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('#')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Tag')
                 :wikitext(i18n.data_sheet.tag)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Weight')
                 :wikitext(i18n.data_sheet.weight)
                 :done()
                 :done()
             :done()
             :done()
Line 669: 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('#')
                 :wikitext(i18n.data_sheet.ordinal)
                 :done()
                 :done()
             :tag('th')
             :tag('th')
                 :wikitext('Item')
                 :wikitext(i18n.data_sheet.item)
                 :done()
                 :done()
             :done()
             :done()
Line 700: Line 590:
     -- Generic messages on the page
     -- Generic messages on the page
      
      
     out = {}
     local out = {}
      
      
     if mw.ustring.find(tpl_args['id'], '_') then
     if mw.ustring.find(tpl_args['id'], '_') then
         out[#out+1] = frame:expandTemplate{ title = 'Incorrect title', args = { title=tpl_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 tpl_args['name'] then
     if tpl_args['name'] then
         out[#out+1] = string.format("'''%s''' is the internal id of modifier '''%s'''.\n", tpl_args['id'], tpl_args['name'])
         out[#out+1] = string.format(i18n.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", tpl_args['id'], tpl_args['name'])
         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
     end
      
      
     -- Categories
     -- Categories
      
      
     cats = {'Mods'}
     local cats = {i18n.categories.mods}
      
      
     -- Done -> output
     -- Done -> output
      
      
     return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)  
     return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)
end
end


--
-- ----------------------------------------------------------------------------
-- Template: SMW query mods
-- Exported functions
--  
-- ----------------------------------------------------------------------------


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


function p.get_mod_domain(cargo_query)
p.table_main = m_cargo.declare_factory{data=mod_map.main}
    --[[
p.table_mod_stats = m_cargo.declare_factory{data=mod_map.mod_stats}
Gets the mod domain based on the item class.
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}
    local out = cargo_query
    local mod_domains = game.constants.mod.domains
   
    -- Set item class as key and the mod domain as value:
    local class_to_domain = {
        ['Life Flasks']=2,
        ['Mana Flasks']=2,
        ['Hybrid Flasks']=2,
        ['Utility Flasks']=2,
        ['Critical Utility Flasks']=2,
        ['Maps']=5,
        ['Jewel']=11,
        ['Leaguestones']=13,
        ['Abyss Jewel']=14,
    }
   
    for i,_ in ipairs(out) do
        -- Get the domain, if it's not defined in the table assume it's
        -- in the item domain.
        out[i]['items.domain'] = class_to_domain[out[i]['items.class']] or 1
       
        -- Convert the mod domain number to understandable text:
        out[i]['items.domain_text'] = mod_domains[out[i]['items.domain']]['short_lower']
    end


    return out
--
end
-- Template:Mod
 
--  
function p.get_item_tags(frame)
p.mod = m_util.misc.invoker_factory(_mod, {
    --[[
     wrappers = 'Template:Mod',
    This function queries for the tags of a specific item.
})
    ]]
   
    -- Args
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)
   
    tpl_args.tables = 'items'
    tpl_args.fields = 'items.name, items.tags, items.class'
    local tbl = {
        {tpl_args.page, 'items._pageName = "%s"'},
        {tpl_args.item, 'items.name = "%s"'},
    }
    for _,v in ipairs(tbl) do
        if v[1] ~= nil then
            condition = string.format(v[2], v[1])
            break
        end
    end
    tpl_args.q_where = condition
    tpl_args.q_groupBy = 'items._pageID'
    tpl_args.q_orderBy = 'items.name'
 
    -- Query mods with cargo:
    results = h.cargo_query(tpl_args)
 
 
 
    -- -- SMW workaround, remove when module:item2 is ready:
    -- local tbl = {
        -- {tpl_args.page, '[[%s]]'},
        -- {tpl_args.item, '[[Has name::%s]]'},
    -- }
    -- for _,v in ipairs(tbl) do
        -- if v[1] ~= nil then
            -- condition = string.format(v[2], v[1])
            -- break
        -- end
    -- end
    -- local results = m_util.smw.query(
        -- {
            -- condition,
            -- '?Has name',
            -- '?Has tags',
            -- '?Has item class'
        -- },
        -- frame
    -- )
    -- for i,_ in ipairs(results) do
        -- results[i]['items.tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ')
        -- results[i]['items.class'] = results[i]['Has item class']
        -- results[i]['items.name'] = results[i]['Has name']
        -- results[i]['items._pageName'] = results[i][1]
    -- end
    -- --
   
   
    results = p.get_mod_domain(results)
 
    return results
end
 
function p.header(str)
    --[[
    This function replace specific numbers with a generic #.
    ]]
   
    local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
    s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
    s = table.concat(m_util.string.split(s, '<br>'), ', ')
 
  return s
end
 
function p.get_spawn_chance(frame)
    --[[
    Calculates the spawn chance of a set of mods that all have a
    spawn weight.
    ]]
    -- Args
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)
   
    local tbl = tpl_args['tbl']
    -- Probabilities affecting the result besides the spawn weight:
    local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1
   
    local N = 0
    for i,_ in ipairs(tbl) do
        -- Total number of outcomes.
        N = N + tbl[i]['spawn_weights.weight']
    end
   
    for i,_ in ipairs(tbl) do
        -- Number of ways it can happen:
        local n = tbl[i]['spawn_weights.weight']
 
        -- Truncated value:
        tbl[i]['spawn_weights.chance'] = string.format(
            "%0.2f%%",
            n/N * chance_multiplier*100
        )
    end
   
    return tbl
end
   
function p.drop_down_table(frame)
    --[[
    This function queries mods that can spawn on an item. It compares
    the item tags and the spawn weight tags. If there's a match and
    the spawn weight is larger than zero, then that mod is added to a
    drop down list.
   
    To Do
    * Misses forsaken masters currently.
    * Add a proper expand/collapse toggle for the entire header row so
        it reacts together with mw-collapsible.
    * Show Mod group in a better way perhaps:
        Mod group (expanded)
            # to Damage (Collapsed)
            3 to Damage
            5 to Damage
   
    Examples:
    Weapons
    p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
    p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
 
    Accessories
    p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
   
    Jewels
    p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
   
    Armour
    p.drop_down_table{item = 'Plate Vest', header = 'Armour body armours'}
    p.drop_down_table{item = 'Iron Greaves', header = 'Armour boots'}
    p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour gloves'}
    p.drop_down_table{item = 'Iron Hat', header = 'Armour helmets'}
    p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour shields'}
   
    p.drop_down_table{
        item = 'Fishing Rod',
        header = 'FISH PLEASE',
        item_tags = 'fishing_rod',
        extra_fields = 'Has spawn weight, Has spawn chance'
    }
   
    = p.drop_down_table{
        item = 'Fishing Rod',
        item_tags = 'axe, one_hand_weapon, onehand, weapon, default'
    }
    = p.drop_down_table{
        item = 'Vaal Blade',
    }
       
    ]]
 
 
    -- Get template args:
    local tpl_args = getArgs(frame, {parentFirst=true})
    local frame = m_util.misc.get_frame(frame)
   
    -- Get the items tags:
    local get_item_tags = p.get_item_tags(tpl_args)[1]
   
    -- For some reason cargo queried item tags, are not comma-space
    -- separated.
    local item_tags = {}
    if tpl_args.item_tags  ~= nil then
        item_tags = m_util.string.split(tpl_args.item_tags, ', ')
    else
        item_tags = m_util.string.split(get_item_tags['items.tags'], ',')
    end
    -- local item_tags = m_util.string.split(
        -- tpl_args.item_tags or get_item_tags['items.tags'],
        -- ', '
    -- )
   
    -- Create drop down lists in these sections and query in these
    -- generation types.
    local section = {}
    section = {
        [1] = {
            header = i18n.drop_down_table.prefix,
            generation_type = 1,
        },
        [2] = {
            header = i18n.drop_down_table.suffix,
            generation_type = 2,
        },
        [3] = {
            header = i18n.drop_down_table.corrupted,
            generation_type = 5,
            chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
        },
        -- [4] = {
            -- header = 'Forsaken masters',
            -- generation_type = 'master',   
        -- },
    }
     
     
    -- Introductory text:
    local out = {}
    out[#out+1] = string.format(
        '==%s== \n',
        tpl_args['header'] or table.concat(item_tags, ', ')
    )
    out[#out+1] = string.format(
        '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[%s]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[%s]</div></div>',
        i18n.drop_down_table.collapse_all,
        i18n.drop_down_table.expand_all
    )
    out[#out+1] = string.format('%s %s.<br><br><br>',  
        i18n.drop_down_table.table_intro,
        f_item_link{page=get_item_tags['items._pageName']}
    )
   
    local item_mods = {}
     local tableIndex = -1       
    for _, sctn in ipairs(section) do
        local container = mw.html.create('div')
            :attr('style', 'vertical-align:top; display:inline-block;')
   
        -- Format the where condition:
        generation_type = sctn['generation_type']
        local where = {}
        for _, item_tag in ipairs(item_tags) do
            where[#where+1] = string.format(
            '(spawn_weights.tag="%s" AND mods.generation_type=%s AND mods.domain=%s)',
            item_tag,
            sctn['generation_type'],
            get_item_tags['items.domain']
        )
        end
       
        tpl_args.tables = 'mods, spawn_weights, mod_stats'
        tpl_args.fields = 'mods.name, mods.id, mods.required_level, mods.generation_type, mods.domain, mods.mod_group, mods.mod_type, mods.stat_text, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal'
        tpl_args.q_join = 'mods._pageID=spawn_weights._pageID, mods._pageID=mod_stats._pageID'
        tpl_args.q_where = table.concat(where, ' OR ')
        tpl_args.q_groupBy = 'mods._pageID, spawn_weights.tag, spawn_weights.weight'
        tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods._pageName, mods.required_level, spawn_weights.ordinal'
       
        local extra_fields = {}
        if tpl_args.extra_fields ~= nil then
            extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
            tpl_args.fields = string.format(
                '%s, %s',
                tpl_args.fields,
                table.concat(extra_fields, ', ')
            )
        end
       
        -- Query mods:
        results = h.cargo_query(tpl_args)
        -- Create own list for spawn weights and group by page name:
        local spawn_weights = {}
local results_unique = {}
local hash = {}
        for _,v in ipairs(results) do
            if spawn_weights[v['mods._pageName']] == nil then
                spawn_weights[v['mods._pageName']] = {}
            end
            local n = #spawn_weights[v['mods._pageName']] or 0
            spawn_weights[v['mods._pageName']][n+1] = v
            -- Get a sorted list that only has unique page names:
            if hash[v['mods._pageName']] ~= true then
                results_unique[#results_unique+1] = v
                hash[v['mods._pageName']] = true
            end
        end
       
        if #results_unique > 0 then
            item_mods[generation_type] = {}
                     
            -- Loop through all the modifiers from the concept pages:
            local last
            for _, v in ipairs(results_unique) do 
                local pagename = v['spawn_weights._pageName']
               
                -- Loop through all the modifier tags until they match
                -- the item tags:
                local j = 0
                local tag_match_stop
                repeat
                    j = j+1
                    local mod_tag = spawn_weights[pagename][j]['spawn_weights.tag']
                    local mod_tag_weight = tonumber(
                        spawn_weights[pagename][j]['spawn_weights.weight']
                    )
 
                    -- Loop through the item tags until it matches the
                    -- spawn weight tag and the mod tag has a value larger than
                    -- zero:
                    local y = 0
                    local tag_match_add = false
                    repeat   
                        y = y+1
                        tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (spawn_weights[pagename][j] == nil)
                        tag_match_add =  (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
                    until tag_match_stop or y == #item_tags
                   
                    -- If there's a match then save that mod and other
                    -- interesting information:
                    if tag_match_add then
                       
                        -- Assume that the mod is global then go through
                        -- all the stat ids and check if any of the
                        -- stats are local:
                        local mod_scope = 'Global'
                        for _, vv in ipairs(spawn_weights[pagename]) do
                            if vv['mod_stats.id']:find('.*local.*') ~= nil then
                                mod_scope = 'Local'
                            end
                        end
                       
                        -- Save the matching modifier tag:
                        local a = #item_mods[generation_type]
                        item_mods[generation_type][a+1] = spawn_weights[pagename][j]
                       
                        -- Save other interesting fields:
                        item_mods[generation_type][a+1]['mods.scope'] = mod_scope   
                        item_mods[generation_type][a+1]['spawn_weight.idx_match'] = j                       
item_mods[generation_type][a+1]['mods.add'] = tag_match_add   
item_mods[generation_type][a+1]['mods.stop'] = tag_match_stop   
                    end
                until tag_match_stop
            end
           
            -- If the user wants to see the spawn chance then do the
            -- calculations and save that result as well:
            if tpl_args.spawn_chance ~= nil then
                extra_fields[#extra_fields+1] = 'spawn_weights.chance'
                item_mods[generation_type] = p.get_spawn_chance{
                    tbl = item_mods[generation_type],
                    chance_multiplier = sctn['chance_multiplier']
                }
            end
 
            -- Create the drop down table with <table></table>:
            local headers = container
            headers
                :tag('h3')
                    :wikitext(string.format(
                        '%s',
                        sctn['header']
                        )
                    )
                    :done()
                :done()
               
            -- Loop through and add all matching mods to the <table>.
            local tbl, last
            for _, rows in ipairs(item_mods[generation_type]) do 
           
                -- If the last mod group is different to the current
                -- mod group then assume the mod isn't related and start
                -- a new drop down list:
                if rows['mods.mod_group'] ~= last then
                   
                    -- Check through all the mods and see if there are
                    -- multiple mod types within the same mod group:
                    local count = {}
                    for _, n in ipairs(item_mods[generation_type]) do
                       
                        -- If the mod has the same mod group, then add
                        -- the mod type to the counter. Only unique mod
                        -- types matter so the number is just a dummy
                        -- value:
                        if n['mods.mod_group'] == rows['mods.mod_group'] then
                            count[n['mods.mod_type']] = 1
                        end
                    end
                   
                    -- Calculate how many unique mod types with the
                    -- same mod group there are:
                    number_of_mod_types = 0
                    for _ in pairs(count) do
                        number_of_mod_types = number_of_mod_types + 1
                    end
                   
                    -- If there are multiple unique mod types with the
                    -- same mod group then change the style of the drop
                    -- down list to indicate it:
                    if number_of_mod_types > 1 then
                        tbl_caption = string.format(
                            '%s',
                            m_util.html.poe_color(
                                'mod',
                                'Mod group: ' .. rows['mods.mod_group']
                            )
                        )
                    else
                        tbl_caption = string.format(
                            '%s (%s)',
                            m_util.html.poe_color(
                                'mod',
                                p.header(rows['mods.stat_text'])
                            ),
                            rows['mods.scope']
                        )
                    end
                   
                    -- Add class and style to the <table>:
                    tableIndex = tableIndex+1
                    tbl = container:tag('table')
                    tbl
                        :attr('class', 'mw-collapsible mw-collapsed')
                        :attr('style',
                            'text-align:left; line-height:1.60em; width:810px;'
                        )
                        :tag('th')
                            :attr('class',
                                string.format(
                                    'mw-customtoggle-%s',
                                    tableIndex
                                )
                            )
                            :attr('style',
                                'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
                            )
                            :attr('colspan', '3' .. #extra_fields)
                            :wikitext(tbl_caption)
                            :done()
                        :done()   
                end
               
                -- If the mod has no name then use the mod id:
                local mod_name = rows['mods.name']
                if  mod_name == '' or mod_name == nil then
                    mod_name = rows['mods.id']
                end
               
                -- Check if there are any extra properties to show in
                -- the drop down list and then add a cell for that,
                -- add this node at the end of the table row:
                local td = mw.html.create('td')
                if extra_fields ~= nil then
                    for _, extra_field in ipairs(extra_fields) do
                        td
                            :attr('width', '*')
                            :wikitext(string.format(
                                '%s:&nbsp;%s ',
                                extra_field,
                                rows[extra_field]
                                )
                            )
                            :done()
                    end
                end
               
                -- Add a table row with the interesting properties that
                -- modifier has:
                tbl
                    :tag('tr')
                        :attr('class', 'mw-collapsible mw-collapsed')
                        :attr(
                            'id',
                            string.format(
                                'mw-customcollapsible-%s',
                                tableIndex
                            )
                        )
                        :tag('td')
                            :attr('width', '160')
                            :wikitext(
                                string.format(
                                    '&nbsp;&nbsp;&nbsp;[[%s|%s]]',
                                    rows['mods._pageName'],
                                    mod_name:gsub('%s', '&nbsp;')
                                )
                            )
                            :done()
                        :tag('td')
                            :attr('width', '1')
                            :wikitext(
                                string.format(
                                    '%s&nbsp;%s',
                                    game.level_requirement['short_upper']:gsub('%s', '&nbsp;'),
                                    rows['mods.required_level']
                                )
                            )
                            :done()       
                        :tag('td')
                            :attr('width', '*')
                            :wikitext(
                                string.format(
                                    '%s',
                                    m_util.html.poe_color(
                                        'mod',
                                        rows['mods.stat_text']:gsub('<br>', ', ')
                                    ) 
                                )
                            )
                            :done()
                        :node(td)
                        :done()
                    :done()
               
                -- Save the last mod group for later comparison:
                last = rows['mods.mod_group']
            end
        end
       
        out[#out+1] = tostring(container)
    end
   
    return table.concat(out,'')
end


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