Module:Area: Difference between revisions
>OmegaK2 mNo edit summary |
Mefisto1029 (talk | contribs) (is_undergound icon) |
||
(47 intermediate revisions by 6 users not shown) | |||
Line 1: | Line 1: | ||
-- | ------------------------------------------------------------------------------- | ||
-- | |||
-- Module:Area | |||
-- | |||
-- This module implements Template:Area and Template:Query area infoboxes | |||
------------------------------------------------------------------------------- | |||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
Line 7: | Line 12: | ||
-- spawn_weight* values | -- spawn_weight* values | ||
-- spawnchacne values | -- spawnchacne values | ||
require('Module:No globals') | |||
local m_util = require('Module:Util') | |||
local m_cargo = require('Module:Cargo') | |||
local | local m_game = mw.loadData('Module:Game') | ||
local | -- Should we use the sandbox version of our submodules? | ||
local use_sandbox = m_util.misc.maybe_sandbox('Area') | |||
local f_infocard = require('Module:Infocard')._main | |||
-- | -- 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:Area/config/sandbox') or mw.loadData('Module:Area/config') | |||
local i18n = cfg.i18n | |||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Utility & helper functions | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local | local h = {} | ||
-- -- | -- | ||
-- Functions for processing tpl_args | |||
-- | |||
h.proc = {} | |||
h.proc.factory = {} | |||
function h.proc.factory.list(args) | |||
function factory. | args = args or {} | ||
return function (tpl_args, | return function (tpl_args, value) | ||
return m_util.cast.table(value, { | |||
pattern = args.pattern, | |||
callback = args.callback, | |||
}) | |||
end | end | ||
end | end | ||
local factory = {} | |||
function factory.display_value(k, args) | function factory.display_value(k, args) | ||
return function(tpl_args | return function(tpl_args) | ||
return tpl_args[k] | return tpl_args[k] | ||
end | end | ||
Line 137: | Line 62: | ||
local util = {} | local util = {} | ||
util.display = {} | util.display = {} | ||
function util.display.multiple_areas(tpl_args | function util.display.multiple_areas(tpl_args, area_ids) | ||
local out = {} | local out = {} | ||
for _, area_id in ipairs(area_ids) do | for _, area_id in ipairs(area_ids) do | ||
out[#out+1] = util.display.single_area(tpl_args | out[#out+1] = util.display.single_area(tpl_args, area_id) | ||
end | end | ||
return table.concat(out, '<br>') | return table.concat(out, '<br>') | ||
end | end | ||
function util.display.single_area(tpl_args | function util.display.single_area(tpl_args, area_id) | ||
if tpl_args.areas[area_id] then | if tpl_args.areas[area_id] then | ||
if tpl_args.areas[area_id]['areas.main_page'] | if tpl_args.areas[area_id]['areas.main_page'] then | ||
return string.format('[[%s|%s]]', tpl_args.areas[area_id]['areas.main_page'], tpl_args.areas[area_id]['areas.name']) | return string.format('[[%s|%s]]', tpl_args.areas[area_id]['areas.main_page'], tpl_args.areas[area_id]['areas.name']) | ||
else | else | ||
Line 160: | Line 85: | ||
-- Argument & display mapping | -- Argument & display mapping | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local display = {} | |||
local argument_map = { | local argument_map = { | ||
table = 'areas', | table = 'areas', | ||
order = { | |||
'main_page', | |||
'id', | |||
'name', | |||
'act', | |||
'area_level', | |||
'level_restriction_max', | |||
'area_type_tags', | |||
'tags', | |||
'loading_screen', | |||
'connection_ids', | |||
'parent_area_id', | |||
'modifier_ids', | |||
-- populated via modifier ids | |||
'stat_text', | |||
'boss_monster_ids', | |||
'monster_ids', | |||
'entry_text', | |||
'entry_npc', | |||
'flavour_text', | |||
'screenshot_ext', | |||
'screenshot', | |||
'vaal_area_spawn_chance', | |||
'vaal_area_ids', | |||
'strongbox_spawn_chance', | |||
'strongbox_max_count', | |||
'strongbox_rarity_weight', | |||
-- those four are parsed via the argument above | |||
'strongbox_weight_normal', | |||
'strongbox_weight_magic', | |||
'strongbox_weight_rare', | |||
'strongbox_weight_unique', | |||
'is_map_area', | |||
'is_unique_map_area', | |||
'is_town_area', | |||
'is_hideout_area', | |||
'is_vaal_area', | |||
'is_labyrinth_area', | |||
'is_labyrinth_airlock_area', | |||
'is_labyrinth_boss_area', | |||
'has_waypoint', | |||
'mainpage_categories', | |||
-- Non argument, but passed to the script | |||
'areas', | |||
}, | |||
-- | -- | ||
-- User supplied arguments | -- User supplied arguments | ||
Line 170: | Line 144: | ||
field = 'main_page', | field = 'main_page', | ||
type = 'Page', | type = 'Page', | ||
func = function(tpl_args, | func = function(tpl_args, value) | ||
if value ~= nil then | |||
if | local page = mw.title.new(value) | ||
page = mw.title.new( | |||
if page == nil then | if page == nil then | ||
error(string.format(i18n.errors.main_page_is_invalid, | error(string.format(i18n.errors.main_page_is_invalid, value)) | ||
elseif not page.exists then | elseif not page.exists then | ||
error(string.format(i18n.errors.main_page_does_not_exist, | error(string.format(i18n.errors.main_page_does_not_exist, value)) | ||
else | else | ||
-- dont need the title object anymore | -- dont need the title object anymore | ||
Line 183: | Line 156: | ||
end | end | ||
end | end | ||
return value | |||
end | end | ||
}, | }, | ||
Line 202: | Line 176: | ||
field = 'act', | field = 'act', | ||
type = 'Integer', | type = 'Integer', | ||
}, | }, | ||
area_level = { | area_level = { | ||
field = 'area_level', | field = 'area_level', | ||
type = 'Integer', | type = 'Integer', | ||
}, | }, | ||
level_restriction_max = { | level_restriction_max = { | ||
field = 'level_restriction_max', | field = 'level_restriction_max', | ||
type = 'Integer', | type = 'Integer', | ||
default = 100, | default = 100, | ||
}, | }, | ||
Line 218: | Line 189: | ||
field = 'area_type_tags', | field = 'area_type_tags', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
func = | func = h.proc.factory.list{ | ||
callback = m_util.validate.factory.in_table_keys{ | |||
tbl = m_game.constants.tags, | |||
errmsg = i18n.errors.invalid_tag, | |||
} | errlvl = 4, | ||
}, | |||
}, | |||
default = {}, | |||
}, | }, | ||
tags = { | tags = { | ||
field = 'tags', | field = 'tags', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
func = | func = h.proc.factory.list{ | ||
callback = m_util.validate.factory.in_table_keys{ | |||
tbl = m_game.constants.tags, | |||
errmsg = i18n.errors.invalid_tag, | |||
} | errlvl = 4, | ||
}, | |||
}, | |||
default = {}, | |||
}, | }, | ||
loading_screen = { | loading_screen = { | ||
field = 'loading_screen', | field = 'loading_screen', | ||
type = 'Page', | type = 'Page', | ||
func = function (tpl_args, | func = function (tpl_args, value) | ||
if value ~= nil then | |||
if | -- value contains loading id | ||
tpl_args. | tpl_args.loading_screen_infobox = string.format(i18n.images.loading_screen_infobox, value) | ||
return string.format(i18n.images.loading_screen, value) | |||
end | end | ||
end, | end, | ||
Line 247: | Line 225: | ||
field = 'connection_ids', | field = 'connection_ids', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
default = {}, | default = {}, | ||
}, | }, | ||
Line 257: | Line 234: | ||
field = 'modifier_ids', | field = 'modifier_ids', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
func = function(tpl_args, | func = function(tpl_args, value) | ||
if value == nil then | |||
if | |||
return | return | ||
end | end | ||
tpl_args.mods = m_cargo.array_query{ | |||
tables={'mods'}, | |||
fields={'mods.stat_text'}, | |||
id_field='mods.id', | |||
id_array=value | |||
} | |||
return value | |||
end, | |||
default = {}, | |||
}, | |||
stat_text = { | |||
field = 'stat_text', | |||
type = 'Text', | |||
func = function (tpl_args, value) | |||
if tpl_args.mods == nil then | |||
return | |||
end | end | ||
local text = {} | local text = {} | ||
for page, row in pairs( | for page, row in pairs(tpl_args.mods) do | ||
if row['mods.stat_text'] ~= '' then | if row['mods.stat_text'] ~= '' then | ||
text[#text+1] = row['mods.stat_text'] | text[#text+1] = row['mods.stat_text'] | ||
end | end | ||
end | end | ||
return table.concat(text, '<br>') | |||
end, | end, | ||
}, | }, | ||
monster_ids = { | monster_ids = { | ||
field = 'monster_ids', | field = 'monster_ids', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
default = {}, | default = {}, | ||
}, | }, | ||
Line 297: | Line 273: | ||
field = 'boss_monster_ids', | field = 'boss_monster_ids', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
func = | func = function(tpl_args, value) | ||
if value == nil then | |||
return | |||
end | |||
-- Format the id so it follows cargo standards: | |||
local id = {} | |||
for i, v in ipairs(value) do | |||
id[#id+1] = string.format('"%s"', v) | |||
end | |||
-- Query monster data: | |||
tpl_args._boss_monster_ids = m_cargo.query( | |||
{'monsters', 'main_pages'}, | |||
{ | |||
'monsters._pageName', | |||
'monsters.name', | |||
'monsters.metadata_id', | |||
'main_pages._pageName', | |||
}, | |||
{ | |||
join='monsters.metadata_id=main_pages.id', | |||
where=string.format( | |||
'monsters.metadata_id IN (%s)', | |||
table.concat(id, ', ') | |||
), | |||
} | |||
) | |||
return value | |||
end, | |||
default = {}, | default = {}, | ||
}, | }, | ||
entry_text = { | entry_text = { | ||
field = 'entry_text', | field = 'entry_text', | ||
type = ' | type = 'Text', | ||
}, | }, | ||
entry_npc = { | entry_npc = { | ||
Line 310: | Line 316: | ||
flavour_text = { | flavour_text = { | ||
field = 'flavour_text', | field = 'flavour_text', | ||
type = ' | type = 'Text', | ||
}, | }, | ||
screenshot_ext = { | screenshot_ext = { | ||
func = nil, | func = nil, | ||
type = 'String', | |||
default = 'jpg', | default = 'jpg', | ||
}, | }, | ||
Line 319: | Line 326: | ||
field = 'screenshot', | field = 'screenshot', | ||
type = 'Page', | type = 'Page', | ||
func = function(tpl_args, | func = function(tpl_args, value) | ||
if tpl_args.name ~= nil then | if tpl_args.name ~= nil then | ||
tpl_args.screenshot = string.format(i18n.images.screenshot, | -- value contains screenshot id | ||
tpl_args.screenshot_infobox = string.format(i18n.images.screenshot_infobox, | local name = value or tpl_args.main_page or tpl_args.name | ||
tpl_args.screenshot = string.format(i18n.images.screenshot, name, tpl_args.screenshot_ext) | |||
tpl_args.screenshot_infobox = string.format(i18n.images.screenshot_infobox, name, tpl_args.screenshot_ext) | |||
end | end | ||
end, | end, | ||
Line 332: | Line 341: | ||
field = 'vaal_area_ids', | field = 'vaal_area_ids', | ||
type = 'List (,) of String', | type = 'List (,) of String', | ||
default = {}, | default = {}, | ||
}, | }, | ||
Line 338: | Line 346: | ||
field = 'vaal_area_spawn_chance', | field = 'vaal_area_spawn_chance', | ||
type = 'Integer', | type = 'Integer', | ||
default = 0, | default = 0, | ||
}, | }, | ||
Line 344: | Line 351: | ||
field = 'strongbox_spawn_chance', | field = 'strongbox_spawn_chance', | ||
type = 'Integer', | type = 'Integer', | ||
default = 0, | default = 0, | ||
}, | }, | ||
Line 350: | Line 356: | ||
field = 'strongbox_max_count', | field = 'strongbox_max_count', | ||
type = 'Integer', | type = 'Integer', | ||
default = 0, | default = 0, | ||
}, | }, | ||
-- fields are handled for this below | -- fields are handled for this below | ||
strongbox_rarity_weight = { | strongbox_rarity_weight = { | ||
func = function (tpl_args, | func = function (tpl_args, value) | ||
local weights = m_util.string.split(tpl_args[ | local weights = m_util.string.split(tpl_args['strongbox_rarity_weight'] or '', ', ') | ||
for index, | for index, rarity in ipairs(m_game.constants.rarity_order) do | ||
local value = tonumber(weights[index]) or 0 | local value = tonumber(weights[index]) or 0 | ||
-- will be read later | |||
tpl_args | tpl_args[string.format('strongbox_weight_%s', rarity)] = value | ||
end | end | ||
end, | end, | ||
}, | |||
strongbox_weight_normal = { | |||
field = 'strongbox_weight_normal', | |||
type = 'Integer', | |||
}, | |||
strongbox_weight_magic = { | |||
field = 'strongbox_weight_magic', | |||
type = 'Integer', | |||
}, | |||
strongbox_weight_rare = { | |||
field = 'strongbox_weight_rare', | |||
type = 'Integer', | |||
}, | |||
strongbox_weight_unique = { | |||
field = 'strongbox_weight_unique', | |||
type = 'Integer', | |||
}, | }, | ||
-- | -- | ||
Line 373: | Line 392: | ||
field = 'is_map_area', | field = 'is_map_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 379: | Line 397: | ||
field = 'is_unique_map_area', | field = 'is_unique_map_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 385: | Line 402: | ||
field = 'is_town_area', | field = 'is_town_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 391: | Line 407: | ||
field = 'is_hideout_area', | field = 'is_hideout_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 397: | Line 412: | ||
field = 'is_vaal_area', | field = 'is_vaal_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 409: | Line 417: | ||
field = 'is_labyrinth_area', | field = 'is_labyrinth_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 415: | Line 422: | ||
field = 'is_labyrinth_airlock_area', | field = 'is_labyrinth_airlock_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 421: | Line 427: | ||
field = 'is_labyrinth_boss_area', | field = 'is_labyrinth_boss_area', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | }, | ||
Line 427: | Line 432: | ||
field = 'has_waypoint', | field = 'has_waypoint', | ||
type = 'Boolean', | type = 'Boolean', | ||
default = false, | default = false, | ||
}, | |||
mainpage_categories = { | |||
field = 'mainpage_categories', | |||
type = 'List (,) of String', | |||
func = function (tpl_args, value) | |||
-- Category handling for main page only by adding the categories to a cargo field: | |||
-- Do notice the plural form. | |||
-- -- Areas, Act X areas/Map areas, Unique map areas | |||
local cats = { | |||
'Category:Areas', | |||
} | |||
local cats_ini_len = #cats | |||
for _, key in ipairs(display.area_type) do | |||
if tpl_args[key] == true then | |||
cats[#cats+1] = string.format('Category:%ss', i18n.tooltips[key]) | |||
end | |||
end | |||
if #cats == cats_ini_len then | |||
cats[#cats+1] = string.format('Category:Act %s areas', tpl_args.act) | |||
end | |||
return cats | |||
end | |||
}, | }, | ||
-- | -- | ||
-- Handled elsewhere | -- Handled elsewhere | ||
-- | -- | ||
release_version = { | release_version = { | ||
field = 'release_version', | field = 'release_version', | ||
type = 'String' | type = 'String', | ||
skip = true, | |||
}, | }, | ||
removal_version = { | removal_version = { | ||
field = 'removal_version', | field = 'removal_version', | ||
type = 'String', | type = 'String', | ||
skip = true, | |||
}, | }, | ||
infobox_html = { | infobox_html = { | ||
field = 'infobox_html', | field = 'infobox_html', | ||
type = ' | type = 'Text', | ||
skip = true, | |||
}, | }, | ||
-- | -- | ||
Line 473: | Line 478: | ||
-- | -- | ||
areas = { | areas = { | ||
func = function(tpl_args, | func = function(tpl_args, value) | ||
local query_ids = {} | local query_ids = {} | ||
for _, arg in ipairs({'parent_area_id', 'connection_ids', 'vaal_area_ids'}) do | for _, arg in ipairs({'parent_area_id', 'connection_ids', 'vaal_area_ids'}) do | ||
if type(tpl_args[arg]) == 'table' then | if type(tpl_args[arg]) == 'table' then | ||
for _, tbl_id in ipairs(tpl_args[arg]) do | for _, tbl_id in ipairs(tpl_args[arg]) do | ||
query_ids[tbl_id] = | query_ids[tbl_id] = true | ||
end | end | ||
elseif tpl_args[arg] then | elseif tpl_args[arg] then | ||
query_ids[tpl_args[arg]] = | query_ids[tpl_args[arg]] = true | ||
end | end | ||
end | end | ||
Line 487: | Line 492: | ||
local query_ids_trimmed = {} | local query_ids_trimmed = {} | ||
for k, _ in pairs(query_ids) do | for k, _ in pairs(query_ids) do | ||
query_ids_trimmed[#query_ids_trimmed+1] = k | |||
end | |||
local results=m_cargo.array_query{ | |||
tables={'areas'}, | |||
fields={'areas._pageName', 'areas.name', 'areas.main_page'}, | |||
id_field='areas.id', | |||
id_array=query_ids_trimmed, | |||
warning_on_missing=true, | |||
} | |||
local areas = {} | |||
for _, data in ipairs(results) do | |||
areas[data['areas.id']] = data | |||
end | end | ||
if | if tpl_args.is_vaal_area then | ||
local spawn_areas = {} | |||
results = m_cargo.query( | |||
{'areas'}, | |||
'areas', | {'areas._pageName', 'areas.id', 'areas.name', 'areas.main_page'}, | ||
'areas._pageName, areas.id, areas.name, areas.main_page', | |||
{ | { | ||
where= | -- TODO using a workaround since HOLDS is bricked | ||
-- Don't show connected areas without a main_page to avoid showing disabled or areas which are not relevant | |||
where=string.format([[ | |||
areas.vaal_area_ids__full LIKE "%%%s%%" | |||
AND areas.main_page IS NOT NULL | |||
AND areas.vaal_area_spawn_chance > 0 | |||
]], tpl_args.id), | |||
-- area id for story areas basically orders them by appearance, should be good enough | |||
orderBy='areas.id ASC', | |||
} | } | ||
) | ) | ||
for _, data in ipairs(results) do | |||
for _, | table.insert(spawn_areas, data['areas.id']) | ||
areas[data['areas.id']] = data | |||
end | end | ||
tpl_args.vaal_spawn_areas = spawn_areas | |||
end | end | ||
return areas | |||
end, | end, | ||
}, | }, | ||
Line 514: | Line 538: | ||
} | } | ||
display.area_type = { | display.area_type = { | ||
'is_map_area', | 'is_map_area', | ||
Line 564: | Line 544: | ||
'is_hideout_area', | 'is_hideout_area', | ||
'is_vaal_area', | 'is_vaal_area', | ||
'is_labyrinth_area', | 'is_labyrinth_area', | ||
'is_labyrinth_airlock_area', | 'is_labyrinth_airlock_area', | ||
Line 577: | Line 556: | ||
}, | }, | ||
header = i18n.headers.id, | header = i18n.headers.id, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return string.format('[[%s|%s]]', mw.title.getCurrentTitle().fullText, tpl_args.id) | return string.format('[[%s|%s]]', mw.title.getCurrentTitle().fullText, tpl_args.id) | ||
end, | end, | ||
Line 605: | Line 584: | ||
header = i18n.headers.level_restriction_max, | header = i18n.headers.level_restriction_max, | ||
func = factory.display_value('level_restriction_max'), | func = factory.display_value('level_restriction_max'), | ||
}, | |||
{ | |||
args = { | |||
boss_monster_ids = { | |||
}, | |||
}, | |||
header = i18n.headers.boss_monster_ids, | |||
func = function(tpl_args) | |||
local out = {} | |||
for _,v in ipairs(tpl_args._boss_monster_ids) do | |||
local page = v['main_pages._pageName'] or v['monsters._pageName'] | |||
local name = v['monsters.name'] or v['monsters.metadata_id'] | |||
out[#out+1] = string.format('[[%s|%s]]', page, name) | |||
end | |||
return table.concat(out, '<br>') | |||
end, | |||
}, | }, | ||
{ | { | ||
Line 612: | Line 607: | ||
}, | }, | ||
header = i18n.headers.area_type_tags, | header = i18n.headers.area_type_tags, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return table.concat(tpl_args.area_type_tags, ', ') | return table.concat(tpl_args.area_type_tags, ', ') | ||
end, | end, | ||
Line 622: | Line 617: | ||
}, | }, | ||
header = i18n.headers.tags, | header = i18n.headers.tags, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return table.concat(tpl_args.tags, ', ') | return table.concat(tpl_args.tags, ', ') | ||
end, | end, | ||
Line 632: | Line 627: | ||
}, | }, | ||
header = i18n.headers.entry_messsage, | header = i18n.headers.entry_messsage, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return m_util.html.poe_color('quest', -- Any other alternatives for spoken text? | return m_util.html.poe_color('quest', -- Any other alternatives for spoken text? | ||
string.format(i18n.tooltips.entry_message, tpl_args.entry_npc, tpl_args.entry_text) | string.format(i18n.tooltips.entry_message, tpl_args.entry_npc, tpl_args.entry_text) | ||
Line 643: | Line 638: | ||
}, | }, | ||
header = i18n.headers.parent_area, | header = i18n.headers.parent_area, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return util.display.single_area(tpl_args | return util.display.single_area(tpl_args, tpl_args.parent_area) | ||
end, | end, | ||
}, | }, | ||
Line 652: | Line 647: | ||
}, | }, | ||
header = i18n.headers.connections, | header = i18n.headers.connections, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return util.display.multiple_areas(tpl_args | return util.display.multiple_areas(tpl_args, tpl_args.connection_ids) | ||
end, | end, | ||
}, | }, | ||
Line 661: | Line 656: | ||
}, | }, | ||
header = i18n.headers.vaal_areas, | header = i18n.headers.vaal_areas, | ||
func = function(tpl_args, | func = function(tpl_args) | ||
return util.display.multiple_areas(tpl_args | return util.display.multiple_areas(tpl_args, tpl_args.vaal_area_ids) | ||
end, | |||
}, | |||
-- virtual field created by querying other area data to find where a vaal area is used | |||
{ | |||
args = { | |||
is_vaal_area = {}, | |||
vaal_spawn_areas = {}, | |||
}, | |||
header = i18n.headers.vaal_spawn_areas, | |||
func = function(tpl_args) | |||
return util.display.multiple_areas(tpl_args, tpl_args.vaal_spawn_areas) | |||
end, | end, | ||
}, | }, | ||
Line 672: | Line 678: | ||
flavour_text = {}, | flavour_text = {}, | ||
}, | }, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return m_util.html.poe_color('flavour', tpl_args.flavour_text) | return m_util.html.poe_color('flavour', tpl_args.flavour_text) | ||
end, | end, | ||
Line 692: | Line 698: | ||
stat_text = {}, | stat_text = {}, | ||
}, | }, | ||
func = function(tpl_args | func = function(tpl_args) | ||
return m_util.html.poe_color('mod', tpl_args.stat_text) | return m_util.html.poe_color('mod', tpl_args.stat_text) | ||
end, | end, | ||
Line 704: | Line 710: | ||
local d = {} | local d = {} | ||
function d.intro_text(tpl_args | function d.intro_text(tpl_args) | ||
--[[ | |||
Display an introductory text about the area. | |||
]] | |||
local out = {} | local out = {} | ||
if mw.ustring.find(tpl_args['id'], '_') then | if mw.ustring.find(tpl_args['id'], '_') then | ||
out[#out+1] = | out[#out+1] = mw.getCurrentFrame():expandTemplate{ | ||
title='Incorrect title', | |||
args = {title=tpl_args['id']} | |||
} | |||
end | end | ||
if tpl_args['name'] then | if tpl_args['name'] then | ||
out[#out+1] = string.format( | out[#out+1] = string.format( | ||
i18n.intro.text_with_name, | |||
tpl_args['id'], | |||
tpl_args['main_page'] or tostring(mw.title.getCurrentTitle()), | |||
tpl_args['name'] | |||
) | |||
else | else | ||
out[#out+1] = string.format( | out[#out+1] = string.format( | ||
i18n.intro.text_without_name, | |||
tpl_args['id'] | |||
) | |||
end | end | ||
Line 721: | Line 741: | ||
for _, id in ipairs(tpl_args[arg]) do | for _, id in ipairs(tpl_args[arg]) do | ||
if tpl_args.areas[id] then | if tpl_args.areas[id] then | ||
connected_areas[#connected_areas+1] = string.format('<li>[[%s|%s]] (%s)</li>', tostring(tpl_args.areas[id]['areas._pageName']), id, tostring(tpl_args.areas[id]['areas.name'])) | connected_areas[#connected_areas+1] = string.format( | ||
'<li>[[%s|%s]] (%s)</li>', | |||
tostring(tpl_args.areas[id]['areas._pageName']), | |||
id, | |||
tostring(tpl_args.areas[id]['areas.name']) | |||
) | |||
end | end | ||
end | end | ||
Line 727: | Line 752: | ||
end | end | ||
if #connected_areas > 0 then | if #connected_areas > 0 then | ||
out[#out+1] = string.format(' | out[#out+1] = string.format( | ||
i18n.intro.connections .. '<ul>%s</ul>', | |||
table.concat(connected_areas) | |||
) | |||
end | end | ||
Line 733: | Line 761: | ||
end | end | ||
function d._check_args(tpl_args | function d._check_args(tpl_args, data) | ||
local continue = true | local continue = true | ||
if data.args ~= nil then | if data.args ~= nil then | ||
Line 761: | Line 789: | ||
end | end | ||
function d.area_box(tpl_args | function d.area_box(tpl_args) | ||
--[[ | |||
Display the area info box. | |||
]] | |||
local infocard_args = {} | local infocard_args = {} | ||
Line 783: | Line 815: | ||
if tpl_args.is_town_area then | if tpl_args.is_town_area then | ||
infocard_args.headerright = i18n.images.waypoint_town | infocard_args.headerright = i18n.images.waypoint_town | ||
elseif tpl_args.has_waypoint and tpl_args.is_underground then | |||
infocard_args.headerright = i18n.images.waypoint_underground_yes | |||
elseif tpl_args.has_waypoint then | elseif tpl_args.has_waypoint then | ||
infocard_args.headerright = i18n.images.waypoint_yes | infocard_args.headerright = i18n.images.waypoint_yes | ||
elseif tpl_args.is_underground then | |||
infocard_args.headerright = i18n.images.waypoint_underground_no | |||
else | else | ||
infocard_args.headerright = i18n.images.waypoint_no | infocard_args.headerright = i18n.images.waypoint_no | ||
Line 792: | Line 828: | ||
local tbl = mw.html.create('table') | local tbl = mw.html.create('table') | ||
for _, data in ipairs(display.table_map) do | for _, data in ipairs(display.table_map) do | ||
if d._check_args(tpl_args | if d._check_args(tpl_args, data) then | ||
tbl | tbl | ||
:tag('tr') | :tag('tr') | ||
Line 799: | Line 835: | ||
:done() | :done() | ||
:tag('td') | :tag('td') | ||
:wikitext(data.func(tpl_args | :wikitext(data.func(tpl_args) or '') | ||
:done() | :done() | ||
:done() | :done() | ||
Line 809: | Line 845: | ||
local i = 2 | local i = 2 | ||
for _, data in ipairs(display.list_map) do | for _, data in ipairs(display.list_map) do | ||
if d._check_args(tpl_args | if d._check_args(tpl_args, data) then | ||
infocard_args[i] = data.func(tpl_args | infocard_args[i] = data.func(tpl_args) | ||
i = i + 1 | i = i + 1 | ||
end | end | ||
Line 818: | Line 854: | ||
end | end | ||
function d.subobject_box(tpl_args | function d.subobject_box(tpl_args) | ||
--[[ | |||
Display the subobject box. | |||
]] | |||
local container = mw.html.create('div') | local container = mw.html.create('div') | ||
container | container | ||
Line 824: | Line 863: | ||
-- spawn weight table | -- spawn weight table | ||
tbl = container:tag('table') | local tbl = container:tag('table') | ||
tbl | tbl | ||
:attr('class', 'wikitable sortable') | :attr('class', 'wikitable sortable') | ||
Line 877: | Line 916: | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
-- | -- Main functions | ||
-- ---------------------------------------------------------------------------- | -- ---------------------------------------------------------------------------- | ||
local | local function _area(tpl_args) | ||
--[[ | |||
This function adds cargo tables and displays information about the | |||
area. | |||
function | |||
-- | |||
Examples | |||
-------- | |||
} | = p.area{ | ||
id = '1_1_1', | |||
name = 'The Twilight Strand', | |||
act = 1, | |||
level = 1, | |||
tags = 'no_tempests, area_with_water', | |||
loading_screen = 'Act1', | |||
connection_ids = '1_1_town', | |||
parent_area_id = '1_1_town', | |||
boss_monster_ids = 'Metadata/Monsters/ZombieBoss/ZombieBossHillockNormal', | |||
flavour_text = 'Hope was drowned here.', | |||
main_page = 'The Twilight Strand (Act 1)' | |||
} | |||
= p.area{ | |||
id = '1_1_1', | |||
name = 'The Twilight Strand', | |||
act = 1, | |||
level = 1, | |||
tags = 'no_tempests, area_with_water', | |||
loading_screen = 'Act1', | |||
connection_ids = '1_1_town', | |||
parent_area_id = '1_1_town', | |||
boss_monster_ids = 'Metadata/Monsters/ZombieBoss/ZombieBossHillockNormal', | |||
flavour_text = 'Hope was drowned here.', main_page = 'The Twilight Strand (Act 1)' | |||
} | |||
= p.area{ | |||
id = '1_4_2', | |||
name = 'The Dried Lake', | |||
act = '4', | |||
level = '34', | |||
area_type_tags = 'shore', | |||
tags = 'act_boss_area', | |||
loading_screen = 'Act4', | |||
connection_ids = '1_4_town', | |||
parent_area_id = '1_4_town', | |||
boss_monster_ids = 'Metadata/Monsters/Voll/VollBoss, Metadata/Monsters/SkeletonSoldier/SkeletonSoldierRangedBoss', | |||
vaal_area_spawn_chance = '18', | |||
vaal_area_ids = '1_SideArea4_2, 1_SideArea4_4', | |||
strongbox_spawn_chance = '30', | |||
strongbox_max = '2', strongbox_rarity_weight = '50, 50, 50, 1', | |||
flavour_text = 'Bones of betrayal, ashes of purity.', main_page = 'The Dried Lake' | |||
} | |||
]] | |||
-- | -- | ||
-- Shared args | -- Shared args | ||
-- | -- | ||
-- Handle release_version and removal_version | -- Handle release_version and removal_version | ||
m_util.args.version(tpl_args | m_util.args.version(tpl_args) | ||
local cargo_values = m_util.args.from_cargo_map{ | |||
tpl_args=tpl_args, | |||
table_map=argument_map, | |||
rtr=true, | |||
} | |||
-- Parse spawn weights | -- Parse spawn weights | ||
m_util.args. | m_util.args.spawn_weight_list(tpl_args) | ||
-- Display only on main pages: | -- Display only on main pages: | ||
local out = {} | local out = {} | ||
out[#out+1] = d.area_box(tpl_args | out[#out+1] = d.area_box(tpl_args) | ||
-- Property to store what's output to main pages: | -- Property to store what's output to main pages: | ||
cargo_values['infobox_html'] = out[1] | |||
-- Set all semantic properties: | -- Set all semantic properties: | ||
mw.logObject('Cargo:' .. | mw.logObject('Cargo:' .. m_cargo.store(cargo_values)) | ||
-- mw.logObject(tpl_args) | |||
-- Display only on data page: | -- Display only on data page: | ||
out[#out+1] = d.subobject_box(tpl_args | out[#out+1] = d.subobject_box(tpl_args) | ||
out[#out+1] = d.intro_text(tpl_args | out[#out+1] = d.intro_text(tpl_args) | ||
-- Attach to table | |||
mw.getCurrentFrame():expandTemplate{title = i18n.templates.attach_template} | |||
-- Category handling for the local data page: | |||
out[#out+1] = m_util.misc.add_category({i18n.categories.area_data}) | |||
-- Output of function | -- Output of function | ||
return table.concat(out | return table.concat(out) | ||
end | end | ||
function | local function _query_area_info(tpl_args) | ||
-- p.query_area_info{conditions = '[[Is area id::test]]', cats='yes'} | --[[ | ||
Queries and displays the area infobox. | |||
]] | |||
-- = p.query_area_info{conditions = '[[Is area id::test]]', cats='yes'} | |||
if tpl_args.where == nil then | |||
return | |||
end | |||
tpl_args.cats = m_util.cast.boolean(tpl_args.cats) | tpl_args.cats = m_util.cast.boolean(tpl_args.cats) | ||
local results = | local results = m_cargo.query( | ||
{'areas'}, | |||
' | {'areas.infobox_html', 'areas.mainpage_categories'}, | ||
{ | |||
where=tpl_args.where, | |||
orderBy=tpl_args.order_by, | |||
} | |||
) | |||
local out = {} | local out = {} | ||
Line 998: | Line 1,036: | ||
for _, row in ipairs(results) do | for _, row in ipairs(results) do | ||
out[#out+1] = row[' | out[#out+1] = row['areas.infobox_html'] | ||
if row[' | if row['areas.mainpage_categories'] ~= '' then | ||
for _, cat in ipairs(m_util.string.split(row[' | for _, cat in ipairs(m_util.string.split(row['areas.mainpage_categories'], ',')) do | ||
cats[#cats+1] = string.gsub(cat, '[Cc]ategory:', '') | cats[#cats+1] = string.gsub(cat, '[Cc]ategory:', '') | ||
end | end | ||
Line 1,011: | Line 1,049: | ||
end | end | ||
end | end | ||
-- ---------------------------------------------------------------------------- | |||
-- Exported functions | |||
-- ---------------------------------------------------------------------------- | |||
local p = {} | |||
p.table_areas = m_cargo.declare_factory{data=argument_map} | |||
-- | |||
-- Template:Area | |||
-- | |||
p.area = m_util.misc.invoker_factory(_area, { | |||
wrappers = cfg.wrappers.area, | |||
}) | |||
-- | |||
-- Template:Query area infoboxes | |||
-- | |||
p.query_area_info = m_util.misc.invoker_factory(_query_area_info, { | |||
wrappers = cfg.wrappers.query_area_info, | |||
}) | |||
return p | return p |
Latest revision as of 08:51, 20 January 2025
![](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5f/Ambox_warning_orange.svg/45px-Ambox_warning_orange.svg.png)
This module is used on 4000+ pages.
To avoid major disruption and server load, do not make unnecessary edits to this module. Test changes to this module first using its /sandbox and /testcases subpages or your user space. All of the changes can then be applied to this module in a single edit.
Consider discussing changes on the talk page or on Discord before implementing them.
The item module provides functionality for various area-related templates.
Overview
This module is responsible for creating area boxes, other area-related tasks. In the process a lot of the input data is verified and also added as semantic property to pages; as such, any templates deriving from this module should not be used on user pages other then for temporary testing purposes.
This template is also backed by an export script in PyPoE (pypoe_exporter wiki area ...
) which can be used to export item data from the game files which then can be used on the wiki. Use the export when possible.
Implemented templates
Core templates
- {{Area}}
- {{Query area infoboxes}}
Editors can experiment in this module's sandbox and testcases pages.
Subpages of this module.
-------------------------------------------------------------------------------
--
-- Module:Area
--
-- This module implements Template:Area and Template:Query area infoboxes
-------------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
-- TODO
-- ----------------------------------------------------------------------------
-- invalid tags -> utl?
-- spawn_weight* values
-- spawnchacne values
require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
local m_game = mw.loadData('Module:Game')
-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Area')
local f_infocard = require('Module:Infocard')._main
-- 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:Area/config/sandbox') or mw.loadData('Module:Area/config')
local i18n = cfg.i18n
-- ----------------------------------------------------------------------------
-- Utility & helper functions
-- ----------------------------------------------------------------------------
local h = {}
--
-- Functions for processing tpl_args
--
h.proc = {}
h.proc.factory = {}
function h.proc.factory.list(args)
args = args or {}
return function (tpl_args, value)
return m_util.cast.table(value, {
pattern = args.pattern,
callback = args.callback,
})
end
end
local factory = {}
function factory.display_value(k, args)
return function(tpl_args)
return tpl_args[k]
end
end
local util = {}
util.display = {}
function util.display.multiple_areas(tpl_args, area_ids)
local out = {}
for _, area_id in ipairs(area_ids) do
out[#out+1] = util.display.single_area(tpl_args, area_id)
end
return table.concat(out, '<br>')
end
function util.display.single_area(tpl_args, area_id)
if tpl_args.areas[area_id] then
if tpl_args.areas[area_id]['areas.main_page'] then
return string.format('[[%s|%s]]', tpl_args.areas[area_id]['areas.main_page'], tpl_args.areas[area_id]['areas.name'])
else
return string.format('%s ([[%s|%s]])', tpl_args.areas[area_id]['areas.name'], tpl_args.areas[area_id]['areas._pageName'], area_id)
end
else
return area_id
end
end
-- ----------------------------------------------------------------------------
-- Argument & display mapping
-- ----------------------------------------------------------------------------
local display = {}
local argument_map = {
table = 'areas',
order = {
'main_page',
'id',
'name',
'act',
'area_level',
'level_restriction_max',
'area_type_tags',
'tags',
'loading_screen',
'connection_ids',
'parent_area_id',
'modifier_ids',
-- populated via modifier ids
'stat_text',
'boss_monster_ids',
'monster_ids',
'entry_text',
'entry_npc',
'flavour_text',
'screenshot_ext',
'screenshot',
'vaal_area_spawn_chance',
'vaal_area_ids',
'strongbox_spawn_chance',
'strongbox_max_count',
'strongbox_rarity_weight',
-- those four are parsed via the argument above
'strongbox_weight_normal',
'strongbox_weight_magic',
'strongbox_weight_rare',
'strongbox_weight_unique',
'is_map_area',
'is_unique_map_area',
'is_town_area',
'is_hideout_area',
'is_vaal_area',
'is_labyrinth_area',
'is_labyrinth_airlock_area',
'is_labyrinth_boss_area',
'has_waypoint',
'mainpage_categories',
-- Non argument, but passed to the script
'areas',
},
--
-- User supplied arguments
--
fields = {
main_page = {
field = 'main_page',
type = 'Page',
func = function(tpl_args, value)
if value ~= nil then
local page = mw.title.new(value)
if page == nil then
error(string.format(i18n.errors.main_page_is_invalid, value))
elseif not page.exists then
error(string.format(i18n.errors.main_page_does_not_exist, value))
else
-- dont need the title object anymore
--tpl_args.main_page = page
end
end
return value
end
},
--
-- Can be populated by PyPoE
--
id = {
field = 'id',
type = 'String',
func = nil,
},
name = {
field = 'name',
type = 'String',
func = nil,
},
act = {
field = 'act',
type = 'Integer',
},
area_level = {
field = 'area_level',
type = 'Integer',
},
level_restriction_max = {
field = 'level_restriction_max',
type = 'Integer',
default = 100,
},
area_type_tags = {
field = 'area_type_tags',
type = 'List (,) of String',
func = h.proc.factory.list{
callback = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.tags,
errmsg = i18n.errors.invalid_tag,
errlvl = 4,
},
},
default = {},
},
tags = {
field = 'tags',
type = 'List (,) of String',
func = h.proc.factory.list{
callback = m_util.validate.factory.in_table_keys{
tbl = m_game.constants.tags,
errmsg = i18n.errors.invalid_tag,
errlvl = 4,
},
},
default = {},
},
loading_screen = {
field = 'loading_screen',
type = 'Page',
func = function (tpl_args, value)
if value ~= nil then
-- value contains loading id
tpl_args.loading_screen_infobox = string.format(i18n.images.loading_screen_infobox, value)
return string.format(i18n.images.loading_screen, value)
end
end,
},
connection_ids = {
field = 'connection_ids',
type = 'List (,) of String',
default = {},
},
parent_area_id = {
field = 'parent_area_id',
type = 'String',
},
modifier_ids = {
field = 'modifier_ids',
type = 'List (,) of String',
func = function(tpl_args, value)
if value == nil then
return
end
tpl_args.mods = m_cargo.array_query{
tables={'mods'},
fields={'mods.stat_text'},
id_field='mods.id',
id_array=value
}
return value
end,
default = {},
},
stat_text = {
field = 'stat_text',
type = 'Text',
func = function (tpl_args, value)
if tpl_args.mods == nil then
return
end
local text = {}
for page, row in pairs(tpl_args.mods) do
if row['mods.stat_text'] ~= '' then
text[#text+1] = row['mods.stat_text']
end
end
return table.concat(text, '<br>')
end,
},
monster_ids = {
field = 'monster_ids',
type = 'List (,) of String',
default = {},
},
boss_monster_ids = {
field = 'boss_monster_ids',
type = 'List (,) of String',
func = function(tpl_args, value)
if value == nil then
return
end
-- Format the id so it follows cargo standards:
local id = {}
for i, v in ipairs(value) do
id[#id+1] = string.format('"%s"', v)
end
-- Query monster data:
tpl_args._boss_monster_ids = m_cargo.query(
{'monsters', 'main_pages'},
{
'monsters._pageName',
'monsters.name',
'monsters.metadata_id',
'main_pages._pageName',
},
{
join='monsters.metadata_id=main_pages.id',
where=string.format(
'monsters.metadata_id IN (%s)',
table.concat(id, ', ')
),
}
)
return value
end,
default = {},
},
entry_text = {
field = 'entry_text',
type = 'Text',
},
entry_npc = {
field = 'entry_npc',
type = 'String',
},
flavour_text = {
field = 'flavour_text',
type = 'Text',
},
screenshot_ext = {
func = nil,
type = 'String',
default = 'jpg',
},
screenshot = {
field = 'screenshot',
type = 'Page',
func = function(tpl_args, value)
if tpl_args.name ~= nil then
-- value contains screenshot id
local name = value or tpl_args.main_page or tpl_args.name
tpl_args.screenshot = string.format(i18n.images.screenshot, name, tpl_args.screenshot_ext)
tpl_args.screenshot_infobox = string.format(i18n.images.screenshot_infobox, name, tpl_args.screenshot_ext)
end
end,
},
--
-- Spawn chances
--
vaal_area_ids = {
field = 'vaal_area_ids',
type = 'List (,) of String',
default = {},
},
vaal_area_spawn_chance = {
field = 'vaal_area_spawn_chance',
type = 'Integer',
default = 0,
},
strongbox_spawn_chance = {
field = 'strongbox_spawn_chance',
type = 'Integer',
default = 0,
},
strongbox_max_count = {
field = 'strongbox_max_count',
type = 'Integer',
default = 0,
},
-- fields are handled for this below
strongbox_rarity_weight = {
func = function (tpl_args, value)
local weights = m_util.string.split(tpl_args['strongbox_rarity_weight'] or '', ', ')
for index, rarity in ipairs(m_game.constants.rarity_order) do
local value = tonumber(weights[index]) or 0
-- will be read later
tpl_args[string.format('strongbox_weight_%s', rarity)] = value
end
end,
},
strongbox_weight_normal = {
field = 'strongbox_weight_normal',
type = 'Integer',
},
strongbox_weight_magic = {
field = 'strongbox_weight_magic',
type = 'Integer',
},
strongbox_weight_rare = {
field = 'strongbox_weight_rare',
type = 'Integer',
},
strongbox_weight_unique = {
field = 'strongbox_weight_unique',
type = 'Integer',
},
--
-- Area flags
--
is_map_area = {
field = 'is_map_area',
type = 'Boolean',
default = false,
},
is_unique_map_area = {
field = 'is_unique_map_area',
type = 'Boolean',
default = false,
},
is_town_area = {
field = 'is_town_area',
type = 'Boolean',
default = false,
},
is_hideout_area = {
field = 'is_hideout_area',
type = 'Boolean',
default = false,
},
is_vaal_area = {
field = 'is_vaal_area',
type = 'Boolean',
default = false,
},
is_labyrinth_area = {
field = 'is_labyrinth_area',
type = 'Boolean',
default = false,
},
is_labyrinth_airlock_area = {
field = 'is_labyrinth_airlock_area',
type = 'Boolean',
default = false,
},
is_labyrinth_boss_area = {
field = 'is_labyrinth_boss_area',
type = 'Boolean',
default = false,
},
has_waypoint = {
field = 'has_waypoint',
type = 'Boolean',
default = false,
},
mainpage_categories = {
field = 'mainpage_categories',
type = 'List (,) of String',
func = function (tpl_args, value)
-- Category handling for main page only by adding the categories to a cargo field:
-- Do notice the plural form.
-- -- Areas, Act X areas/Map areas, Unique map areas
local cats = {
'Category:Areas',
}
local cats_ini_len = #cats
for _, key in ipairs(display.area_type) do
if tpl_args[key] == true then
cats[#cats+1] = string.format('Category:%ss', i18n.tooltips[key])
end
end
if #cats == cats_ini_len then
cats[#cats+1] = string.format('Category:Act %s areas', tpl_args.act)
end
return cats
end
},
--
-- Handled elsewhere
--
release_version = {
field = 'release_version',
type = 'String',
skip = true,
},
removal_version = {
field = 'removal_version',
type = 'String',
skip = true,
},
infobox_html = {
field = 'infobox_html',
type = 'Text',
skip = true,
},
--
-- Not argument to the template, but still parsing arguments
--
areas = {
func = function(tpl_args, value)
local query_ids = {}
for _, arg in ipairs({'parent_area_id', 'connection_ids', 'vaal_area_ids'}) do
if type(tpl_args[arg]) == 'table' then
for _, tbl_id in ipairs(tpl_args[arg]) do
query_ids[tbl_id] = true
end
elseif tpl_args[arg] then
query_ids[tpl_args[arg]] = true
end
end
local query_ids_trimmed = {}
for k, _ in pairs(query_ids) do
query_ids_trimmed[#query_ids_trimmed+1] = k
end
local results=m_cargo.array_query{
tables={'areas'},
fields={'areas._pageName', 'areas.name', 'areas.main_page'},
id_field='areas.id',
id_array=query_ids_trimmed,
warning_on_missing=true,
}
local areas = {}
for _, data in ipairs(results) do
areas[data['areas.id']] = data
end
if tpl_args.is_vaal_area then
local spawn_areas = {}
results = m_cargo.query(
{'areas'},
{'areas._pageName', 'areas.id', 'areas.name', 'areas.main_page'},
{
-- TODO using a workaround since HOLDS is bricked
-- Don't show connected areas without a main_page to avoid showing disabled or areas which are not relevant
where=string.format([[
areas.vaal_area_ids__full LIKE "%%%s%%"
AND areas.main_page IS NOT NULL
AND areas.vaal_area_spawn_chance > 0
]], tpl_args.id),
-- area id for story areas basically orders them by appearance, should be good enough
orderBy='areas.id ASC',
}
)
for _, data in ipairs(results) do
table.insert(spawn_areas, data['areas.id'])
areas[data['areas.id']] = data
end
tpl_args.vaal_spawn_areas = spawn_areas
end
return areas
end,
},
},
}
display.area_type = {
'is_map_area',
'is_unique_map_area',
'is_town_area',
'is_hideout_area',
'is_vaal_area',
'is_labyrinth_area',
'is_labyrinth_airlock_area',
'is_labyrinth_boss_area',
}
display.table_map = {
{
args = {
id = {
},
},
header = i18n.headers.id,
func = function(tpl_args)
return string.format('[[%s|%s]]', mw.title.getCurrentTitle().fullText, tpl_args.id)
end,
},
{
args = {
act = {
},
},
header = i18n.headers.act,
func = factory.display_value('act'),
},
{
args = {
area_level = {
},
},
header = i18n.headers.area_level,
func = factory.display_value('area_level'),
},
{
args = {
level_restriction_max = {
hide = 100,
},
},
header = i18n.headers.level_restriction_max,
func = factory.display_value('level_restriction_max'),
},
{
args = {
boss_monster_ids = {
},
},
header = i18n.headers.boss_monster_ids,
func = function(tpl_args)
local out = {}
for _,v in ipairs(tpl_args._boss_monster_ids) do
local page = v['main_pages._pageName'] or v['monsters._pageName']
local name = v['monsters.name'] or v['monsters.metadata_id']
out[#out+1] = string.format('[[%s|%s]]', page, name)
end
return table.concat(out, '<br>')
end,
},
{
args = {
area_type_tags = {
},
},
header = i18n.headers.area_type_tags,
func = function(tpl_args)
return table.concat(tpl_args.area_type_tags, ', ')
end,
},
{
args = {
tags = {
},
},
header = i18n.headers.tags,
func = function(tpl_args)
return table.concat(tpl_args.tags, ', ')
end,
},
{
args = {
entry_text = {},
entry_npc = {},
},
header = i18n.headers.entry_messsage,
func = function(tpl_args)
return m_util.html.poe_color('quest', -- Any other alternatives for spoken text?
string.format(i18n.tooltips.entry_message, tpl_args.entry_npc, tpl_args.entry_text)
)
end,
},
{
args = {
parent_area_ids = {},
},
header = i18n.headers.parent_area,
func = function(tpl_args)
return util.display.single_area(tpl_args, tpl_args.parent_area)
end,
},
{
args = {
connection_ids = {},
},
header = i18n.headers.connections,
func = function(tpl_args)
return util.display.multiple_areas(tpl_args, tpl_args.connection_ids)
end,
},
{
args = {
vaal_area_ids = {},
},
header = i18n.headers.vaal_areas,
func = function(tpl_args)
return util.display.multiple_areas(tpl_args, tpl_args.vaal_area_ids)
end,
},
-- virtual field created by querying other area data to find where a vaal area is used
{
args = {
is_vaal_area = {},
vaal_spawn_areas = {},
},
header = i18n.headers.vaal_spawn_areas,
func = function(tpl_args)
return util.display.multiple_areas(tpl_args, tpl_args.vaal_spawn_areas)
end,
},
}
display.list_map = {
{
args = {
flavour_text = {},
},
func = function(tpl_args)
return m_util.html.poe_color('flavour', tpl_args.flavour_text)
end,
},
{
args = {
loading_screen_infobox = {},
},
func = factory.display_value('loading_screen_infobox'),
},
{
args = {
screenshot_infobox = {},
},
func = factory.display_value('screenshot_infobox'),
},
{
args = {
stat_text = {},
},
func = function(tpl_args)
return m_util.html.poe_color('mod', tpl_args.stat_text)
end,
},
}
-- ----------------------------------------------------------------------------
-- display functions
-- ----------------------------------------------------------------------------
local d = {}
function d.intro_text(tpl_args)
--[[
Display an introductory text about the area.
]]
local out = {}
if mw.ustring.find(tpl_args['id'], '_') then
out[#out+1] = mw.getCurrentFrame():expandTemplate{
title='Incorrect title',
args = {title=tpl_args['id']}
}
end
if tpl_args['name'] then
out[#out+1] = string.format(
i18n.intro.text_with_name,
tpl_args['id'],
tpl_args['main_page'] or tostring(mw.title.getCurrentTitle()),
tpl_args['name']
)
else
out[#out+1] = string.format(
i18n.intro.text_without_name,
tpl_args['id']
)
end
local connected_areas = {}
for _, arg in ipairs({'connection_ids', 'vaal_area_ids'}) do
if tpl_args[arg] then
for _, id in ipairs(tpl_args[arg]) do
if tpl_args.areas[id] then
connected_areas[#connected_areas+1] = string.format(
'<li>[[%s|%s]] (%s)</li>',
tostring(tpl_args.areas[id]['areas._pageName']),
id,
tostring(tpl_args.areas[id]['areas.name'])
)
end
end
end
end
if #connected_areas > 0 then
out[#out+1] = string.format(
i18n.intro.connections .. '<ul>%s</ul>',
table.concat(connected_areas)
)
end
return table.concat(out)
end
function d._check_args(tpl_args, data)
local continue = true
if data.args ~= nil then
for key, key_data in pairs(data.args) do
if tpl_args[key] == nil or (type(tpl_args[key]) == 'table' and #tpl_args[key] == 0) then
continue = false
break
elseif type(key_data.hide) == 'table' then
local br = false
for _, value in ipairs(key_data.hide) do
if tpl_args[key] == value then
br = true
break
end
end
if br then
continue = false
break
end
elseif key_data.hide ~= nil and tpl_args[key] == key_data.hide then
continue = false
break
end
end
end
return continue
end
function d.area_box(tpl_args)
--[[
Display the area info box.
]]
local infocard_args = {}
infocard_args.header = tpl_args.name
-- Subheader
local out = {}
for _, key in ipairs(display.area_type) do
if tpl_args[key] == true then
out[#out+1] = i18n.tooltips[key]
end
end
if #out > 0 then
infocard_args.subheader = table.concat(out, ', ')
else
infocard_args.subheader = i18n.tooltips.area
end
-- Side header
if tpl_args.is_town_area then
infocard_args.headerright = i18n.images.waypoint_town
elseif tpl_args.has_waypoint and tpl_args.is_underground then
infocard_args.headerright = i18n.images.waypoint_underground_yes
elseif tpl_args.has_waypoint then
infocard_args.headerright = i18n.images.waypoint_yes
elseif tpl_args.is_underground then
infocard_args.headerright = i18n.images.waypoint_underground_no
else
infocard_args.headerright = i18n.images.waypoint_no
end
-- Main sections, loop through
local tbl = mw.html.create('table')
for _, data in ipairs(display.table_map) do
if d._check_args(tpl_args, data) then
tbl
:tag('tr')
:tag('th')
:wikitext(data.header or '')
:done()
:tag('td')
:wikitext(data.func(tpl_args) or '')
:done()
:done()
end
end
infocard_args[1] = tostring(tbl)
local i = 2
for _, data in ipairs(display.list_map) do
if d._check_args(tpl_args, data) then
infocard_args[i] = data.func(tpl_args)
i = i + 1
end
end
return f_infocard(infocard_args)
end
function d.subobject_box(tpl_args)
--[[
Display the subobject box.
]]
local container = mw.html.create('div')
container
:attr('class', 'modbox floatright')
-- spawn weight table
local tbl = container:tag('table')
tbl
:attr('class', 'wikitable sortable')
-- :attr('style', 'style="width: 100%;"')
:tag('tr')
:tag('th')
:attr('colspan', 3)
:wikitext('Spawn Weights')
:done()
:done()
:tag('tr')
:tag('th')
:wikitext('#')
:done()
:tag('th')
:wikitext('Tag')
:done()
:tag('th')
:wikitext('Has spawn weight')
:done()
:done()
:done()
local i = 0
local value = nil
repeat
i = i + 1
value = {
tag = tpl_args[string.format('spawn_weight%s_tag', i)],
value = tpl_args[string.format('spawn_weight%s_value', i)],
}
if value.tag then
tbl
:tag('tr')
:tag('td')
:wikitext(i)
:done()
:tag('td')
:wikitext(value.tag)
:done()
:tag('td')
:wikitext(value.value)
:done()
:done()
:done()
end
until value.tag == nil
return tostring(container)
end
-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------
local function _area(tpl_args)
--[[
This function adds cargo tables and displays information about the
area.
Examples
--------
= p.area{
id = '1_1_1',
name = 'The Twilight Strand',
act = 1,
level = 1,
tags = 'no_tempests, area_with_water',
loading_screen = 'Act1',
connection_ids = '1_1_town',
parent_area_id = '1_1_town',
boss_monster_ids = 'Metadata/Monsters/ZombieBoss/ZombieBossHillockNormal',
flavour_text = 'Hope was drowned here.',
main_page = 'The Twilight Strand (Act 1)'
}
= p.area{
id = '1_1_1',
name = 'The Twilight Strand',
act = 1,
level = 1,
tags = 'no_tempests, area_with_water',
loading_screen = 'Act1',
connection_ids = '1_1_town',
parent_area_id = '1_1_town',
boss_monster_ids = 'Metadata/Monsters/ZombieBoss/ZombieBossHillockNormal',
flavour_text = 'Hope was drowned here.', main_page = 'The Twilight Strand (Act 1)'
}
= p.area{
id = '1_4_2',
name = 'The Dried Lake',
act = '4',
level = '34',
area_type_tags = 'shore',
tags = 'act_boss_area',
loading_screen = 'Act4',
connection_ids = '1_4_town',
parent_area_id = '1_4_town',
boss_monster_ids = 'Metadata/Monsters/Voll/VollBoss, Metadata/Monsters/SkeletonSoldier/SkeletonSoldierRangedBoss',
vaal_area_spawn_chance = '18',
vaal_area_ids = '1_SideArea4_2, 1_SideArea4_4',
strongbox_spawn_chance = '30',
strongbox_max = '2', strongbox_rarity_weight = '50, 50, 50, 1',
flavour_text = 'Bones of betrayal, ashes of purity.', main_page = 'The Dried Lake'
}
]]
--
-- Shared args
--
-- Handle release_version and removal_version
m_util.args.version(tpl_args)
local cargo_values = m_util.args.from_cargo_map{
tpl_args=tpl_args,
table_map=argument_map,
rtr=true,
}
-- Parse spawn weights
m_util.args.spawn_weight_list(tpl_args)
-- Display only on main pages:
local out = {}
out[#out+1] = d.area_box(tpl_args)
-- Property to store what's output to main pages:
cargo_values['infobox_html'] = out[1]
-- Set all semantic properties:
mw.logObject('Cargo:' .. m_cargo.store(cargo_values))
-- mw.logObject(tpl_args)
-- Display only on data page:
out[#out+1] = d.subobject_box(tpl_args)
out[#out+1] = d.intro_text(tpl_args)
-- Attach to table
mw.getCurrentFrame():expandTemplate{title = i18n.templates.attach_template}
-- Category handling for the local data page:
out[#out+1] = m_util.misc.add_category({i18n.categories.area_data})
-- Output of function
return table.concat(out)
end
local function _query_area_info(tpl_args)
--[[
Queries and displays the area infobox.
]]
-- = p.query_area_info{conditions = '[[Is area id::test]]', cats='yes'}
if tpl_args.where == nil then
return
end
tpl_args.cats = m_util.cast.boolean(tpl_args.cats)
local results = m_cargo.query(
{'areas'},
{'areas.infobox_html', 'areas.mainpage_categories'},
{
where=tpl_args.where,
orderBy=tpl_args.order_by,
}
)
local out = {}
local cats = {}
for _, row in ipairs(results) do
out[#out+1] = row['areas.infobox_html']
if row['areas.mainpage_categories'] ~= '' then
for _, cat in ipairs(m_util.string.split(row['areas.mainpage_categories'], ',')) do
cats[#cats+1] = string.gsub(cat, '[Cc]ategory:', '')
end
end
end
if tpl_args.cats then
return table.concat(out) .. m_util.misc.add_category(cats)
else
return table.concat(out)
end
end
-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------
local p = {}
p.table_areas = m_cargo.declare_factory{data=argument_map}
--
-- Template:Area
--
p.area = m_util.misc.invoker_factory(_area, {
wrappers = cfg.wrappers.area,
})
--
-- Template:Query area infoboxes
--
p.query_area_info = m_util.misc.invoker_factory(_query_area_info, {
wrappers = cfg.wrappers.query_area_info,
})
return p