Path of Exile Wiki

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

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

READ MORE

Path of Exile Wiki
(Fixed due to item_link moving)
m (tab -> 4 spaces)
Line 847: Line 847:
   
 
function p.item_sell_price(frame)
 
function p.item_sell_price(frame)
-- Query and sum the vendor prices for an item.
+
-- Query and sum the vendor prices for an item.
-- Unidentified items won't currently show the correct vendor price. Not sure how that is specified, nor is it used at all.
+
-- Unidentified items won't currently show the correct vendor price. Not sure how that is specified, nor is it used at all.
-- Expanding {{il}} seems to give a nil /n the first time a new command is run. Doesn't always happen.
+
-- Expanding {{il}} seems to give a nil /n the first time a new command is run. Doesn't always happen.
  +
 
-- = p.item_sell_price{page="Voideye"}
+
-- = p.item_sell_price{page="Voideye"}
-- = p.item_sell_price{page="Pyre"}
+
-- = p.item_sell_price{page="Pyre"}
-- = p.item_sell_price{page="Vessel of Vinktar (Lightning Penetration)"}
+
-- = p.item_sell_price{page="Vessel of Vinktar (Lightning Penetration)"}
  +
 
-- Args
+
-- Args
local g_args = getArgs(frame, {
+
local g_args = getArgs(frame, {
parentFirst = true
+
parentFirst = true
})
+
})
local g_frame = util.misc.get_frame(frame)
+
local g_frame = util.misc.get_frame(frame)
   
-- Only the explicit modifiers are counted when vendors calculates the price.
+
-- Only the explicit modifiers are counted when vendors calculates the price.
local condition = string.format('[[%s]]', g_args['page'])
+
local condition = string.format('[[%s]]', g_args['page'])
local query_item_mods = {
+
local query_item_mods = {
condition,
+
condition,
'?Has explicit mod ids',
+
'?Has explicit mod ids',
'?Has rarity',
+
'?Has rarity',
  +
}
}
 
local results_query_item_mods = util.smw.query(query_item_mods, g_frame)
+
local results_query_item_mods = util.smw.query(query_item_mods, g_frame)
local item_mods = util.string.split(results_query_item_mods[1]['Has explicit mod ids'], '<MANY>')
+
local item_mods = util.string.split(results_query_item_mods[1]['Has explicit mod ids'], '<MANY>')
  +
 
-- If the item has a Normal rarity then the sell price would be a fixed price.
+
-- If the item has a Normal rarity then the sell price would be a fixed price.
if results_query_item_mods[1]['Has rarity'] == 'Normal' then
+
if results_query_item_mods[1]['Has rarity'] == 'Normal' then
local amount_normal = 1
+
local amount_normal = 1
local currency_normal = 'Scroll Fragment'
+
local currency_normal = 'Scroll Fragment'
return string.format('%s %s', amount_normal, g_frame:expandTemplate{title = 'il', args = {currency_normal, currency_normal .. 's'}})
+
return string.format('%s %s', amount_normal, g_frame:expandTemplate{title = 'il', args = {currency_normal, currency_normal .. 's'}})
  +
 
-- return string.format('%s %s', amount_normal, currency_normal)
+
-- return string.format('%s %s', amount_normal, currency_normal)
end
+
end
  +
 
local mods_sell_price = {}
+
local mods_sell_price = {}
for _, modid in ipairs(item_mods) do
+
for _, modid in ipairs(item_mods) do
local query_mod_page = {
+
local query_mod_page = {
string.format('[[Is mod::%s]]', modid),
+
string.format('[[Is mod::%s]]', modid),
  +
}
}
 
local mod_page = util.smw.query(query_mod_page, g_frame)
+
local mod_page = util.smw.query(query_mod_page, g_frame)
  +
 
local query_mod_sell_price = {
+
local query_mod_sell_price = {
string.format('[[-Has subobject::%s]]', mod_page[1][1]),
+
string.format('[[-Has subobject::%s]]', mod_page[1][1]),
'?Has sell price amount#',
+
'?Has sell price amount#',
'?Has sell price item name',
+
'?Has sell price item name',
  +
}
}
 
local results = util.smw.query(query_mod_sell_price, g_frame)
+
local results = util.smw.query(query_mod_sell_price, g_frame)
  +
 
for _, k in ipairs(results) do
+
for _, k in ipairs(results) do
if k['Has sell price amount'] ~= '' then
+
if k['Has sell price amount'] ~= '' then
if mods_sell_price[k['Has sell price item name']] == nil then
+
if mods_sell_price[k['Has sell price item name']] == nil then
mods_sell_price[k['Has sell price item name']] = k['Has sell price amount']
+
mods_sell_price[k['Has sell price item name']] = k['Has sell price amount']
  +
else
else
 
mods_sell_price[k['Has sell price item name']] = k['Has sell price amount'] + mods_sell_price[k['Has sell price item name']]
+
mods_sell_price[k['Has sell price item name']] = k['Has sell price amount'] + mods_sell_price[k['Has sell price item name']]
  +
end
end
 
  +
end
end
 
end
+
end
end
+
end
  +
 
local out = {}
+
local out = {}
for currency, amount in pairs(mods_sell_price) do
+
for currency, amount in pairs(mods_sell_price) do
out[#out+1] = string.format('%s %s', amount, g_frame:expandTemplate{title = 'il', args = {currency, currency .. 's'}})
+
out[#out+1] = string.format('%s %s', amount, g_frame:expandTemplate{title = 'il', args = {currency, currency .. 's'}})
end
+
end
  +
 
return table.concat(out, ', ')
+
return table.concat(out, ', ')
end
+
end
  +
 
   
 
function p.find_mod_domain(input)
 
function p.find_mod_domain(input)
-- Find the mod domain based on the item class.
+
-- Find the mod domain based on the item class.
  +
 
local out = input
+
local out = input
local mod_domains = game.constants.mod.domains
+
local mod_domains = game.constants.mod.domains
  +
 
for i,_ in ipairs(out) do
+
for i,_ in ipairs(out) do
out[i]['Has mod domain'] = 1
+
out[i]['Has mod domain'] = 1
for j, row in pairs(mod_domains) do
+
for j, row in pairs(mod_domains) do
if out[i]['Has item class']:gsub('Map', 'Area'):match(mod_domains[j]['short_upper']) then -- This may need updating if an area item class doesn't have 'Map' in the string, or if the mod domain descriptions doesn't match the item class.
+
if out[i]['Has item class']:gsub('Map', 'Area'):match(mod_domains[j]['short_upper']) then -- This may need updating if an area item class doesn't have 'Map' in the string, or if the mod domain descriptions doesn't match the item class.
out[i]['Has mod domain'] = j
+
out[i]['Has mod domain'] = j
  +
end
end
 
end
+
end
  +
 
out[i]['Has mod domain text'] = mod_domains[out[i]['Has mod domain']]['short_lower']
+
out[i]['Has mod domain text'] = mod_domains[out[i]['Has mod domain']]['short_lower']
end
+
end
   
return out
+
return out
 
end
 
end
   
 
function p.get_item_tags(frame)
 
function p.get_item_tags(frame)
-- This function queries for the tags of a specific item.
+
-- This function queries for the tags of a specific item.
  +
 
-- Args
+
-- Args
local g_args = getArgs(frame, {
+
local g_args = getArgs(frame, {
parentFirst = true
+
parentFirst = true
})
+
})
local g_frame = util.misc.get_frame(frame)
+
local g_frame = util.misc.get_frame(frame)
  +
 
local item_name = g_args[1]
+
local item_name = g_args[1]
  +
 
local query = {
+
local query = {
string.format('[[Has name::%s]]', item_name),
+
string.format('[[Has name::%s]]', item_name),
'?Has tags',
+
'?Has tags',
'?Has base strength requirement',
+
'?Has base strength requirement',
'?Has base intelligence requirement',
+
'?Has base intelligence requirement',
'?Has base dexterity requirement',
+
'?Has base dexterity requirement',
'?Has item class'
+
'?Has item class'
  +
}
}
 
local results = util.smw.query(query, g_frame)
+
local results = util.smw.query(query, g_frame)
  +
 
for i,_ in ipairs(results) do
+
for i,_ in ipairs(results) do
results[i]['Has tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ') -- Remove unnecessary symbols.
+
results[i]['Has tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ') -- Remove unnecessary symbols.
end
+
end
  +
 
results = p.find_mod_domain(results)
+
results = p.find_mod_domain(results)
  +
 
return results
+
return results
 
end
 
end
   
 
function p.header(str)
 
function p.header(str)
-- This function replace specific numbers with a generic #.
+
-- This function replace specific numbers with a generic #.
  +
 
local s = table.concat(util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
+
local s = table.concat(util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
+
s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
s = table.concat(util.string.split(s, '<br>'), ', ')
+
s = table.concat(util.string.split(s, '<br>'), ', ')
 
 
 
return s
 
return s
Line 975: Line 975:
   
 
function p.get_spawn_chance(frame)
 
function p.get_spawn_chance(frame)
-- Calculates the spawn chance of a set of mods that all have a spawn weight.
+
-- Calculates the spawn chance of a set of mods that all have a spawn weight.
  +
 
-- Args
+
-- Args
local g_args = getArgs(frame, {
+
local g_args = getArgs(frame, {
parentFirst = true
+
parentFirst = true
})
+
})
local g_frame = util.misc.get_frame(frame)
+
local g_frame = util.misc.get_frame(frame)
  +
 
local tbl = g_args['tbl']
+
local tbl = g_args['tbl']
local chance_multiplier = tonumber(g_args['chance_multiplier']) or 1 -- Probabillities affecting the result besides the spawn weight.
+
local chance_multiplier = tonumber(g_args['chance_multiplier']) or 1 -- Probabillities affecting the result besides the spawn weight.
  +
 
local N = 0
+
local N = 0
for i,_ in ipairs(tbl) do
+
for i,_ in ipairs(tbl) do
N = N + tbl[i]['Has spawn weight'] -- Total number of outcomes.
+
N = N + tbl[i]['Has spawn weight'] -- Total number of outcomes.
end
+
end
  +
 
for i,_ in ipairs(tbl) do
+
for i,_ in ipairs(tbl) do
local n = tbl[i]['Has spawn weight'] -- Number of ways it can happen.
+
local n = tbl[i]['Has spawn weight'] -- Number of ways it can happen.
  +
 
tbl[i]['Has spawn chance'] = string.format("%0.2f%%", n/N * chance_multiplier*100) -- Truncated value.
+
tbl[i]['Has spawn chance'] = string.format("%0.2f%%", n/N * chance_multiplier*100) -- Truncated value.
end
+
end
  +
 
return tbl
+
return tbl
 
end
 
end
  +
 
 
function p.drop_down_table(frame)
 
function p.drop_down_table(frame)
-- This function queries mods in concept pages, queries the subobjects in each mod page for the mod tags. Then compares the tags on the item with the mod tags. If there's a match and the spawn weighting is larger than zero, then that mod is added to a drop down list.
+
-- This function queries mods in concept pages, queries the subobjects in each mod page for the mod tags. Then compares the tags on the item with the mod tags. If there's a match and the spawn weighting is larger than zero, then that mod is added to a drop down list.
  +
 
-- Misses forsaken masters currently.
+
-- Misses forsaken masters currently.
-- Add a proper expand/collapse toggle for the entire header row so it reacts together with mw-collapsible.
+
-- 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:
+
-- Show Mod group in a better way perhaps:
-- Mod group (expanded)
+
-- Mod group (expanded)
-- # to Damage (Collapsed)
+
-- # to Damage (Collapsed)
-- 3 to Damage
+
-- 3 to Damage
-- 5 to Damage
+
-- 5 to Damage
-- Add a better solution for properties inside subobjects, for extra_properties. Note that properties already queried for should not be added with this solution.
+
-- Add a better solution for properties inside subobjects, for extra_properties. Note that properties already queried for should not be added with this solution.
  +
 
-- Weapons
+
-- Weapons
-- p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
+
-- p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
-- p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
+
-- p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}
   
-- Accessories
+
-- Accessories
-- p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
+
-- p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
  +
 
-- Jewels
+
-- Jewels
-- p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
+
-- p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
  +
 
-- Armour
+
-- Armour
-- p.drop_down_table{item = 'Plate Vest', header = 'Armour body armours'}
+
-- p.drop_down_table{item = 'Plate Vest', header = 'Armour body armours'}
-- p.drop_down_table{item = 'Iron Greaves', header = 'Armour boots'}
+
-- p.drop_down_table{item = 'Iron Greaves', header = 'Armour boots'}
-- p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour gloves'}
+
-- p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour gloves'}
-- p.drop_down_table{item = 'Iron Hat', header = 'Armour helmets'}
+
-- p.drop_down_table{item = 'Iron Hat', header = 'Armour helmets'}
-- p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour shields'}
+
-- p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour shields'}
  +
 
-- p.drop_down_table{item = 'Fishing Rod', header = 'FISH PLEASE', item_tags = 'fishing_rod', extra_properties = 'Has spawn weight, Has spawn chance'}
+
-- p.drop_down_table{item = 'Fishing Rod', header = 'FISH PLEASE', item_tags = 'fishing_rod', extra_properties = 'Has spawn weight, Has spawn chance'}
   
   
-- Args
+
-- Args
local g_args = getArgs(frame, {
+
local g_args = getArgs(frame, {
parentFirst = true
+
parentFirst = true
})
+
})
local g_frame = util.misc.get_frame(frame)
+
local g_frame = util.misc.get_frame(frame)
 
 
local get_item_tags = p.get_item_tags{g_args.item}[1]
+
local get_item_tags = p.get_item_tags{g_args.item}[1]
local item_tags = {}
+
local item_tags = {}
if g_args.item_tags ~= nil then
+
if g_args.item_tags ~= nil then
item_tags = util.string.split(g_args.item_tags, ', ')
+
item_tags = util.string.split(g_args.item_tags, ', ')
else
+
else
item_tags = util.string.split(get_item_tags['Has tags'], ', ')
+
item_tags = util.string.split(get_item_tags['Has tags'], ', ')
end
+
end
  +
 
local header = g_args['header']
+
local header = g_args['header']
if header == nil then
+
if header == nil then
header = table.concat(item_tags, ', ')
+
header = table.concat(item_tags, ', ')
end
+
end
  +
 
-- Conditions
+
-- Conditions
local conditions = {}
+
local conditions = {}
conditions[1] = 'concept' -- Reserve for Concepts.
+
conditions[1] = 'concept' -- Reserve for Concepts.
conditions[#conditions+1] = string.format('[[Has subobject::<q> [[Has tag::%s]] </q>]]', table.concat(item_tags, ' || '))
+
conditions[#conditions+1] = string.format('[[Has subobject::<q> [[Has tag::%s]] </q>]]', table.concat(item_tags, ' || '))
 
 
-- Fields
+
-- Fields
local all_fields = {
+
local all_fields = {
  +
1,
1,
 
'Is mod',
+
'Is mod',
'Has name',
+
'Has name',
'Has level requirement',
+
'Has level requirement',
'Has mod group',
+
'Has mod group',
'Has mod type',
+
'Has mod type',
'Has stat text',
+
'Has stat text',
}
+
}
  +
 
local extra_properties = {}
+
local extra_properties = {}
if g_args.extra_properties ~= nil then
+
if g_args.extra_properties ~= nil then
extra_properties = util.string.split(g_args.extra_properties, ', ')
+
extra_properties = util.string.split(g_args.extra_properties, ', ')
local a = #all_fields
+
local a = #all_fields
for _,v in ipairs(extra_properties) do
+
for _,v in ipairs(extra_properties) do
table.insert(all_fields, a+1, v)
+
table.insert(all_fields, a+1, v)
end
+
end
end
+
end
  +
 
local fields = {}
+
local fields = {}
for _,v in ipairs(all_fields) do
+
for _,v in ipairs(all_fields) do
fields[#fields+1] = string.format('?%s', v)
+
fields[#fields+1] = string.format('?%s', v)
end
+
end
  +
 
-- Parameters
+
-- Parameters
local parameters = {
+
local parameters = {
sort = 'Has mod group, Has mod type, Has level requirement',
+
sort = 'Has mod group, Has mod type, Has level requirement',
-- limit = 1000, -- lets see
+
-- limit = 1000, -- lets see
offset = 0,
+
offset = 0,
  +
}
}
 
  +
  +
-- Create drop down lists in these sections and look in these concept pages for the modifiers:
  +
local section = {}
  +
section = {
  +
[1] = {
  +
header = 'Prefix',
  +
generation_type = 'prefix',
  +
condition = string.format('[[Concept:Spawnable named prefix %s mods]]', get_item_tags['Has mod domain text']),
  +
},
  +
[2] = {
  +
header = 'Suffix',
  +
generation_type = 'suffix',
  +
condition = string.format('[[Concept:Spawnable named suffix %s mods]]', get_item_tags['Has mod domain text']),
  +
},
  +
[3] = {
  +
header = 'Corrupted',
  +
generation_type = 'corrupted',
  +
condition = string.format('[[Concept:Spawnable corrupted %s mods]]', get_item_tags['Has mod domain text']),
  +
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
  +
},
  +
-- [4] = {
  +
-- header = 'Forsaken masters',
  +
-- generation_type = 'master',
  +
-- condition = '[[Concept:AAAAAAAAAAAAAAAAAAAA]]',
  +
-- },
  +
}
 
 
-- Create drop down lists in these sections and look in these concept pages for the modifiers:
 
local section = {}
 
section = {
 
[1] = {
 
header = 'Prefix',
 
generation_type = 'prefix',
 
condition = string.format('[[Concept:Spawnable named prefix %s mods]]', get_item_tags['Has mod domain text']),
 
},
 
[2] = {
 
header = 'Suffix',
 
generation_type = 'suffix',
 
condition = string.format('[[Concept:Spawnable named suffix %s mods]]', get_item_tags['Has mod domain text']),
 
},
 
[3] = {
 
header = 'Corrupted',
 
generation_type = 'corrupted',
 
condition = string.format('[[Concept:Spawnable corrupted %s mods]]', get_item_tags['Has mod domain text']),
 
chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events.
 
},
 
-- [4] = {
 
-- header = 'Forsaken masters',
 
-- generation_type = 'master',
 
-- condition = '[[Concept:AAAAAAAAAAAAAAAAAAAA]]',
 
-- },
 
}
 
 
   
local out = {}
+
local out = {}
  +
 
-- Introductory text:
+
-- Introductory text:
out[#out+1] = string.format('==%s== \n', header)
+
out[#out+1] = string.format('==%s== \n', header)
out[#out+1] = '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[Collapse All]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[Expand All]</div></div>'
+
out[#out+1] = '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[Collapse All]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[Expand All]</div></div>'
out[#out+1] = string.format('The table below displays the available [[modifiers]] for [[item]]s such as %s.<br><br><br>', f_item_link{get_item_tags[1]})
+
out[#out+1] = string.format('The table below displays the available [[modifiers]] for [[item]]s such as %s.<br><br><br>', f_item_link{get_item_tags[1]})
  +
 
local results = {}
+
local results = {}
local item_mods = {}
+
local item_mods = {}
local tableIndex = -1
+
local tableIndex = -1
  +
 
-- Loop through all the sections and query the mods in the concept pages:
+
-- Loop through all the sections and query the mods in the concept pages:
for i_type,_ in ipairs(section) do
+
for i_type,_ in ipairs(section) do
local generation_type = section[i_type]['generation_type']
+
local generation_type = section[i_type]['generation_type']
  +
 
local query = {}
+
local query = {}
  +
 
conditions[1] = section[i_type]['condition']
+
conditions[1] = section[i_type]['condition']
query[#query+1] = table.concat(conditions, ' ')
+
query[#query+1] = table.concat(conditions, ' ')
  +
 
for _, v in ipairs(fields) do
+
for _, v in ipairs(fields) do
query[#query+1] = v
+
query[#query+1] = v
end
+
end
for k, v in pairs(parameters) do
+
for k, v in pairs(parameters) do
query[k] = v
+
query[k] = v
end
+
end
  +
 
if results[generation_type] == nil then
+
if results[generation_type] == nil then
results[generation_type] = {}
+
results[generation_type] = {}
end
+
end
  +
 
-- Query the mods in the concept page, if there are too many results then repeat and add the remaining values:
+
-- Query the mods in the concept page, if there are too many results then repeat and add the remaining values:
repeat
+
repeat
local result = util.smw.query(query, g_frame)
+
local result = util.smw.query(query, g_frame)
query.offset = query.offset + #result -- Possible error source if only one mod is missing.
+
query.offset = query.offset + #result -- Possible error source if only one mod is missing.
  +
 
for _,v in ipairs(result) do
+
for _,v in ipairs(result) do
results[generation_type][#results[generation_type]+1] = v
+
results[generation_type][#results[generation_type]+1] = v
  +
end
end
 
 
until #result < 1000
 
until #result < 1000
  +
 
item_mods[generation_type] = {}
+
item_mods[generation_type] = {}
  +
 
local container = mw.html.create('div')
+
local container = mw.html.create('div')
:attr('style', 'vertical-align:top; display:inline-block;')
+
:attr('style', 'vertical-align:top; display:inline-block;')
  +
 
if #results[generation_type] > 0 then
+
if #results[generation_type] > 0 then
query_mod_tags = {
+
query_mod_tags = {
string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' ')),
+
string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' ')),
'?Has tag#',
+
'?Has tag#',
'?Has spawn weight#',
+
'?Has spawn weight#',
'?Is tag number#',
+
'?Is tag number#',
offset = 0,
+
offset = 0,
  +
}
}
 
  +
 
  +
 
local mod_data_subobject = {}
+
local mod_data_subobject = {}
  +
 
-- Query the subobjects in each mod page for the mod tags, if there are too many results then repeat and add the remaining values:
+
-- Query the subobjects in each mod page for the mod tags, if there are too many results then repeat and add the remaining values:
repeat
+
repeat
local results_mod_tags = util.smw.query(query_mod_tags, g_frame)
+
local results_mod_tags = util.smw.query(query_mod_tags, g_frame)
query_mod_tags.offset = query_mod_tags.offset + #results_mod_tags
+
query_mod_tags.offset = query_mod_tags.offset + #results_mod_tags
  +
 
for _,v in ipairs(results_mod_tags) do
+
for _,v in ipairs(results_mod_tags) do
local pagename = util.string.split(v[1], '#')[1]
+
local pagename = util.string.split(v[1], '#')[1]
if mod_data_subobject[pagename] == nil then
+
if mod_data_subobject[pagename] == nil then
mod_data_subobject[pagename] = {}
+
mod_data_subobject[pagename] = {}
  +
end
end
 
  +
 
local subobjectname = v[1]
+
local subobjectname = v[1]
mod_data_subobject[pagename][subobjectname] = v
+
mod_data_subobject[pagename][subobjectname] = v
  +
end
end
 
until #results_mod_tags < 1000
+
until #results_mod_tags < 1000
  +
 
-- Loop through all the modifiers from the concept pages:
+
-- Loop through all the modifiers from the concept pages:
local last
+
local last
for i, v in ipairs(results[generation_type]) do
+
for i, v in ipairs(results[generation_type]) do
local pagename = results[generation_type][i][1]
+
local pagename = results[generation_type][i][1]
  +
 
  +
 
-- Loop through all the modifier tags until they match the item tags:
+
-- Loop through all the modifier tags until they match the item tags:
local j = 0
+
local j = 0
local tag_match_stop
+
local tag_match_stop
repeat
+
repeat
j = j+1
+
j = j+1
local subobjectname = string.format('%s#%s_%s', pagename, 'spawn_weight', j)
+
local subobjectname = string.format('%s#%s_%s', pagename, 'spawn_weight', j)
  +
 
-- We know that the mod query found subobjects with tags. If the subobject missed any subobject for some reason make sure to catch that potential error and requery only for that particular mod page:
+
-- We know that the mod query found subobjects with tags. If the subobject missed any subobject for some reason make sure to catch that potential error and requery only for that particular mod page:
local j2 = 0
+
local j2 = 0
while not pcall(function () return mod_data_subobject[pagename][subobjectname]['Has tag'] end) do
+
while not pcall(function () return mod_data_subobject[pagename][subobjectname]['Has tag'] end) do
j2 = j2+1
+
j2 = j2+1
local query_mod_tags_missed = query_mod_tags
+
local query_mod_tags_missed = query_mod_tags
query_mod_tags_missed[1] = string.format('[[-Has subobject::<q>%s [[%s]]</q>]]', table.concat(conditions, ' '), pagename)
+
query_mod_tags_missed[1] = string.format('[[-Has subobject::<q>%s [[%s]]</q>]]', table.concat(conditions, ' '), pagename)
local results_mod_tags = util.smw.query(query_mod_tags, g_frame)
+
local results_mod_tags = util.smw.query(query_mod_tags, g_frame)
  +
 
for _,v in ipairs(results_mod_tags) do
+
for _,v in ipairs(results_mod_tags) do
if mod_data_subobject[pagename] == nil then
+
if mod_data_subobject[pagename] == nil then
mod_data_subobject[pagename] = {}
+
mod_data_subobject[pagename] = {}
  +
end
end
 
mod_data_subobject[pagename][v[1]] = v
+
mod_data_subobject[pagename][v[1]] = v
  +
end
end
 
  +
 
  +
 
if (not pcall(function () return mod_data_subobject[pagename][subobjectname]['Has tag'] end)) and j2 == 3 then
+
if (not pcall(function () return mod_data_subobject[pagename][subobjectname]['Has tag'] end)) and j2 == 3 then
error(string.format('mod_data_subobject doesnt have tags: pagename = %s, subobjectname = %s ? Queried %s times. Try to null edit the mod page and the mod list page.', pagename, subobjectname, j2))
+
error(string.format('mod_data_subobject doesnt have tags: pagename = %s, subobjectname = %s ? Queried %s times. Try to null edit the mod page and the mod list page.', pagename, subobjectname, j2))
  +
end
end
 
  +
end
end
 
  +
 
local mod_tag = mod_data_subobject[pagename][subobjectname]['Has tag']
+
local mod_tag = mod_data_subobject[pagename][subobjectname]['Has tag']
local mod_tag_weight = tonumber(mod_data_subobject[pagename][subobjectname]['Has spawn weight'])
+
local mod_tag_weight = tonumber(mod_data_subobject[pagename][subobjectname]['Has spawn weight'])
  +
 
-- Loop through the item tags until it matches the mod tag and the mod tag has a value larger than zero:
+
-- Loop through the item tags until it matches the mod tag and the mod tag has a value larger than zero:
local y = 0
+
local y = 0
local tag_match_add
+
local tag_match_add
repeat
+
repeat
y = y+1
+
y = y+1
tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (mod_data_subobject[pagename][subobjectname] == nil)
+
tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (mod_data_subobject[pagename][subobjectname] == nil)
tag_match_add = (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
+
tag_match_add = (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
until tag_match_stop or y == #item_tags
+
until tag_match_stop or y == #item_tags
  +
 
-- If there's a match then save that mod and other interesting information:
+
-- If there's a match then save that mod and other interesting information:
if tag_match_add then
+
if tag_match_add then
  +
 
-- Assume that the mod is global then go through all the subobjects and check if any of the stats are local:
+
-- Assume that the mod is global then go through all the subobjects and check if any of the stats are local:
local mod_scope = 'Global'
+
local mod_scope = 'Global'
for subobject, subobject_val in pairs(mod_data_subobject[pagename]) do
+
for subobject, subobject_val in pairs(mod_data_subobject[pagename]) do
  +
 
-- If at least one stat is local then change the scope of the mod to local:
+
-- If at least one stat is local then change the scope of the mod to local:
if subobject:find('stat.*local') ~= nil then
+
if subobject:find('stat.*local') ~= nil then
mod_scope = 'Local'
+
mod_scope = 'Local'
  +
end
end
 
  +
 
-- Complement with the properties the matching subobject has:
+
-- Complement with the properties the matching subobject has:
if subobject == subobjectname then
+
if subobject == subobjectname then
for property,w in pairs(subobject_val) do
+
for property,w in pairs(subobject_val) do
if results[generation_type][i][property] == nil then
+
if results[generation_type][i][property] == nil then
results[generation_type][i][property] = {}
+
results[generation_type][i][property] = {}
  +
end
end
 
  +
 
results[generation_type][i][property] = w
+
results[generation_type][i][property] = w
  +
end
end
 
  +
end
end
 
  +
end
end
 
  +
 
-- Save the matching modifier and all other interesting properties:
+
-- Save the matching modifier and all other interesting properties:
local a = #item_mods[generation_type]
+
local a = #item_mods[generation_type]
item_mods[generation_type][a+1] = {}
+
item_mods[generation_type][a+1] = {}
for _,property in ipairs(all_fields) do
+
for _,property in ipairs(all_fields) do
item_mods[generation_type][a+1][property] = results[generation_type][i][property]
+
item_mods[generation_type][a+1][property] = results[generation_type][i][property]
  +
end
end
 
item_mods[generation_type][a+1]['Has mod scope'] = mod_scope
+
item_mods[generation_type][a+1]['Has mod scope'] = mod_scope
  +
end
end
 
until tag_match_stop
+
until tag_match_stop
  +
end
end
 
  +
 
-- If the user wants to see the spawn chance then do the calculations and save that result as well:
+
-- If the user wants to see the spawn chance then do the calculations and save that result as well:
for _,v in ipairs(extra_properties) do
+
for _,v in ipairs(extra_properties) do
if v == 'Has spawn chance' then
+
if v == 'Has spawn chance' then
item_mods[generation_type] = p.get_spawn_chance{tbl = item_mods[generation_type], chance_multiplier = section[i_type]['chance_multiplier']}
+
item_mods[generation_type] = p.get_spawn_chance{tbl = item_mods[generation_type], chance_multiplier = section[i_type]['chance_multiplier']}
break
+
break
  +
end
end
 
  +
end
end
 
  +
 
-- Create the drop down table with <table></table>:
+
-- Create the drop down table with <table></table>:
local headers = container
+
local headers = container
headers
+
headers
:tag('h3')
+
:tag('h3')
:wikitext(string.format('%s', section[i_type]['header']))
+
:wikitext(string.format('%s', section[i_type]['header']))
:done()
+
:done()
:done()
+
:done()
  +
 
-- Loop through and add all matching mods to the <table>.
+
-- Loop through and add all matching mods to the <table>.
local tbl, last
+
local tbl, last
for _, mod_properties in ipairs(item_mods[generation_type]) do
+
for _, mod_properties in ipairs(item_mods[generation_type]) do
  +
 
-- If the last mod group is different to the current mod group then assume the mod isn't related and start a new drop down list:
+
-- If the last mod group is different to the current mod group then assume the mod isn't related and start a new drop down list:
if mod_properties['Has mod group'] ~= last then
+
if mod_properties['Has mod group'] ~= last then
  +
 
-- Check through all the mods and see if there are multiple mod types within the same mod group:
+
-- Check through all the mods and see if there are multiple mod types within the same mod group:
local count = {}
+
local count = {}
for _, n in ipairs(item_mods[generation_type]) do
+
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 the mod has the same mod group, then add the mod type to the counter. Only unique mod types matter so the number is just a dummy value:
if n['Has mod group'] == mod_properties['Has mod group'] then
+
if n['Has mod group'] == mod_properties['Has mod group'] then
count[n['Has mod type']] = 1
+
count[n['Has mod type']] = 1
  +
end
end
 
  +
end
end
 
  +
 
-- Calculate how many unique mod types with the same mod group there are:
+
-- Calculate how many unique mod types with the same mod group there are:
local number_of_mod_types = 0
+
local number_of_mod_types = 0
for _ in pairs(count) do
+
for _ in pairs(count) do
number_of_mod_types = number_of_mod_types + 1
+
number_of_mod_types = number_of_mod_types + 1
  +
end
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 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
+
if number_of_mod_types > 1 then
tbl_caption = string.format('%s', util.html.poe_color('mod', 'Mod group: ' .. mod_properties['Has mod group']))
+
tbl_caption = string.format('%s', util.html.poe_color('mod', 'Mod group: ' .. mod_properties['Has mod group']))
  +
else
else
 
tbl_caption = string.format('%s (%s)', util.html.poe_color('mod', p.header(mod_properties['Has stat text'])), mod_properties['Has mod scope'])
+
tbl_caption = string.format('%s (%s)', util.html.poe_color('mod', p.header(mod_properties['Has stat text'])), mod_properties['Has mod scope'])
  +
end
end
 
  +
 
-- Add class and style to the <table>:
+
-- Add class and style to the <table>:
tableIndex = tableIndex+1
+
tableIndex = tableIndex+1
tbl = container:tag('table')
+
tbl = container:tag('table')
  +
tbl
tbl
 
:attr('class', 'mw-collapsible mw-collapsed')
+
:attr('class', 'mw-collapsible mw-collapsed')
:attr('style', 'text-align:left; line-height:1.60em; width:810px;')
+
:attr('style', 'text-align:left; line-height:1.60em; width:810px;')
:tag('th')
+
:tag('th')
:attr('class', string.format('mw-customtoggle-%s', tableIndex))
+
:attr('class', string.format('mw-customtoggle-%s', tableIndex))
:attr('style', 'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;')
+
:attr('style', 'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;')
:attr('colspan', '3' .. #extra_properties)
+
:attr('colspan', '3' .. #extra_properties)
:wikitext(tbl_caption)
+
:wikitext(tbl_caption)
  +
:done()
:done()
 
  +
:done()
:done()
 
  +
end
end
 
  +
 
-- If the mod has no name then use the mod id:
+
-- If the mod has no name then use the mod id:
local mod_name = mod_properties['Has name']
+
local mod_name = mod_properties['Has name']
if mod_name == '' then
+
if mod_name == '' then
mod_name = mod_properties['Is mod']
+
mod_name = mod_properties['Is mod']
  +
end
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:
+
-- 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')
+
local td = mw.html.create('td')
if extra_properties ~= nil then
+
if extra_properties ~= nil then
for _, extra_property in ipairs(extra_properties) do
+
for _, extra_property in ipairs(extra_properties) do
  +
td
td
 
:attr('width', '*')
+
:attr('width', '*')
:wikitext(string.format('%s:&nbsp;%s ', extra_property, mod_properties[extra_property]))
+
:wikitext(string.format('%s:&nbsp;%s ', extra_property, mod_properties[extra_property]))
  +
:done()
:done()
 
  +
end
end
 
  +
end
end
 
  +
 
-- Add a table row with the interesting properties that modifier has:
+
-- Add a table row with the interesting properties that modifier has:
  +
tbl
tbl
 
:tag('tr')
+
:tag('tr')
:attr('class', 'mw-collapsible mw-collapsed')
+
:attr('class', 'mw-collapsible mw-collapsed')
:attr('id', string.format('mw-customcollapsible-%s', tableIndex))
+
:attr('id', string.format('mw-customcollapsible-%s', tableIndex))
:tag('td')
+
:tag('td')
:attr('width', '160')
+
:attr('width', '160')
:wikitext(string.format('&nbsp;&nbsp;&nbsp;[[%s|%s]]', mod_properties[1], mod_name:gsub('%s', '&nbsp;')))
+
:wikitext(string.format('&nbsp;&nbsp;&nbsp;[[%s|%s]]', mod_properties[1], mod_name:gsub('%s', '&nbsp;')))
  +
:done()
:done()
 
:tag('td')
+
:tag('td')
:attr('width', '1')
+
:attr('width', '1')
:wikitext(string.format('%s&nbsp;%s',game.level_requirement['short_upper']:gsub('%s', '&nbsp;'), mod_properties['Has level requirement']))
+
:wikitext(string.format('%s&nbsp;%s',game.level_requirement['short_upper']:gsub('%s', '&nbsp;'), mod_properties['Has level requirement']))
  +
:done()
:done()
 
:tag('td')
+
:tag('td')
:attr('width', '*')
+
:attr('width', '*')
:wikitext(string.format('%s', util.html.poe_color('mod', mod_properties['Has stat text']:gsub('<br>', ', ')) ))
+
:wikitext(string.format('%s', util.html.poe_color('mod', mod_properties['Has stat text']:gsub('<br>', ', ')) ))
  +
:done()
:done()
 
:node(td)
+
:node(td)
:done()
+
:done()
:done()
+
:done()
  +
 
-- Save the last mod group for later comparison:
+
-- Save the last mod group for later comparison:
last = mod_properties['Has mod group']
+
last = mod_properties['Has mod group']
  +
end
end
 
end
+
end
  +
 
out[#out+1] = tostring(container)
+
out[#out+1] = tostring(container)
 
end
 
end
  +
 
return table.concat(out,'')
+
return table.concat(out,'')
 
end
 
end
   

Revision as of 09:57, 14 September 2017

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

Module for handling for Mods with Semantic MediaWiki support.

List of currently implemented templates

--
-- Module for mod related templates
--

local util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local game = require('Module:Game')
local f_item_link = require('Module:Item link').item_link

local p = {}

-- ----------------------------------------------------------------------------
-- Utility / Helper functions
-- ----------------------------------------------------------------------------

local h = {}

-- Validate single value properties and set them

h.validate = {}

function h.validate.not_nil (args)
    return function (arg)
        if g_args[arg] == nil then
            error(string.format('%s must not be nil', arg))
        end
    end
end

function h.validate.number (args)
    return function (arg)
        g_args[arg] = util.cast.number(g_args[arg], args)
        return g_args[arg]
    end
end


function h.handle_mapped_property_args (map)
    local properties = {}
    
    for _, data in ipairs(map) do
        if data.func ~= nil then
            data.func(data.name)
        end
        
        if data.property ~= nil then
            properties[data.property] = g_args[data.name]
        end
    end
    
    g_frame:callParserFunction('#set:', properties)
end

function h.create_header(row)
    local stat = mw.html.create('span')
    local text, nsub = mw.ustring.gsub(row['Has stat text'], '%d+', '?')
    stat
        :attr('class', 'mod-table-header-stat')
        :wikitext(text)
        :done()
        
    local mgroup = mw.html.create('span')
    mgroup
        :attr('class', 'mod-table-header-modgroup')
        :wikitext(row['Has mod group'])
        :done()
        
    local tbl = mw.html.create('table')
    tbl
        :attr('class', 'wikitable mw-collapsible mw-collapsed mod-table') 
        :tag('tr')
            :tag('th')
                :attr('class', 'mod-table-header')
                :attr('colspan', g_args.colspan)
                :tag('span')
                    :attr('class', 'mod-table-header-container')
                    :wikitext(tostring(stat) .. tostring(mgroup))
                :done()
            :done()
    return tbl
end

function h.format_mod(tbl, row, tags)
    local tr = tbl:tag('tr')
    tr
        :tag('td')
            :wikitext(string.format('[[%s|%s]]', row[1], row['Has name']))
            :attr('class', 'mod-table-cell-name')
            :done()
        :tag('td')
            :wikitext(row['Has level requirement'])
            :attr('class', 'mod-table-cell-level')
            :done()
        :tag('td')
            :wikitext(row['Has stat text'])
            :attr('class', 'mod-table-cell-stat')
            :done()
        :tag('td')
            :wikitext(table.concat(tags, ', '))
            :attr('class', 'mod-table-cell-tags')
            :done()
end


-- ----------------------------------------------------------------------------
-- Templates
-- ----------------------------------------------------------------------------

--
-- Template: Mod
--

-- p.mod{id = "LocalIncreasedPhysicalDamagePercentUniqueOneHandSword2", name = "", mod_group = "LocalPhysicalDamagePercent", domain = "1", generation_type = "3", required_level = "1", mod_type = "LocalPhysicalDamagePercent", stat_text = "150% increased Physical Damage", stat1_id = "local_physical_damage_+%", stat1_min = "150", stat1_max = "150"}
function p.mod(frame)
    -- Get args
    g_args = getArgs(frame, {
        parentFirst = true
    })
    g_frame = util.misc.get_frame(frame)
    
    --
    -- Validation & semantic properties
    --
    
    -- Validate single value properties and set them
    
    local map = {
        {
            name = 'id',
            property = 'Is mod',
            wikitext = 'Mod Id',
        },
        {
            name = 'name',
            property = 'Has name',
            wikitext = 'Name',
        },
        {
            name = 'mod_group',
            property = 'Has mod group',
            wikitext = 'Group',
        },
        {
            name = 'mod_type',
            property = 'Has mod type',
            wikitext = 'Mod type',
        },
        {
            name = 'domain',
            property = 'Has mod domain',
            func = h.validate.number{min=1, max=13},
            wikitext = 'Mod domain',
            display = function (value)
                return game.constants.mod.domains[value]['short_upper'] .. ' (Id: ' .. value .. ')'
            end,
        },
        {
            name = 'generation_type',
            property = 'Has mod generation type',
            func = h.validate.number{min=1, max=12},
            wikitext = 'Generation type',
            display = function (value)
                return game.constants.mod.generation_types[value]['short_upper'] .. ' (Id: ' .. value .. ')'
            end,
        },
        {
            name = 'required_level',
            property = 'Has level requirement',
            func = h.validate.number{min=0, max=100},
            wikitext = 'Req. level',
        },
        {
            name = 'stat_text',
            property = 'Has stat text',
            wikitext = 'Effect',
        },
        {
            name = 'granted_buff_id',
            property = 'Has granted buff id',
            wikitext = 'Granted Buff Id',
        },
        {
            name = 'granted_buff_value',
            property = 'Has granted buff value',
            wikitext = 'Granted Buff Value',
        },
        {
            name = 'granted_skill',
            property = 'Has granted skill id',
            wikitext = 'Granted Skill',
        },
    }
    
    h.handle_mapped_property_args(map)
    
    -- Validate & set multi value property
    
    if g_args['tags'] == nil then
        g_args['tags'] = {}
    else
        local tags = mw.text.split(g_args['tags'], ', ')
        
        g_args['tags'] = tags
    
        properties = {}
        properties['Has tag'] = table.concat(tags, ';')
        properties['+sep'] = ';'
        util.smw.set(g_frame, properties)
    end
    
    -- Validate % set the stat subobjects
    util.args.stats(g_args, {frame=g_frame})
    
    -- Validate & set spawn weight subobjects
    util.args.weight_list(g_args, {
        frame=g_frame, 
        output_argument='spawn_weights',
    })
    
    -- Validate & set generation weight subobjects
    util.args.weight_list(g_args, {
        frame=g_frame, 
        input_argument='generation_weight',
        output_argument='generation_weights',
        weight_proprety='Has generation weight',
        subobject_name='generation weight',
    })
    
    -- Validate & set mod sell values
    i = 0
    local names = {}
    local sell_prices = {}
    repeat 
        i = i + 1
        id = {
            name = string.format('sell_price%s_name', i),
            amount = string.format('sell_price%s_amount', i),
        }
    
        value = {
            name = g_args[id.name],
            amount = tonumber(g_args[id.amount]),
        }
        
        if value.name == nil and value.amount == nil then
            value = nil
        elseif value.name ~= nil and value.amount ~= nil then
            if names[value.name] then
                error('Do not specify a sell price item name multiple times. Instead adjust the amount')
            else
                names[value.name] = true
            end
            
            properties = {}
            properties['Has sell price item name'] = value.name
            properties['Has sell price amount'] = value.amount
            
            util.smw.subobject(g_frame, 'sell price ' .. value.name, properties)
            
            sell_prices[#sell_prices+1] = value
        else
            error ('Both ' .. id.name .. ' and ' .. id.amount .. ' must be specified')
        end
        
    until value == nil
    
    --
    -- Display
    --
    
    local container = mw.html.create('div')
    container
        :attr('class', 'modbox')
    
    -- core stats
    
    local tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable')
    
    for _, data in ipairs(map) do
        local text
        if data.display == nil then
            text = g_args[data.name]
        else
            text = data.display(g_args[data.name])
        end
        
        tbl
            :tag('tr')
                :tag('th')
                    :wikitext(string.format('[[Property:%s|%s]]', data.property, data.wikitext))
                    :done()
                :tag('td')
                    :wikitext(text)
                    :done()
                :done()
            :done()
    end
    
    tbl
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Has tag|Tags]]')
                :done()
            :tag('td')
                :wikitext(table.concat(g_args['tags'], ', '))
                :done()
            :done()
        :done()
    
    -- stat table
    
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 4)
                :wikitext('Stats')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Is stat number|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has stat id|Stat Id]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has minimum stat value|Minimum]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has maximum stat value|Maximum]]')
                :done()
            :done()
        :done()
        
    for i=1, 5 do
        local value = {
            id = g_args['stat' .. i .. '_id'],
            min = g_args['stat' .. i .. '_min'],
            max = g_args['stat' .. i .. '_max'],
        }
        
        if value.id then
            tbl
                :tag('tr')
                    :tag('td')
                        :wikitext(i)
                        :done()
                    :tag('td')
                        :wikitext(value.id)
                        :done()
                    :tag('td')
                        :wikitext(value.min)
                        :done()
                    :tag('td')
                        :wikitext(value.max)
                        :done()
                    :done()
                :done()
        end
    end
    
    -- spawn weight table
    
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext('Spawn Weights')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Is tag number|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has tag|Tag]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has spawn weight|Weight]]')
                :done()
            :done()
        :done()
        
    i = 0
    value = nil
    repeat
        i = i + 1
        value = {
            tag = g_args[string.format('spawn_weight%s_tag', i)],
            value = g_args[string.format('spawn_weight%s_value', i)],
        }
        
        if value.tag then
            tbl
                :tag('tr')
                    :tag('td')
                        :wikitext(i)
                        :done()
                    :tag('td')
                        :wikitext(value.tag)
                        :done()
                    :tag('td')
                        :wikitext(value.value)
                        :done()
                    :done()
                :done()
        end
    until value.tag == nil
    
    -- generation weight table
    
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 3)
                :wikitext('Generation Weights')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Is tag number|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has tag|Tag]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has generation weight|Weight]]')
                :done()
            :done()
        :done()
    
    i = 0
    value = nil
    repeat
        i = i + 1
        value = {
            tag = g_args[string.format('generation_weight%s_tag', i)],
            value = g_args[string.format('generation_weight%s_value', i)],
        }
        
        if value.tag then
            tbl
                :tag('tr')
                    :tag('td')
                        :wikitext(i)
                        :done()
                    :tag('td')
                        :wikitext(value.tag)
                        :done()
                    :tag('td')
                        :wikitext(value.value)
                        :done()
                    :done()
                :done()
        end
    until value.tag == nil
    
    -- Sell prices
    tbl = container:tag('table')
    tbl
        :attr('class', 'wikitable sortable')
        :tag('tr')
            :tag('th')
                :attr('colspan', 2)
                :wikitext('Modifier sell price')
                :done()
            :done()
        :tag('tr')
            :tag('th')
                :wikitext('[[Property:Has sell price amount|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has sell price item name|Item]]')
                :done()
            :done()
        :done()
    
    for i, value in ipairs(sell_prices) do
        tbl
            :tag('tr')
                :tag('td')
                    :wikitext(value.amount)
                    :done()
                :tag('td')
                    :wikitext(f_item_link{item_name_exact=value.name})
                    :done()
                :done()
    end
    
    -- Generic messages on the page
    
    out = {}
    
    if mw.ustring.find(g_args['id'], '_') then
        out[#out+1] = g_frame:expandTemplate{ title = 'Incorrect title', args = { title=g_args['id'] } } .. '\n\n\n'
    end
    
    if g_args['name'] then
        out[#out+1] = string.format("'''%s''' is the internal id of modifier '''%s'''.\n", g_args['id'], g_args['name'])
    else
        out[#out+1] = string.format("'''%s''' is the internal id of an unnamed modifier.\n", g_args['id'], g_args['name'])
    end
    
    -- Categories
    
    cats = {'Mods'}
    
    -- Done -> output
    
    return tostring(container) .. util.misc.add_category(cats) .. '\n' .. table.concat(out) 
end

--
-- Template: SMW query mods
-- 

function p.query_mods(frame)
    -- Args
    g_args = getArgs(frame, {
        parentFirst = true
    })
    g_frame = util.misc.get_frame(frame)
    
    g_args.colspan = 4
    
    local conditions = {}
    conditions[#conditions+1] = 'concept'
    if g_args.tag then
        conditions[#conditions+1] = string.format('[[Has subobject::<q>[[-Has subobject::+]] [[Has spawn weight::>>0]] [[Has tag::%s]]</q>]]', g_args.tag)
    end
    
    g_args.header_level = g_args.header_level or 2
    
    -- Fields
    local fields = {}
    fields[#fields+1] = '?Is mod'
    fields[#fields+1] = '?Has name'
    fields[#fields+1] = '?Has level requirement'
    fields[#fields+1] = '?Has mod group'
    fields[#fields+1] = '?Has stat text'
    -- parameters
    local parameters = {}
    parameters.sort = 'Has mod group, '
    parameters.limit = 1000 -- lets see
    
    local data = {}
    data.header = {
        prefix = 'Prefix',
        suffix = 'Suffix',
    }
    
    local out = {}
    for _, v in ipairs({'prefix', 'suffix'}) do
        out[#out+1] = string.format('<h%i>%s</h%i>', g_args.header_level, data.header[v], g_args.header_level)
        conditions[1] = string.format('[[Concept:Spawnable named %s item mods]]', v)
        
        local query
        local results
        --
        -- Query tags
        --
        query = {}
        query[#query+1] = string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' '))
        query[#query+1] = '[[Has tag::+]]'
        query[#query+1] = '[[Has spawn weight::+]]'
        --query[#query+1] = '[[Has spawn weight::>>0]]'
        query[#query+1] = '?Has tag'
        query[#query+1] = '?Has spawn weight#' -- need native number
        query.limit = 1000
        query.offset = 0
        -- Tag order is very important
        query.sort = ', Is tag number'
        
        local tags = {}
        -- this works because lua only considers nil to be false >_>
        while query.offset do
            results = util.smw.query(query, g_frame)
            query.offset = query.offset + #results
            -- terminates the while if enough reuslts have been fetched
            if query.offset % 1000 ~= 0 then
                query.offset = nil
            end
            
            for _, row in ipairs(results) do
                local page, _ = string.gsub(row[1], '#_[%x]+', '')
                if tags[page] == nil then
                    tags[page] = {}
                end
                
                local text
                if tonumber(row['Has spawn weight']) > 0 then
                    text = '[[File:Yes.png|yes|link=]]'
                else
                    text = '[[File:No.png|no|link=]]'
                end
                
                tags[page][#tags[page]+1] = string.format('%s %s', row['Has tag'], text)
            end
        end
        
        --
        -- Query mods
        --
        query = {}
        for _, v in ipairs(conditions) do
            query[#query+1] = v
        end
        for _, v in ipairs(fields) do
            query[#query+1] = v
        end
        for k, v in pairs(parameters) do
            query[k] = v
        end
        
        results = util.smw.query(query, g_frame)
        
        local last = ''
        local tbl = ''
        for _, row in ipairs(results) do
            local current = string.gsub(row['Is mod'], '%d+.*', '%%d.*')
            if string.match(last, current) then
                h.format_mod(tbl, row, tags[row[1]])
            else
                out[#out+1] = tostring(tbl)
                tbl = h.create_header(row)
                h.format_mod(tbl, row, tags[row[1]])
            end
            last = row['Is mod']
        end
        
        -- add the last table
        out[#out+1] = tostring(tbl)
    end
    
    return table.concat(out, '')
end

--
-- Template: SMW mod table
-- 

-- =p.mod_list{'Strength1', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'ColdCritMultiplier', 'shitty name', 'test', 'test2', userparam='extra_rows=2, show_jewels=1'}
-- =p.mod_list{'MapMultipleExilesMap2Tier', 'asdasda', 'Area yields 15% more Items<br>8% increased Rarity of Items found in this Area<br>Area is inhabited by 2 additional Rogue Exiles<br>Extra monsters ignore rarity bias (Hidden)<br>+14% Monster pack size', userparam='extra_rows=1, type=map, effect_rowid=2'}

function p.mod_list(frame)
    local types = {'map', 'jewel'}
    
    -- Args
    g_args = getArgs(frame, {
        parentFirst = true
    })
    g_frame = util.misc.get_frame(frame)
    
    --
    local args = util.string.split_args(g_args.userparam, {sep=', '})
    g_args.userparam = args
    
    args.extra_rows = (tonumber(args.extra_rows) or 0)
    if args.show_tags == nil then
        args.show_tags = true
    else
        args.show_tags = util.cast.boolean(args.show_tags)
    end
    args.effect_rowid = (tonumber(args.effect_rowid) or 0) + 1

    tr = mw.html.create('tr')
    tr
        :tag('td')
            :attr('data-sort-value', g_args[2] or g_args[1])
            :wikitext(string.format('[[%s|%s]]', g_args[1], g_args[2] or g_args[1]))
            :done()
    
    local i = 2
    local row_max = i + args.extra_rows
    local text
    while i < row_max do
        i = i + 1
        text = g_args[i] or ''
        text = table.concat(mw.text.split(text, ';', true), '<br>')
        
        if args.type == 'map' and i == args.effect_rowid then
            text = mw.text.split(text, '<br>', true)
            
            local map = {
                '%d+%% increased Quantity of Items found in this Area',
                '%d+%% increased Rarity of Items found in this Area',
                '%+%d+%% Monster pack size',
            }
            out = {}
            
            local valid
            for k, v in pairs(text) do
                valid = true
                for _, pattern in ipairs(map) do
                    if mw.ustring.find(v, pattern) ~= nil then
                        valid = false
                        break
                    end
                end
                
                if valid then
                    table.insert(out, v)
                end
            end
            
            text = table.concat(out, '<br>')
        end
            
        tr
            :tag('td')
                :wikitext(text)
                :done()
    end
    
    
    local query
    local result
    
    if args.type == 'map' then
        query = {
            string.format('[[-Has subobject::%s]]', g_args[1]),
            '[[Has stat id::+]]',
            '?Has stat id',
            '?Has minimum stat value',
            '?Has maximum stat value',
        }
        
        result = util.smw.query(query, g_frame)
        
        local stat_map = {
            ['map_item_drop_quantity_+%'] = {disp=0, sort=0},
            ['map_item_drop_rarity_+%'] = {disp=0, sort=0},
            ['map_pack_size_+%'] = {disp=0, sort=0},
        }
        
        local stat
        for _, row in ipairs(result) do
            stat = stat_map[row['Has stat id']]
            if stat ~= nil then
                stat.sort = (row['Has minimum stat value'] + row['Has maximum stat value']) / 2
                if row['Has minimum stat value'] ~= row['Has minimum stat value'] then
                    stat.disp = string.format('(%s%-%s)', row['Has minimum stat value'], row['Has maximum stat value'])
                else
                    stat.disp = row['Has minimum stat value']
                end
            end
        end
        
        for _, k in ipairs({'map_item_drop_quantity_+%', 'map_item_drop_rarity_+%', 'map_pack_size_+%'}) do
            stat = stat_map[k]
            tr
                :tag('td')
                    :attr('data-sort-value', stat.sort)
                    :wikitext(stat.disp)
                    :done()
                :done()
        end
    end
    
    local tags
    if args.show_tags or args.type == 'jewel' then
        query = {
            string.format('[[-Has subobject::%s]]', g_args[1]),
            '[[Has tag::+]]',
            '?Has tag',
            '?Has spawn weight',
            sort='Is tag number',
        }

        tags = {}
        result = util.smw.query(query, g_frame)
    end
    
    if args.type == 'jewel' then
        local jewels = {
            dex=0,
            str=0,
            int=0,
            pris=0,
        }
        
        local cast_tbl = {
            not_dex={'str', 'int'},
            not_int={'str', 'dex'},
            not_str={'int', 'dex'},
            default={'str','int','dex','pris'},
        }
        
        local i = #result
        local row
        local cast
        while i > 0 do
            row = result[i]
            cast = cast_tbl[row['Has tag']]
            if cast ~= nil then
                for _, k in ipairs(cast) do
                    jewels[k] = row['Has spawn weight']
                end
            end
            
            i = i - 1
        end
        
        tr
            :tag('td')
                :attr('class', 'table-cell-dex')
                :wikitext(jewels.dex)
                :done()
            :tag('td')
                :attr('class', 'table-cell-int')
                :wikitext(jewels.int)
                :done()
            :tag('td')
                :attr('class', 'table-cell-str')
                :wikitext(jewels.str)
                :done()
            :tag('td')
                :attr('class', 'table-cell-prismatic')
                :wikitext(jewels.pris)
                :done()
            :done()
    end
    
    if args.show_tags then
        for _, row in ipairs(result) do
            tags[#tags+1] = string.format('%s %s', row['Has tag'], row['Has spawn weight'])
        end
        
        tr
            :tag('td')
                :wikitext(table.concat(tags, '<br>'))
                :done()
            :done()
    end
    
    return tostring(tr)
end

function p.item_sell_price(frame)
    -- Query and sum the vendor prices for an item. 
    -- Unidentified items won't currently show the correct vendor price. Not sure how that is specified, nor is it used at all. 
    -- Expanding {{il}} seems to give a nil /n the first time a new command is run. Doesn't always happen.
    
    -- = p.item_sell_price{page="Voideye"}
    -- = p.item_sell_price{page="Pyre"}
    -- = p.item_sell_price{page="Vessel of Vinktar (Lightning Penetration)"}
    
    -- Args
    local g_args = getArgs(frame, {
        parentFirst = true
    })
    local g_frame = util.misc.get_frame(frame)

    -- Only the explicit modifiers are counted when vendors calculates the price.
    local condition = string.format('[[%s]]', g_args['page'])
    local query_item_mods = {
        condition,
        '?Has explicit mod ids',
        '?Has rarity',
    }
    local results_query_item_mods = util.smw.query(query_item_mods, g_frame)
    local item_mods = util.string.split(results_query_item_mods[1]['Has explicit mod ids'], '<MANY>')    
    
    -- If the item has a Normal rarity then the sell price would be a fixed price.
    if results_query_item_mods[1]['Has rarity'] == 'Normal' then
        local amount_normal = 1
        local currency_normal = 'Scroll Fragment'
        return string.format('%s %s', amount_normal, g_frame:expandTemplate{title = 'il', args = {currency_normal, currency_normal .. 's'}})
        
        -- return string.format('%s %s', amount_normal, currency_normal)
    end 
    
    local mods_sell_price = {}
    for _, modid in ipairs(item_mods) do
        local query_mod_page = {
            string.format('[[Is mod::%s]]', modid),
        }
        local mod_page = util.smw.query(query_mod_page, g_frame)
        
        local query_mod_sell_price = {
            string.format('[[-Has subobject::%s]]', mod_page[1][1]),
            '?Has sell price amount#',
            '?Has sell price item name',
        }
        local results = util.smw.query(query_mod_sell_price, g_frame)
        
        for _, k in ipairs(results) do 
            if k['Has sell price amount'] ~= '' then
                if mods_sell_price[k['Has sell price item name']] == nil then
                    mods_sell_price[k['Has sell price item name']] = k['Has sell price amount']
                else
                    mods_sell_price[k['Has sell price item name']] = k['Has sell price amount'] + mods_sell_price[k['Has sell price item name']]
                end
            end
        end 
    end
    
    local out = {}
    for currency, amount in pairs(mods_sell_price) do
        out[#out+1] = string.format('%s %s', amount, g_frame:expandTemplate{title = 'il', args = {currency, currency .. 's'}})
    end
    
    return table.concat(out, ', ')
end    
    

function p.find_mod_domain(input)
    -- Find the mod domain based on the item class.
    
    local out = input
    local mod_domains = game.constants.mod.domains
    
    for i,_ in ipairs(out) do
        out[i]['Has mod domain'] = 1
        for j, row in pairs(mod_domains) do
            if out[i]['Has item class']:gsub('Map', 'Area'):match(mod_domains[j]['short_upper']) then -- This may need updating if an area item class doesn't have 'Map' in the string, or if the mod domain descriptions doesn't match the item class. 
                out[i]['Has mod domain'] = j
            end
        end
        
        out[i]['Has mod domain text'] = mod_domains[out[i]['Has mod domain']]['short_lower']
    end

    return out
end

function p.get_item_tags(frame)
    -- This function queries for the tags of a specific item.
    
    -- Args
    local g_args = getArgs(frame, {
        parentFirst = true
    })
    local g_frame = util.misc.get_frame(frame)
    
    local item_name = g_args[1]
    
    local query = {
        string.format('[[Has name::%s]]', item_name),
        '?Has tags',
        '?Has base strength requirement',
        '?Has base intelligence requirement',
        '?Has base dexterity requirement',
        '?Has item class'
    }
    local results = util.smw.query(query, g_frame)
    
    for i,_ in ipairs(results) do
        results[i]['Has tags'] = results[i]['Has tags']:gsub('(<MANY>)', ', ') -- Remove unnecessary symbols.
    end 
    
    results = p.find_mod_domain(results)
    
    return results
end

function p.header(str)
    -- This function replace specific numbers with a generic #. 
    
    local s = table.concat(util.string.split(str, '%(%d+%.*%d*%-%d+%.*%d*%)'), '#')
    s = table.concat(util.string.split(s, '%d+%.*%d*'), '#')
    s = table.concat(util.string.split(s, '<br>'), ', ')
   
   return s
end

function p.get_spawn_chance(frame)
    -- Calculates the spawn chance of a set of mods that all have a spawn weight.
    
    -- Args
    local g_args = getArgs(frame, {
        parentFirst = true
    })
    local g_frame = util.misc.get_frame(frame)
    
    local tbl = g_args['tbl']
    local chance_multiplier = tonumber(g_args['chance_multiplier']) or 1 -- Probabillities affecting the result besides the spawn weight.
    
    local N = 0
    for i,_ in ipairs(tbl) do
        N = N + tbl[i]['Has spawn weight'] -- Total number of outcomes.
    end
    
    for i,_ in ipairs(tbl) do
        local n = tbl[i]['Has spawn weight'] -- Number of ways it can happen.
        
        tbl[i]['Has spawn chance'] = string.format("%0.2f%%", n/N * chance_multiplier*100) -- Truncated value.
    end 
    
    return tbl
end
    
function p.drop_down_table(frame)
    -- This function queries mods in concept pages, queries the subobjects in each mod page for the mod tags. Then compares the tags on the item with the mod tags. If there's a match and the spawn weighting is larger than zero, then that mod is added to a drop down list.
    
    -- Misses forsaken masters currently.
    -- Add a proper expand/collapse toggle for the entire header row so it reacts together with mw-collapsible. 
    -- Show Mod group in a better way perhaps: 
    -- Mod group (expanded)
    --   # to Damage (Collapsed)
    --      3 to Damage
    --      5 to Damage
    -- Add a better solution for properties inside subobjects, for extra_properties. Note that properties already queried for should not be added with this solution.
    
    -- Weapons
    -- p.drop_down_table{item = 'Rusted Hatchet', header = 'One Handed Axes'}
    -- p.drop_down_table{item = 'Stone Axe', header = 'Two Handed Axes'}

    -- Accessories
    -- p.drop_down_table{item = 'Amber Amulet', header = 'Amulets'}
    
    -- Jewels
    -- p.drop_down_table{item = 'Cobalt Jewel', header = 'Jewels'}
    
    -- Armour
    -- p.drop_down_table{item = 'Plate Vest', header = 'Armour body armours'}
    -- p.drop_down_table{item = 'Iron Greaves', header = 'Armour boots'}
    -- p.drop_down_table{item = 'Iron Gauntlets', header = 'Armour gloves'}
    -- p.drop_down_table{item = 'Iron Hat', header = 'Armour helmets'}
    -- p.drop_down_table{item = 'Splintered Tower Shield', header = 'Armour shields'}
    
    -- p.drop_down_table{item = 'Fishing Rod', header = 'FISH PLEASE', item_tags = 'fishing_rod', extra_properties = 'Has spawn weight, Has spawn chance'}


    -- Args
    local g_args = getArgs(frame, {
        parentFirst = true
    })
    local g_frame = util.misc.get_frame(frame)
    
    local get_item_tags = p.get_item_tags{g_args.item}[1]
    local item_tags = {}
    if g_args.item_tags ~= nil then
        item_tags = util.string.split(g_args.item_tags, ', ')    
    else 
        item_tags = util.string.split(get_item_tags['Has tags'], ', ')
    end
    
    local header = g_args['header']
    if header == nil then
        header = table.concat(item_tags, ', ')
    end
    
    -- Conditions
    local conditions = {}
    conditions[1] = 'concept' -- Reserve for Concepts.
    conditions[#conditions+1] = string.format('[[Has subobject::<q> [[Has tag::%s]] </q>]]', table.concat(item_tags, ' || '))
 
    -- Fields
    local all_fields = {
        1,
        'Is mod',
        'Has name',
        'Has level requirement',
        'Has mod group',
        'Has mod type',
        'Has stat text',
    } 
    
    local extra_properties = {}
    if g_args.extra_properties ~= nil then 
        extra_properties = util.string.split(g_args.extra_properties, ', ')
        local a = #all_fields
        for _,v in ipairs(extra_properties) do 
            table.insert(all_fields, a+1, v)
        end
    end 
    
    local fields = {}
    for _,v in ipairs(all_fields) do
        fields[#fields+1] = string.format('?%s', v)
    end 
    
    -- Parameters
    local parameters = {
        sort = 'Has mod group, Has mod type, Has level requirement',
        -- limit = 1000, -- lets see
        offset = 0,
    }
    
    -- Create drop down lists in these sections and look in these concept pages for the modifiers:
    local section = {}
    section = {
        [1] = {
            header = 'Prefix',
            generation_type = 'prefix',
            condition = string.format('[[Concept:Spawnable named prefix %s mods]]', get_item_tags['Has mod domain text']),
        },
        [2] = {
            header = 'Suffix',
            generation_type = 'suffix',
            condition = string.format('[[Concept:Spawnable named suffix %s mods]]', get_item_tags['Has mod domain text']),
        },
        [3] = {
            header = 'Corrupted',
            generation_type = 'corrupted',
            condition = string.format('[[Concept:Spawnable corrupted %s mods]]', get_item_tags['Has mod domain text']),
            chance_multiplier = 1/4, -- See Vaal orb, for the 4 possible events. 
        },
        -- [4] = {
            -- header = 'Forsaken masters',
            -- generation_type = 'master',    
            -- condition = '[[Concept:AAAAAAAAAAAAAAAAAAAA]]',
        -- },
    }
    

    local out = {}
    
    -- Introductory text:
    out[#out+1] = string.format('==%s== \n', header)
    out[#out+1] = '<div style="float: right; text-align:center"><div class="mw-collapsible-collapse-all" style="cursor:pointer;">[Collapse All]</div><hr><div class="mw-collapsible-expand-all" style="cursor:pointer;">[Expand All]</div></div>'    
    out[#out+1] = string.format('The table below displays the available [[modifiers]] for [[item]]s such as %s.<br><br><br>', f_item_link{get_item_tags[1]})
    
    local results = {}
    local item_mods = {}
    local tableIndex = -1        
    
    -- Loop through all the sections and query the mods in the concept pages:
    for i_type,_ in ipairs(section) do
        local generation_type = section[i_type]['generation_type']
        
        local query = {}
        
        conditions[1] = section[i_type]['condition']
        query[#query+1] = table.concat(conditions, ' ')
        
        for _, v in ipairs(fields) do
            query[#query+1] = v
        end
        for k, v in pairs(parameters) do
            query[k] = v
        end
        
        if results[generation_type] == nil then 
            results[generation_type] = {}
        end
        
        -- Query the mods in the concept page, if there are too many results then repeat and add the remaining values:
        repeat
            local result = util.smw.query(query, g_frame)
            query.offset = query.offset + #result -- Possible error source if only one mod is missing.
            
            for _,v in ipairs(result) do
                results[generation_type][#results[generation_type]+1] = v
            end
        until #result < 1000
        
        item_mods[generation_type] = {}
        
        local container = mw.html.create('div')
            :attr('style', 'vertical-align:top; display:inline-block;')
        
        if #results[generation_type] > 0 then 
            query_mod_tags = {
                string.format('[[-Has subobject::<q>%s</q>]]', table.concat(conditions, ' ')),
                '?Has tag#',
                '?Has spawn weight#',
                '?Is tag number#',
                offset = 0,
            }
            
            
            local mod_data_subobject = {}
            
            -- Query the subobjects in each mod page for the mod tags, if there are too many results then repeat and add the remaining values:
            repeat 
                local results_mod_tags = util.smw.query(query_mod_tags, g_frame)
                query_mod_tags.offset = query_mod_tags.offset + #results_mod_tags
                
                for _,v in ipairs(results_mod_tags) do
                    local pagename = util.string.split(v[1], '#')[1]
                    if mod_data_subobject[pagename] == nil then 
                        mod_data_subobject[pagename] = {}
                    end
                    
                    local subobjectname = v[1]
                    mod_data_subobject[pagename][subobjectname] = v
                end                
            until #results_mod_tags < 1000
            
            -- Loop through all the modifiers from the concept pages:
            local last
            for i, v in ipairs(results[generation_type]) do 
                local pagename = results[generation_type][i][1]
                
                
                -- Loop through all the modifier tags until they match the item tags:
                local j = 0 
                local tag_match_stop
                repeat 
                    j = j+1
                    local subobjectname = string.format('%s#%s_%s', pagename, 'spawn_weight', j)
                    
                    -- We know that the mod query found subobjects with tags. If the subobject missed any subobject for some reason make sure to catch that potential error and requery only for that particular mod page:
                    local j2 = 0
                    while not pcall(function () return mod_data_subobject[pagename][subobjectname]['Has tag'] end) do 
                        j2 = j2+1
                        local query_mod_tags_missed = query_mod_tags
                        query_mod_tags_missed[1] = string.format('[[-Has subobject::<q>%s [[%s]]</q>]]', table.concat(conditions, ' '), pagename)
                        local results_mod_tags =  util.smw.query(query_mod_tags, g_frame)
                        
                        for _,v in ipairs(results_mod_tags) do
                            if mod_data_subobject[pagename] == nil then 
                                mod_data_subobject[pagename] = {}
                            end
                            mod_data_subobject[pagename][v[1]] = v
                        end
                    
                        
                        if (not pcall(function () return mod_data_subobject[pagename][subobjectname]['Has tag'] end)) and j2 == 3 then
                            error(string.format('mod_data_subobject doesnt have tags: pagename = %s, subobjectname = %s ? Queried %s times. Try to null edit the mod page and the mod list page.', pagename, subobjectname, j2))
                        end
                    end
                    
                    local mod_tag = mod_data_subobject[pagename][subobjectname]['Has tag']
                    local mod_tag_weight = tonumber(mod_data_subobject[pagename][subobjectname]['Has spawn weight'])
                    
                    -- Loop through the item tags until it matches the mod tag and the mod tag has a value larger than zero:
                    local y = 0
                    local tag_match_add
                    repeat     
                        y = y+1
                        tag_match_stop = ((mod_tag == item_tags[y]) and ((mod_tag_weight or -1) >= 0)) or (mod_data_subobject[pagename][subobjectname] == nil)
                        tag_match_add = (mod_tag == item_tags[y]) and ((mod_tag_weight or -1) > 0)
                    until tag_match_stop or y == #item_tags
                    
                    -- If there's a match then save that mod and other interesting information:
                    if tag_match_add then 
                        
                        -- Assume that the mod is global then go through all the subobjects and check if any of the stats are local:
                        local mod_scope = 'Global' 
                        for subobject, subobject_val in pairs(mod_data_subobject[pagename]) do 
                        
                            -- If at least one stat is local then change the scope of the mod to local:
                            if subobject:find('stat.*local') ~= nil then 
                                mod_scope = 'Local'
                            end
                            
                            -- Complement with the properties the matching subobject has:
                            if subobject == subobjectname then 
                                for property,w in pairs(subobject_val) do 
                                    if results[generation_type][i][property] == nil then
                                        results[generation_type][i][property] = {}
                                    end
                                    
                                    results[generation_type][i][property] = w
                                end
                            end
                        end 
                        
                        -- Save the matching modifier and all other interesting properties:
                        local a = #item_mods[generation_type]
                        item_mods[generation_type][a+1] = {}
                        for _,property in ipairs(all_fields) do 
                            item_mods[generation_type][a+1][property] = results[generation_type][i][property]
                        end
                        item_mods[generation_type][a+1]['Has mod scope'] = mod_scope
                    end
                until tag_match_stop 
            end
            
            -- If the user wants to see the spawn chance then do the calculations and save that result as well:
            for _,v in ipairs(extra_properties) do
                if v == 'Has spawn chance' then 
                    item_mods[generation_type] = p.get_spawn_chance{tbl = item_mods[generation_type], chance_multiplier = section[i_type]['chance_multiplier']}
                    break 
                end
            end
            
            -- Create the drop down table with <table></table>:
            local headers = container 
            headers
                :tag('h3')
                    :wikitext(string.format('%s', section[i_type]['header']))
                    :done()
                :done()
                
            -- Loop through and add all matching mods to the <table>. 
            local tbl, last
            for _, mod_properties in ipairs(item_mods[generation_type]) do 
            
                -- If the last mod group is different to the current mod group then assume the mod isn't related and start a new drop down list:
                if mod_properties['Has mod group'] ~= last then 
                    
                    -- Check through all the mods and see if there are multiple mod types within the same mod group:
                    local count = {}
                    for _, n in ipairs(item_mods[generation_type]) do 
                        
                        -- If the mod has the same mod group, then add the mod type to the counter. Only unique mod types matter so the number is just a dummy value:
                        if n['Has mod group'] == mod_properties['Has mod group'] then
                            count[n['Has mod type']] = 1 
                        end
                    end
                    
                    -- Calculate how many unique mod types with the same mod group there are:
                    local number_of_mod_types = 0
                    for _ in pairs(count) do 
                        number_of_mod_types = number_of_mod_types + 1 
                    end
                    
                    -- If there are multiple unique mod types with the same mod group then change the style of the drop down list to indicate it:
                    if number_of_mod_types > 1 then 
                        tbl_caption = string.format('%s', util.html.poe_color('mod', 'Mod group: ' .. mod_properties['Has mod group']))
                    else
                        tbl_caption = string.format('%s (%s)', util.html.poe_color('mod', p.header(mod_properties['Has stat text'])), mod_properties['Has mod scope'])
                    end
                    
                    -- Add class and style to the <table>:
                    tableIndex = tableIndex+1
                    tbl = container:tag('table')
                    tbl
                        :attr('class', 'mw-collapsible mw-collapsed')
                        :attr('style', 'text-align:left; line-height:1.60em; width:810px;')
                        :tag('th')
                            :attr('class', string.format('mw-customtoggle-%s', tableIndex))
                            :attr('style', 'text-align:left; line-height:1.40em; border-bottom:1pt solid dimgrey;')
                            :attr('colspan', '3' .. #extra_properties)
                            :wikitext(tbl_caption)
                            :done()
                        :done()    
                end
                
                -- If the mod has no name then use the mod id:
                local mod_name = mod_properties['Has name']
                if  mod_name == '' then 
                    mod_name = mod_properties['Is mod']
                end
                
                -- Check if there are any extra properties to show in the drop down list and then add a cell for that, add this node at the end of the table row:
                local td = mw.html.create('td')
                if extra_properties ~= nil then 
                    for _, extra_property in ipairs(extra_properties) do 
                        td
                            :attr('width', '*')
                            :wikitext(string.format('%s:&nbsp;%s ', extra_property, mod_properties[extra_property]))
                            :done()
                    end
                end
                
                -- Add a table row with the interesting properties that modifier has:
                tbl
                    :tag('tr')
                        :attr('class', 'mw-collapsible mw-collapsed')
                        :attr('id', string.format('mw-customcollapsible-%s', tableIndex))
                        :tag('td')
                            :attr('width', '160')
                            :wikitext(string.format('&nbsp;&nbsp;&nbsp;[[%s|%s]]', mod_properties[1], mod_name:gsub('%s', '&nbsp;')))
                            :done()
                        :tag('td')
                            :attr('width', '1')
                            :wikitext(string.format('%s&nbsp;%s',game.level_requirement['short_upper']:gsub('%s', '&nbsp;'), mod_properties['Has level requirement']))
                            :done()        
                        :tag('td')
                            :attr('width', '*')
                            :wikitext(string.format('%s', util.html.poe_color('mod', mod_properties['Has stat text']:gsub('<br>', ', '))  ))
                            :done()
                        :node(td)
                        :done()
                    :done()
                
                -- Save the last mod group for later comparison:
                last = mod_properties['Has mod group']
            end    
        end
        
        out[#out+1] = tostring(container)
    end
    
    return table.concat(out,'')
end

return p