Module:Animate Dead calculator

From WIDEVERSE Wiki
Jump to navigation Jump to search

Documentation for this module may be created at Module:Animate Dead calculator/doc

-- <pre>
local p = {}

local yesno = require ("Module:Yesno")
local chart = require( 'Module:Chart data' )
local lang = mw.getContentLanguage()
local min = math.min
local max = math.max
local floor = math.floor
local ceil = math.ceil

function p.main(frame)
	local invokeArgs = frame.args
    local args = frame:getParent().args
    
    return p._main( invokeArgs, args )
end
	
function p._main( invokeArgs, args )
	--arguments
	local defenceLevel = tonumber(args.defenceLevel) or 0
    local headSlot = yesno(args.headSlot) or false
    local headSlotTier = tonumber(args.headSlotTier) or 0
    local bodySlot = yesno(args.bodySlot) or false
    local bodySlotTier = tonumber(args.bodySlotTier) or 0
    local legsSlot = yesno(args.legsSlot) or false
    local legsSlotTier = tonumber(args.legsSlotTier) or 0
    local glovesSlot = yesno(args.glovesSlot) or false
    local glovesSlotTier = tonumber(args.glovesSlotTier) or 0
    local bootsSlot = yesno(args.bootsSlot) or false
    local bootsSlotTier = tonumber(args.bootsSlotTier) or 0
    local shieldSlot = yesno(args.shieldSlot) or false
    local shieldSlotType = args.shieldSlotType
    local shieldSlotTier = tonumber(args.shieldSlotTier) or 0
    local capeSlot = yesno(args.capeSlot) or false
    local capeSlotTier = tonumber(args.capeSlotTier) or 0
    local damagetoggle = args.damagetoggle
    local damage = tonumber(args.damage) or 0
    local damageLow = tonumber(args.damageLow) or 0
    local damageHigh = tonumber(args.damageHigh) or 0
    local outputType = args.outputType
    local scale = args.scale
    
    if(damageLow>damageHigh) then 
    	return "<div style=\"font-weight:bold\">The lower bound must not be higher than upper bound.</div>" 
    end
    
    --calculate Animate Dead value on buff bar
    local animateDeadValue = calculateAnimateDeadValue(defenceLevel,headSlot,headSlotTier,bodySlot,bodySlotTier,legsSlot,legsSlotTier,glovesSlot,glovesSlotTier,bootsSlot,bootsSlotTier,shieldSlot,shieldSlotType,shieldSlotTier,capeSlot,capeSlotTier)
    
    --calculate damage reduction for specific value of base damage taken
    local damageReduction = calculateDamageReduction(animateDeadValue,damage)
    local damageReductionLow = calculateDamageReduction(animateDeadValue,damageLow)
    local damageReductionHigh = calculateDamageReduction(animateDeadValue,damageHigh)
    
    --crate plot of damage reduction (%) vs base damage taken
    local damageReductionArr = { {x=0, y=100} }
    local damageReductionBoundsArr = { {x=damageLow, y=(1-damageReductionLow/damageLow)*100} }
    local lifepointsSubtractedArr = { {x=0, y=0} }
    local lifepointsSubtractedBoundsArr = { {x=damageLow, y=damageReductionLow} }
    local arrLen = 1
    local arrLenBounds = 1
    local damageBoundsSum = 0
    local damageReductionBoundsSum = 0
    local stepSize = max(1,ceil(max(damage,damageHigh)/15000))
    
    local plot = chart.newChart{ type='scatter' }
	:setDimensions( '40vw', '40vh', '400px', '400px', true )
	:setXLabel( 'Base damage taken' )
	:showLegend( false )
	if(scale=='Logarithmic') then plot:setYAxisType( 'logarithmic' ) end
    
    --calculate damageReductionValues and store
	local damageMax = max(damage,(damageLow+damageHigh)/2)
    for i = 1, 2*damageMax, stepSize do
    	local DamageValueStep = calculateDamageReduction(animateDeadValue,i)
    	local damageReductionValueStep = (1-DamageValueStep/i)*100
    	damageReductionArr[arrLen + 1] = { x=i, y=damageReductionValueStep }
    	lifepointsSubtractedArr[arrLen + 1] = { x=i, y=DamageValueStep } 
    	if(damagetoggle=="Range" and i>=damageLow and i<=damageHigh) then
    		damageReductionBoundsArr[arrLenBounds + 1] = { x=i, y=damageReductionValueStep }
    		damageBoundsSum = damageBoundsSum + i
    		damageReductionBoundsSum = damageReductionBoundsSum + DamageValueStep
    		lifepointsSubtractedBoundsArr[arrLenBounds + 1] =  { x=i, y=DamageValueStep }
    		arrLenBounds = arrLenBounds + 1
    	end
    	arrLen = arrLen + 1
    end

	if(outputType=="Percent damage reduction") then
		plot:setTitle( 'Damage reduction (%) vs Base damage taken' )
		:setYLabel( 'Damage reduction (%)' )
		if (damagetoggle=="Single") then
			plot:newDataSet{
		        data = { {x=0, y=(1-damageReduction/damage)*100}, {x=damage, y=(1-damageReduction/damage)*100}, {x=damage, y=0} },
		        pointRadius = 0,
		        borderDash = {10, 10},
		        borderWidth = 1,
		        lineTension = 0,
		        label = '- - -'
		    }
			plot:newDataSet{
				data = damageReductionArr,
				pointRadius = 0,
				label = 'Damage reduction (%)'
			}
		elseif (damagetoggle=="Range") then
			plot.options.colorPallet = {
		        chart.rgba.new( 31,120,180 ),
		        chart.rgba.new( 166,206,227 ),
				chart.rgba.new( 31,120,180 ),
		        chart.rgba.new( 166,206,227 ),
			}
			plot:newDataSet{
				data = damageReductionArr,
				pointRadius = 0,
				label = 'Damage reduction (%)'
			}
			:done()
			:newDataSet{
		        data = { {x=0, y=(1-damageReductionLow/damageLow)*100}, {x=damageLow, y=(1-damageReductionLow/damageLow)*100}, {x=damageLow, y=0} },
		        pointRadius = 0,
		        borderDash = {10, 10},
		        borderWidth = 1,
		        lineTension = 0,
		        label = 'Lower bound'
			}
			:done()
			:newDataSet{
				data = damageReductionBoundsArr,
				pointRadius = 0,
				label = 'Bounded region',
				fill = true
			}
			:done()
			:newDataSet{
		        data = { {x=0, y=(1-damageReductionHigh/damageHigh)*100}, {x=damageHigh, y=(1-damageReductionHigh/damageHigh)*100}, {x=damageHigh, y=0} },
		        pointRadius = 0,
		        borderDash = {10, 10},
		        borderWidth = 1,
		        lineTension = 0,
		        label = 'Upper bound'
			}
		end
	elseif(outputType=="Life points subtracted") then
		plot:setTitle( 'Life points subtracted vs Base damage taken' )
		:setYLabel( 'Life points subtracted' )
		if (damagetoggle=="Single") then
			plot:newDataSet{
		        data = { {x=0, y=damageReduction}, {x=damage, y=damageReduction}, {x=damage, y=0} },
		        pointRadius = 0,
		        borderDash = {10, 10},
		        borderWidth = 1,
		        lineTension = 0,
		        label = '- - -'
		    }
			plot:newDataSet{
				data = lifepointsSubtractedArr,
				pointRadius = 0,
				label = 'Life points subtracted'
			}
		elseif (damagetoggle=="Range") then
			plot.options.colorPallet = {
		        chart.rgba.new( 31,120,180 ),
		        chart.rgba.new( 166,206,227 ),
				chart.rgba.new( 31,120,180 ),
		        chart.rgba.new( 166,206,227 ),
			}
			plot:newDataSet{
				data = lifepointsSubtractedArr,
				pointRadius = 0,
				label = 'Life points subtracted'
			}
			:done()
			:newDataSet{
		        data = { {x=0, y=damageReductionLow}, {x=damageLow, y=damageReductionLow}, {x=damageLow, y=0} },
		        pointRadius = 0,
		        borderDash = {10, 10},
		        borderWidth = 1,
		        lineTension = 0,
		        label = 'Lower bound'
			}
			:done()
			:newDataSet{
				data = lifepointsSubtractedBoundsArr,
				pointRadius = 0,
				label = 'Bounded region',
				fill = true
			}
			:done()
			:newDataSet{
		        data = { {x=0, y=damageReductionHigh}, {x=damageHigh, y=damageReductionHigh}, {x=damageHigh, y=0} },
		        pointRadius = 0,
		        borderDash = {10, 10},
		        borderWidth = 1,
		        lineTension = 0,
		        label = 'Upper bound'
			}
		end
	end
    --return results
	local res = ""
	if(animateDeadValue>0) then 	
		res = res .. tostring( plot ) .. "<br>"
	end
	res = res .. "Damage reduction value on buff bar: (" .. animateDeadValue ..")<br>"
	if(damagetoggle=="Single") then
		res = res .. "Damage taken: " .. string.format("%s", lang:formatNum(damageReduction)) .. "<br>"
		res = res .. "<br>"
		res = res .. "Percent damage reduction: " .. string.format("%.1f",(1-damageReduction/damage)*100) .. "%" .. "<br>"
	elseif(damagetoggle=="Range") then 	
		res = res .. "Damage taken between: " .. string.format("%s", lang:formatNum(damageReductionLow)) .. " and " .. string.format("%s", lang:formatNum(damageReductionHigh)) .."<br>"
		res = res .. "<br>"
		res = res .. "Percent damage reduction between: " .. string.format("%.1f",(1-damageReductionHigh/damageHigh)*100) .. "% and " .. string.format("%.1f",(1-damageReductionLow/damageLow)*100) .. "%<br>"
		res = res .. "Average percent damage reduction: " .. string.format("%.1f",(1-damageReductionBoundsSum/damageBoundsSum)*100) .. "%<br>"
	end
	if(animateDeadValue>0) then 	
		res = res .. "75%+ damage reduction for base damage under or equal to " .. string.format("%s", lang:formatNum(floor(animateDeadValue/0.75))) .. "<br>"
		res = res .. "50%+ damage reduction for base damage under or equal to " .. string.format("%s", lang:formatNum(floor(animateDeadValue/0.50))) .. "<br>"
		res = res .. "10%+ damage reduction for base damage under or equal to " .. string.format("%s", lang:formatNum(floor(animateDeadValue/0.10))) .. "<br>"
	end

	return res
   
end

--base defence value
function calculateBaseValue(gearTier,shieldType)
	--Armour#Calculating_Armour_bonus
	local base = ( gearTier^3/500 + 10*gearTier + 100 )
	if (shieldType=="Rebounder") then base = base / 2 end
	return base
end

--armor piece defence value
function calculateArmourPieceValue(modifier,armourPieceBaseValue)
	--return to 1 decimal
	return floor( modifier*armourPieceBaseValue*10 ) / 10
end

--check if piece of tank gear is equipped
function equippedGear(value)
	return value and 1 or 0
end

function calculateAnimateDeadValue(defenceLevel,headSlot,headSlotTier,bodySlot,bodySlotTier,legsSlot,legsSlotTier,glovesSlot,glovesSlotTier,bootsSlot,bootsSlotTier,shieldSlot,shieldSlotType,shieldSlotTier,capeSlot,capeSlotTier)
	
	--calculate base defensive value for each slot
    local headBaseValue = calculateBaseValue(headSlotTier)
    local bodyBaseValue = calculateBaseValue(bodySlotTier)
    local legsBaseValue = calculateBaseValue(legsSlotTier)
    local glovesBaseValue = calculateBaseValue(glovesSlotTier)
    local bootsBaseValue = calculateBaseValue(bootsSlotTier)
    local shieldBaseValue = calculateBaseValue(shieldSlotTier,shieldSlotType)
    local capeBaseValue = calculateBaseValue(capeSlotTier)
	
	--calculate armour bonus of each slot
	local headArmourValue = calculateArmourPieceValue(0.2,headBaseValue)
	local bodyArmourValue = calculateArmourPieceValue(0.23,bodyBaseValue)
	local legsArmourValue = calculateArmourPieceValue(0.22,legsBaseValue)
	local glovesArmourValue = calculateArmourPieceValue(0.05,glovesBaseValue)
	local bootsArmourValue = calculateArmourPieceValue(0.05,bootsBaseValue)
	local shieldArmourValue = calculateArmourPieceValue(0.2,shieldBaseValue)
	local capeArmourValue = calculateArmourPieceValue(0.03,capeBaseValue)
	
	--calculate 10% of armour value towards damage reduction
	local headArmourValueDamageReduction = 0.1*headArmourValue
	local bodyArmourValueDamageReduction = 0.1*bodyArmourValue
	local legsArmourValueDamageReduction = 0.1*legsArmourValue
	local glovesArmourValueDamageReduction = 0.1*glovesArmourValue
	local bootsArmourValueDamageReduction = 0.1*bootsArmourValue
	local shieldArmourValueDamageReduction = 0.1*shieldArmourValue
	local capeArmourValueDamageReduction = 0.1*capeArmourValue

	--calculate 33% of defence level toward damage reduction (it appears that 1/3 gives correct values)
	local defenceLevelDamageReduction = floor(defenceLevel/3)
	
	--calculate combination of armour value damage reduction and defence level damage reduction for each equipped piece of magic tank gear
	local headDamageReduction = headArmourValueDamageReduction + defenceLevelDamageReduction
	local bodyDamageReduction = bodyArmourValueDamageReduction + defenceLevelDamageReduction
	local legsDamageReduction = legsArmourValueDamageReduction + defenceLevelDamageReduction
	local glovesDamageReduction = glovesArmourValueDamageReduction + defenceLevelDamageReduction
	local bootsDamageReduction = bootsArmourValueDamageReduction + defenceLevelDamageReduction
	local shieldDamageReduction = shieldArmourValueDamageReduction + defenceLevelDamageReduction
	local capeDamageReduction = capeArmourValueDamageReduction + defenceLevelDamageReduction
	
	--sum the values and return sum
	return floor(equippedGear(headSlot)*headDamageReduction + equippedGear(bodySlot)*bodyDamageReduction + equippedGear(legsSlot)*legsDamageReduction + equippedGear(glovesSlot)*glovesDamageReduction + equippedGear(bootsSlot)*bootsDamageReduction + equippedGear(shieldSlot)*shieldDamageReduction + equippedGear(capeSlot)*capeDamageReduction)
	
end

function calculateDamageReduction(animateDeadValue,damage)
	--calculate damage reduction
	return max(damage-animateDeadValue,floor(0.25*damage))
end

return p