Module:Sandbox/Illviljan/Mod

-- -- Module for mod related templates --

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

local cargo = mw.ext.cargo

local p = {}

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

local i18n = { 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 items such as', prefix = 'Prefix', suffix = 'Suffix', corrupted = 'Corrupted' }, }

-- -- m_utility / Helper functions --

local h = {}

-- Validate single value properties and set them

h.validate = {}

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

function h.validate.number (args) return function (tpl_args, frame, value) return m_util.cast.number(value, args) end end

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

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

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

for _,v in ipairs(result) do           results[#results + 1] = v        end until #result < query_limit return results end

-- -- Templates --

-- -- Template: Mod --

local mod_map = { main = { 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'}, 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'}, fields = { id = { name = i18n.args.id, field = i18n.args.id, type = 'String', wikitext = 'Mod Id', },           name = { name = i18n.args.name, field = i18n.args.name, type = 'String', wikitext = 'Name', },           mod_group = { name = i18n.args.mod_group, field = i18n.args.mod_group, type = 'String', wikitext = 'Group', },           mod_type = { name = i18n.args.mod_type, field = i18n.args.mod_type, type = 'String', wikitext = 'Mod type', },           domain = { name = i18n.args.domain, field = i18n.args.domain, type = 'Integer', func = h.validate.number{min=1, max=15}, wikitext = 'Mod domain', display = function (value) return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')' end, },           generation_type = { name = i18n.args.generation_type, field = i18n.args.generation_type, type = 'Integer', func = h.validate.number{min=1, max=12}, wikitext = 'Generation type', display = function (value) return game.constants.mod.generation_types[value]['short_upper'] .. ' (Id: ' .. value .. ')' end, },           required_level = { name = i18n.args.required_level, field = i18n.args.required_level, type = 'Integer', func = h.validate.number{min=0, max=100}, wikitext = 'Req. level', },           stat_text = { name = i18n.args.stat_text, field = i18n.args.stat_text, type = 'Text', wikitext = 'Effect', },           stat_text_raw = { name = nil, field = 'stat_text_raw', type = 'Text', func = function(tpl_args, frame) if tpl_args.stat_text then tpl_args.stat_text_raw = string.gsub(                           -- x -> x                            string.gsub( tpl_args.stat_text, '%[%[([^%]|]+)%]%]', '%1' ),                            -- x|y -> y                            '%[%[[^|]+|([^%]|]+)%]%]', '%1'                        )                    end                    return tpl_args.stat_text_raw                end            },            granted_buff_id = {                name = i18n.args.granted_buff_id,                field = i18n.args.granted_buff_id,                type = 'String',                wikitext = 'Granted Buff Id',            },            granted_buff_value = {                name = i18n.args.granted_buff_value,                field = i18n.args.granted_buff_value,                type = 'Integer',                wikitext = 'Granted Buff Value',            },            granted_skill = {                name = i18n.args.granted_skill,                field = i18n.args.granted_skill,                type = 'String',                wikitext = 'Granted Skill',            },            tags = {                name = i18n.args.tags,                field = i18n.args.tags,                type = 'List  of String',                wikitext = 'Tags',                 func = function (tpl_args, frame, value)                    if value == nil then                        return {}                    else                        return m_util.string.split(value, ', ')                    end                end,                func_smw = function(tpl_args, frame)                    return table.concat(tpl_args.tags, ';')                  end,                 func_cargo = function(tpl_args, frame)                    return table.concat(tpl_args.tags, ',')                  end,                display = function(value)                    return table.concat(value, ', ')                  end,                             },        },    },    mod_sell_prices = {        table = 'mod_sell_prices',        order = {'name', 'amount'},        fields = {            name = {                name = i18n.args.item_name,                field = i18n.args.item_name,                type = 'String',                func = function (value) return value end,            },            amount = {                name = i18n.args.amount,                field = i18n.args.amount,                type = 'Integer',                func = tonumber,            },        },    }, }

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}

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

-- p.mod{id = "LocalIncreasedPhysicalDamagePercentUniqueOneHandSword2", name = "", mod_group = "LocalPhysicalDamagePercent", domain = "1", generation_type = "3", required_level = "1", mod_type = "LocalPhysicalDamagePercent", stat_text = "150% increased Physical Damage", stat1_id = "local_physical_damage_+%", stat1_min = "150", stat1_max = "150"} function p.mod(frame) -- Get args tpl_args = getArgs(frame, {       parentFirst = true    }) frame = m_util.misc.get_frame(frame) --   -- Validation & semantic properties --   -- Validate single value properties and set them local cargo_data = { _table = mod_map.main.table, }   for _, key in pairs(mod_map.main.parse_order) do        data = mod_map.main.fields[key] local value if data.func ~= nil then if data.name then value = data.func(tpl_args, frame, tpl_args[data.name]) else value = data.func(tpl_args, frame) end else value = tpl_args[data.name] end tpl_args[key] = value

if data.field ~= nil then if data.func_cargo then cargo_data[data.field] = data.func_cargo(tpl_args, frame) else cargo_data[data.field] = value end end end

m_util.cargo.store(frame, cargo_data) -- Validate % set the stat subobjects m_util.args.stats(tpl_args, {frame=frame}) for _, stat_data in pairs(tpl_args.stats) do       m_util.cargo.store(frame, {            _table = 'mod_stats',             id = stat_data.id,            min = stat_data.min,            max = stat_data.max,        }) end -- Validate & set spawn weight subobjects m_util.args.spawn_weight_list(tpl_args, {       frame=frame,     }) -- Validate & set generation weight subobjects m_util.args.generation_weight_list(tpl_args, {       frame=frame,     }) -- Validate & set mod sell values i = 0 local names = {} local sell_prices = {} repeat i = i + 1 local id = {} value = {} for key, data in pairs(mod_map.mod_sell_prices.fields) do           id[key] = string.format('%s%s_%s', i18n.args.sell_price_prefix, 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_util.cargo.store(frame, cargo_data) sell_prices[#sell_prices+1] = value else error (string.format(i18n.errors.sell_price_missing_arguments, id.name, id.amount)) end until value == nil --   -- Display --   local container = mw.html.create('div') container :attr('class', 'modbox') -- core stats local tbl = container:tag('table') tbl :attr('class', 'wikitable') for _, key in ipairs(mod_map.main.order) do       local data = mod_map.main.fields[key] local text if data.display == nil then text = tpl_args[key] else text = data.display(tpl_args[key]) end tbl :tag('tr') :tag('th') :wikitext(data.wikitext) :done :tag('td') :wikitext(text) :done :done :done end tbl :tag('tr') :tag('th') :wikitext('Tags') :done :tag('td') :wikitext(table.concat(tpl_args['tags'], ', ')) :done :done :done -- stat table tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 4) :wikitext('Stats') :done :done :tag('tr') :tag('th') :wikitext('#') :done :tag('th') :wikitext('Stat Id') :done :tag('th') :wikitext('Minimum') :done :tag('th') :wikitext('Maximum') :done :done :done for i=1, #tpl_args.stats do       local value = { id = tpl_args['stat' .. i .. '_id'], min = tpl_args['stat' .. i .. '_min'], max = tpl_args['stat' .. i .. '_max'], }       if value.id then tbl :tag('tr') :tag('td') :wikitext(i) :done :tag('td') :wikitext(value.id) :done :tag('td') :wikitext(value.min) :done :tag('td') :wikitext(value.max) :done :done :done end end -- spawn weight table tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 3) :wikitext('Spawn Weights') :done :done :tag('tr') :tag('th') :wikitext('#') :done :tag('th') :wikitext('Tag') :done :tag('th') :wikitext('Weight') :done :done :done i = 0 value = nil repeat i = i + 1 value = { tag = tpl_args[string.format('spawn_weight%s_tag', i)], value = tpl_args[string.format('spawn_weight%s_value', i)], }       if value.tag then tbl :tag('tr') :tag('td') :wikitext(i) :done :tag('td') :wikitext(value.tag) :done :tag('td') :wikitext(value.value) :done :done :done end until value.tag == nil -- generation weight table tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 3) :wikitext('Generation Weights') :done :done :tag('tr') :tag('th') :wikitext('#') :done :tag('th') :wikitext('Tag') :done :tag('th') :wikitext('Weight') :done :done :done i = 0 value = nil repeat i = i + 1 value = { tag = tpl_args[string.format('generation_weight%s_tag', i)], value = tpl_args[string.format('generation_weight%s_value', i)], }       if value.tag then tbl :tag('tr') :tag('td') :wikitext(i) :done :tag('td') :wikitext(value.tag) :done :tag('td') :wikitext(value.value) :done :done :done end until value.tag == nil -- Sell prices tbl = container:tag('table') tbl :attr('class', 'wikitable sortable') :tag('tr') :tag('th') :attr('colspan', 2) :wikitext('Modifier sell price') :done :done :tag('tr') :tag('th') :wikitext('#') :done :tag('th') :wikitext('Item') :done :done :done for i, value in ipairs(sell_prices) do       tbl :tag('tr') :tag('td') :wikitext(value.amount) :done :tag('td') :wikitext(string.format('%s', value.name)) :done :done end -- Generic messages on the page out = {} if mw.ustring.find(tpl_args['id'], '_') then out[#out+1] = frame:expandTemplate{ title = 'Incorrect title', args = { title=tpl_args['id'] } } .. '\n\n\n' end if tpl_args['name'] then out[#out+1] = string.format("%s is the internal id of modifier %s.\n", tpl_args['id'], tpl_args['name']) else out[#out+1] = string.format("%s is the internal id of an unnamed modifier.\n", tpl_args['id'], tpl_args['name']) end -- Categories cats = {'Mods'} -- Done -> output return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out) end

-- -- Template: SMW query mods --

function p.query_mods(frame) -- Args g_args = getArgs(frame, {       parentFirst = true    }) g_frame = m_util.misc.get_frame(frame) g_args.colspan = 4 local conditions = {} conditions[#conditions+1] = 'concept' if g_args.tag then conditions[#conditions+1] = string.format('Has subobject:: [[-Has subobject::+ Has spawn weight::>>0 Has tag::%s ]]', 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('%s', 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:: %s ', 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 = '' else text = '' end tags[page][#tags[page]+1] = string.format('%s %s', row['Has tag'], text) end end --       -- Query mods --       query = {} for _, v in ipairs(conditions) do           query[#query+1] = v        end for _, v in ipairs(fields) do           query[#query+1] = v        end for k, v in pairs(parameters) do           query[k] = v        end results = m_util.smw.query(query, g_frame) local last = '' local tbl = '' for _, row in ipairs(results) do           local current = string.gsub(row['Is mod'], '%d+.*', '%%d.*') if string.match(last, current) then h.format_mod(tbl, row, tags[row[1]]) else out[#out+1] = tostring(tbl) tbl = h.create_header(row) h.format_mod(tbl, row, tags[row[1]]) end last = row['Is mod'] end -- add the last table out[#out+1] = tostring(tbl) end return table.concat(out, '') end

-- -- Template: SMW mod table --

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

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

tr = mw.html.create('tr') tr       :tag('td') :attr('data-sort-value', g_args[2] or g_args[1]) :wikitext(string.format('%s', 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), ' ') if args.type == 'map' and i == args.effect_rowid then text = mw.text.split(text, ' ', 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, ' ') end tr           :tag('td') :wikitext(text) :done end local query local result if args.type == 'map' then query = { string.format('-Has subobject::%s', g_args[1]), 'Has stat id::+', '?Has stat id', '?Has minimum stat value', '?Has maximum stat value', }       result = m_util.smw.query(query, g_frame) local stat_map = { ['map_item_drop_quantity_+%'] = {disp=0, sort=0}, ['map_item_drop_rarity_+%'] = {disp=0, sort=0}, ['map_pack_size_+%'] = {disp=0, sort=0}, }       local stat for _, row in ipairs(result) do           stat = stat_map[row['Has stat id']] if stat ~= nil then stat.sort = (row['Has minimum stat value'] + row['Has maximum stat value']) / 2 if row['Has minimum stat value'] ~= row['Has minimum stat value'] then stat.disp = string.format('(%s%-%s)', row['Has minimum stat value'], row['Has maximum stat value']) else stat.disp = row['Has minimum stat value'] end end end for _, k in ipairs({'map_item_drop_quantity_+%', 'map_item_drop_rarity_+%', 'map_pack_size_+%'}) do           stat = stat_map[k] tr               :tag('td') :attr('data-sort-value', stat.sort) :wikitext(stat.disp) :done :done end end local tags if args.show_tags or args.type == 'jewel' then query = { string.format('-Has subobject::%s', g_args[1]), 'Has tag::+', '?Has tag', '?Has spawn weight', sort='Is tag number', }

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

function p.item_sell_price(frame) --   Query and sum the vendor prices for an item.     Unidentified items won't currently show the correct vendor price.     Not sure how that is specified, nor is it used at all.     Expanding  seems to give a nil /n the first time a new command     is run. Doesn't always happen.    = p.item_sell_price{page="Voideye"}    = p.item_sell_price{page="Pyre"}    = p.item_sell_price{page="Vessel of Vinktar (Lightning Penetration)"}

-- Args local g_args = getArgs(frame, {       parentFirst = true    }) local g_frame = m_util.misc.get_frame(frame)

-- Only the explicit modifiers are counted when vendors calculates -- the price. local condition = string.format('%s', g_args['page']) local query_item_mods = { condition, '?Has explicit mod ids', '?Has rarity', }   local results_query_item_mods = m_util.smw.query(        query_item_mods,         g_frame    ) local item_mods = m_util.string.split(       results_query_item_mods[1]['Has explicit mod ids'],         ''    ) -- If the item has a Normal rarity then the sell price would be a    -- fixed price. if results_query_item_mods[1]['Has rarity'] == 'Normal' then local amount_normal = 1 local currency_normal = 'Scroll Fragment' return string.format(           '%s %s',             amount_normal,             g_frame:expandTemplate{                title = 'il',                 args = {currency_normal, currency_normal .. 's'}            }        ) -- return string.format('%s %s', amount_normal, currency_normal) end local mods_sell_price = {} for _, modid in ipairs(item_mods) do       local query_mod_page = { string.format('Is mod::%s', modid), }       local mod_page = m_util.smw.query(query_mod_page, g_frame) local query_mod_sell_price = { string.format('-Has subobject::%s', mod_page[1][1]), '?Has sell price amount#', '?Has sell price item name', }       local results = m_util.smw.query(query_mod_sell_price, g_frame) for _, k in ipairs(results) do            if k['Has sell price amount'] ~= '' then if mods_sell_price[k['Has sell price item name']] == nil then mods_sell_price[k['Has sell price item name']] = k['Has sell price amount'] else mods_sell_price[k['Has sell price item name']] = k['Has sell price amount'] + mods_sell_price[k['Has sell price item name']] end end end end local out = {} for currency, amount in pairs(mods_sell_price) do       out[#out+1] = string.format(            '%s %s',             amount,             g_frame:expandTemplate{                title = 'il',                 args = {currency, currency .. 's'}            }        ) end return table.concat(out, ', ') end

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

return out end

function p.get_item_tags(frame) --    This function queries for the tags of a specific item. -- Args local tpl_args = getArgs(frame, {parentFirst=true}) local frame = m_util.misc.get_frame(frame) -- local condition = string.format('items.pageName = "%s"', mw.title.getCurrentTitle) -- if tpl_args.item ~= nil then -- condition = string.format('items.name = "%s"', tpl_args.item) -- end -- tpl_args.tables = 'items' -- tpl_args.fields = 'items.name, items.tags, items.class' -- tpl_args.q_where = string.format('items.name = "%s"', item_name) -- 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('()', ', ') 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, ' '), ', ') 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]

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(       '  [%s]  [%s]  ',        i18n.drop_down_table.collapse_all,        i18n.drop_down_table.expand_all    ) out[#out+1] = string.format('%s %s.  ',         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 : local headers = container headers :tag('h3') :wikitext(string.format( '%s', sctn['header'] )                   )                    :done :done -- Loop through and add all matching mods to the. 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 : 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: %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( '  %s', rows['mods._pageName'], mod_name:gsub('%s', ' ') )                           )                            :done :tag('td') :attr('width', '1') :wikitext(                               string.format( '%s %s', game.level_requirement['short_upper']:gsub('%s', ' '), rows['mods.required_level'] )                           )                            :done :tag('td') :attr('width', '*') :wikitext(                               string.format( '%s', m_util.html.poe_color(                                       'mod',                                         rows['mods.stat_text']:gsub(' ', ', ')                                    ) )                           )                            :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