Module:Incursion

From Path of Exile Wiki
Jump to navigation Jump to search
Module documentation[view] [edit] [history] [purge]


Handles various incursion related templates:

-------------------------------------------------------------------------------
-- 
--                            Module:Incursion
-- 
-- This module implements Template:Incursion room and 
-- Template:Incursion room progression
-------------------------------------------------------------------------------

require('Module:No globals')
local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')

-- Should we use the sandbox version of our submodules?
local use_sandbox = m_util.misc.maybe_sandbox('Incursion')

-- Lazy loading
local f_infocard -- require('Module:Infocard')._main

-- The cfg table contains all localisable strings and configuration, to make it
-- easier to port this module to another wiki.
local cfg = use_sandbox and mw.loadData('Module:Incursion/config/sandbox') or mw.loadData('Module:Incursion/config')

local i18n = cfg.i18n

-- ----------------------------------------------------------------------------
-- Tables
-- ----------------------------------------------------------------------------

local tables = {
    incursion_rooms = {
        table = 'incursion_rooms',
        order = {'id', 'name', 'description', 'flavour_text', 'tier', 'upgrade_room_id', 'icon', 'min_level', 'architect_metadata_id', 'architect_name', 'modifier_ids', 'stat_text'},
        display_table_order = {'id', 'tier', 'upgrade_room_id', 'min_level', 'architect_name'},
        display_list_order = {'description', 'stat_text', 'icon', 'flavour_text',},
        fields = {
            id = {
                field = 'id',
                type = 'String(size=100; unique)',
                required = true,
                
                header = i18n.headings.id,
            },
            name = {
                field = 'name',
                type = 'String',
            },
            description = {
                field = 'description',
                type = 'Text',
                display = function (tpl_args)
                    return m_util.html.poe_color('mod', tpl_args.description)
                end
            },
            flavour_text = {
                field = 'flavour_text',
                type = 'Text',
                display = function (tpl_args)
                    return m_util.html.poe_color('flavour', tpl_args.flavour_text)
                end
            },
            tier = {
                field = 'tier',
                type = 'Integer',
                
                header = i18n.headings.tier,
            },
            upgrade_room_id = {
                field = 'upgrade_room_id',
                type = 'String',
                func = function (tpl_args, value)
                    if value == nil then
                        return
                    end
                    local results = m_cargo.query(
                        {'incursion_rooms'},
                        {'incursion_rooms._pageName', 'incursion_rooms.name'},
                        {
                            where=string.format('incursion_rooms.id="%s"', value)
                        }
                    )
                    if #results == 0 then
                        return
                    end
                    -- Save the upgrade room to tpl_args so we can make a link for it in the display
                    tpl_args.upgrade_room = results[1]
                    return value
                end,
                
                header = i18n.headings.upgrade_room_id,
                display = function(tpl_args)
                    if tpl_args.upgrade_room == nil then
                        if tpl_args.upgrade_room_id == nil then
                            return
                        else
                            return tpl_args.upgrade_room_id
                        end
                    end
                    return string.format('[[%s|%s]]', tpl_args.upgrade_room['incursion_rooms._pageName'], tpl_args.upgrade_room['incursion_rooms.name'])
                end
            },
            icon = {
                field = 'icon',
                type = 'Page',
                func = function (tpl_args, value)
                    if value == nil then
                        return
                    end
                    
                    return string.format(i18n.files.icon_format, value)
                end,
                
                display = function (tpl_args)
                    return string.format('[[%s]]', tpl_args.icon)
                end,
            },
            min_level = {
                field = 'min_level',
                type = 'Integer',
                default = 0,
                
                header = i18n.headings.min_level,
            },
            -- architect_name is temporary until monsters are added to the wiki
            architect_metadata_id = {
                field = 'architect_metadata_id',
                type = 'String',
            },
            architect_name = {
                field = 'architect_name',
                type = 'String',
                header = i18n.headings.architect,
                display = function (tpl_args)
                    return string.format('[[%s]]', tpl_args.architect_name)
                end,
            },
            modifier_ids = {
                field = 'modifier_ids',
                type = 'List (,) of String',
                func = function (tpl_args, value)
                    if value == nil then
                        return
                    end
                    tpl_args.mods = m_cargo.array_query{
                        tables={'mods'},
                        fields={'mods.stat_text'},
                        id_field='mods.id',
                        id_array=value
                    }
                    
                    return value
                end,
                
                header = i18n.headings.modifier_ids,
            },
            stat_text = {
                field = 'stat_text',
                type = 'Text',
                func = function (tpl_args, value)
                    if tpl_args.mods == nil then
                        return
                    end
                    local text = {}
                    for page, row in pairs(tpl_args.mods) do
                        if row['mods.stat_text'] then
                            text[#text+1] = row['mods.stat_text']
                        end
                    end
                    return table.concat(text, '<br>')
                end,
            },
        },
    },
}

-- ----------------------------------------------------------------------------
-- Helper functions
-- ----------------------------------------------------------------------------

local h = {}

function h.check_args(tpl_args, key, data)
    local value = tpl_args[key]
    if value == nil then
        return false
    end
    
    if data.default == value then
        return false
    end
    
    return true
end

-- Lazy loading for Module:Infocard
function h.infocard(args)
    if not f_infocard then
        f_infocard = require('Module:Infocard')._main
    end
    return f_infocard(args)
end

-- Query room
function h.query_room(args)
    if not m_util.table.has_any_key(args, {'id', 'page'}) then
        error(i18n.errors.missing_search_term)
    end
    local tables = {'incursion_rooms'}
    local fields = {
        'incursion_rooms._pageName=_pageName',
        'incursion_rooms.id=id',
        'incursion_rooms.architect_metadata_id=architect_metadata_id',
    }
    local query = {
        groupBy = 'incursion_rooms._pageID',
    }
    local search_param
    if args.id then
        query.where = string.format(
            'incursion_rooms.id = "%s"',
            args.id
        )
        search_param = 'id'
    else
        query.where = string.format(
            'incursion_rooms._pageName = "%s"',
            args.page
        )
        search_param = 'page'
    end
    local results = m_cargo.query(tables, fields, query)
    local err
    if #results == 0 then
        -- No results found
        err = m_util.Error{
            message = string.format(
                i18n.errors.no_results_found,
                search_param,
                args[search_param]
            ),
            code = 'no_results_found',
        }
    end
    if err then
        return {error = err}
    end
    return results[1]
end

-- ----------------------------------------------------------------------------
-- Main functions
-- ----------------------------------------------------------------------------

local function _incursion_room(tpl_args)
    --[[
    Stores cargo data for incursion rooms and displays it in a infobox.
    
    Examples
    --------
    = p.incursion_room{
        id = 'CorruptionRoomIII',
        name = 'Locus of Corruption',
        description = 'Reduces player maximum resistances throughout the Temple.<br />Contains an Altar of Corruption.',
        flavour_text = 'Darkness coalesces, imbues, and blesses.',
        tier = '3',
        icon = 'CorruptionRoom3',
        modifier_ids = 'MapPlayerMaxResists3__',
        architect_metadata_id = 'Metadata/Monsters/VaalArchitect/VaalArchitect8',
        architect_name = 'Paquate, Architect of Corruption',
    }
    
    ]]
    
    m_util.args.from_cargo_map{
        tpl_args=tpl_args,
        table_map=tables.incursion_rooms,
    }
    
    --
    -- infobox
    -- 
    local infocard_args = {}
    infocard_args.header = tpl_args.name
    
    local function display (value, data)
        if data.display then
            return data.display(tpl_args) or ''
        end
        
        if type(value) == 'table' then
            return table.concat(value, ', ')
        end
        
        return value or ''
    end
    
    -- Main sections, loop through
    local tbl = mw.html.create('table')
    tbl:attr('style', 'width: 100%;')
    for _, key in ipairs(tables.incursion_rooms.display_table_order) do
        local data = tables.incursion_rooms.fields[key]
        if h.check_args(tpl_args, key, data) then
            tbl
                :tag('tr')
                    :tag('th')
                        :wikitext(data.header or '')
                        :done()
                    :tag('td')
                        :wikitext(display(tpl_args[key], data))
                        :done()
                    :done()
        end
    end
    
    infocard_args[1] = tostring(tbl)
    
    local i = 2
    for _, key in ipairs(tables.incursion_rooms.display_list_order) do
        local data = tables.incursion_rooms.fields[key]
        if h.check_args(tpl_args, key, data) then
            infocard_args[i] = display(tpl_args[key], data)
            i = i + 1
        end
    end
    
    --
    -- Categories
    -- 
    
    local cats = {
        'Incursion rooms',
    }

    -- Attach to table
    mw.getCurrentFrame():expandTemplate{title = 'Template:Incursion room/cargo/incursion_rooms/attach'}
    
    --
    -- Done - output
    --
    
    return h.infocard(infocard_args) .. m_util.misc.add_category(cats)
end

local function _room_progression(args)
    --[[
    Displays a succession box of the rooms for one architect
    --]]

    args.page = args.page or mw.title.getCurrentTitle().prefixedText

    -- Query room
    local room = h.query_room(args)
    if room.error then
        room.error:throw()
    end

    -- Query room progression
    local results = m_cargo.query(
        {
            'incursion_rooms',
        },
        {
            'incursion_rooms._pageName=_pageName',
            'incursion_rooms.name=name',
            'incursion_rooms.tier=tier',
            'incursion_rooms.icon=icon',
            'incursion_rooms.architect_name=architect_name',
        },
        {
            where = string.format(
                'incursion_rooms.architect_metadata_id = "%s" AND incursion_rooms.tier > 0',
                room.architect_metadata_id
            ),
            groupBy = 'incursion_rooms._pageID',
            orderBy = 'incursion_rooms.tier ASC',
            limit = cfg.tier_max,
        }
    )
    if #results == 0 then
        error(i18n.errors.no_progression)
    end

    -- Build succession box
    local html = mw.html.create('table')
        :addClass('wikitable successionbox')
        :tag('tr')
            :tag('th')
                :attr('colspan', #results)
                :wikitext( m_util.html.wikilink(results[1].architect_name) )
                :done()
            :done()
    local row = html:tag('tr')
    for _, v in ipairs(results) do
        row:tag('td')
            :cssText('width: 33.3%')
            :wikitext(
                string.format(
                    '[[%s|160px|link=%s]] <br> %s. %s',
                    v.icon,
                    v._pageName,
                    i18n.tiers_roman[tonumber(v.tier)],
                    m_util.html.wikilink(v._pageName, v.name)
                )
            )
    end

    return tostring(html)
end

-- ----------------------------------------------------------------------------
-- Exported functions
-- ----------------------------------------------------------------------------

local p = {}

p.table_incursion_rooms = m_cargo.declare_factory{data=tables.incursion_rooms}

--
-- Template:Incursion room
-- 
p.incursion_room = m_util.misc.invoker_factory(_incursion_room, {
    wrappers = cfg.wrappers.incursion_room,
})

--
-- Template:Incursion room progression
-- 
p.room_progression = m_util.misc.invoker_factory(_room_progression, {
    wrappers = cfg.wrappers.room_progression,
})

return p