Module:Critical hit chance calculator: Difference between revisions

From WIDEVERSE Wiki
Jump to navigation Jump to search
No edit summary
(No difference)

Revision as of 09:23, 7 October 2021

Documentation for this module may be created at Module:Critical hit chance calculator/doc

-- <pre>
local p = {}

local htnmlext = require('Module:Mw.html extension')
local yesno = require ("Module:Yesno")
local lang = mw.getContentLanguage()
local min = math.min
local max = math.min
local floor = math.floor

function p.main(frame)
	local invokeArgs = frame.args
    local args = frame:getParent().args
    
    return p._main( invokeArgs, args )
end

function onOff(value)
	return value and 1 or 0
end


function p._main(invokeArgs,args)
	--args
	--player values
	local level = tonumber(args.level) or 0
	local ad = tonumber(args.AD) or 0
	--fCrit Modifiers
	local bitingRank = tonumber(args.biting) or 0
	local level20biting = yesno(args.level20biting) or false
	local pocket = args.pocket
	local ring = args.ring
	local ringChannellers = tonumber(args.ringChannellers) or 0
	local forcedAbility = args.forcedAbility
	local abilFury = tonumber(args.abilityFury) or 0
	local abilGreaterFury = args.abilityGreaterFury
	local abilConcBlast = tonumber(args.abilityConcentratedBlast) or 0
	local abilGConcBlast = tonumber(args.abilityGreaterConcentratedBlast) or 0
	local familiar = yesno(args.familiar) or false
	local ammunition = yesno(args.ammunition) or false
	local corbicula = tonumber(args.corbicula) or 0
	local setEffect = args.setEffect
	local warpriestOfTuska = tonumber(args.warpriestOfTuska) or 0
	--nCrit Modifiers
	local abilMin = tonumber(args.abilityMin) or 0
	local abilMax = tonumber(args.abilityMax) or 0
	local prayer = args.prayer
	local potion = args.potion
	local essence = yesno(args.essence) or false
	local aura = args.aura
	local slayerHelm = args.slayerHelm
	local genocidal = tonumber(args.genocidal) or 0
	local baneAmmo = yesno(args.baneAmmo) or false
	local baneWeaponry = yesno(args.baneWeaponry) or false
	local revenge = yesno(args.revenge) or false
	local revengeStacks = tonumber(args.revengeStacks) or 0
	local shieldType = args.shieldType
	local preciseRank = tonumber(args.precise) or 0
	local equilibriumRank = tonumber(args.equilibrium) or 0
	
    if(abilMin>abilMax) then 
    	return "<div style=\"font-weight:bold\">The minimum must not be higher than the maximum.</div>" 
    end
    
    if(ammunition and baneAmmo) then
    	return "<div style=\"font-weight:bold\">Two different types of ammunition cannot be used at the same time.</div>" 
    end
    
    local fCritProb = calculateForcedCriticalHitProbability(bitingRank,level20biting,pocket,ring,ringChannellers,forcedAbility,abilFury,abilGreaterFury,abilConcBlast,abilGConcBlast,familiar,ammunition,corbicula,setEffect,warpriestOfTuska)
    
    local nCritProb = calculateNaturalCriticalHitProbability(level,ad,abilMin,abilMax,prayer,potion,essence,aura,slayerHelm,genocidal,baneAmmo,baneWeaponry,revenge,revengeStacks,shieldType,preciseRank,equilibriumRank)
    
    local nCritProb = (1-fCritProb/100)*nCritProb
    local totalCritProb = fCritProb + (1-fCritProb/100)*nCritProb
    local nonCritProb = 100 - totalCritProb
    
    fCritProb = floor(fCritProb*10^2 + 0.5)/10^2
    nCritProb = floor(nCritProb*10^2 + 0.5)/10^2
    totalCritProb = floor(totalCritProb*10^2 + 0.5)/10^2
    nonCritProb = floor(nonCritProb*10^2 + 0.5)/10^2

    --table for output
	local resultsDiv = mw.html.create( 'div' )
	local resultsTable = mw.html.create( 'table' )
	
	
	resultsTable:addClass( 'wikitable' )
		:addClass( 'align-center-1 align-center-2 align-center-3 align-center-4' )
		:tag( 'tr' )
			:tag( 'th' )
				:wikitext('Critical hit chance')
					:attr( 'colspan', 3)
			:done()
			:tag( 'th' )
				:wikitext('Non-critical hit chance')
					:attr( 'rowspan', 2)
			:done()
		:done()
		:tag( 'tr' )
			:tag( 'th' )
				:wikitext('Total critical hit chance')
			:done()
			:tag( 'th' )
				:wikitext('Forced critical hit chance')
			:done()
			:tag( 'th' )
				:wikitext('Natural critical hit chance')
			:done()
		:done()
		:tag ( 'tr' )
			:tag( 'td' )
				:wikitext( string.format("<b>%.2f</b>",lang:formatNum(totalCritProb)) .. "<b>%</b>" )
			:done()
			:tag( 'td' )
				:wikitext( string.format("%.2f",lang:formatNum(fCritProb)) .. "%" )
			:done()
			:tag( 'td' )
				:wikitext( string.format("%.2f",lang:formatNum(nCritProb)) .. "%" )
			:done()
			:tag( 'td' )
				:wikitext( string.format("<b>%.2f</b>",lang:formatNum(nonCritProb)) .. "<b>%</b>" )
			:done()
		:done()

	--return results (table)
	resultsDiv:node(tostring(resultsTable))
	return resultsDiv
end

	function calculateNaturalCriticalHitProbability(lvl,abilitydamage,aMin,aMax,pray,pot,ess,auraSlot,slayerHelmet,genocidalPerk,baneAmmunition,baneMeleeWeaponry,aRevenge,aRevengeStacks,shieldSlot,pRank,eRank)
		-- calculate effective level and Ability damage (AD)
		local lvlMult = 1
		local lvlAdd = 0
		if(pot=="Elder overload potion") then lvlMult = 1.17; lvlAdd = 5
		elseif(pot=="Supreme overload potion" or potion=="Supreme strength/magic/ranging potion") then lvlMult = 1.16; lvlAdd = 4
		elseif(pot=="Overload") then lvlMult = 1.15; lvlAdd = 3
		end
		local lvlAura = 0
		if(auraSlot=="Berserker/Maniacal/Reckless aura") then lvlAura = 0.1 end
		
		local lvlEff = lvl
		if(pot=="Supreme strength/magic/ranging potion") then
			lvlEff = floor(lvl*lvlMult+lvlAdd)
			if(ess) then -- 133 stats at level 99 (with or without berserker auras)
				lvlEff = lvlEff + floor(lvl*1.14+2) - lvl
			end
		elseif(pot=="None") then
			lvlEff = floor(lvl*(lvlAura + lvlMult))
			if(ess) then -- 123 stats at level 99 with berserker auras (114 without)
				lvlEff = lvlEff + floor(lvl*1.14+2) - lvl
			end
		else --overloads
			lvlEff = floor(lvl*(lvlAura + lvlMult)+lvlAdd)
		end
		
		local prayerMod = 1
		if(pray=="Malevolence/Affliction/Desolation") then prayerMod = 1.12
		elseif(pray=="Turmoil/Torment/Anguish") then prayerMod = 1.10
		elseif(pray=="Leech (Magic/Range) Strength + Amulet of zealots") then prayerMod = 1.18
		elseif(pray=="Ultimate Strength/OverCharge/Overpowering Force + Amulet of zealots") then prayerMod = 1.16
		elseif(pray=="Piety/Augury/Rigour") then prayerMod = 1.08
		end
		
		local adEff = abilitydamage
		--base
		local abilFixed = floor(adEff*aMin/100)
		local abilVar = floor(adEff*(aMax-aMin)/100)
		--prayer
		abilFixed = floor(abilFixed*prayerMod)
		abilVar = floor(abilVar*prayerMod)
		
		--boosted
		abilFixed = abilFixed + 4*(lvlEff - lvl)
		abilVar = abilVar + 4*(lvlEff - lvl)
		
		--revenge
		if(aRevenge) then
			local revengeMod = 0
			if(shieldSlot=="Shield") then revengeMod = 0.10
			elseif(shieldSlot=="Defender/Rebounder/Repriser") then revengeMod = 0.05
			else revengeMod = 0
			end
			abilVar = floor(abilVar*(1+aRevengeStacks*revengeMod))
		end
		--slayer helmet
		if(slayerHelmet == "Full slayer helmet") then
			abilVar = floor(abilVar*1.125)
		elseif(slayerHelmet == "Reinforced slayer helmet") then
			abilVar = floor(abilVar*1.13)
		elseif(slayerHelmet == "Strong slayer helmet") then
			abilVar = floor(abilVar*1.135)
		elseif(slayerHelmet == "Mighty slayer helmet") then
			abilVar = floor(abilVar*1.14)
		elseif(slayerHelmet == "Corrupted slayer helmet") then
			abilVar = floor(abilVar*1.145)
		end
		--genocidal
		abilVar = floor(abilVar*(1 + genocidalPerk/100))
		--bane ammo
		abilVar = floor(abilVar*(1 + onOff(baneAmmunition)*0.40))
		--bane weaponry
		abilVar = floor(abilVar*(1 + onOff(baneMeleeWeaponry)*0.25))
			
		--precise
		local Aprecise = floor(0.015*pRank*(abilFixed+abilVar))
		local abilFixedPrecise = abilFixed + Aprecise
		local abilVarPrecise = abilVar - Aprecise
		
		if(abilVarPrecise<=0) then
			abilFixed = abilFixed+abilVar
			abilVar = 0
		else
			abilFixed = abilFixedPrecise
			abilVar = abilVarPrecise
		end
		
		--calculate where natural crit hits begin with precise
		local pNatCritPrecise = 0
		if(abilVar==0) then 
			pNatCritPrecise = 1
		else
			pNatCritPrecise = min(1,floor(0.05*(abilFixed+abilVar))/abilVar)
		end
		
		--equilibrium (aura overrides perk)
		if(auraSlot=="Equilibrium aura") then 
			abilFixed = abilFixed + floor(.25*abilVar)
			abilVar = floor((1-.50)*abilVar)
		else
			abilFixed = abilFixed + floor(0.03*eRank*abilVar)
			abilVar = floor((1-0.04*eRank)*abilVar)
		end
		
		--calculate natural crit chance
		local minNCrit = 0
		local nCritChance = 0
		if(abilVar==0) then
			minNCrit = abilFixed+abilVar
			nCritChance = 1
		else
			minNCrit = abilFixed + floor((1-pNatCritPrecise)*abilVar)
			nCritChance = (abilFixed+abilVar-minNCrit)/(abilVar)
		end
		
		--return natural crit chance
		return min(100,nCritChance*100)
		--return abilFixed .. " " .. abilVar .. " " .. minNCrit .. " " .. nCritChance .. " " .. pNatCritPrecise .. " "
	end

	function calculateForcedCriticalHitProbability(bRank,lv20b,pocketSlot,ringSlot,channellers,fAbil,fury,gfury,conc,gconc,fam,ammo,corbi,setEff,tuska)
		--forced crit probabilities are addititve. warpriest of tuska appears to override biting
		local fCritChance = 0
		if(setEff == "Tuska's Might (Sliske's Parody)") then 
			fCritChance = 6
		elseif(setEff == "Tuska's Might (Warpriest of Tuska)") then
			fCritChance = tuska
		else
			fCritChance = bRank*2 * ( 1 + 0.1*onOff(lv20b) )
		end
			
		--pocket
		if(pocketSlot == "Erethdor's grimoire") then
			fCritChance = fCritChance + 12
		end
		
		--rings
		if(ringSlot == "Reaver's ring") then
			fCritChance = fCritChance + 5
		elseif(ringSlot == "Channeller's ring") then
			fCritChance = fCritChance + 4*channellers
		elseif(ringSlot == "Stalker's ring") then
			fCritChance = fCritChance + 3
		elseif(ringSlot == "Champion's ring") then
			fCritChance = fCritChance + 3
		end
		
		--abilities
		if(fAbil == "Fury") then
			fCritChance = fCritChance + 5*fury
		elseif(fAbil == "Greater Fury") then
			if(gfury == "+10%") then
				fCritChance = fCritChance + 10
			elseif(gfury == "100%") then
				fCritChance = 100
			end
		elseif(fAbil == "Concentrated Blast") then
			fCritChance = fCritChance + 5*conc
		elseif(fAbil == "Greater Concentrated Blast") then
			fCritChance = fCritChance + 5*gconc
		end
		
		--familiar
		if(fam) then
			fCritChance = fCritChance + 5
		end
		
		--ammunition
		if(ammo) then
			fCritChance = fCritChance + 3
		end
		
		--corbicula (only affects meteor strike)
		fCritChance = fCritChance + 20*corbi
		
		--return forced crit chance (max of 100%)
		return min(100,fCritChance)
	end

return p