Module:Area: Difference between revisions

From Path of Exile 2 Wiki
Jump to navigation Jump to search
>Illviljan
m (Add a link to the main page if it's defined.)
>OmegaK2
(port of p.area to cargo)
Line 18: Line 18:
local m_game = require('Module:Game')
local m_game = require('Module:Game')
local f_infocard = require('Module:Infocard')._main
local f_infocard = require('Module:Infocard')._main
local cargo = mw.ext.cargo


-- ----------------------------------------------------------------------------
-- ----------------------------------------------------------------------------
Line 160: Line 162:


local argument_map = {
local argument_map = {
    table = 'areas',
     --
     --
     -- User supplied arguments
     -- User supplied arguments
     --  
     --  
     main_page = {
     fields = {
        property = 'Has main page',
        main_page = {
        func = function(tpl_args, frame)
            field = 'main_page',
            local page = tpl_args.main_page  
            type = 'Page',
            if page ~= nil then
            func = function(tpl_args, frame)
                page = mw.title.new(tpl_args.main_page)
                local page = tpl_args.main_page  
                if page == nil then
                if page ~= nil then
                    error(string.format(i18n.errors.main_page_is_invalid, tpl_args.main_page))
                    page = mw.title.new(tpl_args.main_page)
                elseif not page.exists then
                    if page == nil then
                    error(string.format(i18n.errors.main_page_does_not_exist, tpl_args.main_page))
                        error(string.format(i18n.errors.main_page_is_invalid, tpl_args.main_page))
                else
                    elseif not page.exists then
                    -- dont need the title object anymore
                        error(string.format(i18n.errors.main_page_does_not_exist, tpl_args.main_page))
                    --tpl_args.main_page = page
                    else
                        -- dont need the title object anymore
                        --tpl_args.main_page = page
                    end
                 end
                 end
             end
             end
         end
         },
    },
       
   
        --
    --
        -- Can be populated by PyPoE
    -- Can be populated by PyPoE
        --
    --
        id = {
    id = {
            field = 'id',
        property = 'Is area id',
            type = 'String',
        func = nil,
            func = nil,
    },
        },
    name = {
        name = {
        property = 'Has name',
            field = 'name',
        func = nil,
            type = 'String',
    },
            func = nil,
    act = {
        },
        property = 'Has act',
        act = {
        func = m_util.cast.factory.number(i18n.args.act, {key_out='act'}),
            field = 'act',
    },
            type = 'Integer',
    area_level = {
            func = m_util.cast.factory.number(i18n.args.act, {key_out='act'}),
        property = 'Has area level',
        },
        func = m_util.cast.factory.number(i18n.args.area_level, {key_out='area_level'}),
        area_level = {
    },
            field = 'area_level',
    level_restriction_max = {
            type = 'Integer',
        property = 'Has maximum level restriction',
            func = m_util.cast.factory.number(i18n.args.area_level, {key_out='area_level'}),
        func = m_util.cast.factory.number(i18n.args.level_restriction_max, {key_out='level_restriction_max'}),
        },
        default = 100,
        level_restriction_max = {
    },
            field = 'level_restriction_max',
    area_type_tags = {
            type = 'Integer',
        property = 'Has area type tags',
            func = m_util.cast.factory.number(i18n.args.level_restriction_max, {key_out='level_restriction_max'}),
        func = m_util.cast.factory.assoc_table(i18n.args.area_type_tags, {
            default = 100,
            tbl = m_game.constants.tags,
        },
            errmsg = i18n.errors.invalid_tag,
        area_type_tags = {
            key_out = 'area_type_tags',
            field = 'area_type_tags',
        }),
            type = 'List (,) of String',
    },
            func = m_util.cast.factory.assoc_table(i18n.args.area_type_tags, {
    tags = {
                tbl = m_game.constants.tags,
        property = 'Has tags',
                errmsg = i18n.errors.invalid_tag,
        func = m_util.cast.factory.assoc_table(i18n.args.tags, {
                key_out = 'area_type_tags',
            tbl = m_game.constants.tags,
            }),
            errmsg = i18n.errors.invalid_tag,
        },
            key_out = 'tags',
        tags = {
        }),
            field = 'tags',
    },
            type = 'List (,) of String',
    loading_screen = {
            func = m_util.cast.factory.assoc_table(i18n.args.tags, {
        property = 'Has loading screen image',
                tbl = m_game.constants.tags,
        func = function (tpl_args, frame)
                errmsg = i18n.errors.invalid_tag,
            local loading_id = tpl_args[i18n.args.loading_screen]
                key_out = 'tags',
            if loading_id ~= nil then
            }),
                tpl_args.loading_screen = string.format(i18n.images.loading_screen, loading_id)
        },
                tpl_args.loading_screen_infobox = string.format(i18n.images.loading_screen_infobox, loading_id)  
        loading_screen = {
            end
            field = 'loading_screen',
        end,
            type = 'Page',
    },
            func = function (tpl_args, frame)
    connection_ids = {
                local loading_id = tpl_args[i18n.args.loading_screen]
        property = 'Has area connection ids',
                if loading_id ~= nil then
        func = factory.arg_list(i18n.args.connection_ids, {key_out='connection_ids'}),
                    tpl_args.loading_screen = string.format(i18n.images.loading_screen, loading_id)
        default = {},
                    tpl_args.loading_screen_infobox = string.format(i18n.images.loading_screen_infobox, loading_id)  
    },
    parent_area_id = {
        property = 'Has parent area id',
    },
    modifier_ids = {
        property = 'Has mod ids',
        func = function(tpl_args, frame)
            factory.arg_list(i18n.args.modifier_ids, {key_out='modifier_ids'})(tpl_args, frame)
           
            if tpl_args.modifier_ids == nil then
                return
            end
            local results = m_util.smw.array_query{
                frame=frame,
                property='Is mod',
                id_array=tpl_args.modifier_ids,
                query={
                    '?Is mod',
                    '?Has stat text',
                },
            }
           
            text = {}
            for page, row in pairs(results) do
                if row['Has stat text'] ~= '' then
                    text[#text+1] = row['Has stat text']
                 end
                 end
             end
             end,
           
         },
            tpl_args.stat_text = table.concat(text, '<br>')
         connection_ids = {
            tpl_args._properties['Has stat text'] = tpl_args.stat_text
            field = 'connection_ids',
         end,
            type = 'List (,) of String',
         default = {},
            func = factory.arg_list(i18n.args.connection_ids, {key_out='connection_ids'}),
    },
            default = {},
    monster_ids = {
         },
        property = 'Has monster ids',
         parent_area_id = {
        func = factory.arg_list(i18n.args.monster_ids, {key_out='monster_ids'}),
            field = 'parent_area_id',
        default = {},
             type = 'String',
    },
         },
    boss_monster_ids = {
         modifier_ids = {
        property = 'Has boss monster ids',
            field = 'modifier_ids',
        func = factory.arg_list(i18n.args.boss_monster_ids, {key_out='boss_monster_ids'}),
            type = 'List (,) of String',
        default = {},
            func = function(tpl_args, frame)
    },
                 factory.arg_list(i18n.args.modifier_ids, {key_out='modifier_ids'})(tpl_args, frame)
    entry_text = {
               
         property = 'Has entry text message',
                 if tpl_args.modifier_ids == nil then
    },
                     return
    entry_npc = {
         property = 'Has entry npc',
    },
    flavour_text = {
        property = 'Has flavour text',
    },
    screenshot_ext = {
        property = nil,
        func = nil,
        default = 'jpg',
    },
    screenshot = {
        property = 'Has screenshot',
        func = function(tpl_args, frame)
             if tpl_args.name ~= nil then
                tpl_args.screenshot = string.format(i18n.images.screenshot, tpl_args.name, tpl_args.screenshot_ext)
                tpl_args.screenshot_infobox = string.format(i18n.images.screenshot_infobox, tpl_args.name, tpl_args.screenshot_ext)
            end
        end,
    },
    --
    -- Spawn chances
    --
    vaal_area_ids = {
        property = 'Has vaal area ids',
         func = factory.arg_list(i18n.args.vaal_area_ids, {key_out='vaal_area_ids'}),
         default = {},
    },
    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 = tonumber(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 = {}
            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] = {}
                    end
                elseif tpl_args[arg] then
                     query_ids[tpl_args[arg]] = {}
                 end
                 end
            end
                local results = m_util.smw.array_query{
           
            local query_ids_trimmed = {}
            for k, _ in pairs(query_ids) do
                if type(k) ~= 'number' then
                    query_ids_trimmed[#query_ids_trimmed+1] = k
                end
            end
           
            if #query_ids_trimmed == 0 then
                tpl_args.areas = {}
            else
                tpl_args.areas = m_util.smw.array_query{
                     frame=frame,
                     frame=frame,
                     property='Is area id',
                     property='Is mod',
                     id_array=query_ids_trimmed,
                     id_array=tpl_args.modifier_ids,
                     query={
                     query={
                         '?Is area id',
                         '?Is mod',
                         '?Has name',
                         '?Has stat text',
                        '?Has main page',
                     },
                     },
                 }
                 }
             end
               
             -- TODO: Error/Warning for missing areas?
                text = {}
         end,
                for page, row in pairs(results) do
                    if row['Has stat text'] ~= '' then
                        text[#text+1] = row['Has stat text']
                    end
                end
               
                tpl_args.stat_text = table.concat(text, '<br>')
                tpl_args._properties['Has stat text'] = tpl_args.stat_text
             end,
            default = {},
        },
        monster_ids = {
            field = 'monster_ids',
            type = 'List (,) of String',
            func = factory.arg_list(i18n.args.monster_ids, {key_out='monster_ids'}),
            default = {},
        },
        boss_monster_ids = {
            field = 'boss_monster_ids',
            type = 'List (,) of String',
            func = factory.arg_list(i18n.args.boss_monster_ids, {key_out='boss_monster_ids'}),
             default = {},
        },
        entry_text = {
            field = 'entry_text',
            type = 'String',
        },
        entry_npc = {
            field = 'entry_npc',
            type = 'String',
        },
        flavour_text = {
            field = 'flavour_text',
            type = 'String',
        },
        screenshot_ext = {
            func = nil,
            default = 'jpg',
        },
        screenshot = {
            field = 'screenshot',
            type = 'Page',
            func = function(tpl_args, frame)
                if tpl_args.name ~= nil then
                    tpl_args.screenshot = string.format(i18n.images.screenshot, tpl_args.name, tpl_args.screenshot_ext)
                    tpl_args.screenshot_infobox = string.format(i18n.images.screenshot_infobox, tpl_args.name, tpl_args.screenshot_ext)
                end
            end,
        },
        --
        -- Spawn chances
        --
        vaal_area_ids = {
            field = 'vaal_area_ids',
            type = 'List (,) of String',
            func = factory.arg_list(i18n.args.vaal_area_ids, {key_out='vaal_area_ids'}),
            default = {},
        },
        vaal_area_spawn_chance = {
            field = 'vaal_area_spawn_chance',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.vaal_area_spawn_chance, {key_out='vaal_area_spawn_chance'}),
            default = 0,
        },
        strongbox_spawn_chance = {
            field = 'strongbox_spawn_chance',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.strongbox_spawn_chance, {key_out='strongbox_spawn_chance'}),
            default = 0,
        },
        strongbox_max_count = {
            field = 'strongbox_max_count',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.strongbox_max_count, {key_out='strongbox_max_count'}),
            default = 0,
        },
        strongbox_rarity_weight = {
            field = 'strongbox_rarity_weight',
            type = 'Integer',
            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 = tonumber(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 = {
            field = 'is_map_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_map_area, {key_out='is_map_area'}),
            default = false,
        },
        is_unique_map_area = {
            field = 'is_unique_map_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_unique_map_area, {key_out='is_unique_map_area'}),
            default = false,
        },
        is_town_area = {
            field = 'is_town_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_town_area, {key_out='is_town_area'}),
            default = false,
        },
        is_hideout_area = {
            field = 'is_hideout_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_hideout_area, {key_out='is_hideout_area'}),
            default = false,
        },
        is_vaal_area = {
            field = 'is_vaal_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_vaal_area, {key_out='is_vaal_area'}),
            default = false,
        },
        is_master_daily_area = {
            field = 'is_is_master_daily_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_master_daily_area, {key_out='is_master_daily_area'}),
            default = false,
        },
        is_labyrinth_area = {
            field = 'is_labyrinth_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_area, {key_out='is_labyrinth_area'}),
            default = false,
        },
        is_labyrinth_airlock_area = {
            field = 'is_labyrinth_airlock_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_airlock_area, {key_out='is_labyrinth_airlock_area'}),
            default = false,
        },
        is_labyrinth_boss_area = {
            field = 'is_labyrinth_boss_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_boss_area, {key_out='is_labyrinth_boss_area'}),
            default = false,
        },
        has_waypoint = {
            field = 'has_waypoint',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.has_waypoint, {key_out='has_waypoint'}),
            default = false,
        },
        --
        -- Handled elsewhere in the old
        --
        release_version = {
            field = 'release_version',
            type = 'String'
        },
        removal_version = {
            field = 'removal_version',
            type = 'String',
        },
        mainpage_categories = {
            field = 'mainpage_categories',
            type = 'String',
        },
        infobox_html = {
            field = 'infobox_html',
            type = 'String',
        },
        --
        -- Not argument to the template, but still parsing arguments
        --
        areas = {
            func = function(tpl_args, frame)
                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] = {}
                        end
                    elseif tpl_args[arg] then
                        query_ids[tpl_args[arg]] = {}
                    end
                end
               
                local query_ids_trimmed = {}
                for k, _ in pairs(query_ids) do
                    if type(k) ~= 'number' then
                        query_ids_trimmed[#query_ids_trimmed+1] = string.format('areas.id="%s"', k)
                    end
                end
               
                if #query_ids_trimmed == 0 then
                    tpl_args.areas = {}
                else
                    local result = cargo.query(
                        'areas',
                        'areas.id, areas.name, areas.main_page',
                        {
                            where=table.concat(query_ids_trimmed, ' OR '),
                        }
                    )
                   
                    tpl_args.areas = {}
                    for _, row in ipairs(result) do
                        tpl_args.areas[row['areas.id']] = row
                    end
                end
                -- TODO: Error/Warning for missing areas?
            end,
         },
     },
     },
}
}
Line 470: Line 527:
     'is_labyrinth_boss_area',
     'is_labyrinth_boss_area',
     'has_waypoint',
     'has_waypoint',
    -- parsed by m_util.args.version:
    -- 'release_version',
    -- 'removal_version',
   
     -- Non argument, but passed to the script
     -- Non argument, but passed to the script
     'areas',
     'areas',
Line 628: Line 689:
      
      
     if tpl_args['name'] then
     if tpl_args['name'] then
        local title = string.format("'''%s'''", tpl_args['name'])
         out[#out+1] = string.format("'''%s''' is the internal id for the '''%s''' area. ", tpl_args['id'], tpl_args['name'])
        if tpl_args['main_page'] ~= nil then
            title = string.format('[[%s|%s]]', tpl_args['main_page'], tpl_args['name'])
        end
         out[#out+1] = string.format("'''%s''' is the internal id for the %s area. ", tpl_args['id'], title)
     else  
     else  
         out[#out+1] = string.format("'''%s''' is the internal id of an unnamed area. ", tpl_args['id'])
         out[#out+1] = string.format("'''%s''' is the internal id of an unnamed area. ", tpl_args['id'])
Line 800: Line 857:


local p = {}
local p = {}
p.table_areas = m_util.cargo.declare_factory{data=argument_map}


function p.area(frame)
function p.area(frame)
Line 817: Line 876:
      
      
     -- Handle release_version and removal_version
     -- Handle release_version and removal_version
     m_util.args.version(tpl_args, {frame=frame, set_properties=true})
     m_util.args.version(tpl_args, {frame=frame})
      
      
     -- Parse args
     -- Parse args
     for _, k in ipairs(argument_order) do
     for _, k in ipairs(argument_order) do
         local data = argument_map[k]
         local data = argument_map.fields[k]
         if data == nil then
         if data == nil then
             error('Missing data in argument_map: ' .. k)
             error('Missing data in argument_map: ' .. k)
Line 832: Line 891:
             tpl_args[k] = data.default
             tpl_args[k] = data.default
         end
         end
          
    end
         if data.property ~= nil and tpl_args[k] ~= nil then
   
             tpl_args._properties[data.property] = tpl_args[k]
    -- this should include all values as long they're set, regardless of whether they are parsed above
    for key, data in pairs(argument_order.fields) do
         local v = tpl_args[k]
         if data.field ~= nil and v ~= nil then
            if type(v) == 'table' then
                v = table.concat(v, ',')
            end
             tpl_args._properties[data.field] = v
         end
         end
     end
     end
Line 860: Line 926:
     end
     end


     tpl_args._properties['Has main page categories'] = cats
     tpl_args._properties['mainpage_categories'] = table.concat(cats, ',')
      
      
     -- Category handling for the local data page:
     -- Category handling for the local data page:
Line 872: Line 938:
      
      
     -- Property to store what's output to main pages:
     -- Property to store what's output to main pages:
     tpl_args._properties['Has infobox HTML'] = out
     tpl_args._properties['infobox_html'] = out
      
      
     -- Set all semantic properties:
     -- Set all semantic properties:
     m_util.smw.set(frame, tpl_args._properties)
     m_util.cargo.store(frame, tpl_args._properties)
      
      
     -- Display only on data page:
     -- Display only on data page:

Revision as of 13:10, 12 December 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

local cargo = mw.ext.cargo

-- ----------------------------------------------------------------------------
-- 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]]',
        loading_screen_infobox = '[[File:%s loading screen.png|250px]]',
        screenshot = '[[File:%s area screenshot.%s]]',
        screenshot_infobox = '[[File:%s area screenshot.%s|250px]]',
    },

    args = {
        main_page = 'main_page',
        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',
        flavour_text = 'flavour_text',
        screenshot_ext = 'screenshot_ext',
        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',
        main_page_is_invalid = 'main_page argument got "%s" which is not a valid wiki page', 
        main_page_does_not_exist = 'main_page argument requires the specified page "%s" to exist in the main wiki namespace',
    },
    tooltips = {
        -- boolean tooltips. Use singular form here, categories makes these plural.
        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 = {
        id = 'Id',
        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)
        if tpl_args[k] ~= nil then
            tpl_args[k] = m_util.string.split(tpl_args[k], ', ')
        end
    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
        if tpl_args.areas[area_id]['Has main page'] ~= '' then
            return string.format('[[%s|%s]]', tpl_args.areas[area_id]['Has main page'], tpl_args.areas[area_id]['Has name'])
        else
            return string.format('%s ([[%s|%s]])', tpl_args.areas[area_id]['Has name'], tpl_args.areas[area_id][1], area_id)
        end
    else
        return area_id
    end
end

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

local argument_map = {
    table = 'areas',
    --
    -- User supplied arguments
    -- 
    fields = {
        main_page = {
            field = 'main_page', 
            type = 'Page',
            func = function(tpl_args, frame)
                local page = tpl_args.main_page 
                if page ~= nil then
                    page = mw.title.new(tpl_args.main_page)
                    if page == nil then
                        error(string.format(i18n.errors.main_page_is_invalid, tpl_args.main_page))
                    elseif not page.exists then
                        error(string.format(i18n.errors.main_page_does_not_exist, tpl_args.main_page))
                    else
                        -- dont need the title object anymore
                        --tpl_args.main_page = page
                    end
                end
            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',
            func = m_util.cast.factory.number(i18n.args.act, {key_out='act'}),
        },
        area_level = {
            field = 'area_level',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.area_level, {key_out='area_level'}),
        },
        level_restriction_max = {
            field = 'level_restriction_max',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.level_restriction_max, {key_out='level_restriction_max'}),
            default = 100,
        },
        area_type_tags = {
            field = 'area_type_tags',
            type = 'List (,) of String',
            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 = {
            field = 'tags',
            type = 'List (,) of String',
            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 = {
            field = 'loading_screen',
            type = 'Page',
            func = function (tpl_args, frame)
                local loading_id = tpl_args[i18n.args.loading_screen]
                if loading_id ~= nil then
                    tpl_args.loading_screen = string.format(i18n.images.loading_screen, loading_id)
                    tpl_args.loading_screen_infobox = string.format(i18n.images.loading_screen_infobox, loading_id) 
                end
            end,
        },
        connection_ids = {
            field = 'connection_ids',
            type = 'List (,) of String',
            func = factory.arg_list(i18n.args.connection_ids, {key_out='connection_ids'}),
            default = {},
        },
        parent_area_id = {
            field = 'parent_area_id',
            type = 'String',
        },
        modifier_ids = {
            field = 'modifier_ids',
            type = 'List (,) of String',
            func = function(tpl_args, frame)
                factory.arg_list(i18n.args.modifier_ids, {key_out='modifier_ids'})(tpl_args, frame)
                
                if tpl_args.modifier_ids == nil then
                    return
                end
                local results = m_util.smw.array_query{
                    frame=frame,
                    property='Is mod',
                    id_array=tpl_args.modifier_ids,
                    query={
                        '?Is mod',
                        '?Has stat text',
                    },
                }
                
                text = {}
                for page, row in pairs(results) do
                    if row['Has stat text'] ~= '' then
                        text[#text+1] = row['Has stat text']
                    end
                end
                
                tpl_args.stat_text = table.concat(text, '<br>')
                tpl_args._properties['Has stat text'] = tpl_args.stat_text
            end,
            default = {},
        },
        monster_ids = {
            field = 'monster_ids',
            type = 'List (,) of String',
            func = factory.arg_list(i18n.args.monster_ids, {key_out='monster_ids'}),
            default = {},
        },
        boss_monster_ids = {
            field = 'boss_monster_ids',
            type = 'List (,) of String',
            func = factory.arg_list(i18n.args.boss_monster_ids, {key_out='boss_monster_ids'}),
            default = {},
        },
        entry_text = {
            field = 'entry_text',
            type = 'String',
        },
        entry_npc = {
            field = 'entry_npc',
            type = 'String',
        },
        flavour_text = {
            field = 'flavour_text',
            type = 'String',
        },
        screenshot_ext = {
            func = nil,
            default = 'jpg',
        },
        screenshot = {
            field = 'screenshot',
            type = 'Page',
            func = function(tpl_args, frame)
                if tpl_args.name ~= nil then
                    tpl_args.screenshot = string.format(i18n.images.screenshot, tpl_args.name, tpl_args.screenshot_ext)
                    tpl_args.screenshot_infobox = string.format(i18n.images.screenshot_infobox, tpl_args.name, tpl_args.screenshot_ext)
                end
            end,
        },
        --
        -- Spawn chances
        --
        vaal_area_ids = {
            field = 'vaal_area_ids',
            type = 'List (,) of String',
            func = factory.arg_list(i18n.args.vaal_area_ids, {key_out='vaal_area_ids'}),
            default = {},
        },
        vaal_area_spawn_chance = {
            field = 'vaal_area_spawn_chance',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.vaal_area_spawn_chance, {key_out='vaal_area_spawn_chance'}),
            default = 0,
        },
        strongbox_spawn_chance = {
            field = 'strongbox_spawn_chance',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.strongbox_spawn_chance, {key_out='strongbox_spawn_chance'}),
            default = 0,
        },
        strongbox_max_count = {
            field = 'strongbox_max_count',
            type = 'Integer',
            func = m_util.cast.factory.number(i18n.args.strongbox_max_count, {key_out='strongbox_max_count'}),
            default = 0,
        },
        strongbox_rarity_weight = {
            field = 'strongbox_rarity_weight',
            type = 'Integer',
            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 = tonumber(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 = {
            field = 'is_map_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_map_area, {key_out='is_map_area'}),
            default = false,
        },
        is_unique_map_area = {
            field = 'is_unique_map_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_unique_map_area, {key_out='is_unique_map_area'}),
            default = false,
        },
        is_town_area = {
            field = 'is_town_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_town_area, {key_out='is_town_area'}),
            default = false,
        },
        is_hideout_area = {
            field = 'is_hideout_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_hideout_area, {key_out='is_hideout_area'}),
            default = false,
        },
        is_vaal_area = {
            field = 'is_vaal_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_vaal_area, {key_out='is_vaal_area'}),
            default = false,
        },
        is_master_daily_area = {
            field = 'is_is_master_daily_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_master_daily_area, {key_out='is_master_daily_area'}),
            default = false,
        },
        is_labyrinth_area = {
            field = 'is_labyrinth_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_area, {key_out='is_labyrinth_area'}),
            default = false,
        },
        is_labyrinth_airlock_area = {
            field = 'is_labyrinth_airlock_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_airlock_area, {key_out='is_labyrinth_airlock_area'}),
            default = false,
        },
        is_labyrinth_boss_area = {
            field = 'is_labyrinth_boss_area',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_boss_area, {key_out='is_labyrinth_boss_area'}),
            default = false,
        },
        has_waypoint = {
            field = 'has_waypoint',
            type = 'Boolean',
            func = m_util.cast.factory.boolean(i18n.args.has_waypoint, {key_out='has_waypoint'}),
            default = false,
        },
        --
        -- Handled elsewhere in the old
        --
        release_version = {
            field = 'release_version',
            type = 'String'
        },
        removal_version = {
            field = 'removal_version',
            type = 'String',
        },
        mainpage_categories = {
            field = 'mainpage_categories',
            type = 'String',
        },
        infobox_html = {
            field = 'infobox_html',
            type = 'String',
        },
        --
        -- Not argument to the template, but still parsing arguments
        --
        areas = {
            func = function(tpl_args, frame)
                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] = {}
                        end
                    elseif tpl_args[arg] then
                        query_ids[tpl_args[arg]] = {}
                    end
                end
                
                local query_ids_trimmed = {}
                for k, _ in pairs(query_ids) do
                    if type(k) ~= 'number' then
                        query_ids_trimmed[#query_ids_trimmed+1] = string.format('areas.id="%s"', k)
                    end
                end
                
                if #query_ids_trimmed == 0 then
                    tpl_args.areas = {}
                else
                    local result = cargo.query(
                        'areas',
                        'areas.id, areas.name, areas.main_page',
                        {
                            where=table.concat(query_ids_trimmed, ' OR '),
                        }
                    )
                    
                    tpl_args.areas = {}
                    for _, row in ipairs(result) do
                        tpl_args.areas[row['areas.id']] = row
                    end
                end
                -- TODO: Error/Warning for missing areas?
            end,
        },
    },
}

local argument_order = {
    'main_page',
    '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',
    'flavour_text',
    'screenshot_ext',
    'screenshot',
    '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',
    -- parsed by m_util.args.version:
    -- 'release_version',
    -- 'removal_version',
    
    -- 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 = {
            id = {
            },
        },
        header = i18n.headers.id,
        func = function(tpl_args, frame)
            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 = {
            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 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, 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.list_map = {
    {
        args = {
            flavour_text = {},
        },
        func = function(tpl_args, frame)
            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, frame)
            return m_util.html.poe_color('mod', tpl_args.stat_text)
        end,
    },
}

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

local d = {}

function d.intro_text(tpl_args, frame)
    local out = {}
    if mw.ustring.find(tpl_args['id'], '_') then
        out[#out+1] = frame:expandTemplate{ title = 'Incorrect title', args = { title=tpl_args['id'] } }
    end
    
    if tpl_args['name'] then
        out[#out+1] = string.format("'''%s''' is the internal id for the '''%s''' area. ", tpl_args['id'], tpl_args['name'])
    else 
        out[#out+1] = string.format("'''%s''' is the internal id of an unnamed area. ", 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
                connected_areas[#connected_areas+1] = string.format('<li>[[%s|%s]] (%s)</li>', tostring(tpl_args.areas[id][1]), id, tostring(tpl_args.areas[id]['Has name']))
            end
        end
    end
    if #connected_areas > 0 then 
        out[#out+1] = string.format('It is connected to the following areas:<ul>%s</ul>', table.concat(connected_areas))
    end
    
    return table.concat(out)
end

function d._check_args(tpl_args, frame, 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, frame)
    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 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
        if d._check_args(tpl_args, frame, data) 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)
    
    local i = 2
    for _, data in ipairs(display.list_map) do
        if d._check_args(tpl_args, frame, data) then
            infocard_args[i] = data.func(tpl_args, frame)
            i = i + 1
        end
    end

    return f_infocard(infocard_args)
end

function d.subobject_box(tpl_args, frame)
    local container = mw.html.create('div')
    container
        :attr('class', 'modbox floatright')
        
    -- spawn weight table 
    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('[[Property:Is tag number|#]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has tag|Tag]]')
                :done()
            :tag('th')
                :wikitext('[[Property:Has spawn weight|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

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

local p = {}

p.table_areas = m_util.cargo.declare_factory{data=argument_map}

function p.area(frame)
    -- = 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'}
    
    local tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    --
    -- Shared args
    --

    tpl_args._properties = {}
    
    -- Handle release_version and removal_version
    m_util.args.version(tpl_args, {frame=frame})
    
    -- Parse args
    for _, k in ipairs(argument_order) do
        local data = argument_map.fields[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
    end
    
    -- this should include all values as long they're set, regardless of whether they are parsed above 
    for key, data in pairs(argument_order.fields) do
        local v = tpl_args[k]
        if data.field ~= nil and v ~= nil then
            if type(v) == 'table' then
                v = table.concat(v, ',')
            end
            tpl_args._properties[data.field] = v
        end
    end
    
    -- Parse spawn weights
    m_util.args.weight_list(tpl_args, {
        frame=frame, 
        output_argument='spawn_weights',
    })
    
    -- Category handling for main page only by adding the categories in a property:
    -- 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

    tpl_args._properties['mainpage_categories'] = table.concat(cats, ',')
    
    -- Category handling for the local data page:
    local page_cats = {
        'Area data',
    }
    
    -- Display only on main pages:
    local out = {}
    out[#out+1] = d.area_box(tpl_args, frame)
    
    -- Property to store what's output to main pages:
    tpl_args._properties['infobox_html'] = out
    
    -- Set all semantic properties:
    m_util.cargo.store(frame, tpl_args._properties)
    
    -- Display only on data page:
    out[#out+1] = d.subobject_box(tpl_args, frame)
    out[#out+1] = d.intro_text(tpl_args, frame)
    
    -- Output of function
    return table.concat(out) .. m_util.misc.add_category(page_cats)
end

function p.query_area_info(frame)
    -- p.query_area_info{conditions = '[[Is area id::test]]', cats='yes'}
    local tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    tpl_args.cats = m_util.cast.boolean(tpl_args.cats)
    tpl_args.sort = tpl_args.sort or 'Is area id'
    
    local results = m_util.smw.query({
        tpl_args.conditions,
        '?Has infobox HTML#',
        '?Has main page categories#',
        sort=tpl_args.sort,
    }, frame)
    
    local out = {}
    local cats = {}
    
    for _, row in ipairs(results) do
        out[#out+1] = row['Has infobox HTML']
        if row['Has main page categories'] ~= '' then
            for _, cat in ipairs(m_util.string.split(row['Has main page categories'], '<MANY>')) 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

return p