Module:Area: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
>OmegaK2
m (Fixes)
>OmegaK2
(use table for values, reworked i18n usage, added some queries for area ids)
Line 81: Line 81:
          
          
         --
         --
         act = 'Act: %s',
         entry_message = '%s: %s',
         area_level = 'Area level: %s',
    },
         level_restriction_max = m_util.html.abbr('Max level', 'Characters above this level can not enter this zone.') .. ': %s',
    headers = {
         area_type_tags = 'Area type tags: %s',
        act = 'Act',
         tags = 'Tags: %s',
         area_level = 'Area level',
         parent_area = m_util.html.abbr('Parent Town', 'Portals will lead to the parent town') .. ': %s',
         level_restriction_max = m_util.html.abbr('Max level', 'Characters above this level can not enter this zone.'),
         connections = 'Connections: %s',
         area_type_tags = 'Area type tags',
         tags = 'Tags',
         parent_area = m_util.html.abbr('Parent Town', 'Portals will lead to the parent town'),
         connections = 'Connections',
         -- monsters
         -- monsters
         -- boss monsters
         -- boss monsters
         entry_message = '%s: %s',
         vaal_areas = 'Vaal Areas',
     },
     },
}
}
-- ----------------------------------------------------------------------------
-- Constats
-- ----------------------------------------------------------------------------
local c = {}
-- TODO: test
c.max_area_query = 8


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
Line 109: Line 120:
     return function(tpl_args, frame)
     return function(tpl_args, frame)
         return tpl_args[k]
         return tpl_args[k]
    end
end
local util = {}
util.display = {}
function util.display.multiple_areas(tpl_args, frame, area_ids)
    local out = {}
    for _, area_id in ipairs(area_ids) do
        out[#out+1] = util.display.single_area(tpl_args, frame, area_id)
    end
    return table.concat(out, '<br>')
end
function util.display.single_area(tpl_args, frame, area_id)
    if tpl_args.areas[area_id] then
        return string.format('%s (%s)', tpl_args.areas[area_id]['Has name'], area_id)
    else
        return area_id
     end
     end
end
end
Line 117: Line 146:


local argument_map = {
local argument_map = {
     [i18n.args.id] = {
     id = {
         property = 'Is area id',
         property = 'Is area id',
         func = nil,
         func = nil,
     },
     },
     [i18n.args.name] = {
     name = {
         property = 'Has name',
         property = 'Has name',
         func = nil,
         func = nil,
     },
     },
     [i18n.args.act] = {
     act = {
         property = 'Has act',
         property = 'Has act',
         func = m_util.cast.factory.number(i18n.args.act),
         func = m_util.cast.factory.number(i18n.args.act, {key_out='act'}),
     },
     },
     [i18n.args.area_level] = {
     area_level = {
         property = 'Has area level',
         property = 'Has area level',
         func = m_util.cast.factory.number(i18n.args.area_level),
         func = m_util.cast.factory.number(i18n.args.area_level, {key_out='area_level'}),
     },
     },
     [i18n.args.level_restriction_max] = {
     level_restriction_max = {
         property = 'Has maximum level restriction',
         property = 'Has maximum level restriction',
         func = m_util.cast.factory.number(i18n.args.level_restriction_max),
         func = m_util.cast.factory.number(i18n.args.level_restriction_max, {key_out='level_restriction_max'}),
         default = 100,
         default = 100,
     },
     },
     [i18n.args.area_type_tags] = {
     area_type_tags = {
         property = 'Has area type tags',
         property = 'Has area type tags',
         func = m_util.cast.factory.assoc_table(i18n.args.area_type_tags, {
         func = m_util.cast.factory.assoc_table(i18n.args.area_type_tags, {
             tbl = m_game.constants.tags,
             tbl = m_game.constants.tags,
             errmsg = i18n.errors.invalid_tag,
             errmsg = i18n.errors.invalid_tag,
            key_out = 'area_type_tags',
         }),
         }),
     },
     },
     [i18n.args.tags] = {
     tags = {
         property = 'Has tags',
         property = 'Has tags',
         func = m_util.cast.factory.assoc_table(i18n.args.tags, {
         func = m_util.cast.factory.assoc_table(i18n.args.tags, {
             tbl = m_game.constants.tags,
             tbl = m_game.constants.tags,
             errmsg = i18n.errors.invalid_tag,
             errmsg = i18n.errors.invalid_tag,
            key_out = 'tags',
         }),
         }),
     },
     },
     [i18n.args.loading_screen] = {
     loading_screen = {
         property = 'Has loading screen image',
         property = 'Has loading screen image',
         func = function (tpl_args, frame)
         func = function (tpl_args, frame)
             if tpl_args[i18n.args.loading_screen] ~= nil then
             if tpl_args[i18n.args.loading_screen] ~= nil then
                 tpl_args[i18n.args.loading_screen] = string.format(i18n.images.loading_screen, tpl_args[i18n.args.loading_screen])  
                 tpl_args.loading_screen = string.format(i18n.images.loading_screen, tpl_args[i18n.args.loading_screen])  
             end
             end
         end,
         end,
     },
     },
     [i18n.args.connection_ids] = {
     connection_ids = {
         property = 'Has area connection ids',
         property = 'Has area connection ids',
         func = factory.arg_list(i18n.args.connection_ids),
         func = factory.arg_list(i18n.args.connection_ids, {key_out='connection_ids'}),
     },
     },
     [i18n.args.parent_area_id] = {
     parent_area_id = {
         property = 'Has parent area id',
         property = 'Has parent area id',
     },
     },
     [i18n.args.modifier_ids] = {
     modifier_ids = {
         property = 'Has mod ids',
         property = 'Has mod ids',
         func = factory.arg_list(i18n.args.modifier_ids),
         func = factory.arg_list(i18n.args.modifier_ids, {key_out='modifier_ids'}),
     },
     },
     [i18n.args.monster_ids] = {
     monster_ids = {
         property = 'Has monster ids',
         property = 'Has monster ids',
         func = factory.arg_list(i18n.args.monster_ids)
         func = factory.arg_list(i18n.args.monster_ids, {key_out='monster_ids'})
     },
     },
     [i18n.args.boss_monster_ids] = {
     boss_monster_ids = {
         property = 'Has boss monster ids',
         property = 'Has boss monster ids',
         func = factory.arg_list(i18n.args.boss_monster_ids)
         func = factory.arg_list(i18n.args.boss_monster_ids, {key_out='boss_monster_ids'})
     },
     },
     [i18n.args.entry_text] = {
     entry_text = {
         property = 'Has entry text message',
         property = 'Has entry text message',
     },
     },
     [i18n.args.entry_npc] = {
     entry_npc = {
         property = 'Has entry npc',
         property = 'Has entry npc',
     },
     },
Line 188: Line 219:
     -- Spawn chances
     -- Spawn chances
     --
     --
     [i18n.args.vaal_area_ids] = {
     vaal_area_ids = {
         property = 'Has vaal area ids',
         property = 'Has vaal area ids',
         func = factory.arg_list(i18n.args.vaal_area_ids),
         func = factory.arg_list(i18n.args.vaal_area_ids, {key_out='vaal_area_ids'}),
     },
     },
     [i18n.args.vaal_area_spawn_chance] = {
     vaal_area_spawn_chance = {
         property = 'Has vaal area spawn chance',
         property = 'Has vaal area spawn chance',
         func = m_util.cast.factory.number(i18n.args.vaal_area_spawn_chance),
         func = m_util.cast.factory.number(i18n.args.vaal_area_spawn_chance, {key_out='vaal_area_spawn_chance'}),
         default = 0,
         default = 0,
     },
     },
     [i18n.args.strongbox_spawn_chance] = {
     strongbox_spawn_chance = {
         property = 'Has strongbox spawn chance',
         property = 'Has strongbox spawn chance',
         func = m_util.cast.factory.number(i18n.args.strongbox_spawn_chance),
         func = m_util.cast.factory.number(i18n.args.strongbox_spawn_chance, {key_out='strongbox_spawn_chance'}),
         default = 0,
         default = 0,
     },
     },
     [i18n.args.strongbox_max_count] = {
     strongbox_max_count = {
         property = 'Has maximum number of strongboxes',
         property = 'Has maximum number of strongboxes',
         func = m_util.cast.factory.number(i18n.args.strongbox_max_count),
         func = m_util.cast.factory.number(i18n.args.strongbox_max_count, {key_out='strongbox_max_count'}),
         default = 0,
         default = 0,
     },
     },
     [i18n.args.strongbox_rarity_weight] = {
     strongbox_rarity_weight = {
         property = nil,
         property = nil,
         func = function (tpl_args, frame)
         func = function (tpl_args, frame)
             local weights = m_util.string.split(tpl_args[i18n.args.strongbox_rarity_weight] or '', ', ')
             local weights = m_util.string.split(tpl_args[i18n.args.strongbox_rarity_weight] or '', ', ')
              
              
             tpl_args[i18n.args.strongbox_rarity_weight] = {}
             tpl_args.strongbox_rarity_weight = {}
              
              
             for index, data in ipairs(m_game.constants.item.rarity) do
             for index, data in ipairs(m_game.constants.item.rarity) do
                 local value = weights[index] or 0
                 local value = weights[index] or 0
                 tpl_args[i18n.args.strongbox_rarity_weight][data.long_lower] = value
                 tpl_args.strongbox_rarity_weight[data.long_lower] = value
                 tpl_args._properties[string.format('Has %s rarity strongbox weight', data.long_lower)] = value  
                 tpl_args._properties[string.format('Has %s rarity strongbox weight', data.long_lower)] = value  
             end
             end
Line 224: Line 255:
     -- Area flags
     -- Area flags
     --
     --
     [i18n.args.is_map_area] = {
     is_map_area = {
         property = 'Is map area',
         property = 'Is map area',
         func = m_util.cast.factory.boolean(i18n.args.is_map_area),
         func = m_util.cast.factory.boolean(i18n.args.is_map_area, {key_out='is_map_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_unique_map_area] = {
     is_unique_map_area = {
         property = 'Is unique map area',
         property = 'Is unique map area',
         func = m_util.cast.factory.boolean(i18n.args.is_unique_map_area),
         func = m_util.cast.factory.boolean(i18n.args.is_unique_map_area, {key_out='is_unique_map_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_town_area] = {
     is_town_area = {
         property = 'Is town area',
         property = 'Is town area',
         func = m_util.cast.factory.boolean(i18n.args.is_town_area),
         func = m_util.cast.factory.boolean(i18n.args.is_town_area, {key_out='is_town_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_hideout_area] = {
     is_hideout_area = {
         property = 'Is hideout area',
         property = 'Is hideout area',
         func = m_util.cast.factory.boolean(i18n.args.is_hideout_area),
         func = m_util.cast.factory.boolean(i18n.args.is_hideout_area, {key_out='is_hideout_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_vaal_area] = {
     is_vaal_area = {
         property = 'Is vaal area',
         property = 'Is vaal area',
         func = m_util.cast.factory.boolean(i18n.args.is_vaal_area),
         func = m_util.cast.factory.boolean(i18n.args.is_vaal_area, {key_out='is_vaal_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_master_daily_area] = {
     is_master_daily_area = {
         property = 'Is master daily area',
         property = 'Is master daily area',
         func = m_util.cast.factory.boolean(i18n.args.is_master_daily_area),
         func = m_util.cast.factory.boolean(i18n.args.is_master_daily_area, {key_out='is_master_daily_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_labyrinth_area] = {
     is_labyrinth_area = {
         property = 'Is labyrinth area',
         property = 'Is labyrinth area',
         func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_area),
         func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_area, {key_out='is_labyrinth_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_labyrinth_airlock_area] = {
     is_labyrinth_airlock_area = {
         property = 'Is labyrinth airlock area',
         property = 'Is labyrinth airlock area',
         func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_airlock_area),
         func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_airlock_area, {key_out='is_labyrinth_airlock_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.is_labyrinth_boss_area] = {
     is_labyrinth_boss_area = {
         property = 'Is labyrinth boss area',
         property = 'Is labyrinth boss area',
         func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_boss_area),
         func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_boss_area, {key_out='is_labyrinth_boss_area'}),
         default = false,
         default = false,
     },
     },
     [i18n.args.has_waypoint] = {
     has_waypoint = {
         property = 'Has waypoint',
         property = 'Has waypoint',
         func = m_util.cast.factory.boolean(i18n.args.has_waypoint),
         func = m_util.cast.factory.boolean(i18n.args.has_waypoint, {key_out='has_waypoint'}),
         default = false,
         default = false,
    },
    --
    -- Not argument to the template, but still parsing arguments
    --
    areas = {
        property = nil,
        func = function(tpl_args, frame)
            local query_ids = {}
            if tpl_args.parent_area_id then
                query_ids[tpl_args.parent_area_id] = {}
            end
            for _, arg in ipairs({'connection_ids', 'vaal_area_ids'}) do
                for _, id in ipairs(tpl_args[arg]) do
                    query_ids[id] = {}
                end
            end
           
            local i = 1
            for k, _ in pairs(query_ids) do
                query_ids[i] = k
                i = i + 1
            end
           
            local areas = {}
           
            for i=1,#query_ids/c.max_area_query+1 do
                local query_ids_slice = {}
                for j=(i-1)*c.max_area_query, i*c.max_area_query do
                    query_ids_slice[#query_ids_slice+1] = query_ids[j]
                end
                local query = {
                    string.format('[[Is area id::%s]]', table.concat(query_ids_slice, '||')),
                    '?Is area id',
                    '?Has name',
                }
               
                local results = m_util.smw.query(query, frame)
                for _, result in ipairs(results) do
                    areas[result['Is area id']] = result
                end
            end
           
            tpl_args.areas = areas
            -- TODO: Error/Warning for missing areas?
        end,
     },
     },
}
}
Line 307: Line 383:
     'is_labyrinth_boss_area',
     'is_labyrinth_boss_area',
     'has_waypoint',
     'has_waypoint',
    -- Non argument, but passed to the script
    'areas',
}
}


Line 322: Line 400:
}
}


display.map = {
display.table_map = {
     {
     {
         args = {
         args = {
             [i18n.args.act] = {
             act = {
             },
             },
         },
         },
         func = function(tpl_args, frame)
         header = i18n.headers.act,
            return string.format(i18n.tooltips.act, tpl_args[i18n.args.act])  
        func = factory.display_value('act'),
        end,
     },  
     },  
     {
     {
         args = {
         args = {
             [i18n.args.area_level] = {
             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 = {
            area_type_tags = {
             },
             },
         },
         },
        header = i18n.headers.area_type_tags,
         func = function(tpl_args, frame)
         func = function(tpl_args, frame)
             return string.format(i18n.tooltips.area_level, tpl_args[i18n.args.area_level])  
             return table.concat(tpl_args.area_type_tags, ', ')
         end,
         end,
     },
     },
     {
     {
         args = {
         args = {
             [i18n.args.level_restriction_max] = {
             tags = {
                hide = 100,
             },
             },
         },
         },
        header = i18n.headers.tags,
         func = function(tpl_args, frame)
         func = function(tpl_args, frame)
             return string.format(i18n.tooltips.level_restriction_max, tpl_args[i18n.args.level_restriction_max])  
             return table.concat(tpl_args.tags, ', ')
         end,
         end,
     },
     },
     {
     {
         args = {
         args = {
             [i18n.args.area_type_tags] = {
             entry_text = {},
             },
             entry_npc = {},
         },
         },
        header = i18n.headers.entry_messsage,
         func = function(tpl_args, frame)
         func = function(tpl_args, frame)
             return string.format(i18n.tooltips.area_type_tags, table.concat(tpl_args[i18n.args.area_type_tags], ', '))  
             return string.format(i18n.tooltips.entry_message, tpl_args.entry_npc, tpl_args.entry_text)  
         end,
         end,
     },
     },
     {
     {
         args = {
         args = {
             [i18n.args.tags] = {
             parent_area_ids = {},
            },
         },
         },
        header = i18n.headers.parent_area,
         func = function(tpl_args, frame)
         func = function(tpl_args, frame)
             return string.format(i18n.tooltips.tags, table.concat(tpl_args[i18n.args.tags], ', '))  
             return util.display.single_area(tpl_args, frame, tpl_args.parent_area)
         end,
         end,
     },
     },
     {
     {
         args = {
         args = {
             [i18n.args.entry_text] = {},
             connection_ids = {},
            [i18n.args.entry_npc] = {},
         },
         },
        header = i18n.headers.connections,
         func = function(tpl_args, frame)
         func = function(tpl_args, frame)
             return string.format(i18n.tooltips.entry_message, tpl_args[i18n.args.entry_npc], tpl_args[i18n.args.entry_text])  
             return util.display.multiple_areas(tpl_args, frame, tpl_args.connection_ids)
         end,
         end,
     },
     },
     {
     {
         args = {
         args = {
          [i18n.args.loading_screen] = {},
            vaal_area_ids = {},
         },
         },
        header = i18n.headers.vaal_areas,
         func = function(tpl_args, frame)
         func = function(tpl_args, frame)
             return tpl_args.loading_screen
             return util.display.multiple_areas(tpl_args, frame, tpl_args.vaal_area_ids)
         end,
         end,
     },
     },
Line 397: Line 494:
      
      
     -- Top header
     -- Top header
     if tpl_args[i18n.args.name] ~= nil then
     if tpl_args.name ~= nil then
         infocard_args.header = string.format('%s (%s)', tpl_args[i18n.args.name], tpl_args[i18n.args.id])
         infocard_args.header = string.format('%s (%s)', tpl_args.name, tpl_args.id)
     else
     else
         infocard_args.header = tpl_args[i18n.args.name]
         infocard_args.header = tpl_args.name
     end
     end
      
      
Line 406: Line 503:
     local out = {}
     local out = {}
     for _, key in ipairs(display.area_type) do
     for _, key in ipairs(display.area_type) do
         if tpl_args[i18n.args[key]] == true then
         if tpl_args[key] == true then
             out[#out+1] = i18n.tooltips[key]
             out[#out+1] = i18n.tooltips[key]
         end
         end
Line 418: Line 515:
      
      
     -- Side header
     -- Side header
     if tpl_args[i18n.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[i18n.args.has_waypoint] then
     elseif tpl_args.has_waypoint then
         infocard_args.headerright = i18n.images.waypoint_yes
         infocard_args.headerright = i18n.images.waypoint_yes
     else
     else
Line 427: Line 524:
      
      
     -- Main sections, loop through
     -- Main sections, loop through
     local i = 1
     local tbl = mw.html.create('table')
     for _, data in ipairs(display.map) do
     for _, data in ipairs(display.table_map) do
         local continue = true
         local continue = true
         if data.args ~= nil then
         if data.args ~= nil then
             for key, key_data in pairs(data.args) do
             for key, key_data in pairs(data.args) do
                 if tpl_args[key] == nil then
                 if tpl_args[key] == nil or (type(tpl_args[key]) == 'table' and #tpl_args[key] == 0) then
                    continue = false
                    break
                elseif key_data.hide == nil and type(tpl_args[key]) == 'table' and #tpl_args[key] == 0 then
                     continue = false
                     continue = false
                     break
                     break
Line 450: Line 544:
                         break
                         break
                     end
                     end
                 elseif tpl_args[key] == key_data.hide then
                 elseif key_data.hide ~= nil and tpl_args[key] == key_data.hide then
                     continue = false
                     continue = false
                     break
                     break
Line 458: Line 552:
          
          
         if continue then
         if continue then
             infocard_args[i] = data.func(tpl_args, frame)
             tbl
           
                :tag('tr')
            i = i +1
                    :tag('th')
                        :wikitext(data.header or '')
                        :done()
                    :tag('td')
                        :wikitext(data.func(tpl_args, frame) or '')
                        :done()
                    :done()
         end
         end
    end
   
    infocard_args[1] = tostring(tbl)
   
    if tpl_args.loading_screen then
        infocard_args[2] = tpl_args.loading_screen
     end
     end


Line 487: Line 593:
     -- parse args
     -- parse args
     for _, k in ipairs(argument_order) do
     for _, k in ipairs(argument_order) do
        local key = i18n.args[k]
         local data = argument_map[k]
        if key == nil then
            error('Missing i18n for argument: ' .. k)
        end
         local data = argument_map[key]
         if data == nil then
         if data == nil then
             error('Missing data in argument_map: ' .. key)
             error('Missing data in argument_map: ' .. k)
         end
         end
         if data.func ~= nil then
         if data.func ~= nil then
             data.func(tpl_args, frame)
             data.func(tpl_args, frame)
         end
         end
         if data.default ~= nil and tpl_args[key] == nil then
       
             tpl_args[key] = data.default
         if data.default ~= nil and tpl_args[k] == nil then
             tpl_args[k] = data.default
         end
         end
          
          
         if data.property ~= nil and tpl_args[key] ~= nil then
         if data.property ~= nil and tpl_args[k] ~= nil then
             tpl_args._properties[data.property] = tpl_args[key]
             tpl_args._properties[data.property] = tpl_args[k]
         end
         end
     end
     end

Revision as of 12:45, 28 June 2017

Module documentation[view] [edit] [history] [purge]


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

-- SMW powered area module

-- ----------------------------------------------------------------------------
-- TODO
-- ----------------------------------------------------------------------------
-- invalid tags -> utl?
-- spawn_weight* values
-- spawnchacne values
-- 
-- i18n for properties

-- ----------------------------------------------------------------------------
-- Imports
-- ----------------------------------------------------------------------------

local m_util = require('Module:Util')
local getArgs = require('Module:Arguments').getArgs
local m_game = require('Module:Game')
local f_infocard = require('Module:Infocard')._main

-- ----------------------------------------------------------------------------
-- Localization
-- ----------------------------------------------------------------------------

-- Strings
local i18n = {
    images = {
        waypoint_no = '[[File:No waypoint area icon.png|link=|No Waypoint]]',
        waypoint_yes = '[[File:Waypoint area icon.png|link=|No Waypoint]]',
        waypoint_town = '[[File:Town area icon.png|link=|Town Hub]]',
        loading_screen = '[[File:%s loading screen.png|250px]]',
    },

    args = {
        id = 'id',
        name = 'name',
        act = 'act',
        area_level = 'level',
        level_restriction_max = 'level_restriction_max',
        area_type_tags = 'area_type_tags',
        tags = 'tags',
        loading_screen = 'loading_screen',
        connection_ids = 'connection_ids',
        parent_area_id = 'parent_area_id',
        modifier_ids = 'modifier_ids',
        boss_monster_ids = 'boss_monster_ids',
        monster_ids = 'monster_ids',
        entry_text = 'entry_text',
        entry_npc = 'entry_npc',
        vaal_area_spawn_chance = 'vaal_area_spawn_chance',
        vaal_area_ids = 'vaal_area_ids',
        strongbox_spawn_chance = 'strongbox_spawn_chance',
        strongbox_max_count = 'strongbox_max',
        strongbox_rarity_weight = 'strongbox_rarity_weight',
        is_map_area = 'is_map_area',
        is_unique_map_area = 'is_unique_map_area',
        is_town_area = 'is_town_area',
        is_hideout_area = 'is_hideout_area',
        is_vaal_area = 'is_vaal_area',
        is_master_daily_area = 'is_master_daily_area',
        is_labyrinth_area = 'is_labyrinth_area',
        is_labyrinth_airlock_area = 'is_labyrinth_airlock_area',
        is_labyrinth_boss_area = 'is_labyrinth_boss_area',
        has_waypoint = 'has_waypoint',
    },
    errors = {
        invalid_tag = '%s is not a valid tag',
    },
    tooltips = {
        -- boolean tooltips
        is_map_area = 'Map area',
        is_unique_map_area = 'Unique Map area',
        is_town_area = 'Town area',
        is_hideout_area = 'Hideout area',
        is_vaal_area = 'Vaal area',
        is_master_daily_area = 'Master daily spawn area',
        is_labyrinth_area = 'Labyrinth area',
        is_labyrinth_airlock_area = 'Labyrinth airlock area',
        is_labyrinth_boss_area = 'Labyrinth boss area',
        area = 'area',
        
        --
        entry_message = '%s: %s',
    },
    headers = {
        act = 'Act',
        area_level = 'Area level',
        level_restriction_max = m_util.html.abbr('Max level', 'Characters above this level can not enter this zone.'),
        area_type_tags = 'Area type tags',
        tags = 'Tags',
        parent_area = m_util.html.abbr('Parent Town', 'Portals will lead to the parent town'),
        connections = 'Connections',
        -- monsters
        -- boss monsters
        vaal_areas = 'Vaal Areas',
    },
}

-- ----------------------------------------------------------------------------
-- Constats
-- ----------------------------------------------------------------------------

local c = {}
-- TODO: test
c.max_area_query = 8

-- ----------------------------------------------------------------------------
-- Utility & helper functions
-- ----------------------------------------------------------------------------

local factory = {}
function factory.arg_list(k, args)
    return function (tpl_args, frame)
        tpl_args[k] = m_util.string.split(tpl_args[k] or '', ', ')
    end
end


function factory.display_value(k, args)
    return function(tpl_args, frame)
        return tpl_args[k]
    end
end

local util = {}
util.display = {}
function util.display.multiple_areas(tpl_args, frame, area_ids)
    local out = {}
    for _, area_id in ipairs(area_ids) do
        out[#out+1] = util.display.single_area(tpl_args, frame, area_id)
    end
    return table.concat(out, '<br>')
end

function util.display.single_area(tpl_args, frame, area_id)
    if tpl_args.areas[area_id] then
        return string.format('%s (%s)', tpl_args.areas[area_id]['Has name'], area_id)
    else
        return area_id
    end
end

-- ----------------------------------------------------------------------------
-- Argument & display mapping
-- ----------------------------------------------------------------------------

local argument_map = {
    id = {
        property = 'Is area id',
        func = nil,
    },
    name = {
        property = 'Has name',
        func = nil,
    },
    act = {
        property = 'Has act',
        func = m_util.cast.factory.number(i18n.args.act, {key_out='act'}),
    },
    area_level = {
        property = 'Has area level',
        func = m_util.cast.factory.number(i18n.args.area_level, {key_out='area_level'}),
    },
    level_restriction_max = {
        property = 'Has maximum level restriction',
        func = m_util.cast.factory.number(i18n.args.level_restriction_max, {key_out='level_restriction_max'}),
        default = 100,
    },
    area_type_tags = {
        property = 'Has area type tags',
        func = m_util.cast.factory.assoc_table(i18n.args.area_type_tags, {
            tbl = m_game.constants.tags,
            errmsg = i18n.errors.invalid_tag,
            key_out = 'area_type_tags',
        }),
    },
    tags = {
        property = 'Has tags',
        func = m_util.cast.factory.assoc_table(i18n.args.tags, {
            tbl = m_game.constants.tags,
            errmsg = i18n.errors.invalid_tag,
            key_out = 'tags',
        }),
    },
    loading_screen = {
        property = 'Has loading screen image',
        func = function (tpl_args, frame)
            if tpl_args[i18n.args.loading_screen] ~= nil then
                tpl_args.loading_screen = string.format(i18n.images.loading_screen, tpl_args[i18n.args.loading_screen]) 
            end
        end,
    },
    connection_ids = {
        property = 'Has area connection ids',
        func = factory.arg_list(i18n.args.connection_ids, {key_out='connection_ids'}),
    },
    parent_area_id = {
        property = 'Has parent area id',
    },
    modifier_ids = {
        property = 'Has mod ids',
        func = factory.arg_list(i18n.args.modifier_ids, {key_out='modifier_ids'}),
    },
    monster_ids = {
        property = 'Has monster ids',
        func = factory.arg_list(i18n.args.monster_ids, {key_out='monster_ids'})
    },
    boss_monster_ids = {
        property = 'Has boss monster ids',
        func = factory.arg_list(i18n.args.boss_monster_ids, {key_out='boss_monster_ids'})
    },
    entry_text = {
        property = 'Has entry text message',
    },
    entry_npc = {
        property = 'Has entry npc',
    },
    --
    -- Spawn chances
    --
    vaal_area_ids = {
        property = 'Has vaal area ids',
        func = factory.arg_list(i18n.args.vaal_area_ids, {key_out='vaal_area_ids'}),
    },
    vaal_area_spawn_chance = {
        property = 'Has vaal area spawn chance',
        func = m_util.cast.factory.number(i18n.args.vaal_area_spawn_chance, {key_out='vaal_area_spawn_chance'}),
        default = 0,
    },
    strongbox_spawn_chance = {
        property = 'Has strongbox spawn chance',
        func = m_util.cast.factory.number(i18n.args.strongbox_spawn_chance, {key_out='strongbox_spawn_chance'}),
        default = 0,
    },
    strongbox_max_count = {
        property = 'Has maximum number of strongboxes',
        func = m_util.cast.factory.number(i18n.args.strongbox_max_count, {key_out='strongbox_max_count'}),
        default = 0,
    },
    strongbox_rarity_weight = {
        property = nil,
        func = function (tpl_args, frame)
            local weights = m_util.string.split(tpl_args[i18n.args.strongbox_rarity_weight] or '', ', ')
            
            tpl_args.strongbox_rarity_weight = {}
            
            for index, data in ipairs(m_game.constants.item.rarity) do
                local value = weights[index] or 0
                tpl_args.strongbox_rarity_weight[data.long_lower] = value
                tpl_args._properties[string.format('Has %s rarity strongbox weight', data.long_lower)] = value 
            end
        end,
    },
    --
    -- Area flags
    --
    is_map_area = {
        property = 'Is map area',
        func = m_util.cast.factory.boolean(i18n.args.is_map_area, {key_out='is_map_area'}),
        default = false,
    },
    is_unique_map_area = {
        property = 'Is unique map area',
        func = m_util.cast.factory.boolean(i18n.args.is_unique_map_area, {key_out='is_unique_map_area'}),
        default = false,
    },
    is_town_area = {
        property = 'Is town area',
        func = m_util.cast.factory.boolean(i18n.args.is_town_area, {key_out='is_town_area'}),
        default = false,
    },
    is_hideout_area = {
        property = 'Is hideout area',
        func = m_util.cast.factory.boolean(i18n.args.is_hideout_area, {key_out='is_hideout_area'}),
        default = false,
    },
    is_vaal_area = {
        property = 'Is vaal area',
        func = m_util.cast.factory.boolean(i18n.args.is_vaal_area, {key_out='is_vaal_area'}),
        default = false,
    },
    is_master_daily_area = {
        property = 'Is master daily area',
        func = m_util.cast.factory.boolean(i18n.args.is_master_daily_area, {key_out='is_master_daily_area'}),
        default = false,
    },
    is_labyrinth_area = {
        property = 'Is labyrinth area',
        func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_area, {key_out='is_labyrinth_area'}),
        default = false,
    },
    is_labyrinth_airlock_area = {
        property = 'Is labyrinth airlock area',
        func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_airlock_area, {key_out='is_labyrinth_airlock_area'}),
        default = false,
    },
    is_labyrinth_boss_area = {
        property = 'Is labyrinth boss area',
        func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_boss_area, {key_out='is_labyrinth_boss_area'}),
        default = false,
    },
    has_waypoint = {
        property = 'Has waypoint',
        func = m_util.cast.factory.boolean(i18n.args.has_waypoint, {key_out='has_waypoint'}),
        default = false,
    },
    --
    -- Not argument to the template, but still parsing arguments
    --
    areas = {
        property = nil,
        func = function(tpl_args, frame)
            local query_ids = {}
            if tpl_args.parent_area_id then
                query_ids[tpl_args.parent_area_id] = {}
            end
            for _, arg in ipairs({'connection_ids', 'vaal_area_ids'}) do
                for _, id in ipairs(tpl_args[arg]) do
                    query_ids[id] = {}
                end
            end
            
            local i = 1
            for k, _ in pairs(query_ids) do
                query_ids[i] = k
                i = i + 1
            end
            
            local areas = {}
            
            for i=1,#query_ids/c.max_area_query+1 do
                local query_ids_slice = {}
                for j=(i-1)*c.max_area_query, i*c.max_area_query do
                    query_ids_slice[#query_ids_slice+1] = query_ids[j]
                end
                local query = {
                    string.format('[[Is area id::%s]]', table.concat(query_ids_slice, '||')),
                    '?Is area id',
                    '?Has name',
                }
                
                local results = m_util.smw.query(query, frame)
                for _, result in ipairs(results) do
                    areas[result['Is area id']] = result
                end
            end
            
            tpl_args.areas = areas
            -- TODO: Error/Warning for missing areas?
        end,
    },
}

local argument_order = {
    'id',
    'name',
    'act',
    'area_level',
    'level_restriction_max',
    'area_type_tags',
    'tags',
    'loading_screen',
    'connection_ids',
    'parent_area_id',
    'modifier_ids',
    'boss_monster_ids',
    'monster_ids',
    'entry_text',
    'entry_npc',
    'vaal_area_spawn_chance',
    'vaal_area_ids',
    'strongbox_spawn_chance',
    'strongbox_max_count',
    'strongbox_rarity_weight',
    'is_map_area',
    'is_unique_map_area',
    'is_town_area',
    'is_hideout_area',
    'is_vaal_area',
    'is_master_daily_area',
    'is_labyrinth_area',
    'is_labyrinth_airlock_area',
    'is_labyrinth_boss_area',
    'has_waypoint',
    -- Non argument, but passed to the script
    'areas',
}

local display  = {}
display.area_type = {
    'is_map_area',
    'is_unique_map_area',
    'is_town_area',
    'is_hideout_area',
    'is_vaal_area',
    'is_master_daily_area',
    'is_labyrinth_area',
    'is_labyrinth_airlock_area',
    'is_labyrinth_boss_area',
}

display.table_map = {
    {
        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 = {
            area_type_tags = {
            },
        },
        header = i18n.headers.area_type_tags,
        func = function(tpl_args, frame)
            return table.concat(tpl_args.area_type_tags, ', ')
        end,
    },
    {
        args = {
            tags = {
            },
        },
        header = i18n.headers.tags,
        func = function(tpl_args, frame)
            return table.concat(tpl_args.tags, ', ')
        end,
    },
    {
        args = {
            entry_text = {},
            entry_npc = {},
        },
        header = i18n.headers.entry_messsage,
        func = function(tpl_args, frame)
            return 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, frame)
            return util.display.single_area(tpl_args, frame, tpl_args.parent_area)
        end,
    },
    {
        args = {
            connection_ids = {},
        },
        header = i18n.headers.connections,
        func = function(tpl_args, frame)
            return util.display.multiple_areas(tpl_args, frame, tpl_args.connection_ids)
        end,
    },
    {
        args = {
            vaal_area_ids = {},
        },
        header = i18n.headers.vaal_areas,
        func = function(tpl_args, frame)
            return util.display.multiple_areas(tpl_args, frame, tpl_args.vaal_area_ids)
        end,
    },
}

-- ----------------------------------------------------------------------------
-- display functions
-- ----------------------------------------------------------------------------

local d = {}
function d.area_box(tpl_args, frame)
    local infocard_args = {}
    
    -- Top header
    if tpl_args.name ~= nil then
        infocard_args.header = string.format('%s (%s)', tpl_args.name, tpl_args.id)
    else
        infocard_args.header = tpl_args.name
    end
    
    -- 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 then
        infocard_args.headerright = i18n.images.waypoint_yes
    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
        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
        
        if continue then
            tbl
                :tag('tr')
                    :tag('th')
                        :wikitext(data.header or '')
                        :done()
                    :tag('td')
                        :wikitext(data.func(tpl_args, frame) or '')
                        :done()
                    :done()
        end
    end
    
    infocard_args[1] = tostring(tbl)
    
    if tpl_args.loading_screen then
        infocard_args[2] = tpl_args.loading_screen
    end

    return f_infocard(infocard_args)
end

-- ----------------------------------------------------------------------------
-- Page functions
-- ----------------------------------------------------------------------------

local p = {}

function p.area(frame)
    local tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    --
    -- Shared args
    --

    tpl_args._properties = {}
    
    -- parse args
    for _, k in ipairs(argument_order) do
        local data = argument_map[k]
        if data == nil then
            error('Missing data in argument_map: ' .. k)
        end
        if data.func ~= nil then
            data.func(tpl_args, frame)
        end
        
        if data.default ~= nil and tpl_args[k] == nil then
            tpl_args[k] = data.default
        end
        
        if data.property ~= nil and tpl_args[k] ~= nil then
            tpl_args._properties[data.property] = tpl_args[k]
        end
    end
    
    -- parse spawn weights
    m_util.args.weight_list(tpl_args, {
        frame=frame, 
        output_argument='spawn_weights',
    })
    
    -- display
    local out = d.area_box(tpl_args, frame)
    
    tpl_args._properties['Has infobox HTML'] = out
    
    -- set semantic properties
    m_util.smw.set(frame, tpl_args._properties)
    
    -- display
    return out
end

return p