Module:Passive skill table

From Path of Exile 2 Wiki
Revision as of 18:52, 4 March 2020 by >Illviljan
Jump to navigation Jump to search
Module documentation[view] [edit] [history] [purge]


Lua logo

This module depends on the following other modules:

Implements {{passive skill table}}.

--
-- Module for passive skill table
--

local m_util = require('Module:Util')
local m_cargo = require('Module:Cargo')
local getArgs = require('Module:Arguments').getArgs
local f_passive_skill_link = require('Module:passive_skill_link').passive_skill_link

-- ----------------------------------------------------------------------------
-- Strings
-- ----------------------------------------------------------------------------

local i18n = {
    icon_name = 'File:%s passive skill icon.png',
    
    cats = {
        data = 'Passive skill data',
        
        keystone = 'Keystone passive skills',
        notable = 'Notable passive skills',
        basic = 'Basic passive skills',
        ascendancy_notable = 'Ascendancy notable passive skills',
        ascendancy_basic = 'Ascendancy basic passive skills',
    },
        
    passive_table = {
        ascendancy_class = 'Ascendancy<br>Class',
        name = 'Name',
        id = 'Id',
        int_id = 'Integer id',
        stats = 'Stats',
        connections = 'Connections',
        flavour_text = 'Flavour text', 
        is_notable = 'Notable',
        is_keystone = 'Keystone',
    },
    
    errors = {
        invalid_args = 'Passive skill table: q_where must be specified',
        no_passives_found = 'No passive skills with the given name found',
        cats = 'Pages with broken passive skill tables',
    },
}


-- ----------------------------------------------------------------------------
-- Constants & Data
-- ----------------------------------------------------------------------------

-- local c = {}

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

local h = {}

function h.make_order(results, field, args)
    --[[
    Merge duplicates and then create a ordered list.
    ]]
    local values = {}
    local order = {}
    for _, row in ipairs(results) do
        local val = row[field]
        if args.fmt then
            val = args.fmt(row[field])
        end 
        -- Can't show results here that don't have a value:
        if val then
            if values[val] == nil then
                values[val] = {row}
                table.insert(order, val)
            else
                table.insert(values[val], row)
            end
        end
    end
    
    return values, order
end

function h.data_page_links(values, order)
    local out = {}
    for i, key in ipairs(order) do
        local links = {}
        for j, row in ipairs(values[key]) do
            links[#links+1] = string.format(
                '[[%s|&#91;%s&#93;]]', 
                row['passive_skills._pageName'], 
                j + (i-1) * #values[key]
            )
        end
        out[i] = string.format(
            '<span class="passive-line">%s <span class="passive-hover">%s</span></span>', 
            key, 
            table.concat(links, ' ')
        )
    end
    
    return table.concat(out, '<hr>')
end

function h.get_type(passive)
    --[[
    Determine what type of passive skill this passive is.
    ]]
    local key
    if tonumber(passive['passive_skills.is_keystone']) == 1 then
        key = 'keystone'
    elseif tonumber(passive['passive_skills.is_notable']) == 1 then
        key = 'notable'
    else
        key = 'basic'
    end
    
    if passive['passive_skills.ascendancy_class'] ~= nil then
        key = 'ascendancy_' .. key
    end
    
    return key
end

function h.na_or_val(tr, value, func, args)
    --[[
    
    Parameters
    ----------
    tr : 
    
    value :
    
    func : function
    
    args : list of 
        Valid keys are:
        * colour
    
    ]]
    local args = args or {}
    if value == nil or value == '' then
        tr:wikitext(m_util.html.td.na())
    else
        local raw_value = value
        if func ~= nil then
            value = func(value)
        end
        
        local td = tr:tag('td')
        td:attr('data-sort-value', raw_value)
        td:wikitext(value)
        if args.colour then
            td:attr('class', 'tc -' .. args.colour)
        end
    end
end

-- Format style for field:
h.fmt = {}
function h.fmt.link(field)
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do 
        fields[i] = string.format('[[%s]]', v)
    end 
    return table.concat(fields, ', ')
end 
function h.fmt.passive_skill_link(field)
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do
        fields[i] = f_passive_skill_link{id=v}
    end 
    return table.concat(fields, ', ')
end
function h.fmt.boolean(field)
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do
        if m_util.cast.boolean(v) then
            fields[i] = '<div class="table-yes" data-sort-value=1><span>&#x2713;</span></div>'
        else 
            fields[i] = '<div class="table-no" data-sort-value=0><span>&#x2717;</span></div>'
        end
    end 
    return table.concat(fields, ', ')
end
function h.fmt.link_passive(field)
    --[[
    Create a link to the passive skill data page from the ID. 
    ]]
    local fields = m_util.string.split(field, ',')
    for i, v in ipairs(fields) do
        fields[i] = string.format(
            '[[Passive Skill:%s|%s]]',
            string.gsub(v, '_', '~'),
            v
        )
    end 
    return table.concat(fields, ', ')
end

-- Display 
h.tbl = {}
h.tbl.display = {}

h.tbl.display.factory = {}
function h.tbl.display.factory.merged_values(args)
    local args = args or {}
    return function (tpl_args, frame, tr, rows, rowinfo)
        local values, order = h.make_order(rows, args.field, args)
        local value = h.data_page_links(values, order)
        h.na_or_val(tr, value, func, args)
    end
end

-- ----------------------------------------------------------------------------
-- Data mappings
-- ----------------------------------------------------------------------------

data = {}
data.passive_skill_table = {
    tables = {
        passive_skill_stats = {
            join='passive_skills._pageID=passive_skill_stats._pageID',
        },
        main_pages = {
            join='passive_skills.id=main_pages.id'
        }
    },
    -- Display data
    {
        args = nil,
        header = i18n.passive_table.name,
        fields = {
            -- Optional:
            'passive_skills.is_keystone',
            'passive_skills.is_notable',
            'passive_skills.ascendancy_class',
            'passive_skills.main_page',
            'main_pages._pageName',
            -- 'passive_skills.html',
            
            -- Required:
            'passive_skills._pageName',
            'passive_skills.name',
            'passive_skills.icon',
        },        
        display = function (tpl_args, frame, tr, data)
            local passive = data[1]
            
            local psl_args = {
                skip_query = true,
                page = passive['passive_skills.main_page'] 
                    or passive['main_pages._pageName'] 
                    or passive['passive_skills.name']
                    or passive['passive_skills._pageName'], 
                name = passive['passive_skills.name'], 
                icon = passive['passive_skills.icon'], 
                is_keystone = passive['passive_skills.is_keystone'],
                is_notable = passive['passive_skills.is_notable'],
                ascendancy_class = passive['passive_skills.ascendancy_class'],
            }
            if tpl_args.no_html == nil then
                psl_args.html = passive['passive_skills.html']
            end
            if tpl_args.large then
                psl_args.large = tpl_args.large
            end
                
            tr
                :tag('td')
                    :attr(
                        'data-sort-value', 
                        passive['passive_skills.name'] .. h.get_type(passive)
                    )
                    :attr('style', 'text-align:center;')
                    :wikitext(f_passive_skill_link(psl_args))
                    :done()
            
        end,
        order = 1000,
        sort_type = 'text',
        options = {
            [1] = {optional=true},
            [2] = {optional=true},
            [3] = {optional=true},
            [4] = {optional=true},
            [5] = {optional=true},
        },
    },
    {
        args = {'ascendancy'},
        header = i18n.passive_table.ascendancy_class,
        fields = {'passive_skills.ascendancy_class'},
        display = function (tpl_args, frame, tr, data)
            local passive = data[1]
            h.na_or_val(tr, passive['passive_skills.ascendancy_class'], 
                function ()  
                    return string.format(
                        '[[%s]]<br>[[File:%s avatar.png|link=%s]]', 
                        passive['passive_skills.ascendancy_class'], 
                        passive['passive_skills.ascendancy_class'], 
                        passive['passive_skills.ascendancy_class']
                    )
                end
            )
        end,
        order = 0,
        sort_type = 'text',
    },
    {
        args = 'id',
        header = i18n.passive_table.id,
        fields = {'passive_skills.id'},               
        display = h.tbl.display.factory.merged_values{field='passive_skills.id'},
        order = 1001,
        sort_type = 'text',
    },
    {
        args = 'int_id',
        header = i18n.passive_table.int_id,
        fields = {'passive_skills.int_id'},        
        display = h.tbl.display.factory.merged_values{field='passive_skills.int_id'},
        order = 1002,
        sort_type = 'text',
    },
    {
        args = {'stat', 'stats', 'stat_text'},
        header = i18n.passive_table.stats,
        fields = {'passive_skills.stat_text'},
        display = h.tbl.display.factory.merged_values{field='passive_skills.stat_text', colour='mod'},
        order = 2000,
        sort_type = 'text',
    },
    {
        args = 'connections',
        header = i18n.passive_table.connections,
        fields = {'passive_skills.connections'},
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.connections', 
            fmt=h.fmt.passive_skill_link,
        },
        order = 3000,
        sort_type = 'text', 
    },
    {
        args = 'flavour_text',
        header = i18n.passive_table.flavour_text,
        fields = {'passive_skills.flavour_text'},
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.flavour_text',
            colour='flavour',
        },
        order = 3001,
        sort_type = 'text', 
    },
    {
        args = 'is_notable',
        header = i18n.passive_table.is_notable,
        fields = {'passive_skills.is_notable'},        
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.is_notable', 
            fmt=h.fmt.boolean
        },
        order = 4000,
        sort_type = 'number',
    },
    {
        args = 'is_keystone',
        header = i18n.passive_table.is_keystone,
        fields = {'passive_skills.is_keystone'},        
        display = h.tbl.display.factory.merged_values{
            field='passive_skills.is_keystone', 
            fmt=h.fmt.boolean
        },
        order = 4001,
        sort_type = 'number',
    },
}

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

local p = {}

function p.passive_skill_table(frame)
    --[[
    Displays a table of passive skills.
    
    TODO: Why is the groupBy necessary?
    
    Examples
    --------
    = p.passive_skill_table{
        q_where = 'passive_skills.ascendancy_class = "Gladiator"',
        large=1,
    }
    = p.passive_skill_table{
        q_where='passive_skills.stat_text LIKE "%damage%taken%as%"',
        ascendancy=1,
        stat_text=1,
    }
    
    ]]
    local t = os.clock()
    
    -- Get args
    local tpl_args = getArgs(frame, {
        parentFirst = true
    })
    frame = m_util.misc.get_frame(frame)
    
    -- Check if the correct parameters have been set:
    if tpl_args.q_where == nil then 
        return m_util.html.error{msg=i18n.errors.invalid_args .. m_util.misc.add_category(i18n.errors.cats)}
    end
    
    -- Cargo query settings:
    tpl_args.q_groupBy = tpl_args.q_groupBy or 'passive_skills._pageID'
    tpl_args.q_orderBy = tpl_args.q_orderBy or [[
        passive_skills.ascendancy_class IS NULL DESC, 
        passive_skills.is_keystone, 
        passive_skills.is_notable, 
        passive_skills.name
    ]]
   
    -- Run the query and display the results:
    local out = m_cargo.table_query{
        tpl_args=tpl_args,
        frame=frame,
        main_table='passive_skills',
        row_unique_fields={'passive_skills.name'},
        data=data.passive_skill_table,
        empty_cell=m_util.html.td.na() 
    }
    
    mw.logObject({os.clock() - t, tpl_args})

    return out 
end


-- ----------------------------------------------------------------------------
-- End
-- ----------------------------------------------------------------------------

return p