Module:Sandbox/Illviljan/Skill

local p = {}

local g_frame, g_args

local getArgs = require('Module:Arguments').getArgs local m_util = require('Module:Util') local m_game = require('Module:Game')

local mwlanguage = mw.language.getContentLanguage

-- -- Data --

local data = {} data.cast = {} data.cast.wrap = function(f) return function(value) if value == nil then return nil else return f(value) end end end

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 = data.cast.wrap(m_util.cast.boolean), },   {        name = 'support_gem_letter', property = 'Has support gem letter', func = nil, },   -- Active Skills.dat {       name = 'cast_time', property = 'Has cast time', func = data.cast.wrap(m_util.cast.number), },   {        name = 'gem_description', property = 'Has description', func = nil, },   {        name = 'active_skill_name', property = 'Has active skill name', func = nil, },   {        name = 'item_class_restriction', property = 'Has item class restrictions', func = function(value) if value == nil then return nil end value = m_util.string.split(value, ', ') for _, v in ipairs(value) do               local result = m_util.table.find_in_nested_array{value=v,key='full', tbl=m_game.constants.item.class} if result == nil then error(string.format('Invalid item class: %s', v)) end end return value end, },   {        name = 'item_class_restriction_difference', property = 'Has item class restrictions difference', func = function(value) if g_args.item_class_restriction == nil then return nil end local classes = {} for _, class in ipairs(g_args.item_class_restriction) do               classes[class] = true end

local rtr = {} for _, data in ipairs(m_game.constants.item.class) do               if classes[data.full] == nil then rtr[#rtr+1] = data.full end end return rtr end },   {        name = 'no_item_class_restriction', property = 'Has no item class restrictions', func = function(value) if g_args['item_class_restriction'] == nil then return true else return nil end end, },   -- Projectiles.dat - manually mapped to the skills {       name = 'projectile_speed', property = 'Has projectile speed', func = data.cast.wrap(m_util.cast.number), },   -- 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 = function -- if g_args['has_percentage_mana_cost'] == nil then -- return false -- else -- return true -- end -- end, -- func = function(value) m_util.cast.boolean(value) end, func = m_util.cast.boolean, },   {        name = 'has_reservation_mana_cost', property = 'Has reservation mana cost', -- func = function -- if g_args['has_reservation_mana_cost'] == nil then -- return false -- else -- return true -- end -- end, -- func = function(value) m_util.cast.boolean(value) end, func = m_util.cast.boolean, },   {        name = 'radius', property = 'Has primary radius', func = data.cast.wrap(m_util.cast.number), },   {        name = 'radius_description', property = 'Has primary radius description', func = nil, },   {        name = 'radius_secondary', property = 'Has secondary radius', func = data.cast.wrap(m_util.cast.number), },   {        name = 'radius_secondary_description', property = 'Has secondary radius description', func = nil, },   -- not sure if any skill actually has 3 radius componets {       name = 'radius_tertiary', property = 'Has tertiary radius', func = data.cast.wrap(m_util.cast.number), },   {        name = 'radius_tertiary_description', property = 'Has tertiary radius description', func = nil, }, }

data.map.progression = { {       name = 'level_requirement', property = 'Has level requirement', func = data.cast.wrap(m_util.cast.number), header = m_util.html.abbr('', 'Required Level', 'nounderline'), },   {        name = 'dexterity_requirement', property = 'Has dexterity requirement', func = data.cast.wrap(m_util.cast.number), header = m_util.html.abbr('', 'Required Dexterity', 'nounderline'), },       {        name = 'strength_requirement', property = 'Has strength requirement', func = data.cast.wrap(m_util.cast.number), header = m_util.html.abbr('', 'Required Strength', 'nounderline'), },       {        name = 'intelligence_requirement', property = 'Has intelligence requirement', func = data.cast.wrap(m_util.cast.number), header = m_util.html.abbr('', 'Required Intelligence', 'nounderline'), },   {        name = 'mana_multiplier', property = 'Has mana multiplier', func = data.cast.wrap(m_util.cast.number), header = 'Mana Multiplier', fmt = '%s%%', },   {        name = 'critical_strike_chance', property = 'Has critical strike chance', func = data.cast.wrap(m_util.cast.number), header = 'Critical Strike Chance', fmt = '%s%%', },   {        name = 'mana_cost', property = 'Has mana cost', func = data.cast.wrap(m_util.cast.number), header = function (skill_data) if skill_data["Has reservation mana cost"] then return 'Mana Reserved' else return 'Mana 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 = data.cast.wrap(m_util.cast.number), header = 'Damage Effectiveness', fmt = '%s%%', },   {        name = 'stored_uses', property = 'Has stored uses', func = data.cast.wrap(m_util.cast.number), header = 'Stored Uses', },   {        name = 'cooldown', property = 'Has cooldown', func = data.cast.wrap(m_util.cast.number), header = 'Cooldown', fmt = '%ss', },   {        name = 'vaal_souls_requirement', property = 'Has vaal souls requirement', func = data.cast.wrap(m_util.cast.number), header = 'Vaal souls', },   {        name = 'vaal_stored_uses', property = 'Has vaal stored uses', func = data.cast.wrap(m_util.cast.number), header = 'Stored Uses', },   {        name = 'damage_multiplier', property = 'Has damage multiplier', func = data.cast.wrap(m_util.cast.number), header = m_util.html.abbr('Damage Multiplier', 'Deals x% of Base Damage'), fmt = '%s%%', },   -- from gem experience, optional {       name = 'experience', property = 'Has experience requirement', func = data.cast.wrap(m_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 row.func ~= nil then val = row.func(val) end if val ~= nil then g_args[id] = val properties[row.property] = val end end end

function h.stats(frame, properties, prefix, level) 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 subobject = {} subobject['Has skill level'] = level subobject[stat_type.property_prefix .. 'id'] = stat_id subobject[stat_type.property_prefix .. 'value'] = stat_val m_util.smw.subobject(frame, string.format('level%s_stat%s_%s', level, i, stat_id), subobject) end g_args[type_prefix .. 's_ids'] = stat_ids g_args[type_prefix .. 's_values'] = stat_values properties[stat_type.property_prefix .. 'ids'] = stat_ids properties[stat_type.property_prefix .. 'values'] = 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 = m_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 data.cast.wrap(m_util.cast.boolean)(g_args[prefix]) then prefix = prefix .. '_'           h.map_to_arg(properties, prefix, data.map.progression) h.stats(g_frame, properties, prefix, i)           properties['Is skill level'] = i            m_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(g_frame, properties, 'static_', 0) local copy = mw.clone(properties) -- add the properties to the page directly m_util.smw.set(g_frame, properties) -- also add them to their own subobject for convienence m_util.smw.subobject(g_frame, 'static', copy) end

function p.progression(frame) g_args = getArgs(frame, {       parentFirst = true    }) g_frame = m_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 m_util.table.has_all_value(statfmt, {'header', 'abbr', 'pattern_extract', 'pattern_value'}) then break end if m_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 = m_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 = m_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 = m_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"] = data.cast.wrap(m_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 = m_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(m_util.html.abbr('Exp.', 'Experience Needed to Level Up')) :done :tag('th') :wikitext(m_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 = m_util.string.split(row['Has stat text'], ' ') 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 return tostring(tbl) end

return p