Module:Combat level

From the RuneScape Wiki, the wiki for all things RuneScape
Jump to navigation Jump to search
Module documentation
This documentation is transcluded from Module:Combat level/doc. [edit] [history] [purge]
This module does not have any documentation. Please consider adding documentation at Module:Combat level/doc. [edit]
Module:Combat level's function main is invoked by Template:Combat level.
Module:Combat level's function main is invoked by Template:Virtual combat level.
Module:Combat level requires Module:Experience.
Module:Combat level requires Module:Hiscores.
Module:Combat level requires Module:Paramtest.
Module:Combat level requires Module:Yesno.
Module:Combat level requires strict.
Module:Combat level loads data from Module:User stats/data.
Function list
L 34 — fnum
L 42 — combat_level
L 53 — p.combat_level_stats
L 70 — p.format_combat_level
L 83 — p.main
L 102 — p._main
L 109 — setLevel
L 146 — addVirtual

--
-- Implements {{Combat level}}
--
-- Partially based on [[Module:User stats]]
--

require("strict");
local experience = require('Module:Experience');
local getStats = require('Module:Hiscores').get_stats;
local hc, dt, ucFirst;
do
	local _paramtest = require('Module:Paramtest');
	hc = _paramtest.has_content;
	dt = _paramtest.default_to;
	ucFirst = _paramtest.ucfirst;
end
local yn = require('Module:Yesno');

local lang = mw.getContentLanguage();
local ustr = mw.ustring;

local statOverrideNames, eliteSkills, skillsMax, skillsVirtualMax, notSkills;
do
	local _data = mw.loadData("Module:User stats/data");
	statOverrideNames	= _data.stat_override_names.rs3;
	eliteSkills			= _data.elite_skills;
	skillsMax			= _data.skills_max;
	skillsVirtualMax	= _data.skills_virtual_max;
	notSkills			= _data.not_skills;
end

local p = {};

local function fnum(x)
	x = tonumber(x)
	if x then
		return lang:formatNum(x)
	end
	return ''
end

local function combat_level(att, str, def, ran, mag, con, pra, sum, nec)
	sum = sum or 0;
	nec = nec or 0;
	local ret = {
		level = (math.max(att+str, 2*mag, 2*ran, 2*nec) * 1.3 + def + con + math.floor(pra/2) + math.floor(sum/2))/4,
		level_nosum = (math.max(att+str, 2*mag, 2*ran, 2*nec) * 1.3 + def + con + math.floor(pra/2))/4,
	};
	ret.sum_add = ret.level - ret.level_nosum;
	return ret;
end

function p.combat_level_stats(user_stats, is_rs3, is_virtual)
	local args = {};
	local l = {'attack', 'strength', 'defence', 'ranged', 'magic', 'constitution', 'prayer'};
	if is_rs3 then
		table.insert(l, 'summoning');
		table.insert(l, 'necromancy');
	end
	for i,v in ipairs(l) do
		local stats = user_stats[v];
		args[i] = is_virtual and math.min(stats.virtual, 120) or stats.level;
		if not args[i] then
			return nil;
		end
	end
	return combat_level(unpack(args));
end

function p.format_combat_level(lvl, is_rs3)
	local ret
	if is_rs3 then
		ret = string.format(
			'<span title="%s + %s" style="border-bottom:1px dotted">%s</span>',
			fnum(lvl.level_nosum), fnum(lvl.sum_add), fnum(lvl.level)
		);
	else
		ret = lvl.level_nosum
	end
	return ret
end

function p.main(frame)
	local args, opts;
	if (type(frame) == "table" and type(frame.getParent) == "function") then
		local parent = frame:getParent();

		opts = frame.args;
		if (parent) then
			args = parent.args;
		else
			args = opts;
		end
	else
		args = frame;
		opts = frame;
	end

	return p._main(args, opts);
end

function p._main(args, opts)
	args = args or {};
	opts = opts or {};

	local userStats = {};
	local virtual = yn(opts.virtual, false);

	local function setLevel(argName)
		local skill = ustr.lower(argName);
		local val = tonumber(args[argName])
		if (not val) then
			userStats[skill] = {
				level = args[argName],
			};
			return false;
		end

		if notSkills[skill] then
			userStats[skill] = {
				score = val,
			};
			return false;
		end

		local xp, lvl;
		local isElite = eliteSkills[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

		lvl = math.min(lvl, skillsMax[skill] or 99);
		userStats[skill] = {
			level = lvl,
			xp = xp,
		};

		return true;
	end

	local function addVirtual(skill)
		local val = userStats[skill].xp;
		if not val then
			return 0;
		end

		local virtLvl = experience._level_at_xp_unr(val, eliteSkills[skill]);
		virtLvl = math.min(virtLvl, skillsVirtualMax[skill] or 120);
		userStats[skill].virtual = virtLvl;
		return virtLvl;
	end

	if hc(args.Name) then
		local ok, stats = pcall(getStats, args.Name, "rs3");
		if ok then
			userStats = stats;
		end
	end

	for i = 2, 30, 1 do
		local stat = statOverrideNames[i];
		local argName = ucFirst(stat);
		if (not userStats[stat] and not hc(args[argName])) then
			-- neither defined, force 1
			args[argName] = 1;
		end
		if hc(args[argName]) then
			setLevel(argName);
		end
		addVirtual(stat);
	end

	local cmb = p.combat_level_stats(userStats, true, virtual);
	assert(cmb, "Missing required parameters");

	local decimalPlaces = tonumber(dt(args.DP, opts.DP));
	if (
		decimalPlaces
		and decimalPlaces >= 0
		-- Intermediate combat level will at most have 2 decimal places
		and decimalPlaces < 10
	) then
		local exponent = 10 ^ decimalPlaces;

		cmb.level = math.floor(cmb.level * exponent) / exponent;
		cmb.level_nosum = math.floor(cmb.level_nosum * exponent) / exponent;
		cmb.sum_add = cmb.level - cmb.level_nosum;
	end

	if (yn(args.plain)) then
		return cmb.level;
	end

	return p.format_combat_level(cmb, true);
end

return p;