Module:Skill: Difference between revisions

From Path of Exile Wiki
Jump to navigation Jump to search
>OmegaK2
(Fixed not showing "reserved" in progression table, added abbr to the dmg multi)
>OmegaK2
m (fixed maxlevel for gems with 0 exp)
Line 318: Line 318:
             util.smw.subobject(g_frame, 'level' .. i, properties)
             util.smw.subobject(g_frame, 'level' .. i, properties)
              
              
             if g_args[prefix .. 'experience'] ~= nil and g_args[prefix .. 'experience'] > 0 then
             if g_args[prefix .. 'experience'] ~= nil then
                 maxlevel = i
                 maxlevel = i
             end
             end

Revision as of 22:14, 7 April 2016

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


Lua logo

This module depends on the following other modules:

Overview

Module for handling skills with semantic media wiki support

Skill templates

Module:Item2

All templates defined in Module:Skill:

Module:Skill link

All templates defined in Module:Skill link:

ru:Модуль:Skill

local p = {}

local g_frame, g_args

local getArgs = require('Module:Arguments').getArgs
local util = require('Module:Util')

local mwlanguage = mw.language.getContentLanguage()

--
-- Data
--

local data = {}

data.map = {}
data.map.stats = {
    {
        prefix = '',
        property_prefix = 'Has stat ',
    },
    {
        prefix = 'quality_',
        property_prefix =  'Has quality stat ',
    },
}

data.map.static = {
    -- GrantedEffects.dat
    {
        name = 'skill_id',
        property = 'Is skill id',
        func = nil,
    },
    {
        name = 'is_support_gem',
        property = 'Is support gem',
        func = util.cast.bool,
    },
    {
        name = 'support_gem_letter',
        property = 'Has support gem letter',
        func = nil,
    },
    -- Active Skills.dat
    {
        name = 'cast_time',
        property = 'Has cast time',
        func = util.cast.number,
    },
    {
        name = 'gem_description',
        property = 'Has description',
        func = nil,
    },
    {
        name = 'active_skill_name',
        property = 'Has active skill name',
        func = nil,
    },
    -- Misc data derieved from stats
    {
        name = 'stat_text',
        property = 'Has stat text',
        func = nil,
    },
    {
        name = 'quality_stat_text',
        property = 'Has quality stat text',
        func = nil,
    },
    -- Misc data currently not from game data
    {
        name = 'has_percentage_mana_cost',
        property = 'Has percentage mana cost',
        func = util.cast.boolean,
    },
    {
        name = 'has_reservation_mana_cost',
        property = 'Has reservation mana cost',
        func = util.cast.boolean,
    },
}

data.map.progression = {
    {
        name = 'level_requirement',
        property = 'Has level requirement',
        func = util.cast.number,
        header = util.html.abbr('[[Image:Level_up_icon_small.png|link=|Lvl.]]', 'Required Level', 'nounderline'),
    },
    {
        name = 'dexterity_requirement',
        property = 'Has dexterity requirement',
        func = util.cast.number,
        header = util.html.abbr('[[Image:DexterityIcon_small.png|link=|dexterity]]', 'Required Dexterity', 'nounderline'),
    },
        {
        name = 'strength_requirement',
        property = 'Has strength requirement',
        func = util.cast.number,
        header = util.html.abbr('[[Image:StrengthIcon_small.png|link=|strength]]', 'Required Strength', 'nounderline'),
    },
        {
        name = 'intelligence_requirement',
        property = 'Has intelligence requirement',
        func = util.cast.number,
        header = util.html.abbr('[[Image:IntelligenceIcon_small.png|link=|intelligence]]', 'Required Intelligence', 'nounderline'),
    },
    {
        name = 'mana_multiplier',
        property = 'Has mana multiplier',
        func = util.cast.number,
        header = 'Mana<br>Multiplier',
        fmt = '%s%%',
    },
    {
        name = 'critical_strike_chance',
        property = 'Has critical strike chance',
        func = util.cast.number,
        header = 'Critical<br>Strike<br>Chance',
        fmt = '%s%%',
    },
    {
        name = 'mana_cost',
        property = 'Has mana cost',
        func = util.cast.number,
        header = function (skill_data)
            if skill_data["Has reservation mana cost"] then
                return 'Mana<br>Reserved'
            else
                return 'Mana<br>Cost'
            end
        end,
        fmt = function (value)
            local str
            if g_args.has_percentage_mana_cost then
                str = '%s%%'
            else
                str = '%s'
            end
        
            return string.format(str, value)
        end,
    },
    {
        name = 'damage_effectiveness',
        property = 'Has damage effectiveness',
        func = util.cast.number,
        header = 'Damage<br>Effectiveness',
        fmt = '%s%%',
    },
    {
        name = 'stored_uses',
        property = 'Has stored uses',
        func = util.cast.number,
        header = 'Stored<br>Uses',
    },
    {
        name = 'cooldown',
        property = 'Has cooldown',
        func = util.cast.number,
        header = 'Cooldown',
        fmt = '%ss',
    },
    {
        name = 'vaal_souls_requirement',
        property = 'Has vaal souls requirement',
        func = util.cast.number,
        header = 'Vaal<br>souls',
    },
    {
        name = 'vaal_stored_uses',
        property = 'Has vaal stored uses',
        func = util.cast.number,
        header = 'Stored<br>Uses',
    },
    {
        name = 'damage_multiplier',
        property = 'Has damage multiplier',
        func = util.cast.number,
        header = util.html.abbr('Damage<br>Multiplier', 'Deals x% of Base Damage'),
        fmt = '%s%%',
    },
    -- from gem experience, optional
    {
        name = 'experience',
        property = 'Has experience requirement',
        func = util.cast.number,
        hide = true,
    },
    {
        name = 'stat_text',
        property = 'Has stat text',
        func = nil,
        hide = true,
    },
    {
        name = 'quality_stat_text',
        property = 'Has quality stat text',
        func = nil,
        hide = true,
    },
}

-- 
-- Helper functions 
--
local h = {}

function h.map_to_arg(properties, prefix, map)
    local id
    local val
    for _, row in ipairs(map) do
        id = prefix .. row.name
        val = g_args[id]
        if val ~= nil then
            if row.func ~= nil then
                val = row.func(val)
            end
            g_args[id] = val
            properties[row.property] = val
        end
    end
end

function h.stats(properties, prefix)
    local stat_ids
    local stat_values
    local stat_id
    local stat_id_key
    local stat_val
    local stat_val_key
    local type_prefix
    
    for _, stat_type in ipairs(data.map.stats) do
        type_prefix = string.format('%s%sstat', prefix, stat_type.prefix)
        stat_ids = {}
        stat_values = {}
        for i=1, 8 do
            stat_id_key = string.format('%s%s_id', type_prefix, i)
            stat_val_key = string.format('%s%s_value', type_prefix, i)
            stat_id = g_args[stat_id_key]
            stat_val = tonumber(g_args[stat_val_key])
            if stat_id == nil or stat_val == nil then
                break
            end
            g_args[stat_val_key] = stat_val
            stat_ids[#stat_ids+1] = stat_id
            stat_values[#stat_values+1] = stat_val
        end
        g_args[type_prefix .. 's_ids'] = stat_ids
        g_args[type_prefix .. 's_values'] = stat_values
        
        properties[stat_type.property_prefix .. 'id'] = stat_ids
        properties[stat_type.property_prefix .. 'value'] = stat_values
    end
end

function h.na(tr)
    tr
        :tag('td')
            :attr('class', 'table-na')
            :wikitext('N/A')
            :done()
end

function h.int_value_or_na(tr, value, pdata)
    value = tonumber(value)
    if value == nil then
        h.na(tr)
    else
        value = mwlanguage:formatNum(value)
        if pdata.fmt ~= nil then
            if type(pdata.fmt) == 'string' then
                value = string.format(pdata.fmt, value)
            elseif type(pdata.fmt) == 'function' then
                value = pdata.fmt(value)
            end
        end
        tr
            :tag('td')
                :wikitext(value)
                :done()
    end
end

--
-- For Template:Skill
--
function p.skill(frame, args)
    if args ~= nil then
        g_args = args
    else
        g_args = getArgs(frame, {
            parentFirst = true
        })
    end
    g_frame = util.misc.get_frame(frame)
    
    --
    -- Args
    --
    local properties
    
    -- Handle level progression
    local maxlevel
    local prefix
    for i=1, 30 do
        properties = {}
        prefix = string.format('level%s', i) 
        if util.cast.boolean(g_args[prefix]) then
            prefix = prefix .. '_'
            h.map_to_arg(properties, prefix, data.map.progression)
            h.stats(properties, prefix)
            properties['Is skill level'] = i
            
            util.smw.subobject(g_frame, 'level' .. i, properties)
            
            if g_args[prefix .. 'experience'] ~= nil then
                maxlevel = i
            end
        end
    end
    
    -- Handle static arguments
    properties = {
        ['Has maximum skill level'] = maxlevel
    }
    g_args.max_level = maxlevel
    
    h.map_to_arg(properties, '', data.map.static)
    h.map_to_arg(properties, 'static_', data.map.progression)
    h.stats(properties, 'static_')
    
    util.smw.set(g_frame, properties)
end

function p.progression(frame)
    g_args = getArgs(frame, {
        parentFirst = true
    })
    g_frame = util.misc.get_frame(frame)
    
    --
    g_args.stat_format = {}
    local prefix
    for i=1, 9 do
        prefix = 'c' .. i .. '_' 
        local statfmt = {
            header = g_args[prefix .. 'header'],
            abbr = g_args[prefix .. 'abbr'],
            pattern_extract = g_args[prefix .. 'pattern_extract'],
            pattern_value = g_args[prefix .. 'pattern_value'],
        }
        if util.table.has_all_value(statfmt, {'header', 'abbr', 'pattern_extract', 'pattern_value'}) then
            break
        end
        
        if util.table.has_one_value(statfmt, {'header', 'abbr', 'pattern_extract', 'pattern_value'}) then
            error(string.format('All formatting keys must be specified for index "%s"', i))
        end
        
        statfmt.header = util.html.abbr(statfmt.abbr, statfmt.header)
        statfmt.abbr = nil
        g_args.stat_format[#g_args.stat_format+1] = statfmt
    end
    
    
    local result
    local query
    local page 
    local skill_data
    if g_args.skill_id then
        query = {}
        query[#query+1] = string.format('[[Is skill id::%s]]', g_args.skill_id)
        query[#query+1] = '?Has reservation mana cost#-'
        query['limit'] = 1
        result = util.smw.query(query, g_frame)
        if #result == 0 or #result[1] == 0 then
            error('Couldn\'t find a page for the specified skill id')
        end
        page = result[1][1]
        skill_data = result[1]
    else
        if g_args.page then
            page = g_args.page
        else
            page = mw.title.getCurrentTitle().prefixedText
        end
        query = {}
        query[#query+1] = string.format('[[%s]]', page)
        query[#query+1] = '?Has reservation mana cost#-'
        
        result = util.smw.query(query, g_frame)
        if #result == 0 then
            error('Couldn\'t find the queried data on the skill page')
        end
        
        skill_data = result[1]
    end
    
    skill_data["Has reservation mana cost"] = util.cast.boolean(skill_data["Has reservation mana cost"])
    
    query = {}
    query[#query+1] = string.format('[[-Has subobject::%s]]', page)
    for _, pdata in ipairs(data.map.progression) do
        query[#query+1] = '?' .. pdata.property .. '#-'
    end
    query[#query+1] = '?Is skill level#-'
    query[#query+1] = '?Has stat text'
    query[#query+1] = '?Has stat id'
    query[#query+1] = '?Has stat value#-'
    query[#query+1] = '?Has quality stat id'
    query[#query+1] = '?Has quality stat value#-'
    
    query.sort = 'Is skill level'
    
    result = util.smw.query(query, g_frame)
    
    if #result == 0 then
        error('No gem level progression data found')
    end
    
    headers = {}
    for i, row in ipairs(result) do
        for k, v in pairs(row) do
            if v ~= "" then
                headers[k] = true
            end
        end
    end
    
    
    
    local tbl = mw.html.create('table')
    tbl:attr('class', 'wikitable skill-progression-table')
    
    local head = tbl:tag('tr')
    head
        :tag('th')
            :wikitext('Level')
            :done()
    
    for _, pdata in ipairs(data.map.progression) do
        -- TODO should be nil?
        if pdata.hide == nil and headers[pdata.property] then
            local text
            if type(pdata.header) == 'function' then
                text = pdata.header(skill_data)
            else
                text = pdata.header
            end
            head
                :tag('th')
                    :wikitext(text)
                    :done()
        end
    end
    
    for _, statfmt in ipairs(g_args.stat_format) do
        head
            :tag('th')
                :wikitext(statfmt.header)
                :done()
    end
    
    if headers['Has experience requirement'] then
        head
            :tag('th')
                :wikitext(util.html.abbr('Exp.', 'Experience Needed to Level Up'))
                :done()
            :tag('th')
                :wikitext(util.html.abbr('Total Exp.', 'Total experience needed'))
                :done()
    end
    
    local tblrow
    local lastexp = 0
    local experience
    for i, row in ipairs(result) do
        tblrow = tbl:tag('tr')
        tblrow
            :tag('th')
                :wikitext(row['Is skill level'])
                :done()
        
        for _, pdata in ipairs(data.map.progression) do
            if pdata.hide == nil and headers[pdata.property] then
                h.int_value_or_na(tblrow, row[pdata.property], pdata)
            end
        end
        
        -- stats
        stats = util.string.split(row['Has stat text'], '<br>')
        for _, statfmt in ipairs(g_args.stat_format) do
            local match = {}
            for j, stat in ipairs(stats) do
                match = {string.match(stat, statfmt.pattern_extract)}
                if #match > 0 then
                    -- TODO maybe remove stat here to avoid testing against in future loops
                    break
                end
            end
            if #match == 0 then
                h.na(tblrow)
            else
                tblrow
                    :tag('td')
                        :wikitext(string.format(statfmt.pattern_value, match[1], match[2], match[3], match[4], match[5]))
                        :done()
            end
        end
        
        -- TODO: Quality stats, afaik no gems use this atm
        
        if headers['Has experience requirement'] then
            experience = tonumber(row['Has experience requirement'])
            if experience ~= nil then
                h.int_value_or_na(tblrow, experience - lastexp, {})
                
                lastexp = experience
            else
                h.na(tblrow)
            end
            h.int_value_or_na(tblrow, experience, {})
        end
    end
    
    -- TODO 
    
    return tostring(tbl)
end

return p