Path of Exile Wiki

Please consider helping keep the wiki up to date. Check the to-do list of updates needed for version 3.14.0.

Game data exports will becoming later as the technical changes in addition to regular changes take some more time.

READ MORE

Path of Exile Wiki
(remove property links)
(Modifier drop down list moved to module:modifier table. Removed old mod list.)
Line 53: Line 53:
 
sell_price_missing_argument = 'Both %s and %s must be specified',
 
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'
 
},
 
 
}
 
}
   
Line 138: Line 130:
 
end
 
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
 
   
 
-- ----------------------------------------------------------------------------
 
-- ----------------------------------------------------------------------------
Line 719: Line 648:
 
 
 
return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)
 
return tostring(container) .. m_util.misc.add_category(cats) .. '\n' .. table.concat(out)
end
 
 
--
 
-- 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
 
 
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:&nbsp;%s ',
 
extra_field,
 
rows[extra_field]
 
)
 
)
 
:done()
 
end
 
end
 
 
-- Add a table row with the interesting properties that
 
-- modifier has:
 
tbl
 
:tag('tr')
 
:attr('class', 'mw-collapsible mw-collapsed')
 
:attr(
 
'id',
 
string.format(
 
'mw-customcollapsible-%s',
 
tableIndex
 
)
 
)
 
:tag('td')
 
:attr('width', '160')
 
:wikitext(
 
string.format(
 
'&nbsp;&nbsp;&nbsp;[[%s|%s]]',
 
rows['mods._pageName'],
 
mod_name:gsub('%s', '&nbsp;')
 
)
 
)
 
:done()
 
:tag('td')
 
:attr('width', '1')
 
:wikitext(
 
string.format(
 
'%s&nbsp;%s',
 
game.level_requirement['short_upper']:gsub('%s', '&nbsp;'),
 
rows['mods.required_level']
 
)
 
)
 
:done()
 
:tag('td')
 
:attr('width', '*')
 
:wikitext(
 
string.format(
 
'%s',
 
m_util.html.poe_color(
 
'mod',
 
rows['mods.stat_text']:gsub('<br>', ', ')
 
)
 
)
 
)
 
:done()
 
:node(td)
 
:done()
 
:done()
 
 
-- Save the last mod group for later comparison:
 
last = rows['mods.mod_group']
 
end
 
end
 
 
out[#out+1] = tostring(container)
 
end
 
 
return table.concat(out,'')
 
 
end
 
end
   

Revision as of 20:57, 10 February 2018

Template info icon Module documentation[view] [edit] [history] [purge]

Module for handling for Mods with Semantic MediaWiki support.

List of currently implemented templates

--
-- 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',
    },

}

-- ----------------------------------------------------------------------------
-- 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


-- ----------------------------------------------------------------------------
-- 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_cargo = function(tpl_args, frame)
                    return table.concat(tpl_args.tags, ',')  
                end,
                display = function(value)
                    return table.concat(value, ', ')  
                end,                 
            },
        },
    },
    mod_sell_prices = {
        table = 'mod_sell_prices',
        order = {'name', 'amount'},
        fields = {
            name = {
                name = i18n.args.item_name,
                field = i18n.args.item_name,
                type = 'String',
                func = function (value) return value end,
            },
            amount = {
                name = i18n.args.amount,
                field = i18n.args.amount,
                type = 'Integer',
                func = tonumber,
            },
        },
    },
}

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

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


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

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

    m_util.cargo.store(frame, cargo_data)
    
    -- Validate % set the stat subobjects
    m_util.args.stats(tpl_args, {frame=frame})
    for _, stat_data in pairs(tpl_args.stats) do
        m_util.cargo.store(frame, {
            _table = 'mod_stats', 
            id = stat_data.id,
            min = stat_data.min,
            max = stat_data.max,
        })
    end
    
    -- Validate & set spawn weight subobjects
    m_util.args.spawn_weight_list(tpl_args, {
        frame=frame, 
    })
    
    -- Validate & set generation weight subobjects
    m_util.args.generation_weight_list(tpl_args, {
        frame=frame, 
    })
    
    -- Validate & set mod sell values
    i = 0
    local names = {}
    local sell_prices = {}
    repeat 
        i = i + 1
        
        local id = {}
        value = {}
        for key, data in pairs(mod_map.mod_sell_prices.fields) do
            id[key] = string.format('%s%s_%s', i18n.args.sell_price_prefix, i, data.name)
            value[key] = data.func(tpl_args[id[key]])
        end
        
        if value.name == nil and value.amount == nil then
            value = nil
        elseif value.name ~= nil and value.amount ~= nil then
            if names[value.name] then
                error(i18n.errors.sell_price_duplicate_name)
            else
                names[value.name] = true
            end

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

return p