Module:Sandbox/User:Fjara/Sandbox/Spade

From Illerai
Jump to navigation Jump to search

Documentation for this module may be created at Module:Sandbox/User:Fjara/Sandbox/Spade/doc

local p = {}

-- Magic spell max hit table generation

-- Spells will non-numbers as max-hit, ie god spells (may need to hardcode)
--revs (avarice) + forthinry

local contains = require('Module:Array').contains
local containsAll = require('Module:Array').containsAll
local paramTest = require('Module:Paramtest')
local yesNo = require('Module:Yesno')

--split bonuses into different tables, add together in one loop with conditions for eacha nd then just 1+amount * maxhit
local PostMagicStrBonuses = {
	head = {
		['Slayer helmet (i)'] = 0.15,
		['Black mask (i)'] = 0.15,
		['Virtus mask'] = 0.03,
	},
}

-- Slot: Item: Magic Strength Bonus
local BaseMagicStrEquipment = {
	head = {
		['Ancestral hat'] = 0.02,
		['Virtus mask'] = 0.01,
		['Zuriel\'s hood (bh)'] = 0.01,
	},
	cape = {
		['Imbued guthix cape'] = 0.02,
		['Imbued saradomin cape'] = 0.02,
		['Imbued zamorak cape'] = 0.02,
	},
	neck = {
		--['Amulet of avarice'] = 0.20,
		['Occult necklace'] = 0.10,
		--['Salve amulet(i)'] = 0.15,
		--['Salve amulet(ei)'] = 0.20,
	},
	ammo = {
	},
	weapon = {
		['Ahrim\'s staff'] = 0.05,
		['Ancient sceptre'] = 0.05,
		['Blood ancient sceptre'] = 0.05,
		['Eldritch nightmare staff'] = 0.15,
		['Harmonised nightmare staff'] = 0.15,
		['Ice ancient sceptre'] = 0.05,
		['Kodai wand'] = 0.15,
		['Nightmare staff'] = 0.15,
		['Shadow ancient sceptre'] = 0.05,
		['Smoke ancient sceptre'] = 0.05,
		--['Smoke battlestaff'] = 0.15,
		['Staff of balance'] = 0.15,
		['Staff of light'] = 0.15,
		['Staff of the dead'] = 0.15,
		['Toxic staff of the dead'] = 0.15,
		['Volatile nightmare staff'] = 0.15,
		['Zuriel\'s staff (bh)'] = 0.1,
	},
	shield = {
		['Elidinis\' ward'] = 0.03,
		['Elidinis\' ward (f)'] = 0.05,
	},
	body = {
		['Ancestral robe top'] = 0.02,
		['Virtus robe top'] = 0.01,
		['Zuriel\'s robe top (bh)'] = 0.03,
	},
	legs = {
		['Ancestral robe bottom'] = 0.02,
		['Virtus robe bottom'] = 0.01,
		['Zuriel\'s robe bottom (bh)'] = 0.02,
	},
	hands = {
		['Castle wars bracelet'] = 0.2,
		['Tormented bracelet'] = 0.05,
	},
	feet = {
	},
	ring = {
		['Magus ring'] = 0.02,
	},
}

local MageStrSetBonus = {
	['ahrims'] = { 'Ahrim\'s hood', 'Ahrim\'s robetop', 'Ahrim\'s robeskirt', 'Ahrim\'s staff', 'Amulet of the damned' }, -- 30%
	['void'] = { 'Void mage helm', 'Elite void top', 'Elite void robe', 'Void knight gloves' }, -- 2.5%
}

local BoltSpells = { 'Wind Bolt', 'Water Bolt', 'Earth Bolt', 'Fire Bolt' }
local FireSpells = { 'Fire Strike', 'Fire Bolt', 'Fire Blast', 'Fire Wave', 'Fire Surge' }
local WaterSpells = { 'Water Strike', 'Water Bolt', 'Water Blast', 'Water Wave', 'Water Surge' }
local GodSpells = { 'Saradomin Strike', 'Claws of Guthix', 'Flames of Zamorak' }
local DemonbaneSpells = { 'Inferior Demonbane', 'Superior Demonbane', 'Dark Demonbane' }

local SaradominStrikeStaves = { 'Saradomin staff', 'Staff of light' }
local SaradominStrikeCapes = { 'Saradomin cape', 'Imbued saradomin cape' }

local ClawsOfGuthixStaves = { 'Guthix staff', 'Void knight mace', 'Staff of balance' }
local ClawsOfGuthixCapes = { 'Guthix cape', 'Imbued guthix cape' }

local FlamesOfZamorakStaves = { 'Zamorak staff', 'Staff of the dead', 'Thammaron\'s sceptre (a)', 'Accursed sceptre (a)', 'Toxic staff of the dead' }
local FlamesOfZamorakCapes = { 'Zamorak cape', 'Imbued zamorak cape' }

local SalveAmulet = { 'Salve amulet(i)', 'Salve amulet(ei)' }
local SlayerHelmet = { 'Slayer helmet (i)', 'Black mask (i)' }
local WildernessStaff = { 'Thammaron\'s sceptre', 'Accursed sceptre' }
local AccursedSceptreSpecialsVariant = { 'Accursed sceptre', 'Accursed sceptre (a)' }

local PoweredStavesMinHit = {
	['Accursed sceptre'] = 0,
	['Trident of the seas'] = 1,
	['Trident of the swamp'] = 4,
	['Sanguinesti staff'] = 5,
	['Thammaron\'s sceptre'] = 0,
	['Tumeken\'s shadow'] = 1,
}

local PoweredStavesModifier = {
	['Accursed sceptre'] = -6,
	['Trident of the seas'] = -5,
	['Trident of the swamp'] = -2,
	['Sanguinesti staff'] = -1,
	['Thammaron\'s sceptre'] = -8,
	['Tumeken\'s shadow'] = 1,
}

local MagicDartStavesTaskBonus = {
	['Slayer\'s staff'] = false,
	['Slayer\'s staff (e)'] =	true,
	['Staff of the dead'] = false,
	['Toxic staff of the dead'] = false,
	['Staff of balance'] = false,
	['Staff of light'] = false,
}

local SalamanderMagicDamageBonus = {
	['Swamp lizard'] = 56,
	['Orange salamander'] = 59,
	['Red salamander'] = 77,
	['Black salamander'] = 92,
}

-- Formulae
function magicDartFormula(magicLevel, onTask)
	if(onTask) then
		return math.floor(magicLevel / 6) + 13
	else
		return math.floor(magicLevel / 10) + 10
	end
end

function poweredStavesFormula(magicLevel, modifier)
	return math.floor(magicLevel / 3) + modifier
end

function salamanderFormula(magicLevel, magicDamageBonus)
	return math.floor(0.5 + ((magicLevel * (64 + magicDamageBonus)) / 640))
end

function specialVolatileNightmareStaff(magicLevel)
	return math.min(math.floor(magicLevel * (263 / 449) + 1), 58)	
end

function specialEldritchNightmareStaff(magicLevel)
	return math.min(math.floor(magicLevel * (200 / 449) + 1), 44)	
end

function loadWeaponSlot(weapon)
	local query = {
		'[[' .. weapon .. ']]',
		'?=#-',
		'?All Equipment slot=slot',
		limit = 1,
		offset = 0,
	}
	local t1 = os.clock()
	local smwData = mw.smw.ask(query)
	local t2 = os.clock()
	assert(smwData ~= nil and #smwData > 0, 'SMW query failed')
	mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000))

	return smwData[1].slot
end

function loadSpellMaxHit(spell)
	local query = {
		'[[' .. spell .. ']]',
		'?=#-',
		'?Max hit=maxhit',
		limit = 1,
		offset = 0,
	}

	local t1 = os.clock()
	local smwData = mw.smw.ask(query)
	local t2 = os.clock()
	assert(smwData ~= nil and #smwData > 0, 'SMW query failed')
	mw.log(string.format('SMW: entries %d, time elapsed: %.3f ms.', #smwData, (t2 - t1) * 1000))

	return smwData[1].maxhit
end

-- Add raw # output for inline?
function p._main(args)
	local magicLevel = paramTest.default_to(tonumber(args.magicLevel), 1)
	local location = paramTest.default_to(args.location, 'Anywhere')
	local toggles = {
		castleWarsBracelet = yesNo(args.castleWarsBracelet or '', false),
		charge = false,
		onTask = yesNo(args.onTask or '', false),
		killUndead = yesNo(args.killUndead or '', false),
		killRevenant = yesNo(args.killRevenant or '', false),
		markOfDarkness = yesNo(args.markOfDarkness or '', false),
		calcSpecial = yesNo(args.calcSpecial or '', false),
	}

	local weaponType = args.weaponType
	local equipment = {
		head = paramTest.default_to(args.head, 'None'),
		cape = paramTest.default_to(args.cape, 'None'),
		neck = paramTest.default_to(args.neck, 'None'),
		ammo = paramTest.default_to(args.ammo, 'None'),
		body = paramTest.default_to(args.body, 'None'),
		shield = paramTest.default_to(args.shield, 'None'),
		legs = paramTest.default_to(args.legs, 'None'),
		hands = paramTest.default_to(args.hands, 'None'),
		feet = paramTest.default_to(args.feet, 'None'),
		ring = paramTest.default_to(args.ring, 'None'),
	}
	local spell = ''
	local spellbook = ''

	local maxHit = 0
	local warning = ''
	if(weaponType == 'Powered Staff') then
		equipment.weapon = args.poweredStaff
		maxHit = math.max(poweredStavesFormula(magicLevel, PoweredStavesModifier[equipment.weapon]), PoweredStavesMinHit[equipment.weapon])
	elseif(weaponType == 'Salamander') then
		equipment.weapon = args.salamander
		maxHit = salamanderFormula(magicLevel, SalamanderMagicDamageBonus[equipment.weapon])
	elseif(weaponType == 'God Staff') then
		equipment.weapon = args.godStaff
		spell = paramTest.default_to(args.godSpell, 'Saradomin Strike')
		toggles.charge = yesNo(args.charge or '', false)
	elseif(weaponType == 'Magic Dart Staff') then
		equipment.weapon = args.dartStaff
		maxHit = magicDartFormula(magicLevel, (toggles.onTask and MagicDartStavesTaskBonus[equipment.weapon]))
		spell = 'Magic Dart'
	elseif(weaponType == 'Ibans Staff') then
		equipment.weapon = args.ibansStaff
		spell = 'Iban Blast'
		maxHit = loadSpellMaxHit(spell)
	elseif(weaponType == 'Other') then
		equipment.weapon = args.weapon
		if(calcSpecial and equipment.weapon == 'Volatile nightmare staff') then
			maxHit = specialVolatileNightmareStaff(magicLevel)
		elseif(calcSpecial and equipment.weapon == 'Eldritch nightmare staff') then
			maxHit = specialEldritchNightmareStaff(magicLevel)
		else
			spellbook = args.spellbook
			if(spellbook == 'Standard') then
				spell = args.standardSpell
			elseif(spellbook == 'Arceuus') then
				spell = args.arceuusSpell
			elseif(spellbook == 'Ancient') then
				spell = args.ancientSpell
			end
			maxHit = loadSpellMaxHit(spell)
		end
	else
		error('Invalid weapon type')
	end

	if(equipment.weapon and loadWeaponSlot(equipment.weapon) == "2h") then
		equipment.shield = 'None'
		warning = warning .. 'The chosen weapon is 2-handed so the shield\'s bonuses have been negated.<br/>'
	end

	if(toggles.castleWarsBracelet) then
		if(equipment.head ~= 'None') then
			warning = warning .. 'The head slot is being treated as empty, which is required to play Wastle Wars.<br/>'
		end
		if(equipment.cape ~= 'None') then
			warning = warning .. 'The cape slot is being treated as empty, which is required to play Castle Wars.<br/>'
		end
		if(toggles.onTask) then
			warning = warning .. 'Max hit is being calculated without a slayer task, which cannot be done in Castle Wars.<br/>'
		end
		if(toggles.markOfDarkness) then
			warning = warning .. 'Max hit is being calculated without Mark of Darkness, demons are not in Castle Wars.<br/>'
		end
		if(location == 'ToA' or location == 'Wilderness') then
			warning = warning .. 'Max hit is being calculated as \'Anywhere\', Castle Wars is no in the Wilderness or ToA.<br/>'
		end
	end

	if(toggles.charge and (((spell == 'Saradomin Strike') and contains(SaradominStrikeStaves, equipment.weapon) and contains(SaradominStrikeCapes, equipment.cape)) or
		((spell == 'Claws of Guthix') and contains(ClawsOfGuthixStaves, equipment.weapon) and contains(ClawsOfGuthixCapes, equipment.cape)) or
		((spell == 'Flames of Zamorak') and contains(FlamesOfZamorakStaves, equipment.weapon) and contains(FlamesOfZamorakCapes, equipment.cape)))) then
			maxHit = maxHit + 10
	elseif(toggles.charge) then
		warning = warning .. 'Charge requires a god staff and god cape aligned with the same god to cast the combat spell and keep the charge effect.<br/>'
	end

	if(contains(BoltSpells, spell) and (equipment.hands == 'Chaos gauntlets')) then
		maxHit = maxHit + 3
	end

	-- Primary Magic Damage
	local magicDamageBonus = 0
	for slot, item in pairs(equipment) do
		if(MageStrEquipment[slot][item]) then
			magicDamageBonus = magicDamageBonus + MageStrEquipment[slot][item]
		end
	end

	if(equipment.weapon == 'Tumeken\'s shadow' and location == 'ToA') then
		magicDamageBonus = math.min(magicDamageBonus * 4, 2) -- Capped at 200%
	elseif(equipment.weapon == 'Tumeken\'s shadow') then
		magicDamageBonus = math.min(magicDamageBonus * 3, 1.75) -- Capped at 175%
	end

	if(containsAll(equipment, MageStrSetBonus['void'])) then
		magicDamageBonus = magicDamageBonus + .025
	end
	
	if(contains(SalveAmulet, equipment.neck)) then
		if(not toggles.killUndead) then -- Skip salve if not killing undead
			warning = warning .. 'In order for a Salve amulet to grant its bonus, you need to be attacking undead monsters.<br/>'
		elseif(equipment.neck == 'Salve amulet(i)') then
			magicDamageBonus = magicDamageBonus + .15
		elseif(equipment.neck == 'Salve amulet(ei)') then
			magicDamageBonus = magicDamageBonus + .20
		end
	elseif(equipment.neck == 'Amulet of avarice') then
		if(not toggles.killRevenant) then
			warning = warning .. 'In order for an Amulet of avarice to grant its bonus, you need to be attacking revenants.<br/>'
		else
			magicDamageBonus = magicDamageBonus + .20
		end
	end
	
	if((equipment.weapon == 'Smoke battlestaff') and (spellbook == 'Standard')) then
		magicDamageBonus = magicDamageBonus + .10
	end
	
	if(spellbook == 'Ancient') then
		if(equipment.head == 'Virtus mask') then
			magicDamageBonus = magicDamageBonus + .03
		end
		if(equipment.head == 'Virtus robe top') then
			magicDamageBonus = magicDamageBonus + .03
		end
		if(equipment.head == 'Virtus robe bottom') then
			magicDamageBonus = magicDamageBonus + .03
		end
	end
	
	maxHit = math.floor(maxHit * (1 + magicDamageBonus))
	
	-- Secondary Magic Damage
	magicDamageBonus = 0
	if(containsAll(equipment, MageStrSetBonus['ahrims'])) then
		magicDamageBonus = magicDamageBonus + 0.30
	end
	
	if(contains(SlayerHelmet, equipment.head)) then
		if(not toggles.onTask) then -- Skip slayer helmet if not killing task monster
			warning = warning .. 'In order for a Slayer helmet to grant its bonus, you need to be attacking monsters on an active Slayer task.<br/>'
		else
			magicDamageBonus = magicDamageBonus + .15
		end
	end
		
	if(location == 'Wilderness' and contains(WildernessStaff, equipment.weapon)) then
		magicDamageBonus = magicDamageBonus + .50
	end

	maxHit = math.floor(maxHit * (1 + magicDamageBonus))
	
	-- Tertiary Magic Damage
	magicDamageBonus = 0
	if(calcSpecial and contains(AccursedSceptreSpecialsVariant, equipment.weapon)) then
		magicDamageBonus = magicDamageBonus + .50
	end

	maxHit = math.floor(maxHit * (1 + magicDamageBonus))

	-- Post hit roll
	local postHit = 0
	if(contains(FireSpells, spell) and equipment.shield == 'Tome of fire') then
		postHit = postHit + 0.50
	elseif(contains(WaterSpells, spell) and equipment.shield == 'Tome of water') then
		postHit = postHit + 0.20
	end
	
	if(toggles.markOfDarkness and contains(DemonbaneSpells, spell)) then
		postHit = postHit + 0.25
	end
	
	maxHit = math.floor(maxHit * (1 + postHit))

	-- Applied hit
	local appliedHit = 0
	if(toggles.castleWarsBracelet) then
		warning = warning .. 'Equip the castle wars bracelet until the match has begun, then it can be swapped for a different hands slot item.<br/>'
	    appliedHit = appliedHit + 0.20
	end

	maxHit = math.floor(maxHit * (1 + appliedHit))

	mw.log(maxHit)
	return maxHit
end

--[[ DEBUG CONSOLE INPUT
args = {
	magicLevel = '99',
	location = 'Anywhere',
	castleWarsBracelet = 'false',
	onTask = 'false',
	killUndead = 'false',
	markOfDarkness = 'false',
	calcSpecial = 'false',
	weaponType = 'Powered Staff',
	poweredStaff = 'Trident of the seas',
	salamander = 'Black salamander',
	godStaff = 'Saradomin staff',
	godSpell = 'Saradomin Strke',
	charge = 'false',
	dartStaff = 'Slayer\'s staff',
	ibansStaff = 'Iban\'s staff',
	weapon = 'Kodai wand',
	spellbook = 'Standard',
	standardSpell = 'Wind Strike',
	arceuusSpell = 'Ghostly Grasp',
	ancientSpell = 'Smoke Rush',
	head = 'Ancestral hat',
	cape = 'Imbued guthix cape',
	neck = 'Occult necklace',
	ammo = 'None',
	body = 'Ancestral robe top',
	shield = 'Elidinis\' ward',
	legs = 'Ancestral robe bottom',
	hands = 'Tormented bracelet',
	feet = 'None',
	ring = 'Magus ring',
}
p._main(args)
--]]

function p.main(frame)
	--mw.logObject(frame)
	local args = frame.args
	return p._main(args)
end

return p