Module:Modifier table: Difference between revisions
Jump to navigation
Jump to search
>OmegaK2 mNo edit summary |
>Illviljan (Modifier link: Added generation type to verbose. Drop down list: Moved it here. Added support for adding extra item tags.) |
||
Line 1: | Line 1: | ||
-- Module responsible for | --[[ | ||
Module responsible for displaying modifiers in various ways. | |||
]] | |||
local m_util = require('Module:Util') | local m_util = require('Module:Util') | ||
local getArgs = require('Module:Arguments').getArgs | local getArgs = require('Module:Arguments').getArgs | ||
local m_game = require('Module:Game') | local m_game = require('Module:Game') | ||
local f_item_link = require('Module:Item link').item_link | |||
local cargo = mw.ext.cargo | local cargo = mw.ext.cargo | ||
Line 36: | Line 40: | ||
spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'), | spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'), | ||
generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'), | generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'), | ||
}, | |||
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 98: | Line 111: | ||
end | end | ||
return table.concat(str, '<br>') | return table.concat(str, '<br>') | ||
end | |||
function h.cargo_query(tpl_args) | |||
--[[ | |||
Returns a Cargo query of all the results. | |||
tpl_args should include these keys: | |||
* tpl_args.tables | |||
* tpl_args.fields | |||
* tpl_args.q_* | |||
]] | |||
-- Split strings into tables: | |||
local tables = m_util.string.split(tpl_args.tables, ', ') | |||
local fields = m_util.string.split(tpl_args.fields, ', ') | |||
-- Parse query arguments | |||
local query = { | |||
limit = 5000, | |||
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 | |||
-- Query cargo table. If there are too many results then repeat, | |||
-- offset, re-query and add the remaining results: | |||
local results = {} | |||
repeat | |||
local result = 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 or true -- Remove true when offset works. | |||
return results | |||
end | end | ||
Line 518: | Line 576: | ||
return tostring(tbl) | return tostring(tbl) | ||
end | end | ||
-- --------------------------------------------------------------------- | |||
-- Modifier link | |||
-- --------------------------------------------------------------------- | |||
function p.modifier_link(frame) | function p.modifier_link(frame) | ||
Line 528: | Line 593: | ||
= p.modifier_link{"Dictator's"} | = p.modifier_link{"Dictator's"} | ||
= p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'} | = p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'} | ||
]] | ]] | ||
Line 534: | Line 600: | ||
local frame = m_util.misc.get_frame(frame) | local frame = m_util.misc.get_frame(frame) | ||
-- | -- Aliases: | ||
tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or '' | tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or '' | ||
-- Define query arguments: | -- Define query arguments: | ||
local tables = {'mods', 'mod_stats', 'spawn_weights'} | local tables = {'mods', 'mod_stats', 'spawn_weights'} | ||
local fields = {'mods.name', 'mods.stat_text', 'mods.stat_text_raw', 'mods._pageName', 'mod_stats.max', 'mod_stats.min', 'spawn_weights.tag', 'spawn_weights.weight', 'mods.id', 'mod_stats.id'} | local fields = {'mods.name', 'mods.stat_text', 'mods.stat_text_raw', 'mods.generation_type', 'mods._pageName', 'mod_stats.max', 'mod_stats.min', 'spawn_weights.tag', 'spawn_weights.weight', 'mods.id', 'mod_stats.id'} | ||
local query = { | local query = { | ||
join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName', | join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName', | ||
Line 554: | Line 620: | ||
local results = m_util.cargo.query(tables, fields, query, args) | local results = m_util.cargo.query(tables, fields, query, args) | ||
-- Create own list for and group by page name: | -- Create own list for each cargo table and group by page name: | ||
tpl_args.tbl = {} | tpl_args.tbl = {} | ||
for _,v in ipairs(tables) do | for _,v in ipairs(tables) do | ||
Line 609: | Line 675: | ||
for _,v in ipairs(err_tbl) do | for _,v in ipairs(err_tbl) do | ||
if v.bool then | if v.bool then | ||
local cats = {' | local cats = {'Pages with modifier link errors'} | ||
return m_util.html.error( | return m_util.html.error( | ||
{msg = string.format(v.disp[1], v.disp[2]) .. m_util.misc.add_category(cats)} | {msg = string.format(v.disp[1], v.disp[2]) .. m_util.misc.add_category(cats)} | ||
Line 645: | Line 711: | ||
display = function(tpl_args, frame) | display = function(tpl_args, frame) | ||
return string.format( | return string.format( | ||
'%s - %s', | '%s - %s (%s)', | ||
m_util.html.poe_color( | m_util.html.poe_color( | ||
'mod', | 'mod', | ||
Line 661: | Line 727: | ||
', ' | ', ' | ||
) | ) | ||
) | ), | ||
m_game.constants.mod.generation_types[ | |||
tonumber( | |||
tpl_args.results_unique[1]['mods.generation_type'] | |||
) | |||
].full | |||
) | ) | ||
end, | end, | ||
Line 721: | Line 792: | ||
return display[tpl_args.display or 'abbr'].display(tpl_args, frame) | return display[tpl_args.display or 'abbr'].display(tpl_args, frame) | ||
end | end | ||
-- --------------------------------------------------------------------- | |||
-- Drop down list | |||
-- --------------------------------------------------------------------- | |||
function h.get_mod_domain(cargo_query) | |||
--[[ | |||
Gets the mod domain based on the item class. | |||
]] | |||
local out = cargo_query | |||
local mod_domains = m_game.constants.mod.domains | |||
-- Set the item class as key and the corresponding 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 h.get_item_tags(frame) | |||
--[[ | |||
This function queries for the tags of a specific item. | |||
]] | |||
-- Get template arguments: | |||
local tpl_args = getArgs(frame, {parentFirst=true}) | |||
local frame = m_util.misc.get_frame(frame) | |||
-- Format the cargo query: | |||
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._pageName' | |||
tpl_args.q_orderBy = 'items.name' | |||
-- Query mods with cargo: | |||
results = h.cargo_query(tpl_args) | |||
-- Find out what mod domain an item class is in: | |||
results = h.get_mod_domain(results) | |||
return results | |||
end | |||
function h.header(str) | |||
--[[ | |||
This function replaces 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 h.get_spawn_chance(frame) | |||
--[[ | |||
Calculates the spawn chance of a set of mods that all have a | |||
spawn weight. | |||
]] | |||
-- Get template arguments: | |||
local tpl_args = getArgs(frame, {parentFirst=true}) | |||
local frame = m_util.misc.get_frame(frame) | |||
-- Get the table: | |||
local tbl = tpl_args['tbl'] | |||
-- Probabilities affecting the result besides the spawn weight: | |||
local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1 | |||
-- Total number of outcomes. | |||
local N = 0 | |||
for i,_ in ipairs(tbl) do | |||
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 = 'Body armours'} | |||
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', | |||
extra_item_tags = 'fishing_rod' | |||
} | |||
= p.drop_down_table{ | |||
item = 'Vaal Blade', | |||
} | |||
]] | |||
-- Get template arguments: | |||
local tpl_args = getArgs(frame, {parentFirst=true}) | |||
local frame = m_util.misc.get_frame(frame) | |||
-- Get the items tags: | |||
local get_item_tags = h.get_item_tags(tpl_args)[1] | |||
-- For some reason cargo queried item tags, are not comma-space | |||
-- separated any more. | |||
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 | |||
if tpl_args.extra_item_tags then | |||
local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ') | |||
for _,v in ipairs(extra_item_tags) do | |||
item_tags[#item_tags+1] = v | |||
end | |||
end | |||
-- local item_tags = m_util.string.split( | |||
-- table.concat( | |||
-- { | |||
-- tpl_args.item_tags or get_item_tags['items.tags'], | |||
-- tpl_args.extra_item_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] = table.concat(item_tags, ', ') | |||
-- 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: | |||
local 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, mods._pageName, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal, spawn_weights._pageName' | |||
tpl_args.q_join = 'mods._pageName=spawn_weights._pageName, mods._pageName=mod_stats._pageName' | |||
tpl_args.q_where = table.concat(where, ' OR ') | |||
tpl_args.q_groupBy = 'mods._pageName, 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] = h.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', | |||
h.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', | |||
m_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 | |||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- |
Revision as of 20:11, 1 February 2018
The above documentation is transcluded from Module:Modifier table/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 responsible for displaying modifiers in various ways.
]]
local m_util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local m_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 = {
mod_table = {
name = m_util.html.abbr('Name', 'Name of the modifier if available or its internal identifier instead'),
mod_group = m_util.html.abbr('Group', 'Only one modifier from the specified group can appear at a time under normal circumstances'),
mod_type = 'Type',
domain = '[[Modifiers#Mod_Domain|Domain]]',
generation_type = '[[Modifiers#Mod_Generation_Type|Generation Type]]',
required_level = '[[Image:Level_up_icon_small.png|link=|For generated item/monster modifiers the minimum item/monster level respectively. Some generation types may not require this condition to be met, however item level restrictions may be raised to 80% of this value.]]',
stat_text = m_util.html.abbr('Stats', 'Stats of the modifier and the range they can roll in (if applicable)'),
buff = m_util.html.abbr('Buff', 'ID of the buff granted and the values associated'),
granted_skill = m_util.html.abbr('Skill', 'ID of the skill granted'),
tags = '[[Tags]]',
iiq = m_util.html.abbr('IIQ', 'increased Quantity of Items found in this Area'),
iir = m_util.html.abbr('IIR', 'increased Rarity of Items found in this Area'),
pack_size = m_util.html.abbr('Pack<br>Size', 'Monster pack size'),
spawn_weights = m_util.html.abbr('Spawn Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
generation_weights = m_util.html.abbr('Generation Weighting', 'List of applicable tags and their values for weighting (i.e. calculating the chances) of the spawning of this modifier'),
},
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'
},
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',
--
-- Modifier link template
--
undefined_statid = 'Please define any of these stat ids: %s',
incorrect_modid = 'Please change the name from "%s" to any of these modifier ids:<br>%s',
multiple_results = 'Please choose only one of these modifier ids:<br>%s',
no_results = 'No results found.',
},
}
--
-- Helper/Utility functions
--
local h = {}
function h.query_weights(table_name, page_ids)
results = cargo.query(
string.format('mods,%s', table_name),
string.format('mods._pageID,%s.tag,%s.weight', table_name, table_name),
{
where=page_ids,
join=string.format('mods._pageID=%s._pageID', table_name),
orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
limit=5000,
}
)
if #results == 5000 then
error('Hit maximum cargo results')
end
return m_util.cargo.map_results_to_id{results=results, table_name='mods'}
end
function h.disambiguate_mod_name(results)
--[[
Disambiguates results from a mods query.
]]
local str = {}
for i,v in pairs(results) do
str[#str+1] = string.format(
'%s - %s ([[%s|page]])',
v['mods.id'] or v['mods._pageName'],
string.gsub(
v['mods.stat_text_raw'],
'<br>',
', '
) or '',
v['mods._pageName']
)
end
return table.concat(str, '<br>')
end
function h.cargo_query(tpl_args)
--[[
Returns a Cargo query of all the results.
tpl_args should include these keys:
* tpl_args.tables
* tpl_args.fields
* tpl_args.q_*
]]
-- Split strings into tables:
local tables = m_util.string.split(tpl_args.tables, ', ')
local fields = m_util.string.split(tpl_args.fields, ', ')
-- Parse query arguments
local query = {
limit = 5000,
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
-- Query cargo table. If there are too many results then repeat,
-- offset, re-query and add the remaining results:
local results = {}
repeat
local result = 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 or true -- Remove true when offset works.
return results
end
-- ----------------------------------------------------------------------------
-- Template: Mod table
-- ----------------------------------------------------------------------------
local mod_table = {}
mod_table.data = {
{
arg = nil,
header = i18n.mod_table.name,
fields = {'mods._pageName', 'mods.id', 'mods.name'},
options = {
[3] = {
optional=true,
},
},
display = function(tpl_args, frame, tr, data)
local name
if data['mods.name'] then
name = data['mods.name']
else
name = data['mods.id']
end
tr
:tag('td')
:wikitext(string.format('[[%s|%s]]', data['mods._pageName'], name))
end,
order = 1000,
sort_type = 'text',
},
{
arg = 'domain',
header = i18n.mod_table.domain,
fields = {'mods.domain'},
display = function(tpl_args, frame, tr, data)
local value = data['mods.domain']
tr
:tag('td')
:attr('data-sort-value', value)
:wikitext(m_game.constants.mod.domains[tonumber(value)]['short_upper'])
end,
order = 2000,
sort_type = 'text',
},
{
arg = 'generation_type',
header = i18n.mod_table.generation_type,
fields = {'mods.generation_type'},
display = function(tpl_args, frame, tr, data)
local value = data['mods.generation_type']
tr
:tag('td')
:attr('data-sort-value', value)
:wikitext(m_game.constants.mod.generation_types[tonumber(value)]['short_upper'])
end,
order = 2001,
sort_type = 'text',
},
{
arg = {'group', 'mod_group'},
header = i18n.mod_table.mod_group,
fields = {'mods.mod_group'},
display = function(tpl_args, frame, tr, data)
tr
:tag('td')
:wikitext(data['mods.mod_group'])
end,
order = 2002,
sort_type = 'text',
},
{
arg = {'mod_type'},
header = i18n.mod_table.mod_type,
fields = {'mods.mod_type'},
display = function(tpl_args, frame, tr, data)
tr
:tag('td')
:wikitext(data['mods.mod_type'])
end,
order = 2003,
sort_type = 'text',
},
{
arg = {'level', 'required_level'},
header = i18n.mod_table.required_level,
fields = {'mods.required_level'},
display = function(tpl_args, frame, tr, data)
tr
:tag('td')
:wikitext(data['mods.required_level'])
end,
order = 2004,
},
{
arg = {'stat_text'},
header = i18n.mod_table.stat_text,
fields = {'mods.stat_text'},
display = function(tpl_args, frame, tr, data)
local text
-- map display type shows this in another column, remove this text to avoid clogging up the list
if tpl_args.type == 'map' then
local texts = m_util.string.split(data['mods.stat_text'], '<br>')
local out = {}
local valid
for _, v in ipairs(texts) do
valid = true
for _, data in pairs(mod_table.stat_ids) do
if string.find(v, data.pattern) ~= nil then
valid = false
break
end
end
if valid then
table.insert(out, v)
end
end
text = table.concat(out, '<br>')
else
text = data['mods.stat_text']
end
tr
:tag('td')
:wikitext(text)
end,
order = 3000,
sort_type = 'text',
},
{
arg = 'buff',
header = i18n.mod_table.buff,
fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
display = function(tpl_args, frame, tr, data)
tr
:tag('td')
:wikitext(string.format('%s %s', data['mods.granted_buff_id'], data['mods.granted_buff_value']))
end,
order = 4000,
sort_type = 'text',
},
{
arg = {'skill', 'granted_skill'},
header = i18n.mod_table.granted_skill,
fields = {'mods.granted_skill'},
display = function(tpl_args, frame, tr, data)
tr
:tag('td')
:wikitext(data['mods.granted_skill'])
end,
order = 4001,
sort_type = 'text',
},
{
arg = {'tags'},
header = i18n.mod_table.tags,
fields = {'mods.tags'},
display = function(tpl_args, frame, tr, data)
tr
:tag('td')
:wikitext(table.concat(m_util.string.split(data['mods.tags'], ','), ', '))
end,
order = 5000,
sort_type = 'text',
},
}
mod_table.stat_ids = {
['map_item_drop_quantity_+%'] = {
header = i18n.mod_table.iiq,
pattern = '%d+%% increased Quantity of Items found in this Area',
},
['map_item_drop_rarity_+%'] = {
header = i18n.mod_table.iir,
pattern = '%d+%% increased Rarity of Items found in this Area',
},
['map_pack_size_+%'] = {
header = i18n.mod_table.pack_size,
pattern = '%+%d+%% Monster pack size',
}
}
mod_table.weights = {'spawn_weights', 'generation_weights'}
function p.mod_table(frame)
tpl_args = getArgs(frame, {
parentFirst = true
})
frame = m_util.misc.get_frame(frame)
for _, key in ipairs(mod_table.weights) do
tpl_args[key] = m_util.cast.boolean(tpl_args[key])
end
if string.find(tpl_args.q_where, '%[%[') ~= nil then
error('SMW leftover in where clause')
end
local row_infos = {}
for _, row_info in ipairs(mod_table.data) do
local enabled = false
if row_info.arg == nil then
enabled = true
elseif type(row_info.arg) == 'string' and m_util.cast.boolean(tpl_args[row_info.arg]) then
enabled = true
elseif type(row_info.arg) == 'table' then
for _, argument in ipairs(row_info.arg) do
if m_util.cast.boolean(tpl_args[argument]) then
enabled = true
break
end
end
end
if enabled then
row_info.options = row_info.options or {}
row_infos[#row_infos+1] = row_info
end
end
-- sort the rows
table.sort(row_infos, function (a, b)
return (a.order or 0) < (b.order or 0)
end)
-- Set tables
local tables = 'mods'
if tpl_args.q_tables then
tables = tables .. ',' .. tpl_args.q_tables
end
-- Set required fields
local fields = {
'mods._pageID',
}
for _, rowinfo in ipairs(row_infos) do
if type(rowinfo.fields) == 'function' then
rowinfo.fields = rowinfo.fields()
end
for index, field in ipairs(rowinfo.fields) do
rowinfo.options[index] = rowinfo.options[index] or {}
fields[#fields+1] = field
end
end
-- Parse query arguments
local query = {
-- Workaround: fix duplicates
groupBy='mods._pageID',
}
for key, value in pairs(tpl_args) do
if string.sub(key, 0, 2) == 'q_' then
query[string.sub(key, 3)] = value
end
end
local results = cargo.query(
tables,
table.concat(fields, ','),
query
)
if #results == 0 then
if tpl_args.default ~= nil then
return tpl_args.default
else
return 'No results found'
end
end
-- this might be needed in other queries, currently not checking if it's actually needed
-- because performance impact should be neglible
local page_ids = {}
for _, row in ipairs(results) do
page_ids[#page_ids+1] = string.format('mods._pageID="%s"', row['mods._pageID'])
end
page_ids = table.concat(page_ids, ' OR ')
local weights = {}
for _, key in ipairs(mod_table.weights) do
if tpl_args[key] then
weights[key] = h.query_weights(key, page_ids)
end
end
local stats
if tpl_args.type == 'map' then
local query_stat_ids = {}
for k, _ in pairs(mod_table.stat_ids) do
query_stat_ids[#query_stat_ids+1] = string.format('mod_stats.id="%s"', k)
end
local stat_results = cargo.query(
'mods,mod_stats',
'mods._pageID,mod_stats.id,mod_stats.min,mod_stats.max',
{
where=string.format('(%s) AND (%s)', page_ids, table.concat(query_stat_ids, ' OR ')),
join='mods._pageID=mod_stats._pageID',
orderBy='mods.id ASC',
limit=5000,
}
)
if #stat_results == 5000 then
error('Hit maximum cargo results')
end
stats = m_util.cargo.map_results_to_id{results=stat_results, table_name='mods'}
-- In addition map stats to stat <-> min/max pairs
for page_id, rows in pairs(stats) do
local stat_id_map = {}
for _, row in ipairs(rows) do
stat_id_map[row['mod_stats.id']] = {min=tonumber(row['mod_stats.min']), max=tonumber(row['mod_stats.max'])}
end
stats[page_id] = stat_id_map
end
end
local tbl = mw.html.create('table')
tbl:attr('class', 'wikitable sortable modifier-table')
-- Header
local tr = tbl:tag('tr')
for _, row_info in ipairs(row_infos) do
tr
:tag('th')
:attr('data-sort-type', row_info.sort_type or 'number')
:wikitext(row_info.header)
:done()
end
if tpl_args.type == 'map' then
for stat_id, data in pairs(mod_table.stat_ids) do
tr
:tag('th')
:attr('data-sort-type', 'number')
:wikitext(data.header)
:done()
end
end
for _, key in ipairs(mod_table.weights) do
if tpl_args[key] then
tr
:tag('th')
:wikitext(i18n.mod_table[key])
end
end
-- Body
for _, row in ipairs(results) do
tr = tbl:tag('tr')
for _, rowinfo in ipairs(row_infos) do
-- this has been cast from a function in an earlier step
local display = true
for index, field in ipairs(rowinfo.fields) do
-- this will bet set to an empty value not nil confusingly
if row[field] == '' then
if rowinfo.options[index].optional ~= true then
display = false
break
else
row[field] = nil
end
end
end
if display then
rowinfo.display(tpl_args, frame, tr, row, rowinfo.fields)
else
tr:wikitext(m_util.html.td.na())
end
end
if tpl_args.type == 'map' then
for stat_id, data in pairs(mod_table.stat_ids) do
local stat_data = stats[row['mods._pageID']]
if stat_data and stat_data[stat_id] then
local v = stat_data[stat_id]
local text
if v.min == v.max then
text = v.min
else
text = string.format('(%s to %s)', v.min, v.max)
end
tr
:tag('td')
:attr('data-sort-value', (v.min+v.max)/2)
:wikitext(string.format('%s%%', text))
:done()
else
tr:wikitext(m_util.html.td.na())
end
end
end
for _, key in ipairs(mod_table.weights) do
if tpl_args[key] then
local weight_out = {}
for _, wrow in ipairs(weights[key][row['mods._pageID']]) do
weight_out[#weight_out+1] = string.format('%s %s', wrow[key .. '.tag'], wrow[key .. '.weight'])
end
if #weight_out > 0 then
tr
:tag('td')
:wikitext(table.concat(weight_out, '<br>'))
:done()
else
tr:wikitext(m_util.html.td.na())
end
end
end
end
return tostring(tbl)
end
-- ---------------------------------------------------------------------
-- Modifier link
-- ---------------------------------------------------------------------
function p.modifier_link(frame)
--[[
Finds and links to a modifier in formatted form.
Examples:
= p.modifier_link{"Tyrannical"}
= p.modifier_link{"Flaring"}
= p.modifier_link{"Dictator's"}
= p.modifier_link{"LocalIncreasedPhysicalDamagePercentAndAccuracyRating8", display='max', statid='local_physical_damage_+%'}
]]
-- Get template args:
local tpl_args = getArgs(frame, {parentFirst = true})
local frame = m_util.misc.get_frame(frame)
-- Aliases:
tpl_args.modid = tpl_args.modid or tpl_args.id or tpl_args[1] or ''
-- Define query arguments:
local tables = {'mods', 'mod_stats', 'spawn_weights'}
local fields = {'mods.name', 'mods.stat_text', 'mods.stat_text_raw', 'mods.generation_type', 'mods._pageName', 'mod_stats.max', 'mod_stats.min', 'spawn_weights.tag', 'spawn_weights.weight', 'mods.id', 'mod_stats.id'}
local query = {
join = 'mods._pageName=mod_stats._pageName, mods._pageName=spawn_weights._pageName',
where = string.format(
'(mods.name="%s" or mods.id="%s") AND mod_stats.id LIKE "%%%s%%"',
tpl_args.modid,
tpl_args.modid,
tpl_args.statid or '%'
),
-- groupBy = 'mods._pageID, mod_stats.id, spawn_weights.tag',
}
-- Query cargo rows:
local results = m_util.cargo.query(tables, fields, query, args)
-- Create own list for each cargo table and group by page name:
tpl_args.tbl = {}
for _,v in ipairs(tables) do
tpl_args.tbl[v] = {}
end
tpl_args.results_unique = {}
local hash = {}
for _,v in ipairs(results) do
for ii, vv in pairs(tpl_args.tbl) do
if tpl_args.tbl[ii][v['mods._pageName']] == nil then
tpl_args.tbl[ii][v['mods._pageName']] = {}
end
local n = #tpl_args.tbl[ii][v['mods._pageName']] or 0
tpl_args.tbl[ii][v['mods._pageName']][n+1] = v
-- Get a sorted list that only has unique page names:
if hash[v['mods._pageName']] ~= true then
local m = #tpl_args.results_unique
tpl_args.results_unique[m+1] = v
hash[v['mods._pageName']] = true
end
end
end
-- Helpful error handling:
local err_tbl = {
{
bool = #results == 0,
disp = {
i18n.errors.no_results,
}
},
{
bool = #tpl_args.results_unique > 1,
disp = {
i18n.errors.multiple_results,
h.disambiguate_mod_name(tpl_args.results_unique),
},
},
{
bool = tpl_args.modid ~= tpl_args.results_unique[1]['mods.id'],
disp = {
string.gsub(
i18n.errors.incorrect_modid,
'%%s',
tpl_args.modid,
1
),
h.disambiguate_mod_name(tpl_args.results_unique),
},
},
}
for _,v in ipairs(err_tbl) do
if v.bool then
local cats = {'Pages with modifier link errors'}
return m_util.html.error(
{msg = string.format(v.disp[1], v.disp[2]) .. m_util.misc.add_category(cats)}
)
end
end
-- Display formats:
local display = {
abbr = {
display = function(tpl_args, frame)
return string.format(
'%s%s',
m_util.html.poe_color(
'mod',
string.format(
'[[%s|%s]]',
tpl_args.results_unique[1]['mods._pageName'],
tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
)
),
m_util.html.abbr(
'[?]',
string.gsub(
tpl_args.results_unique[1]['mods.stat_text_raw'],
'<br>',
', '
),
class
)
)
end,
},
verbose = {
display = function(tpl_args, frame)
return string.format(
'%s - %s (%s)',
m_util.html.poe_color(
'mod',
string.format(
'[[%s|%s]]',
tpl_args.results_unique[1]['mods._pageName'],
tpl_args.results_unique[1]['mods.name'] or tpl_args.results_unique[1]['mods.id']
)
),
m_util.html.poe_color(
'mod',
string.gsub(
tpl_args.results_unique[1]['mods.stat_text'],
'<br>',
', '
)
),
m_game.constants.mod.generation_types[
tonumber(
tpl_args.results_unique[1]['mods.generation_type']
)
].full
)
end,
},
stat_text = {
display = function(tpl_args, frame)
return m_util.html.poe_color(
'mod',
tpl_args.results_unique[1]['mods.stat_text']
)
end,
},
max = {
display = function(tpl_args, frame)
local statid = {}
for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
statid[#statid+1] = v['mod_stats.id']
if tpl_args.statid == v['mod_stats.id'] then
return v['mod_stats.max']
end
end
if tpl_args.statid == nil then
return m_util.html.error(
{
msg = string.format(
i18n.errors.undefined_statid,
table.concat(statid, ', ')
)
}
)
end
end
},
min = {
display = function(tpl_args, frame)
local statid = {}
for _,v in ipairs(tpl_args.tbl.mod_stats[tpl_args.results_unique[1]['mods._pageName']]) do
statid[#statid+1] = v['mod_stats.id']
if tpl_args.statid == v['mod_stats.id'] then
return v['mod_stats.min']
end
end
if tpl_args.statid == nil then
return m_util.html.error(
{
msg = string.format(
i18n.errors.undefined_statid,
table.concat(statid, ', ')
)
}
)
end
end
},
}
return display[tpl_args.display or 'abbr'].display(tpl_args, frame)
end
-- ---------------------------------------------------------------------
-- Drop down list
-- ---------------------------------------------------------------------
function h.get_mod_domain(cargo_query)
--[[
Gets the mod domain based on the item class.
]]
local out = cargo_query
local mod_domains = m_game.constants.mod.domains
-- Set the item class as key and the corresponding 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 h.get_item_tags(frame)
--[[
This function queries for the tags of a specific item.
]]
-- Get template arguments:
local tpl_args = getArgs(frame, {parentFirst=true})
local frame = m_util.misc.get_frame(frame)
-- Format the cargo query:
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._pageName'
tpl_args.q_orderBy = 'items.name'
-- Query mods with cargo:
results = h.cargo_query(tpl_args)
-- Find out what mod domain an item class is in:
results = h.get_mod_domain(results)
return results
end
function h.header(str)
--[[
This function replaces 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 h.get_spawn_chance(frame)
--[[
Calculates the spawn chance of a set of mods that all have a
spawn weight.
]]
-- Get template arguments:
local tpl_args = getArgs(frame, {parentFirst=true})
local frame = m_util.misc.get_frame(frame)
-- Get the table:
local tbl = tpl_args['tbl']
-- Probabilities affecting the result besides the spawn weight:
local chance_multiplier = tonumber(tpl_args['chance_multiplier']) or 1
-- Total number of outcomes.
local N = 0
for i,_ in ipairs(tbl) do
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 = 'Body armours'}
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',
extra_item_tags = 'fishing_rod'
}
= p.drop_down_table{
item = 'Vaal Blade',
}
]]
-- Get template arguments:
local tpl_args = getArgs(frame, {parentFirst=true})
local frame = m_util.misc.get_frame(frame)
-- Get the items tags:
local get_item_tags = h.get_item_tags(tpl_args)[1]
-- For some reason cargo queried item tags, are not comma-space
-- separated any more.
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
if tpl_args.extra_item_tags then
local extra_item_tags = m_util.string.split(tpl_args.extra_item_tags, ', ')
for _,v in ipairs(extra_item_tags) do
item_tags[#item_tags+1] = v
end
end
-- local item_tags = m_util.string.split(
-- table.concat(
-- {
-- tpl_args.item_tags or get_item_tags['items.tags'],
-- tpl_args.extra_item_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] = table.concat(item_tags, ', ')
-- 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:
local 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, mods._pageName, mod_stats.id, spawn_weights.tag, spawn_weights.weight, spawn_weights.ordinal, spawn_weights._pageName'
tpl_args.q_join = 'mods._pageName=spawn_weights._pageName, mods._pageName=mod_stats._pageName'
tpl_args.q_where = table.concat(where, ' OR ')
tpl_args.q_groupBy = 'mods._pageName, 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] = h.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',
h.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',
m_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
-- ----------------------------------------------------------------------------
-- Debug functions
-- ----------------------------------------------------------------------------
p.debug = {}
function p.debug.tbl_data(tbl)
keys = {}
for _, data in ipairs(mod_table.data) do
if type(data.arg) == 'string' then
keys[data.arg] = 1
elseif type(data.arg) == 'table' then
for _, arg in ipairs(data.arg) do
keys[arg] = 1
end
end
end
for _, key in ipairs(mod_table.weights) do
keys[key] = 1
end
local out = {}
for key, _ in pairs(keys) do
out[#out+1] = string.format("['%s'] = '1'", key)
end
return table.concat(out, ', ')
end
-- ----------------------------------------------------------------------------
--
-- ----------------------------------------------------------------------------
return p