Module:Sandbox/User:Fjara/Sandbox/Spade
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