Module:Mod: Difference between revisions
Jump to navigation
Jump to search
>Illviljan m (Adding some comments before the code stops making any sense.) |
>OmegaK2 (Moved code for parsing and settings weights from Module:Mod into Module:Util) |
||
Line 213: | Line 213: | ||
-- Validate & set spawn weight subobjects | -- Validate & set spawn weight subobjects | ||
util.args.weight_list(g_args, { | |||
frame=g_frame, | |||
output_argument='spawn_weights', | |||
}) | |||
-- Validate & set generation weight subobjects | -- Validate & set generation weight subobjects | ||
util.args.weight_list(g_args, { | |||
frame=g_frame, | |||
input_argument='generation_weight', | |||
output_argument='generation_weights', | |||
weight_proprety='Has generation weight', | |||
subobject_name='generation weight', | |||
}) | |||
-- Validate & set mod sell values | -- Validate & set mod sell values |
Revision as of 08:15, 12 June 2017
![](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/Ambox_warning_orange.svg/45px-Ambox_warning_orange.svg.png)
This module is used on 34000+ pages.
To avoid major disruption and server load, do not make unnecessary edits to this module. Test changes to this module first using its /sandbox and /testcases subpages . All of the changes can then be applied to this module in a single edit.
Consider discussing changes on the talk page or on Discord before implementing them.
Module for handling for modifiers with Cargo support.
List of currently implemented templates
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 util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local game = require('Module:Game')
local m_item = require('Module:Item2')
local p = {}
-- ----------------------------------------------------------------------------
-- 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 (arg)
g_args[arg] = util.cast.number(g_args[arg], args)
return g_args[arg]
end
end
function h.handle_mapped_property_args (map)
local properties = {}
for _, data in ipairs(map) do
if data.func ~= nil then
data.func(data.name)
end
if data.property ~= nil then
properties[data.property] = g_args[data.name]
end
end
g_frame:callParserFunction('#set:', properties)
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
-- ----------------------------------------------------------------------------
-- Templates
-- ----------------------------------------------------------------------------
--
-- Template: Mod
--
-- 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
g_args = getArgs(frame, {
parentFirst = true
})
g_frame = util.misc.get_frame(frame)
--
-- Validation & semantic properties
--
-- Validate single value properties and set them
local map = {
{
name = 'id',
property = 'Is mod',
wikitext = 'Mod Id',
},
{
name = 'name',
property = 'Has name',
wikitext = 'Name',
},
{
name = 'mod_group',
property = 'Has mod group',
wikitext = 'Group',
},
{
name = 'mod_type',
property = 'Has mod type',
wikitext = 'Mod type',
},
{
name = 'domain',
property = 'Has mod domain',
func = h.validate.number{min=1, max=13},
wikitext = 'Mod domain',
display = function (value)
return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')'
end,
},
{
name = 'generation_type',
property = 'Has mod generation type',
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,
},
{
name = 'required_level',
property = 'Has level requirement',
func = h.validate.number{min=0, max=100},
wikitext = 'Req. level',
},
{
name = 'stat_text',
property = 'Has stat text',
wikitext = 'Effect',
},
{
name = 'granted_buff_id',
property = 'Has granted buff id',
wikitext = 'Granted Buff Id',
},
{
name = 'granted_buff_value',
property = 'Has granted buff value',
wikitext = 'Granted Buff Value',
},
{
name = 'granted_skill',
property = 'Has granted skill id',
wikitext = 'Granted Skill',
},
}
h.handle_mapped_property_args(map)
-- Validate & set multi value property
if g_args['tags'] == nil then
g_args['tags'] = {}
else
local tags = mw.text.split(g_args['tags'], ', ')
g_args['tags'] = tags
properties = {}
properties['Has tag'] = table.concat(tags, ';')
properties['+sep'] = ';'
util.smw.set(g_frame, properties)
end
-- Validate % set the stat subobjects
util.args.stats(g_args, {frame=g_frame})
-- Validate & set spawn weight subobjects
util.args.weight_list(g_args, {
frame=g_frame,
output_argument='spawn_weights',
})
-- Validate & set generation weight subobjects
util.args.weight_list(g_args, {
frame=g_frame,
input_argument='generation_weight',
output_argument='generation_weights',
weight_proprety='Has generation weight',
subobject_name='generation weight',
})
-- Validate & set mod sell values
i = 0
local names = {}
local sell_prices = {}
repeat
i = i + 1
id = {
name = string.format('sell_price%s_name', i),
amount = string.format('sell_price%s_amount', i),
}
value = {
name = g_args[id.name],
amount = tonumber(g_args[id.amount]),
}
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('Do not specify a sell price item name multiple times. Instead adjust the amount')
else
names[value.name] = true
end
properties = {}
properties['Has sell price item name'] = value.name
properties['Has sell price amount'] = value.amount
util.smw.subobject(g_frame, 'sell price ' .. value.name, properties)
sell_prices[#sell_prices+1] = value
else
error ('Both ' .. id.name .. ' and ' .. id.amount .. ' must be specified')
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 _, data in ipairs(map) do
local text
if data.display == nil then
text = g_args[data.name]
else
text = data.display(g_args[data.name])
end
tbl
:tag('tr')
:tag('th')
:wikitext(string.format('[[Property:%s|%s]]', data.property, 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(g_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, 5 do
local value = {
id = g_args['stat' .. i .. '_id'],
min = g_args['stat' .. i .. '_min'],
max = g_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 = g_args[string.format('spawn_weight%s_tag', i)],
value = g_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 = g_args[string.format('generation_weight%s_tag', i)],
value = g_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(m_item.item_link{item_name_exact=value.name})
:done()
:done()
end
-- Generic messages on the page
out = {}
if mw.ustring.find(g_args['id'], '_') then
out[#out+1] = g_frame:expandTemplate{ title = 'Incorrect title', args = { title=g_args['id'] } } .. '\n\n\n'
end
if g_args['name'] then
out[#out+1] = string.format("'''%s''' is the internal id of modifier '''%s'''.\n", g_args['id'], g_args['name'])
else
out[#out+1] = string.format("'''%s''' is the internal id of an unnamed modifier.\n", g_args['id'], g_args['name'])
end
-- Categories
cats = {'Mods'}
-- Done -> output
return tostring(container) .. 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 = 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 = util.smw.query(query, g_frame)
query.offset = query.offset + #results
-- terminates the while if enough reuslts have been fetched
if query.offset % 1000 ~= 0 then
query.offset = nil
end
for _, row in ipairs(results) do
local page, _ = string.gsub(row[1], '#_[%x]+', '')
if tags[page] == nil then
tags[page] = {}
end
local text
if tonumber(row['Has spawn weight']) > 0 then
text = '[[File:Yes.png|yes|link=]]'
else
text = '[[File:No.png|no|link=]]'
end
tags[page][#tags[page]+1] = string.format('%s %s', row['Has tag'], text)
end
end
--
-- Query mods
--
query = {}
for _, v in ipairs(conditions) do
query[#query+1] = v
end
for _, v in ipairs(fields) do
query[#query+1] = v
end
for k, v in pairs(parameters) do
query[k] = v
end
results = util.smw.query(query, g_frame)
local last = ''
local tbl = ''
for _, row in ipairs(results) do
local current = string.gsub(row['Is mod'], '%d+.*', '%%d.*')
if string.match(last, current) then
h.format_mod(tbl, row, tags[row[1]])
else
out[#out+1] = tostring(tbl)
tbl = h.create_header(row)
h.format_mod(tbl, row, tags[row[1]])
end
last = row['Is mod']
end
-- add the last table
out[#out+1] = tostring(tbl)
end
return table.concat(out, '')
end
--
-- Template: SMW mod table
--
-- =p.mod_list{'Strength1', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'ColdCritMultiplier', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'MapMultipleExilesMap2Tier', 'asdasda', 'Area yields 15% more Items<br>8% increased Rarity of Items found in this Area<br>Area is inhabited by 2 additional Rogue Exiles<br>Extra monsters ignore rarity bias (Hidden)<br>+14% Monster pack size', userparam='extra_rows=1, type=map, effect_rowid=2'}
function p.mod_list(frame)
local types = {'map', 'jewel'}
-- Args
g_args = getArgs(frame, {
parentFirst = true
})
g_frame = util.misc.get_frame(frame)
--
local args = util.string.split_args(g_args.userparam, {sep=', '})
g_args.userparam = args
args.extra_rows = (tonumber(args.extra_rows) or 0)
if args.show_tags == nil then
args.show_tags = true
else
args.show_tags = util.cast.boolean(args.show_tags)
end
args.effect_rowid = (tonumber(args.effect_rowid) or 0) + 1
tr = mw.html.create('tr')
tr
:tag('td')
:attr('data-sort-value', g_args[2] or g_args[1])
:wikitext(string.format('[[%s|%s]]', g_args[1], g_args[2] or g_args[1]))
:done()
local i = 2
local row_max = i + args.extra_rows
local text
while i < row_max do
i = i + 1
text = g_args[i] or ''
text = table.concat(mw.text.split(text, ';', true), '<br>')
if args.type == 'map' and i == args.effect_rowid then
text = mw.text.split(text, '<br>', true)
local map = {
'%d+%% increased Quantity of Items found in this Area',
'%d+%% increased Rarity of Items found in this Area',
'%+%d+%% Monster pack size',
}
out = {}
local valid
for k, v in pairs(text) do
valid = true
for _, pattern in ipairs(map) do
if mw.ustring.find(v, pattern) ~= nil then
valid = false
break
end
end
if valid then
table.insert(out, v)
end
end
text = table.concat(out, '<br>')
end
tr
:tag('td')
:wikitext(text)
:done()
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 = 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 = 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 = 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 = util.smw.query(query_item_mods, g_frame)
local item_mods = util.string.split(results_query_item_mods[1]['Has explicit mod ids'], '<MANY>')
-- If the item has a Normal rarity then the sell price would be a fixed price.
if results_query_item_mods[1]['Has rarity'] == 'Normal' then
local amount_normal = 1
local currency_normal = 'Scroll Fragment'
return string.format('%s %s', amount_normal, g_frame:expandTemplate{title = 'il', args = {currency_normal, currency_normal .. 's'}})
-- return string.format('%s %s', amount_normal, currency_normal)
end
local mods_sell_price = {}
for _, modid in ipairs(item_mods) do
local query_mod_page = {
string.format('[[Is mod::%s]]', modid),
}
local mod_page = util.smw.query(query_mod_page, g_frame)
local query_mod_sell_price = {
string.format('[[-Has subobject::%s]]', mod_page[1][1]),
'?Has sell price amount#',
'?Has sell price item name',
}
local results = util.smw.query(query_mod_sell_price, g_frame)
for _, k in ipairs(results) do
if k['Has sell price amount'] ~= '' then
if mods_sell_price[k['Has sell price item name']] == nil then
mods_sell_price[k['Has sell price item name']] = k['Has sell price amount']
else
mods_sell_price[k['Has sell price item name']] = k['Has sell price amount'] + mods_sell_price[k['Has sell price item name']]
end
end
end
end
local out = {}
for currency, amount in pairs(mods_sell_price) do
out[#out+1] = string.format('%s %s', amount, g_frame:expandTemplate{title = 'il', args = {currency, currency .. 's'}})
end
return table.concat(out, ', ')
end
function p.find_mod_domain(input)
-- Find the mod domain based on the item class.
local out = input
local mod_domains = game.constants.mod.domains
for i,_ in ipairs(out) do
out[i]['Has mod domain'] = 1
for j, row in pairs(mod_domains) do
if out[i]['Has item class']:gsub('Map', 'Area'):match(mod_domains[j]['short_upper']) then -- This may need updating if an area item class doesn't have 'Map' in the string, or if the mod domain descriptions doesn't match the item class.
out[i]['Has mod domain'] = j
end
end
out[i]['Has mod domain text'] = mod_domains[out[i]['Has mod domain']]['short_lower']
end
return out
end
function p.get_item_tags(frame)
-- This function queries for the tags of a specific item.
-- Args
local g_args = getArgs(frame, {
parentFirst = true
})
local g_frame = util.misc.get_frame(frame)
local item_name = g_args[1]
local query = {
string.format('[[Has name::%s]]', item_name),
'?Has tags',
'?Has base strength requirement',
'?Has base intelligence requirement',
'?Has base dexterity requirement',
'?Has item class'
}
local results = util.smw.query(query, g_frame)
for i,_ in ipairs(results) do
results[i]['Has tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ') -- Remove unnecessary symbols.
function table.reverse(a) -- Reverse order. Item tags are often sorted from lowest to highest priority. Therefore reversing order should lead to finding a match faster most of the time.
local res = {}
for i = #a, 1, -1 do
res[#res+1] = a[i]
end
return res
end
results[i]['Has tags'] = table.concat(table.reverse(util.string.split(results[i]['Has tags'], ', ')), ', ')
end
results = p.find_mod_domain(results)
return results
end
function p.header(str)
-- This function replace specific numbers with a generic #.
local s = table.concat(util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
s = table.concat(util.string.split(s, '<br>'), ', ')
return s
end
function p.get_spawn_chance(frame)
-- Calculates the spawn chance of a set of mods that all have a spawn weight.
-- Args
local g_args = getArgs(frame, {
parentFirst = true
})
local g_frame = util.misc.get_frame(frame)
local tbl = g_args['tbl']
local chance_multiplier = tonumber(g_args['chance_multiplier']) or 1 -- Probabillities affecting the result besides the spawn weight.
local N = 0
for i,_ in ipairs(tbl) do
N = N + tbl[i]['Has spawn weight'] -- Total number of outcomes.
end
for i,_ in ipairs(tbl) do
local n = tbl[i]['Has spawn weight'] -- Number of ways it can happen.
tbl[i]['Has spawn chance'] = string.format("%0.2f%%", n/N * chance_multiplier*100) -- Truncated value.
end
return tbl
end
function p.drop_down_table(frame)
-- This function queries mods in concept pages, queries the subobjects in each mod page for the mod tags. Then compares the tags on the item with the mod tags. If there's a match and the spawn weighting is larger than zero, then that mod is added to a drop down list.
-- Misses forsaken masters currently.
-- Add a proper expand/collapse toggle for the entire header row so it reacts together with mw-collapsible.
-- Show Mod group in a better way perhaps:
-- Mod group (expanded)
-- # to Damage (Collapsed)
-- 3 to Damage
-- 5 to Damage
-- Add a better solution for properties inside subobjects, for extra_properties. Note that properties already queried for should not be added with this solution.
-- Weapons
-- p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
-- p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
-- Accessories
-- p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
-- Jewels
-- p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
-- Armour
-- p.drop_down_table{item = 'Plate Vest', header = 'Armour body armours'}
-- p.drop_down_table{item = 'Iron Greaves', header = 'Armour boots'}
-- p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour gloves'}
-- p.drop_down_table{item = 'Iron Hat', header = 'Armour helmets'}
-- p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour shields'}
-- p.drop_down_table{item = 'Fishing Rod', header = 'FISH PLEASE', item_tags = 'fishing_rod', extra_properties = 'Has spawn weight, Has spawn chance'}
-- Args
local g_args = getArgs(frame, {
parentFirst = true
})
local g_frame = util.misc.get_frame(frame)
local get_item_tags = p.get_item_tags{g_args.item}[1]
local item_tags = {}
if g_args.item_tags ~= nil then
item_tags = util.string.split(g_args.item_tags, ', ')
else
item_tags = util.string.split(get_item_tags['Has tags'], ', ')
end
local header = g_args['header']
if header == nil then
header = table.concat(item_tags, ', ')
end
-- Conditions
local conditions = {}
conditions[1] = 'concept' -- Reserve for Concepts.
conditions[#conditions+1] = string.format('[[Has subobject::<q> [[Has tag::%s]] </q>]]', table.concat(item_tags, ' || '))
-- Fields
local all_fields = {
1,
'Is mod',
'Has name',
'Has level requirement',
'Has mod group',
'Has mod type',
'Has stat text',
}
local extra_properties = {}
if g_args.extra_properties ~= nil then
extra_properties = util.string.split(g_args.extra_properties, ', ')
local a = #all_fields
for _,v in ipairs(extra_properties) do
table.insert(all_fields, a+1, v)
end
end
local fields = {}
for _,v in ipairs(all_fields) do
fields[#fields+1] = string.format('?%s', v)
end
-- Parameters
local parameters = {
sort = 'Has mod group, Has mod type, Has level requirement',
-- limit = 1000, -- lets see
offset = 0,
}
-- Create drop down lists in these sections and look in these concept pages for the modifiers:
local section = {}
section = {
[1] = {
header = 'Prefix',
generation_type = 'prefix',
condition = string.format('[[Concept:Spawnable named prefix %s mods]]', get_item_tags['Has mod domain text']),
},
[2] = {
header = 'Suffix',
generation_type = 'suffix',
condition = string.format('[[Concept:Spawnable named suffix %s mods]]', get_item_tags['Has mod domain text']),
},
[3] = {
header = 'Corrupted',
generation_type = 'corrupted',
condition = string.format('[[Concept:Spawnable corrupted %s mods]]', get_item_tags['Has mod domain text']),
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
},
-- [4] = {
-- header = 'Forsaken masters',
-- generation_type = 'master',
-- condition = '[[Concept:AAAAAAAAAAAAAAAAAAAA]]',
-- },
}
local out = {}
-- Introductory text:
out[#out+1] = string.format('==%s== \n', header)
out[#out+1] = '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[Collapse All]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[Expand All]</div></div>'
out[#out+1] = string.format('The table below displays the available [[modifiers]] for [[item]]s such as %s.<br><br><br>', m_item.item_link{get_item_tags[1]})
local results = {}
local item_mods = {}
local tableIndex = -1
-- Loop through all the sections and query the mods in the concept pages:
for i_type,_ in ipairs(section) do
local generation_type = section[i_type]['generation_type']
local query = {}
conditions[1] = section[i_type]['condition']
query[#query+1] = table.concat(conditions, ' ')
for _, v in ipairs(fields) do
query[#query+1] = v
end
for k, v in pairs(parameters) do
query[k] = v
end
if results[generation_type] == nil then
results[generation_type] = {}
end
-- Query the mods in the concept page, if there are too many results then repeat and add the remaining values:
repeat
local result = util.smw.query(query, g_frame)
query.offset = query.offset + #result -- Possible error source if only one mod is missing.
for _,v in ipairs(result) do
results[generation_type][#results[generation_type]+1] = v
end
until #result < 1000
item_mods[generation_type] = {}
local container = mw.html.create('div')
:attr('style', 'vertical-align:top; display:inline-block;')
if #results[generation_type] > 0 then
query_mod_tags = {
string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' ')),
'?Has tag#',
'?Has spawn weight#',
'?Is tag number#',
offset = 0,
}
local results_mod_tags = {}
local mod_data_subobject = {}
-- Query the subobjects in each mod page for the mod tags, if there are too many results then repeat and add the remaining values:
repeat
results_mod_tags[#results_mod_tags+1] = util.smw.query(query_mod_tags, g_frame)
query_mod_tags.offset = query_mod_tags.offset + #results_mod_tags[#results_mod_tags]
for _,v in ipairs(results_mod_tags[#results_mod_tags]) do
local pagename = util.string.split(v[1], '#')[1]
if mod_data_subobject[pagename] == nil then
mod_data_subobject[pagename] = {}
end
local subobjectname = v[1]
mod_data_subobject[pagename][subobjectname] = v
end
until #results_mod_tags[#results_mod_tags] < 1000
-- Loop through all the modifiers from the concept pages:
local last
for i, v in ipairs(results[generation_type]) do
local pagename = results[generation_type][i][1]
-- Loop through all the modifier tags until they match the item tags:
local j = 0
local tag_match_stop
repeat
j = j+1
local subobjectname = string.format('%s#%s_%s', pagename, 'spawn_weight', j)
local mod_tag = mod_data_subobject[pagename][subobjectname]['Has tag']
local mod_tag_weight = tonumber(mod_data_subobject[pagename][subobjectname]['Has spawn weight'])
-- Loop through the item tags until it matches the mod tag and the mod tag has a value larger than zero:
local y = 0
local tag_match_add
repeat
y = y+1
tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (mod_data_subobject[pagename][subobjectname] == nil)
tag_match_add = (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
until tag_match_stop or y == #item_tags
-- If 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 subobjects and check if any of the stats are local:
local mod_scope = 'Global'
for subobject, subobject_val in pairs(mod_data_subobject[pagename]) do
-- If at least one stat is local then change the scope of the mod to local:
if subobject:find('stat.*local') ~= nil then
mod_scope = 'Local'
end
-- Complement with the properties the matching subobject has:
if subobject == subobjectname then
for property,w in pairs(subobject_val) do
if results[generation_type][i][property] == nil then
results[generation_type][i][property] = {}
end
results[generation_type][i][property] = w
end
end
end
-- Save the matching modifier and all other interesting properties:
local a = #item_mods[generation_type]
item_mods[generation_type][a+1] = {}
for _,property in ipairs(all_fields) do
item_mods[generation_type][a+1][property] = results[generation_type][i][property]
end
item_mods[generation_type][a+1]['Has mod scope'] = mod_scope
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:
for _,v in ipairs(extra_properties) do
if v == 'Has spawn chance' then
item_mods[generation_type] = p.get_spawn_chance{tbl = item_mods[generation_type], chance_multiplier = section[i_type]['chance_multiplier']}
break
end
end
-- Create the drop down table with <table></table>:
local headers = container
headers
:tag('h3')
:wikitext(string.format('%s', section[i_type]['header']))
:done()
:done()
-- Loop through and add all matching mods to the <table>.
local tbl, last
for _, mod_properties 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 mod_properties['Has 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['Has mod group'] == mod_properties['Has mod group'] then
count[n['Has mod type']] = 1
end
end
-- Calculate how many unique mod types with the same mod group there are:
local 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', util.html.poe_color('mod', 'Mod group: ' .. mod_properties['Has mod group']))
else
tbl_caption = string.format('%s (%s)', util.html.poe_color('mod', p.header(mod_properties['Has stat text'])), mod_properties['Has mod 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_properties)
:wikitext(tbl_caption)
:done()
:done()
end
-- If the mod has no name then use the mod id:
local mod_name = mod_properties['Has name']
if mod_name == '' then
mod_name = mod_properties['Is mod']
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_properties ~= nil then
for _, extra_property in ipairs(extra_properties) do
td
:attr('width', '*')
:wikitext(string.format('%s: %s ', extra_property, mod_properties[extra_property]))
: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]]', mod_properties[1], mod_name:gsub('%s', ' ')))
:done()
:tag('td')
:attr('width', '1')
:wikitext(string.format('%s %s',game.level_requirement['short_upper']:gsub('%s', ' '), mod_properties['Has level requirement']))
:done()
:tag('td')
:attr('width', '*')
:wikitext(string.format('%s', util.html.poe_color('mod', mod_properties['Has stat text']:gsub('<br>', ', ')) ))
:done()
:node(td)
:done()
:done()
-- Save the last mod group for later comparison:
last = mod_properties['Has mod group']
end
end
out[#out+1] = tostring(container)
end
return table.concat(out,'')
end
return p