Module:Mod: Difference between revisions
Jump to navigation
Jump to search
>Illviljan m (Cargo port, items.tag doesn't return comma-space separated values when queried oddly enough.) |
>OmegaK2 (Item sell price is part of item infobox, no longer needed) |
||
Line 1,050: | Line 1,050: | ||
return tostring(tr) | return tostring(tr) | ||
end | end | ||
function p.get_mod_domain(cargo_query) | function p.get_mod_domain(cargo_query) |
Revision as of 07:51, 3 January 2018
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.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