Module:User stats
Documentation for this module may be created at Module:User stats/doc
local experience = require('Module:Experience')
local hiscore = require('Module:Hiscores')
local yesno = require('Module:Yesno')
local paramtest = require('Module:Paramtest')
local hc = paramtest.has_content
local dt = paramtest.default_to
local scm = require('Module:Skill clickpic')._main
require('Module:Mw.html extension')
local lang = mw.getContentLanguage()
local p = {}
function combat_level(att, str, def, ran, mag, con, pra, sum)
sum = sum or 0
local ret = {
level = (math.max(att+str, 2*mag, 2*ran) * 1.3 + def + con + math.floor(pra/2) + math.floor(sum/2))/4,
level_nosum = (math.max(att+str, 2*mag, 2*ran) * 1.3 + def + con + math.floor(pra/2))/4,
}
ret.sum_add = ret.level - ret.level_nosum
return ret
end
function combat_level_stats(user_stats, is_rs3)
local args = {}
local l = {'attack', 'strength', 'defence', 'ranged', 'magic', 'constitution', 'prayer'}
if is_rs3 then
table.insert(l, 'summoning')
end
for i,v in ipairs(l) do
args[i] = user_stats[v].level
if not args[i] then
return nil
end
end
local lvl = combat_level(unpack(args))
local ret
if is_rs3 then
ret = string.format('<span title="%s + %s">%s</span>', lvl.level_nosum, lvl.sum_add, lvl.level)
else
ret = lvl.level_nosum
end
return ret
end
function normalise_args(args)
local out = {}
for k,v in pairs(args) do
local key = string.lower(k)
local add_key = true
if key ~= k then
if args[key] then
add_key = false
end
end
if add_key then
out[key] = v
end
end
return out
end
function fnum(x)
x = tonumber(x)
if x then
return lang:formatNum(x)
end
return ''
end
local stat_override_names = {
[true] = {
'overall',
'attack',
'defence',
'strength',
'constitution',
'ranged',
'prayer',
'magic',
'cooking',
'woodcutting',
'fletching',
'fishing',
'firemaking',
'crafting',
'smithing',
'mining',
'herblore',
'agility',
'thieving',
'slayer',
'farming',
'runecrafting',
'hunter',
'construction',
'summoning',
'dungeoneering',
'divination',
'invention',
'archaeology',
'runescore'
},
[false] = {
'overall',
'attack',
'defence',
'strength',
'constitution',
'ranged',
'prayer',
'magic',
'cooking',
'woodcutting',
'fletching',
'fishing',
'firemaking',
'crafting',
'smithing',
'mining',
'herblore',
'agility',
'thieving',
'slayer',
'farming',
'runecrafting',
'hunter',
'construction'
}
}
local rs3_modes = {
['rs3'] = true,
['rs3-ironman'] = true,
['rs3-hardcore'] = true
}
local elite_skills = {
invention=true
}
local skills_max = {
herblore = 120,
farming = 120,
slayer = 120,
dungeoneering = 120,
invention = 120,
archaeology = 120
}
local skills_virtual_max = {
invention = 150
}
local not_skills = {
runescore=true,
overall=true
}
local hiscore_links = {
['rs3'] = 'm=hiscore/compare.ws',
['rs3-ironman'] = 'm=hiscore_ironman/compare.ws',
['rs3-hardcore'] = 'm=hiscore_hardcore_ironman/compare.ws',
['osrs'] = 'm=hiscore_oldschool/compare.ws',
['osrs-ironman'] = 'm=hiscore_oldschool_ironman/hiscorepersonal',
['osrs-ultimate'] = 'm=hiscore_oldschool_ultimate/hiscorepersonal',
-- not currently supported by #hs:
['osrs-hardcore'] = 'm=hiscore_oldschool_hardcore_ironman/hiscorepersonal',
['osrs-deadman'] = 'm=hiscore_oldschool_deadman/hiscorepersonal',
['osrs-seasonal'] = 'm=hiscore_oldschool_seasonal/hiscorepersonal',
['osrs-tournament'] = 'm=hiscore_oldschool_tournament/hiscorepersonal'
}
local ranks_string = {
['rs3'] = 'Ranks',
['rs3-ironman'] = 'Ranks',
['rs3-hardcore'] = 'Ranks',
['osrs'] = 'Ranks',
['osrs-ironman'] = 'Ranks',
['osrs-ultimate'] = 'Ranks',
-- not currently supported by #hs:
['osrs-hardcore'] = 'Ranks',
['osrs-deadman'] = 'Ranks and XP',
['osrs-seasonal'] = 'Ranks and XP',
['osrs-tournament'] = 'Ranks and XP'
}
local game_string = {
['rs3'] = 'RuneScape',
['rs3-ironman'] = 'RS Ironman',
['rs3-hardcore'] = 'RS Hardcore Ironman',
['osrs'] = 'Old School RuneScape',
['osrs-ironman'] = 'OSRS Ironman',
['osrs-ultimate'] = 'OSRS Ultimate Ironman',
-- not currently supported by #hs:
['osrs-hardcore'] = 'OSRS Hardcore Ironman',
['osrs-deadman'] = 'OSRS Deadman mode',
['osrs-seasonal'] = 'OSRS League',
['osrs-tournament'] = 'OSRS Tournament'
}
function hs(name, game, title)
if not hc(name) then
return title or 'Hiscores'
end
local url = hiscore_links[game] or hiscore_links.rs3
return string.format('<span class="plainlinks">[http://services.runescape.com/%s?user1=%s %s]</span>', url, mw.uri.encode(name), title or name)
end
function p.main(frame)
return p._main(frame:getParent().args)
end
function p._main(raw_args)
local args = normalise_args(raw_args)
local user_stats = {}
local virtual = yesno(args.virtual, false)
local game = string.lower(args.game or 'rs3')
local game_is_rs3 = rs3_modes[game] or false
function set_level(skill)
local val = tonumber(args[skill])
if not val then
user_stats[skill] = {level=args[skill]}
return false
end
if not_skills[skill] then
user_stats[skill]={
score=val
}
return false
end
local xp, lvl
local iselite = game_is_rs3 and elite_skills[skill]
if val > 150 then
xp = val
lvl = experience._level_at_xp(val, iselite)
else
lvl = val
xp = experience._xp_at_level(val, iselite)
end
if game_is_rs3 then
lvl = math.min(lvl, skills_max[skill] or 99)
else
lvl = math.min(lvl, 99)
end
user_stats[skill] = {
level = lvl,
xp = xp
}
return true
end
function add_virtual(skill)
local val = user_stats[skill].xp
if not val then
return 0
end
local virtlvl = experience._level_at_xp_unr(val, game_is_rs3 and elite_skills[skill])
virtlvl = math.min(virtlvl, skills_virtual_max[skill] or 126)
user_stats[skill].virtual = virtlvl
return virtlvl
end
args.margin = yesno(args.margin)
args.overall = dt(args.overall, args.total)
args.achievement = dt(args.achievement, args.task)
if hc(args.name) then
local is_ok, pc_user_stats = pcall(hiscore.get_stats, args.name, game, true)
if is_ok then
user_stats = pc_user_stats
end
end
local ei = 29
if not game_is_rs3 then
ei = 24
end
local has_override = false
local running_lvl, running_xp, running_virt = 0, 0, 0
for i = 2, ei, 1 do
local s = stat_override_names[game_is_rs3][i]
if not user_stats[s] and not hc(args[s]) then
-- neither defined, force 1
args[s] = 1
end
if hc(args[s]) then
local overridden = set_level(s)
has_override = has_override or overridden
end
if not hc(args.overall) then
running_xp = (type(user_stats[s].xp) == 'number' and user_stats[s].xp or 0) + running_xp
running_lvl = (type(user_stats[s].level) == 'number' and user_stats[s].level or 0) + running_lvl
running_virt = running_virt + add_virtual(s)
end
end
local overall_rank_str = ''
if hc(args.overall) then
user_stats['overall'] = {
level = args.overall
}
elseif has_override then
user_stats['overall'] = {
xp = running_xp,
level = running_lvl,
virtual = running_virt
}
else
user_stats.overall.virtual = running_virt
if user_stats.overall.rank then
overall_rank_str = '@NL@Rank: '..fnum(user_stats.overall.rank)
end
end
if game_is_rs3 then
if not user_stats.runescore then
user_stats.runescore = {}
end
if hc(args.runescore) then
user_stats.runescore = {score=args.runescore}
end
end
local cmb
if hc(args.combat) then
cmb = args.combat
else
cmb = combat_level_stats(user_stats, game_is_rs3)
end
function make_cell(td, skill)
td:css('width', '33%')
td:wikitext(scm(skill), ' ')
local vals = user_stats[skill]
if not vals then
td:wikitext('----')
return
end
local rank_str = ''
if vals.rank then
rank_str = '@NL@Rank: ' .. fnum(vals.rank)
end
if virtual and vals.virtual > vals.level then
td :wikitext(vals.virtual)
:attr('title', string.format('Real level: %s@NL@XP: %s%s', vals.level, fnum(vals.xp), rank_str))
else
td :wikitext(vals.level)
:attr('title', string.format('XP: %s%s', fnum(vals.xp), rank_str))
end
end
function make_row(tr, s1, s2, s3)
tr:done():newline()
make_cell(tr:tag('td'), s1)
make_cell(tr:tag('td'), s2)
make_cell(tr:tag('td'), s3)
end
function make_cell_simple(td, stat, val)
td:css('width', '33%')
td:wikitext(scm(stat), ' ')
if hc(val) then
if tonumber(val) then
td:wikitext(fnum(tonumber(val)))
else
td:wikitext(val)
end
else
td:wikitext('----')
end
end
local ret = mw.html.create('table')
ret:addClass('rsw-infobox infobox-user-stats userstats')
:css({['text-align']='center', float=dt(args.align, 'right')})
:cssText(args['extra-css'])
ret :tag('tr')
:tag('th')
:addClass('infobox-header')
:wikitext(hs(args.name, game, args.box_title or args.name or 'Hiscores'))
:attr('colspan', 3)
:done()
:newline()
:tag('tr')
:tag('th')
:addClass('infobox-subheader')
:wikitext('Total level: ', user_stats.overall.level)
:wikitextIf(virtual, string.format(' (%s)', user_stats.overall.virtual or ''))
:attr('colspan', 3)
:attrIf(user_stats.overall.xp, {title=string.format('Total experience: %s%s', fnum(user_stats.overall.xp), overall_rank_str)})
make_row(ret:tag('tr'), 'attack', 'constitution', 'mining')
make_row(ret:tag('tr'), 'strength', 'agility', 'smithing')
make_row(ret:tag('tr'), 'defence', 'herblore', 'fishing')
make_row(ret:tag('tr'), 'ranged', 'thieving', 'cooking')
make_row(ret:tag('tr'), 'prayer', 'crafting', 'firemaking')
make_row(ret:tag('tr'), 'magic', 'fletching', 'woodcutting')
make_row(ret:tag('tr'), 'runecrafting', 'slayer', 'farming')
if game_is_rs3 then
make_row(ret:tag('tr'), 'construction', 'hunter', 'summoning')
make_row(ret:tag('tr'), 'dungeoneering', 'divination', 'invention')
local tr = ret:tag('tr')
ret:newline()
make_cell(tr:tag('td'), 'archaeology')
make_cell_simple(tr:tag('td'), 'combat', cmb)
make_cell_simple(tr:tag('td'), 'quest', args.quest)
tr = ret:tag('tr')
ret:newline()
make_cell_simple(tr:tag('td'), 'music', args.music)
make_cell_simple(tr:tag('td'), 'task', args.achievement)
local rs_td = tr:tag('td')
make_cell_simple(rs_td, 'runescore', user_stats.runescore.score)
if user_stats.runescore.rank then
rs_td:attr('title', 'Rank: '..fnum(user_stats.runescore.rank))
end
else
local tr = ret:tag('tr')
make_cell(tr:tag('td'), 'construction')
make_cell(tr:tag('td'), 'hunter')
tr:tag('td') -- this is where i'd put warding, IF I HAD IT
tr = ret:tag('tr')
ret:newline()
make_cell_simple(tr:tag('td'), 'combat', cmb)
make_cell_simple(tr:tag('td'), 'quest', args.quest)
make_cell_simple(tr:tag('td'), 'music', args.music)
end
if hc(args.date) then
ret:tag('tr')
:tag('th')
:attr('colspan', 3)
:wikitext('As of ', args.date)
end
if not yesno(args.hide_inforow) then
ret:tag('tr'):tag('td')
:attr('colspan', 3)
:wikitext(string.format('Hover for XP and rank. %s for %s.', ranks_string[game] or 'Ranks', game_string[game] or 'RuneScape'))
end
-- mw.html escapes & (but scribunto/wikitext does not) so we gotta do a gsub on the entire string
-- note: is a new line that will work in a title attribute
ret = tostring(ret)
ret = ret:gsub('@NL@', ' ')
return ret
end
return p