Module:Modifier table: Difference between revisions
Jump to navigation
Jump to search
(Global variable `class` is not defined anywhere in this module) |
(Translation strings moved to Module:Modifier table/config for easier porting. Use invoker factory from Module:Util. No globals. Passing the rarely used frame object around the entire codebase through function parameters is a code smell. Testing shows that it doesn't significantly impact execution time to simply call mw.getCurrentFrame() as needed.) |
||
Line 1: | Line 1: | ||
-- | ------------------------------------------------------------------------------- | ||
Module responsible for displaying modifiers in various ways. | -- | ||
-- Module:Modifier table | |||
-- | |||
-- Module responsible for displaying modifiers in various ways. | |||
------------------------------------------------------------------------------- | |||
require('Module:No globals') | |||
local m_util = require('Module:Util') | |||
local m_cargo = require('Module:Cargo') | |||
-- Should we use the sandbox version of our submodules? | |||
local | local use_sandbox = m_util.misc.maybe_sandbox('Modifier table') | ||
local m_game = mw.loadData('Module:Game') | local m_game = mw.loadData('Module:Game') | ||
local f_item_link | -- Lazy loading | ||
local f_item_link -- require('Module:Item link').item_link | |||
local | -- The cfg table contains all localisable strings and configuration, to make it | ||
-- easier to port this module to another wiki. | |||
local cfg = use_sandbox and mw.loadData('Module:Modifier table/config/sandbox') or mw.loadData('Module:Modifier table/config') | |||
local | local i18n = cfg.i18n | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Helper functions | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local | local h = {} | ||
-- Lazy loading for Module:Item link | |||
function h.item_link(args) | |||
if not f_item_link then | |||
f_item_link = require('Module:Item link').item_link | |||
end | |||
return f_item_link(args) | |||
end | |||
function h.header(str) | function h.header(str) | ||
Line 157: | Line 100: | ||
args.delimiter = args.delimiter or ', ' | args.delimiter = args.delimiter or ', ' | ||
return function(tpl_args | return function(tpl_args, tr, data, fields) | ||
local values = {} | local values = {} | ||
local fmt_values = {} | local fmt_values = {} | ||
Line 193: | Line 136: | ||
end | end | ||
-- | function h.get_spawn_chance(args) | ||
--[[ | |||
Calculates the spawn chance of a set of mods that all have a | |||
spawn weight. | |||
]] | |||
-- Get the table: | |||
local tbl = args['tbl'] | |||
-- Probabilities affecting the result besides the spawn weight: | |||
local chance_multiplier = tonumber(args['chance_multiplier']) or 1 | |||
tr | -- Total number of outcomes. | ||
:tag('td') | local N = 0 | ||
:wikitext(string.format('[[%s|%s]]', data['mods._pageName'], name)) | for i,_ in ipairs(tbl) do | ||
end, | N = N + tbl[i]['spawn_weights.weight'] | ||
order = 1000, | end | ||
sort_type = 'text', | |||
}, | 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 | |||
-- ---------------------------------------------------------------------------- | |||
-- Additional configuration | |||
-- ---------------------------------------------------------------------------- | |||
local mod_table = {} | |||
mod_table.data = { | |||
{ | |||
arg = 'name', | |||
header = i18n.mod_table.name, | |||
fields = {'mods._pageName', 'mods.id', 'mods.name'}, | |||
options = { | |||
[3] = { | |||
optional=true, | |||
}, | |||
}, | |||
display = function(tpl_args, tr, data, fields) | |||
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', | arg = 'domain', | ||
header = i18n.mod_table.domain, | header = i18n.mod_table.domain, | ||
fields = {'mods.domain'}, | fields = {'mods.domain'}, | ||
display = function(tpl_args | display = function(tpl_args, tr, data, fields) | ||
local k = 'mods.domain' | local k = 'mods.domain' | ||
local i = tonumber(data[k]) | local i = tonumber(data[k]) | ||
Line 234: | Line 210: | ||
end | end | ||
data[k] = m_game.constants.mod.domains[i]['short_upper'] | data[k] = m_game.constants.mod.domains[i]['short_upper'] | ||
h.tbl.display.factory.value({})(tpl_args | h.tbl.display.factory.value({})(tpl_args, tr, data, fields) | ||
end, | end, | ||
order = 2000, | order = 2000, | ||
Line 243: | Line 219: | ||
header = i18n.mod_table.generation_type, | header = i18n.mod_table.generation_type, | ||
fields = {'mods.generation_type'}, | fields = {'mods.generation_type'}, | ||
display = function(tpl_args | display = function(tpl_args, tr, data, fields) | ||
local k = 'mods.generation_type' | local k = 'mods.generation_type' | ||
local i = tonumber(data[k]) | local i = tonumber(data[k]) | ||
Line 250: | Line 226: | ||
end | end | ||
data[k] = m_game.constants.mod.generation_types[i]['short_upper'] | data[k] = m_game.constants.mod.generation_types[i]['short_upper'] | ||
h.tbl.display.factory.value{}(tpl_args | h.tbl.display.factory.value{}(tpl_args, tr, data, fields) | ||
end, | end, | ||
order = 2001, | order = 2001, | ||
Line 303: | Line 279: | ||
header = i18n.mod_table.stat_text, | header = i18n.mod_table.stat_text, | ||
fields = {'mods.stat_text'}, | fields = {'mods.stat_text'}, | ||
display = function(tpl_args | display = function(tpl_args, tr, data, fields) | ||
local value | local value | ||
-- map display type shows this in another column, remove this text to avoid clogging up the list | -- map display type shows this in another column, remove this text to avoid clogging up the list | ||
Line 330: | Line 306: | ||
end | end | ||
data['mods.stat_text'] = value | data['mods.stat_text'] = value | ||
h.tbl.display.factory.value{color='mod'}(tpl_args | h.tbl.display.factory.value{color='mod'}(tpl_args, tr, data, fields) | ||
end, | end, | ||
order = 3000, | order = 3000, | ||
Line 355: | Line 331: | ||
header = i18n.mod_table.tags, | header = i18n.mod_table.tags, | ||
fields = {'mods.tags'}, | fields = {'mods.tags'}, | ||
display = function(tpl_args | display = function(tpl_args, tr, data, fields) | ||
local k = 'mods.tags' | local k = 'mods.tags' | ||
data[k] = table.concat(m_util.string.split(data[k], ',%s*'), ', ') | data[k] = table.concat(m_util.string.split(data[k], ',%s*'), ', ') | ||
h.tbl.display.factory.value{}(tpl_args | h.tbl.display.factory.value{}(tpl_args, tr, data, fields) | ||
end, | end, | ||
order = 5000, | order = 5000, | ||
Line 380: | Line 356: | ||
mod_table.weights = {'spawn_weights', 'generation_weights'} | mod_table.weights = {'spawn_weights', 'generation_weights'} | ||
function | -- ---------------------------------------------------------------------------- | ||
-- Main functions | |||
-- ---------------------------------------------------------------------------- | |||
local function _mod_table(tpl_args) | |||
--[[ | --[[ | ||
Creates a generic table for modifiers. | Creates a generic table for modifiers. | ||
Line 395: | Line 375: | ||
enchantment=1, | enchantment=1, | ||
} | } | ||
]] | ]] | ||
-- default to enabled | |||
-- default to enabled | |||
tpl_args.name = tpl_args.name or true | tpl_args.name = tpl_args.name or true | ||
Line 629: | Line 602: | ||
end | end | ||
if display then | if display then | ||
row_info.display(tpl_args | row_info.display(tpl_args, tr, row, display_fields[i]) | ||
else | else | ||
tr:wikitext(m_util.html.td.na()) | tr:wikitext(m_util.html.td.na()) | ||
Line 695: | Line 668: | ||
end | end | ||
local function _drop_down_table(tpl_args) | |||
function | |||
--[[ | --[[ | ||
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: | ||
* Add support to: | |||
* Forsaken masters | |||
* Bestiary | |||
* 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 [Collapsible, default=Expanded] | |||
# to Damage [Collapsible, default=Collapsed] | |||
3 to Damage | |||
5 to Damage | |||
* Add a where condition that somehow filters out mods that obviously | |||
wont match with the item. spawn_weights.weight>0 isn't enough due | |||
to possible edge cases. | |||
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'} | |||
Boots | |||
= p.drop_down_table{item='Iron Greaves', header='Boots'} | |||
= p.drop_down_table{ | |||
item='Fishing Rod', | |||
header='FISH PLEASE', | |||
item_tags='fishing_rod', | |||
extra_fields='mods.tags' | |||
} | |||
= 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', | |||
} | |||
]] | |||
-- Format the cargo query: | |||
local where | |||
for _,v in ipairs({ | |||
{tpl_args.page, 'items._pageName = "%s"'}, | |||
{tpl_args.item, 'items.name = "%s"'}, | |||
}) do | |||
if v[1] ~= nil then | |||
where = string.format(v[2], v[1]) | |||
break | |||
end | |||
end | |||
local item_info = m_cargo.query( | |||
{'items'}, | |||
{ | |||
'items.name', | |||
'items.tags', | |||
'items.class_id', | |||
'items.inventory_icon', | |||
'items.html', | |||
'items.release_version', | |||
'items._pageName' | |||
}, | |||
{ | |||
where=where, | |||
groupBy='items._pageName', | |||
orderBy='items.name, items.release_version DESC', | |||
} | |||
)[1] | |||
-- Set the item class as key and the corresponding mod domain as | |||
= | -- value: | ||
local value | |||
local class_to_domain = { | |||
['LifeFlask']=2, | |||
['ManaFlask']=2, | |||
['HybridFlask']=2, | |||
['UtilityFlask']=2, | |||
['UtilityFlaskCritical']=2, | |||
['Map']=5, | |||
['Jewel']=10, | |||
['Leaguestone']=12, | |||
['AbyssJewel']=13, | |||
} | |||
-- Get the domain, if it's not defined in the table assume it's | |||
-- in the item domain. | |||
item_info['items.domain'] = tonumber(tpl_args.domain) or class_to_domain[item_info['items.class_id']] or 1 | |||
-- Convert the mod domain number to understandable text: | |||
= | item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower'] | ||
= | -- Format item tags: | ||
tpl_args.item_tags = m_util.string.split_args( | |||
tpl_args.item_tags or item_info['items.tags'], | |||
{sep=',%s*'} | |||
) | |||
for _,v in ipairs(m_util.string.split_args(tpl_args.extra_item_tags, {sep=',%s*'})) do | |||
tpl_args.item_tags[#tpl_args.item_tags+1] = v | |||
end | |||
= | -- Format extra fields: | ||
local extra_fields = m_util.string.split_args(tpl_args.extra_fields, {sep=',%s*'}) | |||
]] | -- Get tags that are appended to special items: | ||
local tags_cfg = m_game.constants.item.classes[item_info['items.class_id']].tags or {} | |||
-- | -- Create drop down lists in these sections and query in these | ||
local | -- generation types. | ||
local section = { | |||
{ | |||
header = i18n.drop_down_table.prefix, | |||
where = function(tpl_args, value) | |||
local where = {} | |||
for _, item_tag in ipairs(tpl_args.item_tags) do | |||
where[#where+1] = string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
item_tag, | |||
1, | |||
item_info['items.domain'] | |||
) | |||
end | |||
return table.concat(where, ' OR ') | |||
end, | |||
}, | |||
{ | { | ||
header = i18n.drop_down_table.suffix, | |||
' | where = function(tpl_args, value) | ||
local where = {} | |||
for _, item_tag in ipairs(tpl_args.item_tags) do | |||
where[#where+1] = string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
item_tag, | |||
2, | |||
item_info['items.domain'] | |||
) | |||
end | |||
return table.concat(where, ' OR ') | |||
end, | |||
}, | }, | ||
{ | { | ||
tags = {tags_cfg.elder}, | |||
header = i18n.drop_down_table.elder_prefix, | |||
where = function(tpl_args, value) | |||
return string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
tags_cfg.elder, | |||
1, | |||
item_info['items.domain'] | |||
) | |||
end, | |||
}, | |||
{ | |||
tags = {tags_cfg.elder}, | |||
header = i18n.drop_down_table.elder_suffix, | |||
where = function(tpl_args, value) | |||
return string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
tags_cfg.elder, | |||
2, | |||
item_info['items.domain'] | |||
) | |||
end, | |||
}, | |||
{ | { | ||
header = i18n.drop_down_table. | tags = {tags_cfg.shaper}, | ||
where = function(tpl_args | header = i18n.drop_down_table.shaper_prefix, | ||
where = function(tpl_args, value) | |||
return string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
tags_cfg.shaper, | |||
1, | |||
item_info['items.domain'] | |||
) | |||
end, | end, | ||
}, | }, | ||
{ | { | ||
header = i18n.drop_down_table. | tags = {tags_cfg.shaper}, | ||
where = function(tpl_args, | header = i18n.drop_down_table.shaper_suffix, | ||
local where = {} | where = function(tpl_args, value) | ||
return string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
tags_cfg.shaper, | |||
2, | |||
item_info['items.domain'] | |||
) | |||
end, | |||
}, | |||
{ | |||
header = i18n.drop_down_table.delve_prefix, | |||
where = function(tpl_args, value) | |||
local where = {} | |||
for _, item_tag in ipairs(tpl_args.item_tags) do | for _, item_tag in ipairs(tpl_args.item_tags) do | ||
where[#where+1] = string.format( | where[#where+1] = string.format( | ||
Line 903: | Line 920: | ||
]], | ]], | ||
item_tag, | item_tag, | ||
1, | |||
16 | |||
) | ) | ||
end | end | ||
Line 912: | Line 929: | ||
}, | }, | ||
{ | { | ||
tags = {tags_cfg. | header = i18n.drop_down_table.delve_suffix, | ||
header = i18n.drop_down_table. | where = function(tpl_args, value) | ||
where = function(tpl_args | local where = {} | ||
return string.format( | for _, item_tag in ipairs(tpl_args.item_tags) do | ||
where[#where+1] = string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
item_tag, | |||
2, | |||
16 | |||
) | |||
end | |||
return table.concat(where, ' OR ') | |||
end, | |||
}, | |||
{ | |||
tags = {tags_cfg.crusader}, | |||
header = i18n.drop_down_table.crusader_prefix, | |||
where = function(tpl_args, value) | |||
return string.format( | |||
[[ | [[ | ||
(spawn_weights.tag="%s" | (spawn_weights.tag="%s" | ||
Line 922: | Line 960: | ||
AND mods.stat_text > '') | AND mods.stat_text > '') | ||
]], | ]], | ||
tags_cfg. | tags_cfg.crusader, | ||
1, | 1, | ||
item_info['items.domain'] | item_info['items.domain'] | ||
Line 929: | Line 967: | ||
}, | }, | ||
{ | { | ||
tags = {tags_cfg. | tags = {tags_cfg.crusader}, | ||
header = i18n.drop_down_table. | header = i18n.drop_down_table.crusader_suffix, | ||
where = function(tpl_args | where = function(tpl_args, value) | ||
return string.format( | return string.format( | ||
[[ | [[ | ||
Line 939: | Line 977: | ||
AND mods.stat_text > '') | AND mods.stat_text > '') | ||
]], | ]], | ||
tags_cfg. | tags_cfg.crusader, | ||
2, | 2, | ||
item_info['items.domain'] | item_info['items.domain'] | ||
Line 946: | Line 984: | ||
}, | }, | ||
{ | { | ||
tags = {tags_cfg. | tags = {tags_cfg.eyrie}, | ||
header = i18n.drop_down_table. | header = i18n.drop_down_table.eyrie_prefix, | ||
where = function(tpl_args | where = function(tpl_args, value) | ||
return string.format( | return string.format( | ||
[[ | [[ | ||
Line 956: | Line 994: | ||
AND mods.stat_text > '') | AND mods.stat_text > '') | ||
]], | ]], | ||
tags_cfg. | tags_cfg.eyrie, | ||
1, | 1, | ||
item_info['items.domain'] | item_info['items.domain'] | ||
Line 963: | Line 1,001: | ||
}, | }, | ||
{ | { | ||
tags = {tags_cfg. | tags = {tags_cfg.eyrie}, | ||
header = i18n.drop_down_table. | header = i18n.drop_down_table.eyrie_suffix, | ||
where = function(tpl_args | where = function(tpl_args, value) | ||
return string.format( | return string.format( | ||
[[ | [[ | ||
Line 973: | Line 1,011: | ||
AND mods.stat_text > '') | AND mods.stat_text > '') | ||
]], | ]], | ||
tags_cfg. | tags_cfg.eyrie, | ||
2, | 2, | ||
item_info['items.domain'] | item_info['items.domain'] | ||
Line 980: | Line 1,018: | ||
}, | }, | ||
{ | { | ||
header = i18n.drop_down_table. | tags = {tags_cfg.basilisk}, | ||
where = function(tpl_args | header = i18n.drop_down_table.basilisk_prefix, | ||
where = function(tpl_args, value) | |||
return string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
tags_cfg.basilisk, | |||
1, | |||
item_info['items.domain'] | |||
) | |||
end, | end, | ||
}, | }, | ||
{ | { | ||
header = i18n.drop_down_table. | tags = {tags_cfg.basilisk}, | ||
where = function(tpl_args | header = i18n.drop_down_table.basilisk_suffix, | ||
where = function(tpl_args, value) | |||
return string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
tags_cfg.basilisk, | |||
2, | |||
item_info['items.domain'] | |||
) | |||
end, | end, | ||
}, | }, | ||
{ | { | ||
tags = {tags_cfg. | tags = {tags_cfg.adjudicator}, | ||
header = i18n.drop_down_table. | header = i18n.drop_down_table.adjudicator_prefix, | ||
where = function(tpl_args | where = function(tpl_args, value) | ||
return string.format( | return string.format( | ||
[[ | [[ | ||
Line 1,032: | Line 1,062: | ||
AND mods.stat_text > '') | AND mods.stat_text > '') | ||
]], | ]], | ||
tags_cfg. | tags_cfg.adjudicator, | ||
1, | 1, | ||
item_info['items.domain'] | item_info['items.domain'] | ||
Line 1,039: | Line 1,069: | ||
}, | }, | ||
{ | { | ||
tags = {tags_cfg. | tags = {tags_cfg.adjudicator}, | ||
header = i18n.drop_down_table. | header = i18n.drop_down_table.adjudicator_suffix, | ||
where = function(tpl_args | where = function(tpl_args, value) | ||
return string.format( | return string.format( | ||
[[ | [[ | ||
Line 1,049: | Line 1,079: | ||
AND mods.stat_text > '') | AND mods.stat_text > '') | ||
]], | ]], | ||
tags_cfg. | tags_cfg.adjudicator, | ||
2, | 2, | ||
item_info['items.domain'] | item_info['items.domain'] | ||
Line 1,056: | Line 1,086: | ||
}, | }, | ||
{ | { | ||
header = i18n.drop_down_table.veiled_prefix, | |||
header = i18n.drop_down_table. | where = function(tpl_args, value) | ||
where = function(tpl_args | local where = {} | ||
for _, item_tag in ipairs(tpl_args.item_tags) do | |||
where[#where+1] = string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '' | |||
AND mods.id LIKE "%%Master%%") | |||
]], | |||
item_tag, | |||
1, | |||
9 | |||
) | |||
end | |||
return table.concat(where, ' OR ') | |||
end, | end, | ||
}, | }, | ||
{ | { | ||
header = i18n.drop_down_table.veiled_suffix, | |||
header = i18n.drop_down_table. | where = function(tpl_args, value) | ||
where = function(tpl_args | local where = {} | ||
for _, item_tag in ipairs(tpl_args.item_tags) do | |||
where[#where+1] = string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '' | |||
AND mods.id LIKE "%%Master%%") | |||
]], | |||
item_tag, | |||
2, | |||
9 | |||
) | |||
end | |||
return table.concat(where, ' OR ') | |||
end, | end, | ||
}, | }, | ||
{ | { | ||
header = i18n.drop_down_table.corrupted, | |||
header = i18n.drop_down_table. | where = function(tpl_args, value) | ||
where = function(tpl_args | local where = {} | ||
for _, item_tag in ipairs(tpl_args.item_tags) do | |||
where[#where+1] = string.format( | |||
[[ | |||
(spawn_weights.tag="%s" | |||
AND mods.generation_type=%s | |||
AND mods.domain=%s | |||
AND mods.stat_text > '') | |||
]], | |||
item_tag, | |||
5, | |||
) | item_info['items.domain'] | ||
) | |||
end | |||
return table.concat(where, ' OR ') | |||
end, | end, | ||
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events. | |||
is_implicit = true, | |||
}, | }, | ||
-- Reduce template expansion size by disabling enchantments, there are | |||
-- other good pages with enchantment lists: | |||
header = i18n.drop_down_table. | -- { | ||
where = function(tpl_args | -- header = i18n.drop_down_table.enchant, | ||
-- where = function(tpl_args, value) | |||
-- local where = {} | |||
-- for _, item_tag in ipairs(tpl_args.item_tags) do | |||
-- where[#where+1] = string.format( | |||
-- [[ | |||
-- (spawn_weights.tag="%s" | |||
-- AND mods.generation_type=%s | |||
-- AND mods.domain=%s | |||
-- AND mods.stat_text > '') | |||
-- ]], | |||
-- item_tag, | |||
-- 10, | |||
-- item_info['items.domain'] | |||
-- ) | |||
-- end | |||
-- return table.concat(where, ' OR ') | |||
-- end, | |||
-- is_implicit = true, | |||
-- }, | |||
} | |||
-- Save the original tag format: | |||
local item_tags_orig = {} | |||
for i,v in ipairs(tpl_args.item_tags) do | |||
item_tags_orig[i] = v | |||
end | |||
local item_mods = {} | |||
local mod_group_counter = {} | |||
mod_group_counter['all'] = {} | |||
local extra_fieldss = {} | |||
local table_index_base = -1 | |||
for _, sctn in ipairs(section) do | |||
item_mods[sctn['header']] = {} | |||
-- Preallocate the mod group counter, implicit and explicit mods | |||
-- are counted separetely because they can spawn together: | |||
mod_group_counter[sctn['header']] = {} | |||
local adj = 'explicit' | |||
if sctn['is_implicit'] then | |||
adj = 'implicit' | |||
end | |||
for _, header in ipairs({sctn['header'], 'all'}) do | |||
if mod_group_counter[header][adj] == nil then | |||
mod_group_counter[header][adj] = {} | |||
end | |||
end | |||
local continue = true | |||
local current_tags | |||
if sctn['tags'] then | |||
-- some item classes do not have shaper/elder items, so the table | |||
-- will not contain any tags: | |||
if #sctn['tags'] == 0 then | |||
continue = false | |||
else | |||
current_tags = sctn['tags'] | |||
end | |||
else | |||
current_tags = {} | |||
-- Reset to original tags: | |||
for i,v in ipairs(item_tags_orig) do | |||
current_tags[i] = v | |||
end | |||
end | |||
if continue then | |||
-- Cargo preparation: | |||
local q_fields = { | |||
'mods._pageName', | |||
'mods.name', | |||
'mods.id', | |||
'mods.required_level', | |||
'mods.generation_type', | |||
'mods.domain', | |||
'mods.mod_group', | |||
'mods.mod_type', | |||
'mods.stat_text', | |||
'mods.stat_text_raw', | |||
'mods.tags', | |||
'mod_stats.id', | |||
'spawn_weights.tag', | |||
'spawn_weights.weight', | |||
'spawn_weights.ordinal', | |||
'spawn_weights._pageName' | |||
end | } | ||
for i, v in ipairs(extra_fields) do | |||
q_fields[#q_fields+1] = v | |||
end | |||
-- Query mods and map the results to the pagename: | |||
local results = m_cargo.map_results_to_id{ | |||
results=m_cargo.query( | |||
{'mods', 'spawn_weights', 'mod_stats'}, | |||
q_fields, | |||
{ | |||
join = [[ | |||
mods._pageName=spawn_weights._pageName, | |||
mods._pageName=mod_stats._pageName | |||
]], | |||
where = sctn['where'](tpl_args, value), | |||
groupBy = [[ | |||
mods._pageName, | |||
spawn_weights.tag, | |||
spawn_weights.weight | |||
]], | |||
orderBy = [[ | |||
mods.generation_type, | |||
mods.mod_group, | |||
mods.mod_type, | |||
mods.required_level, | |||
mods._pageName, | |||
spawn_weights.ordinal | |||
]], | |||
} | |||
), | |||
field='mods._pageName', | |||
keep_id_field=true, | |||
append_id_field=true, | |||
} | |||
-- | if #results > 0 then | ||
-- Loop through all found modifiers: | |||
local last | |||
for _, id in ipairs(results) do | |||
-- 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 = results[id][j]['spawn_weights.tag'] | |||
local mod_tag_weight = tonumber( | |||
results[id][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 == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (results[id][j] == nil) | |||
tag_match_add = (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0) | |||
until tag_match_stop or y == #current_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(results[id]) do | |||
if vv['mod_stats.id']:find('.*local.*') ~= nil then | |||
mod_scope = 'Local' | |||
end | |||
end | |||
-- Save the matching modifier tag: | |||
local a = #item_mods[sctn['header']] | |||
item_mods[sctn['header']][a+1] = results[id][j] | |||
-- Save other interesting fields: | |||
item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope | |||
item_mods[sctn['header']][a+1]['spawn_weight.idx_match'] = j | |||
item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add | |||
item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop | |||
-- Count the mod groups: | |||
local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group' | |||
for _, header in ipairs({sctn['header'], 'all'}) do | |||
if mod_group_counter[header][adj][group] == nil then | |||
mod_group_counter[header][adj][group] = {} | |||
end | |||
local tp = results[id][j]['mods.mod_type'] | |||
local bef = mod_group_counter[header][adj][group][tp] or 0 | |||
mod_group_counter[header][adj][group][tp] = 1 + bef | |||
end | |||
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[sctn['header']] = h.get_spawn_chance{ | |||
tbl = item_mods[sctn['header']], | |||
chance_multiplier = sctn['chance_multiplier'] | |||
]], | |||
} | } | ||
end | |||
extra_fieldss[sctn['header']] = extra_fields | |||
end | |||
end | |||
end | |||
-- | |||
-- Display the item mods | |||
-- | |||
-- Introductory text: | |||
local out = {} | |||
out[#out+1] = string.format( | |||
'==%s== \n', | |||
tpl_args['header'] or table.concat(tpl_args.item_tags, ', ') | |||
) | |||
local expand_button = 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] = expand_button | |||
out[#out+1] = string.format('%s %s.<br><br><br>', | |||
i18n.drop_down_table.table_intro, | |||
h.item_link{ | |||
page=item_info['items._pageName'], | |||
name=item_info['items.name'], | |||
inventory_icon=item_info['items.inventory_icon'] or '', | |||
html=item_info['items.html'] or '', | |||
skip_query=true | |||
} | |||
) | |||
-- Loop through the sections: | |||
for _, sctn in ipairs(section) do | |||
local extra_fields = extra_fieldss[sctn['header']] | |||
local adj = 'explicit' | |||
if sctn['is_implicit'] then | |||
adj = 'implicit' | |||
end | |||
-- Create html container: | |||
local container = mw.html.create('div') | |||
:attr('style', 'vertical-align:top; display:inline-block;') | |||
-- Create the drop down table with <table></table>: | |||
local headers = container | |||
if #item_mods[sctn['header']] > 0 then | |||
headers | |||
:tag('h3') | |||
:wikitext(string.format('%s', sctn['header'])) | |||
:done() | |||
:done() | |||
end | |||
end | |||
local total_mod_groups = 0 | |||
for _ in pairs(mod_group_counter[sctn['header']][adj]) do | |||
total_mod_groups = 1+total_mod_groups | |||
end | |||
-- Loop through and add all matching mods to the <table>. | |||
local tbl, last_group, last_type, table_index | |||
for _, rows in ipairs(item_mods[sctn['header']]) 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 there's only one mod group | |||
-- then use mod type instead: | |||
if rows['mods.mod_group'] ~= last_group or (total_mod_groups == 1 and rows['mods.mod_type'] ~= last_type) 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[sctn['header']]) 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 for all explicit or implicit | |||
-- sections since a mod with the same mod group can't | |||
-- spawn. Doesn't matter if it's prefix or suffix. | |||
local number_of_mod_types = 0 | |||
for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do | |||
number_of_mod_types = 1 + number_of_mod_types | |||
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 there's only one | |||
-- mod group in the generation type then ignore it: | |||
local table_index_mod_group | |||
local tbl_caption | |||
if number_of_mod_types > 1 and total_mod_groups > 1 then | |||
table_index_mod_group = table.concat( | |||
{string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])}, | |||
'' | |||
) | |||
tbl_caption = string.format( | |||
'%s', | |||
m_util.html.poe_color( | |||
'stat', | |||
string.format( | |||
'%s %s', | |||
i18n.drop_down_table.mod_group, | |||
rows['mods.mod_group'] | |||
) | |||
) or '' | |||
) | |||
else | |||
tbl_caption = string.format( | |||
'%s (%s)', | |||
m_util.html.poe_color( | |||
'mod', | |||
h.header(rows['mods.stat_text_raw']) | |||
) or '', | |||
rows['mods.scope'] | |||
) | |||
end | |||
-- Create a table index for handling the collapsible: | |||
table_index_base = table_index_base+1 | |||
if table_index_mod_group ~= nil then | |||
table_index = table_index_mod_group | |||
else | |||
table_index = table_index_base | |||
end | |||
-- Add class and style to the <table>: | |||
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', table_index) | |||
) | |||
: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 | -- If the mod has no name then use the mod type: | ||
local mod_name = rows['mods.name'] or '' | |||
if mod_name == '' then | |||
mod_name = rows['mods.mod_type'] | |||
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] or '' | |||
) | |||
) | |||
:done() | |||
end | end | ||
end | |||
-- Style mods.tags: | |||
local mods_tags = table.concat( | |||
m_util.string.split_args(rows['mods.tags'], {sep=',%s*'}), | |||
', ' | |||
) | |||
if mods_tags ~= '' then | |||
mods_tags = m_util.html.tooltip('*', mods_tags) | |||
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', table_index) | |||
) | ) | ||
:tag('td') | |||
:attr('width', '160') | |||
' | :wikitext( | ||
string.format( | string.format( | ||
'%s %s', | ' [[%s|%s]]', | ||
rows['mods._pageName'], | |||
rows['mods. | 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%s', | |||
m_util.html.poe_color( | |||
'mod', | |||
rows['mods.stat_text'] | |||
:gsub('<br>', ', ') | |||
:gsub('<br />', ' ') | |||
) or '', | |||
mods_tags | |||
) | ) | ||
) | ) | ||
:done() | :done() | ||
:node(td) | |||
:done() | :done() | ||
end | :done() | ||
-- Save the last mod group for later comparison: | |||
last_group = rows['mods.mod_group'] | |||
last_type = rows['mods.mod_type'] | |||
end | |||
out[#out+1] = tostring(container) | |||
end | |||
-- Outro text: | |||
out[#out+1] = '<br>' | |||
out[#out+1] = m_util.html.poe_color( | |||
'normal', | |||
string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top) | |||
) | |||
return table.concat(out,'') | |||
end | |||
-- ---------------------------------------------------------------------------- | |||
-- Exported functions | |||
-- ---------------------------------------------------------------------------- | |||
local p = {} | |||
-- | |||
-- Template: Mod table | |||
-- | |||
p.mod_table = m_util.misc.invoker_factory(_mod_table, { | |||
parentFirst = true, | |||
}) | |||
-- | |||
-- Drop down list | |||
-- | |||
p.drop_down_table = m_util.misc.invoker_factory(_drop_down_table, { | |||
parentFirst = true, | |||
}) | |||
-- | |||
-- Debug | |||
-- | |||
p.debug = {} | p.debug = {} | ||
function p.debug.tbl_data(tbl) | function p.debug.tbl_data(tbl) | ||
Line 1,706: | Line 1,653: | ||
return table.concat(out, ', ') | return table.concat(out, ', ') | ||
end | end | ||
return p | return p |
Revision as of 23:32, 23 January 2022
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:Modifier table
--
-- Module responsible for displaying modifiers in various ways.
-------------------------------------------------------------------------------
require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Modifier table')
local m_game = mw.loadData('Module:Game')
-- Lazy loading
local f_item_link -- require('Module:Item link').item_link
-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Modifier table/config/sandbox') or mw.loadData('Module:Modifier table/config')
local i18n = cfg.i18n
-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------
local h = {}
-- Lazy loading for Module:Item link
function h.item_link(args)
if not f_item_link then
f_item_link = require('Module:Item link').item_link
end
return f_item_link(args)
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>'), ', ')
s = table.concat(m_util.string.split(s, '<br />'), ' ')
return s
end
function h.query_weights(table_name, page_ids)
return m_cargo.map_results_to_id{
results=m_cargo.query(
{'mods', table_name},
{
'mods._pageID',
table_name .. '.tag',
table_name .. '.weight'
},
{
where=page_ids,
join=string.format('mods._pageID=%s._pageID', table_name),
orderBy=string.format('mods.id ASC,%s.ordinal ASC', table_name),
}
),
field='mods._pageID',
}
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'] or '',
string.gsub(
v['mods.stat_text_raw'] or 'N/A',
'<br>',
', '
) or '',
v['mods._pageName'] or ''
)
end
return table.concat(str, '<br>')
end
h.tbl = {}
h.tbl.display = {}
h.tbl.display.factory = {}
function h.tbl.display.factory.value(args)
-- Format options for each field:
args.options = args.options or {}
-- Separator between fields:
args.delimiter = args.delimiter or ', '
return function(tpl_args, tr, data, fields)
local values = {}
local fmt_values = {}
for index, field in ipairs(fields) do
local value = {
min=data[field],
max=data[field],
base=data[field],
}
if value.min then
values[#values+1] = value.max
local opts = args.options[index] or {}
-- Global colour is set, no overrides.
if args.color ~= nil or opts.color == nil then
opts.no_color = true
end
fmt_values[#fmt_values+1] = m_util.html.format_value(nil, nil, value, opts)
end
end
if #values == 0 then
tr
:wikitext(m_util.html.td.na())
else
local td = tr:tag('td')
td
:attr('data-sort-value', table.concat(values, args.delimiter))
:wikitext(table.concat(fmt_values, args.delimiter))
if args.color then
td:attr('class', 'tc -' .. args.color)
end
end
end
end
function h.get_spawn_chance(args)
--[[
Calculates the spawn chance of a set of mods that all have a
spawn weight.
]]
-- Get the table:
local tbl = args['tbl']
-- Probabilities affecting the result besides the spawn weight:
local chance_multiplier = tonumber(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
-- ----------------------------------------------------------------------------
-- Additional configuration
-- ----------------------------------------------------------------------------
local mod_table = {}
mod_table.data = {
{
arg = 'name',
header = i18n.mod_table.name,
fields = {'mods._pageName', 'mods.id', 'mods.name'},
options = {
[3] = {
optional=true,
},
},
display = function(tpl_args, tr, data, fields)
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, tr, data, fields)
local k = 'mods.domain'
local i = tonumber(data[k])
if m_game.constants.mod.domains[i] == nil then
error('Undefined Modifier Domain ['..i..'] needs to be added to Module:Game')
end
data[k] = m_game.constants.mod.domains[i]['short_upper']
h.tbl.display.factory.value({})(tpl_args, tr, data, fields)
end,
order = 2000,
sort_type = 'text',
},
{
arg = 'generation_type',
header = i18n.mod_table.generation_type,
fields = {'mods.generation_type'},
display = function(tpl_args, tr, data, fields)
local k = 'mods.generation_type'
local i = tonumber(data[k])
if m_game.constants.mod.generation_types[i] == nil then
error('Undefined Modifier Generation Type ['..i..'] needs to be added to Module:Game')
end
data[k] = m_game.constants.mod.generation_types[i]['short_upper']
h.tbl.display.factory.value{}(tpl_args, tr, data, fields)
end,
order = 2001,
sort_type = 'text',
},
{
arg = {'group', 'mod_group'},
header = i18n.mod_table.mod_group,
fields = {'mods.mod_group'},
display = h.tbl.display.factory.value{},
order = 2002,
sort_type = 'text',
},
{
arg = {'mod_type'},
header = i18n.mod_table.mod_type,
fields = {'mods.mod_type'},
display = h.tbl.display.factory.value{},
order = 2003,
sort_type = 'text',
},
{
arg = {'level', 'required_level'},
header = i18n.mod_table.required_level,
fields = {'mods.required_level'},
display = h.tbl.display.factory.value{},
order = 2004,
},
{
arg = {'enchantment', 'labyrinth'},
header = i18n.mod_table.labyrinth,
fields = {string.format([[
CONCAT(
CASE mods.required_level
WHEN 32 THEN "%s"
WHEN 53 THEN "%s"
WHEN 66 THEN "%s"
WHEN 75 THEN "%s"
END
)=labyrinth_text]],
i18n.mod_table.normal_labyrinth,
i18n.mod_table.cruel_labyrinth,
i18n.mod_table.merciless_labyrinth,
i18n.mod_table.eternal_labyrinth)
},
display = h.tbl.display.factory.value{},
order = 2005,
sort_type = 'text',
},
{
arg = {'stat_text'},
header = i18n.mod_table.stat_text,
fields = {'mods.stat_text'},
display = function(tpl_args, tr, data, fields)
local value
-- 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
value = table.concat(out, '<br>')
else
value = data['mods.stat_text']
end
data['mods.stat_text'] = value
h.tbl.display.factory.value{color='mod'}(tpl_args, tr, data, fields)
end,
order = 3000,
sort_type = 'text',
},
{
arg = 'buff',
header = i18n.mod_table.buff,
fields = {'mods.granted_buff_id', 'mods.granted_buff_value'},
display = h.tbl.display.factory.value{delimiter=' '},
order = 4000,
sort_type = 'text',
},
{
arg = {'skill', 'granted_skill'},
header = i18n.mod_table.granted_skill,
fields = {'mods.granted_skill'},
display = h.tbl.display.factory.value{},
order = 4001,
sort_type = 'text',
},
{
arg = {'tags'},
header = i18n.mod_table.tags,
fields = {'mods.tags'},
display = function(tpl_args, tr, data, fields)
local k = 'mods.tags'
data[k] = table.concat(m_util.string.split(data[k], ',%s*'), ', ')
h.tbl.display.factory.value{}(tpl_args, tr, data, fields)
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'}
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _mod_table(tpl_args)
--[[
Creates a generic table for modifiers.
Examples
--------
= p.mod_table{
q_tables='spawn_weights',
q_join='mods._pageID=spawn_weights._pageID',
q_where='mods.generation_type = 10 AND spawn_weights.tag = "boots" AND spawn_weights.weight > 0',
q_orderBy='mods.id, mods.required_level',
q_limit=100,
stat_text=1,
enchantment=1,
}
]]
-- default to enabled
tpl_args.name = tpl_args.name or true
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 required and extra tables:
local tables = {'mods'}
for _, v in ipairs(m_util.string.split_args(tpl_args.q_tables, {',%s*'})) do
tables[#tables+1] = v
end
-- Set required and extra fields:
local fields = {
'mods._pageID',
}
for _, row_info in ipairs(row_infos) do
if type(row_info.fields) == 'function' then
row_info.fields = row_info.fields()
end
for index, field in ipairs(row_info.fields) do
row_info.options[index] = row_info.options[index] or {}
fields[#fields+1] = field
end
end
tpl_args._extra_fields = m_util.string.split_args(tpl_args.q_fields, {',%s*'})
for _, v in ipairs(tpl_args._extra_fields) do
fields[#fields+1] = v
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 = m_cargo.query(tables, 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
stats = m_cargo.map_results_to_id{
results=m_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',
}
),
field='mods._pageID'
}
-- 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
--
-- Display
--
-- Preformance optimization
for index, field in ipairs(tpl_args._extra_fields) do
field = m_util.string.split(field, '%s*=%s*')
-- field[2] will be nil if there is no alias
tpl_args._extra_fields[index] = field[2] or field[1]
end
local tbl = mw.html.create('table')
tbl:attr('class', 'wikitable sortable modifier-table')
-- Header
local tr = tbl:tag('tr')
local display_fields = {}
for i, row_info in ipairs(row_infos) do
for j, field in ipairs(row_info.fields) do
-- Aliased name is used as keys in the results:
field = m_util.string.split(field, '%s*=%s*')
field = field[2] or field[1]
-- Make a new field table since mod_table.data will remain
-- modified the 2nd time a function is run and then crash.
if j == 1 then
display_fields[i] = {}
end
display_fields[i][j] = field
end
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
for _, field in ipairs(tpl_args._extra_fields) do
tr
:tag('th')
:wikitext(field)
end
-- Body
for _, row in ipairs(results) do
tr = tbl:tag('tr')
for i, row_info in ipairs(row_infos) do
-- this has been cast from a function in an earlier step
local display = true
for j, field in ipairs(display_fields[i]) do
-- this will bet set to an empty value not nil confusingly
if row[field] == nil or row[field] == '' then
if row_info.options[j].optional ~= true then
display = false
break
else
row[field] = nil
end
end
end
if display then
row_info.display(tpl_args, tr, row, display_fields[i])
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
if wrow[key .. '.tag'] and wrow[key .. '.weight'] then
weight_out[#weight_out+1] = string.format(
'%s %s',
wrow[key .. '.tag'],
wrow[key .. '.weight']
)
end
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
for _, field in ipairs(tpl_args._extra_fields) do
if row[field] then
tr
:tag('td')
:wikitext(row[field])
else
tr:wikitext(m_util.html.td.na())
end
end
end
return tostring(tbl)
end
local function _drop_down_table(tpl_args)
--[[
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:
* Add support to:
* Forsaken masters
* Bestiary
* 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 [Collapsible, default=Expanded]
# to Damage [Collapsible, default=Collapsed]
3 to Damage
5 to Damage
* Add a where condition that somehow filters out mods that obviously
wont match with the item. spawn_weights.weight>0 isn't enough due
to possible edge cases.
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'}
Boots
= p.drop_down_table{item='Iron Greaves', header='Boots'}
= p.drop_down_table{
item='Fishing Rod',
header='FISH PLEASE',
item_tags='fishing_rod',
extra_fields='mods.tags'
}
= 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',
}
]]
-- Format the cargo query:
local where
for _,v in ipairs({
{tpl_args.page, 'items._pageName = "%s"'},
{tpl_args.item, 'items.name = "%s"'},
}) do
if v[1] ~= nil then
where = string.format(v[2], v[1])
break
end
end
local item_info = m_cargo.query(
{'items'},
{
'items.name',
'items.tags',
'items.class_id',
'items.inventory_icon',
'items.html',
'items.release_version',
'items._pageName'
},
{
where=where,
groupBy='items._pageName',
orderBy='items.name, items.release_version DESC',
}
)[1]
-- Set the item class as key and the corresponding mod domain as
-- value:
local value
local class_to_domain = {
['LifeFlask']=2,
['ManaFlask']=2,
['HybridFlask']=2,
['UtilityFlask']=2,
['UtilityFlaskCritical']=2,
['Map']=5,
['Jewel']=10,
['Leaguestone']=12,
['AbyssJewel']=13,
}
-- Get the domain, if it's not defined in the table assume it's
-- in the item domain.
item_info['items.domain'] = tonumber(tpl_args.domain) or class_to_domain[item_info['items.class_id']] or 1
-- Convert the mod domain number to understandable text:
item_info['items.domain_text'] = m_game.constants.mod.domains[item_info['items.domain']]['short_lower']
-- Format item tags:
tpl_args.item_tags = m_util.string.split_args(
tpl_args.item_tags or item_info['items.tags'],
{sep=',%s*'}
)
for _,v in ipairs(m_util.string.split_args(tpl_args.extra_item_tags, {sep=',%s*'})) do
tpl_args.item_tags[#tpl_args.item_tags+1] = v
end
-- Format extra fields:
local extra_fields = m_util.string.split_args(tpl_args.extra_fields, {sep=',%s*'})
-- Get tags that are appended to special items:
local tags_cfg = m_game.constants.item.classes[item_info['items.class_id']].tags or {}
-- Create drop down lists in these sections and query in these
-- generation types.
local section = {
{
header = i18n.drop_down_table.prefix,
where = function(tpl_args, value)
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
where[#where+1] = string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
item_tag,
1,
item_info['items.domain']
)
end
return table.concat(where, ' OR ')
end,
},
{
header = i18n.drop_down_table.suffix,
where = function(tpl_args, value)
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
where[#where+1] = string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
item_tag,
2,
item_info['items.domain']
)
end
return table.concat(where, ' OR ')
end,
},
{
tags = {tags_cfg.elder},
header = i18n.drop_down_table.elder_prefix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.elder,
1,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.elder},
header = i18n.drop_down_table.elder_suffix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.elder,
2,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.shaper},
header = i18n.drop_down_table.shaper_prefix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.shaper,
1,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.shaper},
header = i18n.drop_down_table.shaper_suffix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.shaper,
2,
item_info['items.domain']
)
end,
},
{
header = i18n.drop_down_table.delve_prefix,
where = function(tpl_args, value)
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
where[#where+1] = string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
item_tag,
1,
16
)
end
return table.concat(where, ' OR ')
end,
},
{
header = i18n.drop_down_table.delve_suffix,
where = function(tpl_args, value)
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
where[#where+1] = string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
item_tag,
2,
16
)
end
return table.concat(where, ' OR ')
end,
},
{
tags = {tags_cfg.crusader},
header = i18n.drop_down_table.crusader_prefix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.crusader,
1,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.crusader},
header = i18n.drop_down_table.crusader_suffix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.crusader,
2,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.eyrie},
header = i18n.drop_down_table.eyrie_prefix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.eyrie,
1,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.eyrie},
header = i18n.drop_down_table.eyrie_suffix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.eyrie,
2,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.basilisk},
header = i18n.drop_down_table.basilisk_prefix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.basilisk,
1,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.basilisk},
header = i18n.drop_down_table.basilisk_suffix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.basilisk,
2,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.adjudicator},
header = i18n.drop_down_table.adjudicator_prefix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.adjudicator,
1,
item_info['items.domain']
)
end,
},
{
tags = {tags_cfg.adjudicator},
header = i18n.drop_down_table.adjudicator_suffix,
where = function(tpl_args, value)
return string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
tags_cfg.adjudicator,
2,
item_info['items.domain']
)
end,
},
{
header = i18n.drop_down_table.veiled_prefix,
where = function(tpl_args, value)
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
where[#where+1] = string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > ''
AND mods.id LIKE "%%Master%%")
]],
item_tag,
1,
9
)
end
return table.concat(where, ' OR ')
end,
},
{
header = i18n.drop_down_table.veiled_suffix,
where = function(tpl_args, value)
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
where[#where+1] = string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > ''
AND mods.id LIKE "%%Master%%")
]],
item_tag,
2,
9
)
end
return table.concat(where, ' OR ')
end,
},
{
header = i18n.drop_down_table.corrupted,
where = function(tpl_args, value)
local where = {}
for _, item_tag in ipairs(tpl_args.item_tags) do
where[#where+1] = string.format(
[[
(spawn_weights.tag="%s"
AND mods.generation_type=%s
AND mods.domain=%s
AND mods.stat_text > '')
]],
item_tag,
5,
item_info['items.domain']
)
end
return table.concat(where, ' OR ')
end,
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
is_implicit = true,
},
-- Reduce template expansion size by disabling enchantments, there are
-- other good pages with enchantment lists:
-- {
-- header = i18n.drop_down_table.enchant,
-- where = function(tpl_args, value)
-- local where = {}
-- for _, item_tag in ipairs(tpl_args.item_tags) do
-- where[#where+1] = string.format(
-- [[
-- (spawn_weights.tag="%s"
-- AND mods.generation_type=%s
-- AND mods.domain=%s
-- AND mods.stat_text > '')
-- ]],
-- item_tag,
-- 10,
-- item_info['items.domain']
-- )
-- end
-- return table.concat(where, ' OR ')
-- end,
-- is_implicit = true,
-- },
}
-- Save the original tag format:
local item_tags_orig = {}
for i,v in ipairs(tpl_args.item_tags) do
item_tags_orig[i] = v
end
local item_mods = {}
local mod_group_counter = {}
mod_group_counter['all'] = {}
local extra_fieldss = {}
local table_index_base = -1
for _, sctn in ipairs(section) do
item_mods[sctn['header']] = {}
-- Preallocate the mod group counter, implicit and explicit mods
-- are counted separetely because they can spawn together:
mod_group_counter[sctn['header']] = {}
local adj = 'explicit'
if sctn['is_implicit'] then
adj = 'implicit'
end
for _, header in ipairs({sctn['header'], 'all'}) do
if mod_group_counter[header][adj] == nil then
mod_group_counter[header][adj] = {}
end
end
local continue = true
local current_tags
if sctn['tags'] then
-- some item classes do not have shaper/elder items, so the table
-- will not contain any tags:
if #sctn['tags'] == 0 then
continue = false
else
current_tags = sctn['tags']
end
else
current_tags = {}
-- Reset to original tags:
for i,v in ipairs(item_tags_orig) do
current_tags[i] = v
end
end
if continue then
-- Cargo preparation:
local q_fields = {
'mods._pageName',
'mods.name',
'mods.id',
'mods.required_level',
'mods.generation_type',
'mods.domain',
'mods.mod_group',
'mods.mod_type',
'mods.stat_text',
'mods.stat_text_raw',
'mods.tags',
'mod_stats.id',
'spawn_weights.tag',
'spawn_weights.weight',
'spawn_weights.ordinal',
'spawn_weights._pageName'
}
for i, v in ipairs(extra_fields) do
q_fields[#q_fields+1] = v
end
-- Query mods and map the results to the pagename:
local results = m_cargo.map_results_to_id{
results=m_cargo.query(
{'mods', 'spawn_weights', 'mod_stats'},
q_fields,
{
join = [[
mods._pageName=spawn_weights._pageName,
mods._pageName=mod_stats._pageName
]],
where = sctn['where'](tpl_args, value),
groupBy = [[
mods._pageName,
spawn_weights.tag,
spawn_weights.weight
]],
orderBy = [[
mods.generation_type,
mods.mod_group,
mods.mod_type,
mods.required_level,
mods._pageName,
spawn_weights.ordinal
]],
}
),
field='mods._pageName',
keep_id_field=true,
append_id_field=true,
}
if #results > 0 then
-- Loop through all found modifiers:
local last
for _, id in ipairs(results) do
-- 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 = results[id][j]['spawn_weights.tag']
local mod_tag_weight = tonumber(
results[id][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 == current_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (results[id][j] == nil)
tag_match_add = (mod_tag == current_tags[y]) and ((mod_tag_weight or -1) > 0)
until tag_match_stop or y == #current_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(results[id]) do
if vv['mod_stats.id']:find('.*local.*') ~= nil then
mod_scope = 'Local'
end
end
-- Save the matching modifier tag:
local a = #item_mods[sctn['header']]
item_mods[sctn['header']][a+1] = results[id][j]
-- Save other interesting fields:
item_mods[sctn['header']][a+1]['mods.scope'] = mod_scope
item_mods[sctn['header']][a+1]['spawn_weight.idx_match'] = j
item_mods[sctn['header']][a+1]['mods.add'] = tag_match_add
item_mods[sctn['header']][a+1]['mods.stop'] = tag_match_stop
-- Count the mod groups:
local group = item_mods[sctn['header']][a+1]['mods.mod_group'] or 'nil_group'
for _, header in ipairs({sctn['header'], 'all'}) do
if mod_group_counter[header][adj][group] == nil then
mod_group_counter[header][adj][group] = {}
end
local tp = results[id][j]['mods.mod_type']
local bef = mod_group_counter[header][adj][group][tp] or 0
mod_group_counter[header][adj][group][tp] = 1 + bef
end
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[sctn['header']] = h.get_spawn_chance{
tbl = item_mods[sctn['header']],
chance_multiplier = sctn['chance_multiplier']
}
end
extra_fieldss[sctn['header']] = extra_fields
end
end
end
--
-- Display the item mods
--
-- Introductory text:
local out = {}
out[#out+1] = string.format(
'==%s== \n',
tpl_args['header'] or table.concat(tpl_args.item_tags, ', ')
)
local expand_button = 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] = expand_button
out[#out+1] = string.format('%s %s.<br><br><br>',
i18n.drop_down_table.table_intro,
h.item_link{
page=item_info['items._pageName'],
name=item_info['items.name'],
inventory_icon=item_info['items.inventory_icon'] or '',
html=item_info['items.html'] or '',
skip_query=true
}
)
-- Loop through the sections:
for _, sctn in ipairs(section) do
local extra_fields = extra_fieldss[sctn['header']]
local adj = 'explicit'
if sctn['is_implicit'] then
adj = 'implicit'
end
-- Create html container:
local container = mw.html.create('div')
:attr('style', 'vertical-align:top; display:inline-block;')
-- Create the drop down table with <table></table>:
local headers = container
if #item_mods[sctn['header']] > 0 then
headers
:tag('h3')
:wikitext(string.format('%s', sctn['header']))
:done()
:done()
end
local total_mod_groups = 0
for _ in pairs(mod_group_counter[sctn['header']][adj]) do
total_mod_groups = 1+total_mod_groups
end
-- Loop through and add all matching mods to the <table>.
local tbl, last_group, last_type, table_index
for _, rows in ipairs(item_mods[sctn['header']]) 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 there's only one mod group
-- then use mod type instead:
if rows['mods.mod_group'] ~= last_group or (total_mod_groups == 1 and rows['mods.mod_type'] ~= last_type) 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[sctn['header']]) 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 for all explicit or implicit
-- sections since a mod with the same mod group can't
-- spawn. Doesn't matter if it's prefix or suffix.
local number_of_mod_types = 0
for _ in pairs(mod_group_counter['all'][adj][rows['mods.mod_group']]) do
number_of_mod_types = 1 + number_of_mod_types
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 there's only one
-- mod group in the generation type then ignore it:
local table_index_mod_group
local tbl_caption
if number_of_mod_types > 1 and total_mod_groups > 1 then
table_index_mod_group = table.concat(
{string.byte(rows['mods.mod_group'], 1, #rows['mods.mod_group'])},
''
)
tbl_caption = string.format(
'%s',
m_util.html.poe_color(
'stat',
string.format(
'%s %s',
i18n.drop_down_table.mod_group,
rows['mods.mod_group']
)
) or ''
)
else
tbl_caption = string.format(
'%s (%s)',
m_util.html.poe_color(
'mod',
h.header(rows['mods.stat_text_raw'])
) or '',
rows['mods.scope']
)
end
-- Create a table index for handling the collapsible:
table_index_base = table_index_base+1
if table_index_mod_group ~= nil then
table_index = table_index_mod_group
else
table_index = table_index_base
end
-- Add class and style to the <table>:
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', table_index)
)
: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 type:
local mod_name = rows['mods.name'] or ''
if mod_name == '' then
mod_name = rows['mods.mod_type']
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] or ''
)
)
:done()
end
end
-- Style mods.tags:
local mods_tags = table.concat(
m_util.string.split_args(rows['mods.tags'], {sep=',%s*'}),
', '
)
if mods_tags ~= '' then
mods_tags = m_util.html.tooltip('*', mods_tags)
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', table_index)
)
: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%s',
m_util.html.poe_color(
'mod',
rows['mods.stat_text']
:gsub('<br>', ', ')
:gsub('<br />', ' ')
) or '',
mods_tags
)
)
:done()
:node(td)
:done()
:done()
-- Save the last mod group for later comparison:
last_group = rows['mods.mod_group']
last_type = rows['mods.mod_type']
end
out[#out+1] = tostring(container)
end
-- Outro text:
out[#out+1] = '<br>'
out[#out+1] = m_util.html.poe_color(
'normal',
string.format('[[#Top|%s]]', i18n.drop_down_table.back_to_top)
)
return table.concat(out,'')
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
--
-- Template: Mod table
--
p.mod_table = m_util.misc.invoker_factory(_mod_table, {
parentFirst = true,
})
--
-- Drop down list
--
p.drop_down_table = m_util.misc.invoker_factory(_drop_down_table, {
parentFirst = true,
})
--
-- Debug
--
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