Module:Area

From Path of Exile Wiki
Revision as of 05:31, 26 June 2017 by >OmegaK2 (Initial SMW area module (WIP))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
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 . 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]]',
    },

    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',
        
        --
        act = 'Act: %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',
        area_type_tags = 'Area type tags: %s',
        tags = 'Tags: %s',
        parent_area = m_util.html.abbr('Parent Town', 'Portals will lead to the parent town') .. ': %s',
        connections = 'Connections: %s',
        -- monsters
        -- boss monsters
        entry_message = '%s: %s',
    },
}

-- ----------------------------------------------------------------------------
-- 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

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

local argument_map = {
    [i18n.args.id] = {
        property = 'Is area id',
        func = nil,
    },
    [i18n.args.name] = {
        property = 'Has name',
        func = nil,
    },
    [i18n.args.act] = {
        property = 'Has act',
        func = m_util.cast.factory.number(i18n.args.act),
    },
    [i18n.args.area_level] = {
        property = 'Has area level',
        func = m_util.cast.factory.number(i18n.args.area_level),
    },
    [i18n.args.level_restriction_max] = {
        property = 'Has maximum level restriction',
        func = m_util.cast.factory.number(i18n.args.level_restriction_max),
        default = 100,
    },
    [i18n.args.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,
        }),
    },
    [i18n.args.tags] = {
        property = 'Has tags',
        func = m_util.cast.factory.assoc_table(i18n.args.tags, {
            tbl = m_game.constants.tags,
            errmsg = i18n.errors.invalid_tag,
        }),
    },
    [i18n.args.loading_screen] = {
        property = 'Has loading screen image',
        func = function (tpl_args, frame)
            if tpl_args[i18n.args.loading_screen] ~= nil then
                tpl_args[i18n.args.loading_screen] = string.format('File:%s.png', tpl_args[i18n.args.loading_screen]) 
            end
        end,
    },
    [i18n.args.connection_ids] = {
        property = 'Has area connection ids',
        func = factory.arg_list(i18n.args.connection_ids),
    },
    [i18n.args.parent_area_id] = {
        property = 'Has parent area id',
    },
    [i18n.args.modifier_ids] = {
        property = 'Has mod ids',
        func = factory.arg_list(i18n.args.modifier_ids),
    },
    [i18n.args.monster_ids] = {
        property = 'Has monster ids',
        func = factory.arg_list(i18n.args.monster_ids)
    },
    [i18n.args.boss_monster_ids] = {
        property = 'Has boss monster ids',
        func = factory.arg_list(i18n.args.boss_monster_ids)
    },
    [i18n.args.entry_text] = {
        property = 'Has entry text message',
    },
    [i18n.args.entry_npc] = {
        property = 'Has entry npc',
    },
    --
    -- Spawn chances
    --
    [i18n.args.vaal_area_ids] = {
        property = 'Has vaal area ids',
        func = factory.arg_list(i18n.args.vaal_area_ids),
    },
    [i18n.args.vaal_area_spawn_chance] = {
        property = 'Has vaal area spawn chance',
        func = m_util.cast.factory.number(i18n.args.vaal_area_spawn_chance),
        default = 0,
    },
    [i18n.args.strongbox_spawn_chance] = {
        property = 'Has strongbox spawn chance',
        func = m_util.cast.factory.number(i18n.args.strongbox_spawn_chance),
        default = 0,
    },
    [i18n.args.strongbox_max_count] = {
        property = 'Has maximum number of strongboxes',
        func = m_util.cast.factory.number(i18n.args.strongbox_max_count),
        default = 0,
    },
    [i18n.args.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[i18n.args.strongbox_rarity_weight] = {}
            
            for index, data in ipairs(m_game.constants.item.rarity) do
                local value = weights[index] or 0
                tpl_args[i18n.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
    --
    [i18n.args.is_map_area] = {
        property = 'Is map area',
        func = m_util.cast.factory.boolean(i18n.args.is_map_area),
        default = false,
    },
    [i18n.args.is_unique_map_area] = {
        property = 'Is unique map area',
        func = m_util.cast.factory.boolean(i18n.args.is_unique_map_area),
        default = false,
    },
    [i18n.args.is_town_area] = {
        property = 'Is town area',
        func = m_util.cast.factory.boolean(i18n.args.is_town_area),
        default = false,
    },
    [i18n.args.is_hideout_area] = {
        property = 'Is hideout area',
        func = m_util.cast.factory.boolean(i18n.args.is_hideout_area),
        default = false,
    },
    [i18n.args.is_vaal_area] = {
        property = 'Is vaal area',
        func = m_util.cast.factory.boolean(i18n.args.is_vaal_area),
        default = false,
    },
    [i18n.args.is_master_daily_area] = {
        property = 'Is master daily area',
        func = m_util.cast.factory.boolean(i18n.args.is_master_daily_area),
        default = false,
    },
    [i18n.args.is_labyrinth_area] = {
        property = 'Is labyrinth area',
        func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_area),
        default = false,
    },
    [i18n.args.is_labyrinth_airlock_area] = {
        property = 'Is labyrinth airlock area',
        func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_airlock_area),
        default = false,
    },
    [i18n.args.is_labyrinth_boss_area] = {
        property = 'Is labyrinth boss area',
        func = m_util.cast.factory.boolean(i18n.args.is_labyrinth_boss_area),
        default = false,
    },
    [i18n.args.has_waypoint] = {
        property = 'Has waypoint',
        func = m_util.cast.factory.boolean(i18n.args.has_waypoint),
        default = false,
    },
}

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',
}

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.map = {
    {
        args = {
            [i18n.args.act] = {
            },
        },
        func = function(tpl_args, frame)
            return string.format(i18n.tooltips.act, tpl_args[i18n.args.act]) 
        end,
    }, 
    {
        args = {
            [i18n.args.area_level] = {
            },
        },
        func = function(tpl_args, frame)
            return string.format(i18n.tooltips.area_level, tpl_args[i18n.args.area_level]) 
        end,
    },
    {
        args = {
            [i18n.args.level_restriction_max] = {
                hide = 100,
            },
        },
        func = function(tpl_args, frame)
            return string.format(i18n.tooltips.level_restriction_max, tpl_args[i18n.args.level_restriction_max]) 
        end,
    },
    {
        args = {
            [i18n.args.area_type_tags] = {
            },
        },
        func = function(tpl_args, frame)
            return string.format(i18n.tooltips.area_type_tags, table.concat(tpl_args[i18n.args.area_type_tags], ', ')) 
        end,
    },
    {
        args = {
            [i18n.args.tags] = {
            },
        },
        func = function(tpl_args, frame)
            return string.format(i18n.tooltips.tags, table.concat(tpl_args[i18n.args.tags], ', ')) 
        end,
    },
    {
        args = {
            [i18n.args.entry_text] = {},
            [i18n.args.entry_npc] = {},
        },
        func = function(tpl_args, frame)
            return string.format(i18n.tooltips.entry_message, tpl_args[i18n.args.entry_npc], tpl_args[i18n.args.entry_text]) 
        end,
    },
    {
        args = {
           [i18n.args.loading_screen] = {},
        },
        func = function(tpl_args, frame)
            return string.format(i18n.images.loading_screen, tpl_args.loading_screen)
        end,
    },
}

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

local d = {}
function d.area_box(tpl_args, frame)
    local infocard_args = {}
    
    -- Top header
    if tpl_args[i18n.args.name] ~= nil then
        infocard_args.header = string.format('%s (%s)', tpl_args[i18n.args.name], tpl_args[i18n.args.id])
    else
        infocard_args.header = tpl_args[i18n.args.name]
    end
    
    -- Subheader
    local out = {}
    for _, key in ipairs(display.area_type) do
        if tpl_args[i18n.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[i18n.args.is_town_area] then
        infocard_args.headerright = i18n.images.waypoint_town
    elseif tpl_args[i18n.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 i = 1
    for _, data in ipairs(display.map) do
        local continue = true
        if data.args ~= nil then
            for key, key_data in pairs(data.args) do
                if tpl_args[key] == nil then
                    continue = false
                    break
                elseif key_data.hide == nil and 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 tpl_args[key] == key_data.hide then
                    continue = false
                    break
                end
            end
        end
        
        if continue then
            infocard_args[i] = data.func(tpl_args, frame)
            
            i = i +1
        end
    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 key = i18n.args[k]
        if key == nil then
            error('Missing i18n for argument: ' .. k)
        end
        local data = argument_map[key]
        if data == nil then
            error('Missing data in argument_map: ' .. key)
        end
        if data.func ~= nil then
            data.func(tpl_args, frame)
        end
        if data.default ~= nil and tpl_args[key] == nil then
            tpl_args[key] = data.default
        end
        
        if data.property ~= nil and tpl_args[key] ~= nil then
            tpl_args._properties[data.property] = tpl_args[key]
        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, properties)
    
    -- display
    return out
end

return p