Module:Mod
The above documentation is transcluded from Module:Mod/doc.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
--
-- 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 [[item]]s 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|%s]]', row[1], row['Has name']))
:attr('class', 'mod-table-cell-name')
:done()
:tag('td')
:wikitext(row['Has level requirement'])
:attr('class', 'mod-table-cell-level')
:done()
:tag('td')
:wikitext(row['Has stat text'])
:attr('class', 'mod-table-cell-stat')
:done()
:tag('td')
:wikitext(table.concat(tags, ', '))
:attr('class', 'mod-table-cell-tags')
:done()
end
function h.cargo_query(tpl_args)
--[[
Returns a Cargo query of all the results, even if there are more
results than the maximum query limit. It also adds popular fields.
tpl_args should include these keys:
tpl_args.tables
tpl_args.fields
tpl_args.q_*
]]
local tables = m_util.string.split(tpl_args.tables, ', ')
local fields = m_util.string.split(tpl_args.fields, ', ')
-- Parse query arguments
local query = {
-- Workaround: Fix duplicates but removes other rows as well.
groupBy = tables[1] .. '._pageID',
limit = 5000,
offset = 0,
}
for key, value in pairs(tpl_args) do
if string.sub(key, 0, 2) == 'q_' then
query[string.sub(key, 3)] = value
end
end
-- Add commonly used fields:
local fields_base = {
'_pageNamespace',
'_pageTitle',
'_ID',
'_rowID',
'_pageID',
'_pageName'
-- '_value'
}
for _, tbl in ipairs(tables) do
for _, fld in ipairs(fields_base) do
fields[#fields+1] = string.format('%s.%s', tbl, fld)
end
end
-- Query cargo table. If there are too many results then repeat,
-- offset, re-query and add the remaining results:
local results = {}
repeat
local result = mw.ext.cargo.query(
table.concat(tables, ', '),
table.concat(fields, ', '),
query
)
query.offset = query.offset + #result
for _,v in ipairs(result) do
results[#results + 1] = v
end
until #result < query.limit
return results
end
-- ----------------------------------------------------------------------------
-- 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('[[Property:Has tag|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('[[Property:Is stat number|#]]')
:done()
:tag('th')
:wikitext('[[Property:Has stat id|Stat Id]]')
:done()
:tag('th')
:wikitext('[[Property:Has minimum stat value|Minimum]]')
:done()
:tag('th')
:wikitext('[[Property:Has maximum stat value|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('[[Property:Is tag number|#]]')
:done()
:tag('th')
:wikitext('[[Property:Has tag|Tag]]')
:done()
:tag('th')
:wikitext('[[Property:Has spawn weight|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('[[Property:Is tag number|#]]')
:done()
:tag('th')
:wikitext('[[Property:Has tag|Tag]]')
:done()
:tag('th')
:wikitext('[[Property:Has generation weight|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('[[Property:Has sell price amount|#]]')
:done()
:tag('th')
:wikitext('[[Property:Has sell price item name|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::<q>[[-Has subobject::+]] [[Has spawn weight::>>0]] [[Has tag::%s]]</q>]]', g_args.tag)
end
g_args.header_level = g_args.header_level or 2
-- Fields
local fields = {}
fields[#fields+1] = '?Is mod'
fields[#fields+1] = '?Has name'
fields[#fields+1] = '?Has level requirement'
fields[#fields+1] = '?Has mod group'
fields[#fields+1] = '?Has stat text'
-- parameters
local parameters = {}
parameters.sort = 'Has mod group, '
parameters.limit = 1000 -- lets see
local data = {}
data.header = {
prefix = 'Prefix',
suffix = 'Suffix',
}
local out = {}
for _, v in ipairs({'prefix', 'suffix'}) do
out[#out+1] = string.format('<h%i>%s</h%i>', g_args.header_level, data.header[v], g_args.header_level)
conditions[1] = string.format('[[Concept:Spawnable named %s item mods]]', v)
local query
local results
--
-- Query tags
--
query = {}
query[#query+1] = string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' '))
query[#query+1] = '[[Has tag::+]]'
query[#query+1] = '[[Has spawn weight::+]]'
--query[#query+1] = '[[Has spawn weight::>>0]]'
query[#query+1] = '?Has tag'
query[#query+1] = '?Has spawn weight#' -- need native number
query.limit = 1000
query.offset = 0
-- Tag order is very important
query.sort = ', Is tag number'
local tags = {}
-- this works because lua only considers nil to be false >_>
while query.offset do
results = m_util.smw.query(query, g_frame)
query.offset = query.offset + #results
-- terminates the while if enough reuslts have been fetched
if query.offset % 1000 ~= 0 then
query.offset = nil
end
for _, row in ipairs(results) do
local page, _ = string.gsub(row[1], '#_[%x]+', '')
if tags[page] == nil then
tags[page] = {}
end
local text
if tonumber(row['Has spawn weight']) > 0 then
text = '[[File:Yes.png|yes|link=]]'
else
text = '[[File:No.png|no|link=]]'
end
tags[page][#tags[page]+1] = string.format('%s %s', row['Has tag'], text)
end
end
--
-- Query mods
--
query = {}
for _, v in ipairs(conditions) do
query[#query+1] = v
end
for _, v in ipairs(fields) do
query[#query+1] = v
end
for k, v in pairs(parameters) do
query[k] = v
end
results = m_util.smw.query(query, g_frame)
local last = ''
local tbl = ''
for _, row in ipairs(results) do
local current = string.gsub(row['Is mod'], '%d+.*', '%%d.*')
if string.match(last, current) then
h.format_mod(tbl, row, tags[row[1]])
else
out[#out+1] = tostring(tbl)
tbl = h.create_header(row)
h.format_mod(tbl, row, tags[row[1]])
end
last = row['Is mod']
end
-- add the last table
out[#out+1] = tostring(tbl)
end
return table.concat(out, '')
end
--
-- Template: SMW mod table
--
-- =p.mod_list{'Strength1', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'ColdCritMultiplier', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'MapMultipleExilesMap2Tier', 'asdasda', 'Area yields 15% more Items<br>8% increased Rarity of Items found in this Area<br>Area is inhabited by 2 additional Rogue Exiles<br>Extra monsters ignore rarity bias (Hidden)<br>+14% Monster pack size', userparam='extra_rows=1, type=map, effect_rowid=2'}
function p.mod_list(frame)
local types = {'map', 'jewel'}
-- Args
g_args = getArgs(frame, {
parentFirst = true
})
g_frame = m_util.misc.get_frame(frame)
--
local args = m_util.string.split_args(g_args.userparam, {sep=', '})
g_args.userparam = args
args.extra_rows = (tonumber(args.extra_rows) or 0)
if args.show_tags == nil then
args.show_tags = true
else
args.show_tags = m_util.cast.boolean(args.show_tags)
end
args.effect_rowid = (tonumber(args.effect_rowid) or 0) + 1
tr = mw.html.create('tr')
tr
:tag('td')
:attr('data-sort-value', g_args[2] or g_args[1])
:wikitext(string.format('[[%s|%s]]', g_args[1], g_args[2] or g_args[1]))
:done()
local i = 2
local row_max = i + args.extra_rows
local text
while i < row_max do
i = i + 1
text = g_args[i] or ''
text = table.concat(mw.text.split(text, ';', true), '<br>')
if args.type == 'map' and i == args.effect_rowid then
text = mw.text.split(text, '<br>', true)
local map = {
'%d+%% increased Quantity of Items found in this Area',
'%d+%% increased Rarity of Items found in this Area',
'%+%d+%% Monster pack size',
}
out = {}
local valid
for k, v in pairs(text) do
valid = true
for _, pattern in ipairs(map) do
if mw.ustring.find(v, pattern) ~= nil then
valid = false
break
end
end
if valid then
table.insert(out, v)
end
end
text = table.concat(out, '<br>')
end
tr
:tag('td')
:wikitext(text)
:done()
end
local query
local result
if args.type == 'map' then
query = {
string.format('[[-Has subobject::%s]]', g_args[1]),
'[[Has stat id::+]]',
'?Has stat id',
'?Has minimum stat value',
'?Has maximum stat value',
}
result = m_util.smw.query(query, g_frame)
local stat_map = {
['map_item_drop_quantity_+%'] = {disp=0, sort=0},
['map_item_drop_rarity_+%'] = {disp=0, sort=0},
['map_pack_size_+%'] = {disp=0, sort=0},
}
local stat
for _, row in ipairs(result) do
stat = stat_map[row['Has stat id']]
if stat ~= nil then
stat.sort = (row['Has minimum stat value'] + row['Has maximum stat value']) / 2
if row['Has minimum stat value'] ~= row['Has minimum stat value'] then
stat.disp = string.format('(%s%-%s)', row['Has minimum stat value'], row['Has maximum stat value'])
else
stat.disp = row['Has minimum stat value']
end
end
end
for _, k in ipairs({'map_item_drop_quantity_+%', 'map_item_drop_rarity_+%', 'map_pack_size_+%'}) do
stat = stat_map[k]
tr
:tag('td')
:attr('data-sort-value', stat.sort)
:wikitext(stat.disp)
:done()
:done()
end
end
local tags
if args.show_tags or args.type == 'jewel' then
query = {
string.format('[[-Has subobject::%s]]', g_args[1]),
'[[Has tag::+]]',
'?Has tag',
'?Has spawn weight',
sort='Is tag number',
}
tags = {}
result = m_util.smw.query(query, g_frame)
end
if args.type == 'jewel' then
local jewels = {
dex=0,
str=0,
int=0,
pris=0,
}
local cast_tbl = {
not_dex={'str', 'int'},
not_int={'str', 'dex'},
not_str={'int', 'dex'},
default={'str','int','dex','pris'},
}
local i = #result
local row
local cast
while i > 0 do
row = result[i]
cast = cast_tbl[row['Has tag']]
if cast ~= nil then
for _, k in ipairs(cast) do
jewels[k] = row['Has spawn weight']
end
end
i = i - 1
end
tr
:tag('td')
:attr('class', 'table-cell-dex')
:wikitext(jewels.dex)
:done()
:tag('td')
:attr('class', 'table-cell-int')
:wikitext(jewels.int)
:done()
:tag('td')
:attr('class', 'table-cell-str')
:wikitext(jewels.str)
:done()
:tag('td')
:attr('class', 'table-cell-prismatic')
:wikitext(jewels.pris)
:done()
:done()
end
if args.show_tags then
for _, row in ipairs(result) do
tags[#tags+1] = string.format('%s %s', row['Has tag'], row['Has spawn weight'])
end
tr
:tag('td')
:wikitext(table.concat(tags, '<br>'))
:done()
:done()
end
return tostring(tr)
end
function p.item_sell_price(frame)
--[[
Query and sum the vendor prices for an item.
Unidentified items won't currently show the correct vendor price.
Not sure how that is specified, nor is it used at all.
Expanding {{il}} seems to give a nil /n the first time a new command
is run. Doesn't always happen.
= p.item_sell_price{page="Voideye"}
= p.item_sell_price{page="Pyre"}
= p.item_sell_price{page="Vessel of Vinktar (Lightning Penetration)"}
]]
-- Args
local g_args = getArgs(frame, {
parentFirst = true
})
local g_frame = m_util.misc.get_frame(frame)
-- Only the explicit modifiers are counted when vendors calculates
-- the price.
local condition = string.format('[[%s]]', g_args['page'])
local query_item_mods = {
condition,
'?Has explicit mod ids',
'?Has rarity',
}
local results_query_item_mods = m_util.smw.query(
query_item_mods,
g_frame
)
local item_mods = m_util.string.split(
results_query_item_mods[1]['Has explicit mod ids'],
'<MANY>'
)
-- If the item has a Normal rarity then the sell price would be a
-- fixed price.
if results_query_item_mods[1]['Has rarity'] == 'Normal' then
local amount_normal = 1
local currency_normal = 'Scroll Fragment'
return string.format(
'%s %s',
amount_normal,
g_frame:expandTemplate{
title = 'il',
args = {currency_normal, currency_normal .. 's'}
}
)
-- return string.format('%s %s', amount_normal, currency_normal)
end
local mods_sell_price = {}
for _, modid in ipairs(item_mods) do
local query_mod_page = {
string.format('[[Is mod::%s]]', modid),
}
local mod_page = m_util.smw.query(query_mod_page, g_frame)
local query_mod_sell_price = {
string.format('[[-Has subobject::%s]]', mod_page[1][1]),
'?Has sell price amount#',
'?Has sell price item name',
}
local results = m_util.smw.query(query_mod_sell_price, g_frame)
for _, k in ipairs(results) do
if k['Has sell price amount'] ~= '' then
if mods_sell_price[k['Has sell price item name']] == nil then
mods_sell_price[k['Has sell price item name']] =
k['Has sell price amount']
else
mods_sell_price[k['Has sell price item name']] =
k['Has sell price amount']
+ mods_sell_price[k['Has sell price item name']]
end
end
end
end
local out = {}
for currency, amount in pairs(mods_sell_price) do
out[#out+1] = string.format(
'%s %s',
amount,
g_frame:expandTemplate{
title = 'il',
args = {currency, currency .. 's'}
}
)
end
return table.concat(out, ', ')
end
function p.get_mod_domain(cargo_query)
--[[
Gets the mod domain based on the item class.
]]
local out = cargo_query
local mod_domains = game.constants.mod.domains
-- Set item class as key and the mod domain as value:
local class_to_domain = {
['Life Flasks']=2,
['Mana Flasks']=2,
['Hybrid Flasks']=2,
['Utility Flasks']=2,
['Critical Utility Flasks']=2,
['Maps']=5,
['Jewel']=11,
['Leaguestones']=13,
['Abyss Jewel']=14,
}
for i,_ in ipairs(out) do
-- Get the domain, if it's not defined in the table assume it's
-- in the item domain.
out[i]['items.domain'] = class_to_domain[out[i]['items.class']] or 1
-- Convert the mod domain number to understandable text:
out[i]['items.domain_text'] = mod_domains[out[i]['items.domain']]['short_lower']
end
return out
end
function p.get_item_tags(frame)
--[[
This function queries for the tags of a specific item.
]]
-- Args
local tpl_args = getArgs(frame, {parentFirst=true})
local frame = m_util.misc.get_frame(frame)
tpl_args.tables = 'items'
tpl_args.fields = 'items.name, items.tags, items.class'
local tbl = {
{tpl_args.page, 'items._pageName = "%s"'},
{tpl_args.item, 'items.name = "%s"'},
}
for _,v in ipairs(tbl) do
if v[1] ~= nil then
condition = string.format(v[2], v[1])
break
end
end
tpl_args.q_where = condition
tpl_args.q_groupBy = 'items._pageID'
tpl_args.q_orderBy = 'items.name'
-- Query mods with cargo:
results = h.cargo_query(tpl_args)
-- -- SMW workaround, remove when module:item2 is ready:
-- local tbl = {
-- {tpl_args.page, '[[%s]]'},
-- {tpl_args.item, '[[Has name::%s]]'},
-- }
-- for _,v in ipairs(tbl) do
-- if v[1] ~= nil then
-- condition = string.format(v[2], v[1])
-- break
-- end
-- end
-- local results = m_util.smw.query(
-- {
-- condition,
-- '?Has name',
-- '?Has tags',
-- '?Has item class'
-- },
-- frame
-- )
-- for i,_ in ipairs(results) do
-- results[i]['items.tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ')
-- results[i]['items.class'] = results[i]['Has item class']
-- results[i]['items.name'] = results[i]['Has name']
-- results[i]['items._pageName'] = results[i][1]
-- end
-- --
results = p.get_mod_domain(results)
return results
end
function p.header(str)
--[[
This function replace specific numbers with a generic #.
]]
local s = table.concat(m_util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
s = table.concat(m_util.string.split(s, '%d+%.*%d*'), '#')
s = table.concat(m_util.string.split(s, '<br>'), ', ')
return s
end
function p.get_spawn_chance(frame)
--[[
Calculates the spawn chance of a set of mods that all have a
spawn weight.
]]
-- Args
local tpl_args = getArgs(frame, {parentFirst=true})
local frame = m_util.misc.get_frame(frame)
local tbl = tpl_args['tbl']
-- Probabilities affecting the result besides the spawn weight:
local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1
local N = 0
for i,_ in ipairs(tbl) do
-- Total number of outcomes.
N = N + tbl[i]['spawn_weights.weight']
end
for i,_ in ipairs(tbl) do
-- Number of ways it can happen:
local n = tbl[i]['spawn_weights.weight']
-- Truncated value:
tbl[i]['spawn_weights.chance'] = string.format(
"%0.2f%%",
n/N * chance_multiplier*100
)
end
return tbl
end
function p.drop_down_table(frame)
--[[
This function queries mods that can spawn on an item. It compares
the item tags and the spawn weight tags. If there's a match and
the spawn weight is larger than zero, then that mod is added to a
drop down list.
To Do
* Misses forsaken masters currently.
* Add a proper expand/collapse toggle for the entire header row so
it reacts together with mw-collapsible.
* Show Mod group in a better way perhaps:
Mod group (expanded)
# to Damage (Collapsed)
3 to Damage
5 to Damage
Examples:
Weapons
p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
Accessories
p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
Jewels
p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
Armour
p.drop_down_table{item = 'Plate Vest', header = 'Armour body armours'}
p.drop_down_table{item = 'Iron Greaves', header = 'Armour boots'}
p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour gloves'}
p.drop_down_table{item = 'Iron Hat', header = 'Armour helmets'}
p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour shields'}
p.drop_down_table{
item = 'Fishing Rod',
header = 'FISH PLEASE',
item_tags = 'fishing_rod',
extra_fields = 'Has spawn weight, Has spawn chance'
}
= p.drop_down_table{
item = 'Fishing Rod',
item_tags = 'axe, one_hand_weapon, onehand, weapon, default'
}
= p.drop_down_table{
item = 'Vaal Blade',
}
]]
-- Get template args:
local tpl_args = getArgs(frame, {parentFirst=true})
local frame = m_util.misc.get_frame(frame)
-- Get the items tags:
local get_item_tags = p.get_item_tags(tpl_args)[1]
-- For some reason cargo queried item tags, are not comma-space
-- separated.
local item_tags = {}
if tpl_args.item_tags ~= nil then
item_tags = m_util.string.split(tpl_args.item_tags, ', ')
else
item_tags = m_util.string.split(get_item_tags['items.tags'], ',')
end
-- local item_tags = m_util.string.split(
-- tpl_args.item_tags or get_item_tags['items.tags'],
-- ', '
-- )
-- Create drop down lists in these sections and query in these
-- generation types.
local section = {}
section = {
[1] = {
header = i18n.drop_down_table.prefix,
generation_type = 1,
},
[2] = {
header = i18n.drop_down_table.suffix,
generation_type = 2,
},
[3] = {
header = i18n.drop_down_table.corrupted,
generation_type = 5,
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
},
-- [4] = {
-- header = 'Forsaken masters',
-- generation_type = 'master',
-- },
}
-- Introductory text:
local out = {}
out[#out+1] = string.format(
'==%s== \n',
tpl_args['header'] or table.concat(item_tags, ', ')
)
out[#out+1] = string.format(
'<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[%s]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[%s]</div></div>',
i18n.drop_down_table.collapse_all,
i18n.drop_down_table.expand_all
)
out[#out+1] = string.format('%s %s.<br><br><br>',
i18n.drop_down_table.table_intro,
f_item_link{page=get_item_tags['items._pageName']}
)
local item_mods = {}
local tableIndex = -1
for _, sctn in ipairs(section) do
local container = mw.html.create('div')
:attr('style', 'vertical-align:top; display:inline-block;')
-- Format the where condition:
generation_type = sctn['generation_type']
local where = {}
for _, item_tag in ipairs(item_tags) do
where[#where+1] = string.format(
'(spawn_weights.tag="%s" AND mods.generation_type=%s AND mods.domain=%s)',
item_tag,
sctn['generation_type'],
get_item_tags['items.domain']
)
end
tpl_args.tables = 'mods, spawn_weights, mod_stats'
tpl_args.fields = 'mods.name, mods.id, mods.required_level, mods.generation_type, mods.domain, mods.mod_group, mods.mod_type, mods.stat_text, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal'
tpl_args.q_join = 'mods._pageID=spawn_weights._pageID, mods._pageID=mod_stats._pageID'
tpl_args.q_where = table.concat(where, ' OR ')
tpl_args.q_groupBy = 'mods._pageID, spawn_weights.tag, spawn_weights.weight'
tpl_args.q_orderBy = 'mods.generation_type, mods.mod_group, mods.mod_type, mods._pageName, mods.required_level, spawn_weights.ordinal'
local extra_fields = {}
if tpl_args.extra_fields ~= nil then
extra_fields = m_util.string.split(tpl_args.extra_fields, ', ')
tpl_args.fields = string.format(
'%s, %s',
tpl_args.fields,
table.concat(extra_fields, ', ')
)
end
-- Query mods:
results = h.cargo_query(tpl_args)
-- Create own list for spawn weights and group by page name:
local spawn_weights = {}
local results_unique = {}
local hash = {}
for _,v in ipairs(results) do
if spawn_weights[v['mods._pageName']] == nil then
spawn_weights[v['mods._pageName']] = {}
end
local n = #spawn_weights[v['mods._pageName']] or 0
spawn_weights[v['mods._pageName']][n+1] = v
-- Get a sorted list that only has unique page names:
if hash[v['mods._pageName']] ~= true then
results_unique[#results_unique+1] = v
hash[v['mods._pageName']] = true
end
end
if #results_unique > 0 then
item_mods[generation_type] = {}
-- Loop through all the modifiers from the concept pages:
local last
for _, v in ipairs(results_unique) do
local pagename = v['spawn_weights._pageName']
-- Loop through all the modifier tags until they match
-- the item tags:
local j = 0
local tag_match_stop
repeat
j = j+1
local mod_tag = spawn_weights[pagename][j]['spawn_weights.tag']
local mod_tag_weight = tonumber(
spawn_weights[pagename][j]['spawn_weights.weight']
)
-- Loop through the item tags until it matches the
-- spawn weight tag and the mod tag has a value larger than
-- zero:
local y = 0
local tag_match_add = false
repeat
y = y+1
tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (spawn_weights[pagename][j] == nil)
tag_match_add = (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
until tag_match_stop or y == #item_tags
-- If there's a match then save that mod and other
-- interesting information:
if tag_match_add then
-- Assume that the mod is global then go through
-- all the stat ids and check if any of the
-- stats are local:
local mod_scope = 'Global'
for _, vv in ipairs(spawn_weights[pagename]) do
if vv['mod_stats.id']:find('.*local.*') ~= nil then
mod_scope = 'Local'
end
end
-- Save the matching modifier tag:
local a = #item_mods[generation_type]
item_mods[generation_type][a+1] = spawn_weights[pagename][j]
-- Save other interesting fields:
item_mods[generation_type][a+1]['mods.scope'] = mod_scope
item_mods[generation_type][a+1]['spawn_weight.idx_match'] = j
item_mods[generation_type][a+1]['mods.add'] = tag_match_add
item_mods[generation_type][a+1]['mods.stop'] = tag_match_stop
end
until tag_match_stop
end
-- If the user wants to see the spawn chance then do the
-- calculations and save that result as well:
if tpl_args.spawn_chance ~= nil then
extra_fields[#extra_fields+1] = 'spawn_weights.chance'
item_mods[generation_type] = p.get_spawn_chance{
tbl = item_mods[generation_type],
chance_multiplier = sctn['chance_multiplier']
}
end
-- Create the drop down table with <table></table>:
local headers = container
headers
:tag('h3')
:wikitext(string.format(
'%s',
sctn['header']
)
)
:done()
:done()
-- Loop through and add all matching mods to the <table>.
local tbl, last
for _, rows in ipairs(item_mods[generation_type]) do
-- If the last mod group is different to the current
-- mod group then assume the mod isn't related and start
-- a new drop down list:
if rows['mods.mod_group'] ~= last then
-- Check through all the mods and see if there are
-- multiple mod types within the same mod group:
local count = {}
for _, n in ipairs(item_mods[generation_type]) do
-- If the mod has the same mod group, then add
-- the mod type to the counter. Only unique mod
-- types matter so the number is just a dummy
-- value:
if n['mods.mod_group'] == rows['mods.mod_group'] then
count[n['mods.mod_type']] = 1
end
end
-- Calculate how many unique mod types with the
-- same mod group there are:
number_of_mod_types = 0
for _ in pairs(count) do
number_of_mod_types = number_of_mod_types + 1
end
-- If there are multiple unique mod types with the
-- same mod group then change the style of the drop
-- down list to indicate it:
if number_of_mod_types > 1 then
tbl_caption = string.format(
'%s',
m_util.html.poe_color(
'mod',
'Mod group: ' .. rows['mods.mod_group']
)
)
else
tbl_caption = string.format(
'%s (%s)',
m_util.html.poe_color(
'mod',
p.header(rows['mods.stat_text'])
),
rows['mods.scope']
)
end
-- Add class and style to the <table>:
tableIndex = tableIndex+1
tbl = container:tag('table')
tbl
:attr('class', 'mw-collapsible mw-collapsed')
:attr('style',
'text-align:left; line-height:1.60em; width:810px;'
)
:tag('th')
:attr('class',
string.format(
'mw-customtoggle-%s',
tableIndex
)
)
:attr('style',
'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;'
)
:attr('colspan', '3' .. #extra_fields)
:wikitext(tbl_caption)
:done()
:done()
end
-- If the mod has no name then use the mod id:
local mod_name = rows['mods.name']
if mod_name == '' or mod_name == nil then
mod_name = rows['mods.id']
end
-- Check if there are any extra properties to show in
-- the drop down list and then add a cell for that,
-- add this node at the end of the table row:
local td = mw.html.create('td')
if extra_fields ~= nil then
for _, extra_field in ipairs(extra_fields) do
td
:attr('width', '*')
:wikitext(string.format(
'%s: %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|%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('<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