Difference between revisions of "Module:Magic"

From Melvor Idle
(Tweaking links for magic types again)
(Okay for real this should be a fix for standard magic spell linking)
 
(20 intermediate revisions by 3 users not shown)
Line 2: Line 2:
  
 
local MagicData = mw.loadData('Module:Magic/data')
 
local MagicData = mw.loadData('Module:Magic/data')
 +
local SkillData = mw.loadData('Module:Skills/data')
  
 
local Areas = require('Module:CombatAreas')
 
local Areas = require('Module:CombatAreas')
Line 7: Line 8:
 
local Icons = require('Module:Icons')
 
local Icons = require('Module:Icons')
 
local Items = require('Module:Items')
 
local Items = require('Module:Items')
 +
local Constants = require('Module:Constants')
  
 
function processSpell(section, index)
 
function processSpell(section, index)
  local result = Shared.clone(MagicData[section][index])
+
local result = Shared.clone(MagicData[section][index])
  result.id = index - 1
+
result.id = index - 1
  result.type = section
+
result.type = section
  return result
+
return result
 
end
 
end
  
 
function p.getSpell(name, type)
 
function p.getSpell(name, type)
  local section = type
+
local section = type
  if type == 'Spell' or type == 'Standard' then
+
if type == 'Spell' or type == 'Standard' then
    section = 'Spells'
+
section = 'Spells'
  elseif type == 'Curse' then
+
elseif type == 'Curse' then
    section = 'Curses'
+
section = 'Curses'
  elseif type == 'Aurora' then
+
elseif type == 'Aurora' then
    section = 'Auroras'
+
section = 'Auroras'
  elseif type == 'Alt Magic' or type == 'Alternative Magic' then
+
elseif type == 'Alt Magic' or type == 'Alternative Magic' then
    section='AltMagic'
+
section = 'AltMagic'
  end
+
end
 
+
 
  if section == nil then
+
name = Shared.fixPagename(name)
    for i, section in Shared.skpairs(MagicData) do
+
if section == nil then
      for j, spell in Shared.skpairs(section) do
+
for i, section in Shared.skpairs(MagicData) do
        if spell.name == name then
+
for j, spell in Shared.skpairs(section) do
          return processSpell(i, j)
+
if spell.name == name then
        end
+
return processSpell(i, j)
      end
+
end
    end
+
end
  elseif section ~= nil and MagicData[section] ~= nil then
+
end
    for i, spell in Shared.skpairs(MagicData[section]) do
+
elseif section ~= nil and MagicData[section] ~= nil then
      if spell.name == name then
+
for i, spell in Shared.skpairs(MagicData[section]) do
        return processSpell(section, i)
+
if spell.name == name then
      end
+
return processSpell(section, i)
    end
+
end
  else
+
end
    return nil
+
else
  end
+
return nil
 +
end
 
end
 
end
  
 
function p.getSpellByID(type, id)
 
function p.getSpellByID(type, id)
  local section = type
+
local section = type
  if type == nil or type == 'Spell' or type == 'Standard' then
+
if type == nil or type == 'Spell' or type == 'Standard' then
    section = 'Spells'
+
section = 'Spells'
  elseif type == 'Curse' then
+
elseif type == 'Curse' then
    section = 'Curses'
+
section = 'Curses'
  elseif type == 'Aurora' then
+
elseif type == 'Aurora' then
    section = 'Auroras'
+
section = 'Auroras'
  elseif type == 'Alt Magic' or type == 'Alternative Magic' then
+
elseif type == 'Alt Magic' or type == 'Alternative Magic' then
    section='AltMagic'
+
section = 'AltMagic'
  end
+
end
 
+
 
  if MagicData[section] ~= nil then
+
if MagicData[section] ~= nil then
    return processSpell(section, id + 1)
+
return processSpell(section, id + 1)
  else
+
else
    return nil
+
return nil
  end
+
end
 
end
 
end
  
 
function p.getTypeString(type)
 
function p.getTypeString(type)
  if type == 'Auroras' then
+
if type == 'Auroras' then
    return 'Aurora'
+
return 'Aurora'
  elseif type == 'Curses' then
+
elseif type == 'Curses' then
    return 'Curse'
+
return 'Curse'
  elseif type == 'AltMagic' then
+
elseif type == 'AltMagic' then
    return 'Alt. Magic'
+
return 'Alt. Magic'
  elseif type == 'Spells' then
+
elseif type == 'Spells' then
    return "Combat Spell"
+
return "Combat Spell"
  elseif type == 'Ancient' then
+
elseif type == 'Ancient' then
    return 'Ancient Magick'
+
return 'Ancient Magick'
  end
+
end
 
end
 
end
  
 
function p._getSpellIcon(spell, size)
 
function p._getSpellIcon(spell, size)
  if size == nil then size = 50 end
+
if size == nil then size = 50 end
  if spell.type == 'Auroras' then
+
if spell.type == 'Auroras' then
    return Icons.Icon({spell.name, type='aurora', notext=true, size=size})
+
return Icons.Icon({spell.name, type='aurora', notext=true, size=size})
  elseif spell.type == 'Curses' then
+
elseif spell.type == 'Curses' then
    return Icons.Icon({spell.name, type='curse', notext=true, size=size})
+
return Icons.Icon({spell.name, type='curse', notext=true, size=size})
  else
+
else
    return Icons.Icon({spell.name, type='spell', notext=true, size=size})
+
return Icons.Icon({spell.name, type='spell', notext=true, size=size})
  end
+
end
 
end
 
end
  
 
function p._getSpellRequirements(spell)
 
function p._getSpellRequirements(spell)
  local result = ''
+
local result = ''
  result = result..Icons._SkillReq('Magic', spell.magicLevelRequired)
+
result = result..Icons._SkillReq('Magic', spell.level)
  if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
+
if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
    local reqItem = Items.getItemByID(spell.requiredItem)
+
local reqItem = Items.getItemByID(spell.requiredItem)
    result = result..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
+
result = result..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
  end
+
end
  if spell.requiredDungeonCompletion ~= nil then
+
if spell.requiredDungeonCompletion ~= nil then
    local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
+
local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
    result = result..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
+
result = result..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
  end
+
end
  return result
+
return result
 
end
 
end
  
 
function p._getSpellRunes(spell)
 
function p._getSpellRunes(spell)
  local result = ''
+
local formatRuneList = function(runes)
  for i, req in Shared.skpairs(spell.runesRequired) do
+
local runeList = {}
    local rune = Items.getItemByID(req.id)
+
for i, req in ipairs(runes) do
    if i > 1 then result = result..', ' end
+
local rune = Items.getItemByID(req.id)
    result = result..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
+
if rune ~= nil then
  end
+
table.insert(runeList, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
  if spell.runesRequiredAlt ~= nil and not Shared.tablesEqual(spell.runesRequired, spell.runesRequiredAlt) then
+
end
    result = result.."<br/>'''OR'''<br/>"
+
end
    for i, req in pairs(spell.runesRequiredAlt) do
+
return table.concat(runeList, ', ')
      local rune = Items.getItemByID(req.id)
+
end
      if i > 1 then result = result..', ' end
+
if type(spell.runesRequired) == 'table' then
      result = result..Icons.Icon({rune.name, type='item', notext=true, qty=req.qty})
+
local resultPart  = {}
    end
+
table.insert(resultPart, formatRuneList(spell.runesRequired))
  end
+
if spell.runesRequiredAlt ~= nil and not Shared.tablesEqual(spell.runesRequired, spell.runesRequiredAlt) then
  return result
+
table.insert(resultPart, "<br/>'''OR'''<br/>" .. formatRuneList(spell.runesRequiredAlt))
 +
end
 +
return table.concat(resultPart)
 +
else
 +
return ''
 +
end
 
end
 
end
  
 
function p.getSpellRunes(frame)
 
function p.getSpellRunes(frame)
  local spellName = frame.args ~= nil and frame.args[1] or frame
+
local spellName = frame.args ~= nil and frame.args[1] or frame
  local spell = p.getSpell(spellName)
+
local spell = p.getSpell(spellName)
  if spell == nil then
+
if spell == nil then
    return "ERROR: No spell named "..spellName.." exists in the data module"
+
return "ERROR: No spell named "..spellName.." exists in the data module"
  end
+
end
  return p._getSpellRunes(spell)
+
return p._getSpellRunes(spell)
 +
end
 +
 
 +
function p._getSpellDescription(spell)
 +
local result = ''
 +
if spell.description ~= nil then
 +
if p.getSpellTypeIndex(spell.type) == 4 and string.find(spell.description, "<br>") ~= nil then
 +
result = string.sub(spell.description, 0, string.find(spell.description, "<br>")-1)
 +
else
 +
result = spell.description
 +
end
 +
elseif (spell.modifiers ~= nil or spell.enemyModifiers ~= nil) then
 +
if spell.modifiers ~= nil then
 +
local playerModText = Constants.getModifiersText(spell.modifiers, false)
 +
result = playerModText
 +
end
 +
if spell.enemyModifiers ~= nil then
 +
local enemyModText = Constants.getModifiersText(spell.enemyModifiers, false)
 +
result = result .. (string.len(result) > 0 and '<br/>' or '') .. 'Enemies are inflicted with:<br/>' .. enemyModText
 +
end
 +
elseif spell.type == 'Spells' then
 +
result = 'Combat spell with a max hit of '..(spell.maxHit * 10)
 +
end
 +
 
 +
return result
 
end
 
end
  
 
function p._getSpellStat(spell, stat)
 
function p._getSpellStat(spell, stat)
  if stat == 'bigIcon' then
+
if stat == 'bigIcon' then
    return p._getSpellIcon(spell, 250)
+
return p._getSpellIcon(spell, 250)
  elseif stat == 'icon' then
+
elseif stat == 'description' then
    return p._getSpellIcon(spell)
+
return p._getSpellDescription(spell)
  elseif stat == 'requirements' then
+
elseif stat == 'icon' then
    return p._getSpellRequirements(spell)
+
return p._getSpellIcon(spell)
  elseif stat == 'runes' then
+
elseif stat == 'requirements' then
    return p._getSpellRunes(spell)
+
return p._getSpellRequirements(spell)
  elseif stat == 'type' then
+
elseif stat == 'runes' then
    return p.getTypeString(spell.type)
+
return p._getSpellRunes(spell)
  end
+
elseif stat == 'type' then
  return spell[stat]
+
return p.getTypeString(spell.type)
 +
end
 +
return spell[stat]
 
end
 
end
  
 
function p.getSpellStat(frame)
 
function p.getSpellStat(frame)
  local spellName = frame.args ~= nil and frame.args[1] or frame[1]
+
local spellName = frame.args ~= nil and frame.args[1] or frame[1]
  local statName = frame.args ~= nil and frame.args[2] or frame[2]
+
local statName = frame.args ~= nil and frame.args[2] or frame[2]
  local spell = p.getSpell(spellName)
+
local spell = p.getSpell(spellName)
  if spell == nil then
+
if spell == nil then
    return "ERROR: No spell named "..spellName.." found"
+
return "ERROR: No spell named "..spellName.." found"
  end
+
end
  return p._getSpellStat(spell, statName)
+
return p._getSpellStat(spell, statName)
 
end
 
end
  
 
function p.getOtherSpellBoxText(frame)
 
function p.getOtherSpellBoxText(frame)
  local spellName = frame.args ~= nil and frame.args[1] or frame
+
local spellName = frame.args ~= nil and frame.args[1] or frame
  local spell = p.getSpell(spellName)
+
local spell = p.getSpell(spellName)
  if spell == nil then
+
if spell == nil then
    return "ERROR: No spell named "..spellName.." found"
+
return "ERROR: No spell named "..spellName.." found"
  end
+
end
  
  local result = ''
+
local result = ''
  if spell.type == 'Spells' then
 
    result = result.."|-\r\n|'''Max Hit:''' "..(spell.maxHit * 10)
 
  end
 
  if spell.description ~= nil then
 
    result = result.."\r\n|-\r\n|'''Description:'''<br/>"..spell.description
 
  end
 
  
  return result
+
--8/20/21: Changed to using the new getSpellDescription function
 +
result = result.."\r\n|-\r\n|'''Description:'''<br/>"..p._getSpellStat(spell, 'description')
 +
 
 +
return result
 
end
 
end
  
 
function p._getSpellCategories(spell)
 
function p._getSpellCategories(spell)
  local result = '[[Category:Spells]]'
+
local result = '[[Category:Spells]]'
  result = result..'[[Category:'..p.getTypeString(spell.type)..']]'
+
result = result..'[[Category:'..p.getTypeString(spell.type)..']]'
  return result
+
return result
 
end
 
end
  
 
function p.getSpellCategories(frame)
 
function p.getSpellCategories(frame)
  local spellName = frame.args ~= nil and frame.args[1] or frame
+
local spellName = frame.args ~= nil and frame.args[1] or frame
  local spell = p.getSpell(spellName)
+
local spell = p.getSpell(spellName)
  if spell == nil then
+
if spell == nil then
    return "ERROR: No spell named "..spellName.." found"
+
return "ERROR: No spell named "..spellName.." found"
  end
+
end
  return p._getSpellCategories(spell)
+
return p._getSpellCategories(spell)
 
end
 
end
  
 +
-- If includeConsumes = true, then checks for Alt Magic spell resource consumptions as well as
 +
-- the rune cost of spells
 +
function p.getSpellsForItem(itemID, includeConsumes)
 +
if type(includeConsumes) ~= 'boolean' then
 +
includeConsumes = false
 +
end
 +
-- The superheat table is built later as & when needed
 +
local superheatCosts, coalID = nil, nil
 +
local spellList = {}
 +
for secName, secArray in pairs(MagicData) do
 +
for i, spell in ipairs(secArray) do
 +
local foundSpell = false
 +
for j, req in pairs(spell.runesRequired) do
 +
if req.id == itemID then
 +
foundSpell = true
 +
break
 +
end
 +
end
 +
if not foundSpell and spell.runesRequiredAlt ~= nil then
 +
for j, req in pairs(spell.runesRequiredAlt) do
 +
if req.id == itemID then
 +
foundSpell = true
 +
break
 +
end
 +
end
 +
end
 +
if includeConsumes and not foundSpell and spell.consumes ~= nil and spell.consumes > 0 then
 +
-- The consumes property of Alt Magic spells is as follows:
 +
-- 0 = Any item
 +
-- 1 = Junk item
 +
-- 2 = Superheat/ores with Coal
 +
-- 3 = Superheat/ores without Coal
 +
-- 4 = Nothing
 +
-- 5 = Coal ore
 +
-- We ignore 4 (no resource consumption) and 0 (as this would flag every item unhelpfully)
 +
 +
-- Determine the coal ID for the first time if we need it
 +
if coalID == nil and Shared.contains({2, 3, 5}, spell.consumes) then
 +
local coalItem = Items.getItem('Coal Ore')
 +
if coalItem ~= nil then
 +
coalID = coalItem.id
 +
end
 +
end
 +
if spell.consumes == 1 and Shared.contains(SkillData.Fishing.JunkItems, itemID) then
 +
foundSpell = true
 +
elseif spell.consumes == 2 or spell.consumes == 3 then
 +
if superheatCosts == nil then
 +
-- Initialize the superheatItems table
 +
superheatCosts = {}
 +
for j, recipe in ipairs(SkillData.Smithing.Recipes) do
 +
if recipe.category == 0 then
 +
-- Bar recipe
 +
for k, itemCost in ipairs(recipe.itemCosts) do
 +
if itemCost.id ~= coalID then
 +
superheatCosts[itemCost.id] = true
 +
end
 +
end
 +
end
 +
end
 +
end
 +
local ignoreCoal = (spell.consumes == 3)
 +
if superheatCosts[itemID] or (not ignoreCoal and itemID == coalID) then
 +
foundSpell = true
 +
end
 +
elseif spell.consumes == 5 and itemID == coalID then
 +
foundSpell = true
 +
end
 +
end
 +
if foundSpell then
 +
table.insert(spellList, processSpell(secName, i))
 +
end
 +
end
 +
end
 +
table.sort(spellList, function(a, b)
 +
if a.type ~= b.type then
 +
return p.getSpellTypeIndex(a.type) < p.getSpellTypeIndex(b.type)
 +
else
 +
return a.level < b.level
 +
end
 +
end)
 +
return spellList
 +
end
 +
 +
-- The below function is included for backwards compatibility
 
function p.getSpellsForRune(runeID)
 
function p.getSpellsForRune(runeID)
  local spellList = {}
+
return p.getSpellsForItem(runeID, false)
  for secName, secArray in Shared.skpairs(MagicData) do
 
    for i, spell in pairs(secArray) do
 
      local foundSpell = false
 
      for j, req in pairs(spell.runesRequired) do
 
        if req.id == runeID then
 
          table.insert(spellList, processSpell(secName, i))
 
          foundSpell = true
 
          break
 
        end
 
      end
 
      if spell.runesRequiredAlt ~= nil and not foundSpell then
 
        for j, req in pairs(spell.runesRequiredAlt) do
 
          if req.id == runeID then
 
            table.insert(spellList, processSpell(secName, i))
 
            break
 
          end
 
        end
 
      end
 
    end
 
  end
 
  table.sort(spellList, function(a, b)
 
              if a.type ~= b.type then
 
                return p.getSpellTypeIndex(a.type) < p.getSpellTypeIndex(b.type)
 
              else
 
                return a.magicLevelRequired < b.magicLevelRequired
 
              end
 
            end)
 
  return spellList
 
 
end
 
end
  
 
function p.getSpellTypeIndex(type)
 
function p.getSpellTypeIndex(type)
  if type == 'Spells' then
+
if type == 'Spells' then
    return 0
+
return 0
  elseif type == 'Curses' then
+
elseif type == 'Curses' then
    return 1
+
return 1
  elseif type == 'Auroras' then
+
elseif type == 'Auroras' then
    return 2
+
return 2
  elseif type == 'Ancient' then
+
elseif type == 'Ancient' then
    return 3
+
return 3
  elseif type == 'AltMagic' then
+
elseif type == 'AltMagic' then
    return 4
+
return 4
  end
+
end
  return -1
+
return -1
 
end
 
end
  
 
function p.getSpellTypeLink(type)
 
function p.getSpellTypeLink(type)
  if type == 'Spells' then
+
if type == 'Spells' then
    return Icons.Icon({'Magic', 'Standard', img='Standard', type='spellType'})
+
return Icons.Icon({'Standard Magic', 'Standard', img='Standard', type='spellType'})
  elseif type == 'Curses' then
+
elseif type == 'Curses' then
    return Icons.Icon({'Curses', 'Curse', img='Curse', type='spellType'})
+
return Icons.Icon({'Curses', 'Curse', img='Curse', type='spellType'})
  elseif type == 'Auroras' then
+
elseif type == 'Auroras' then
    return Icons.Icon({'Auroras', 'Aurora', img='Aurora', type='spellType'})
+
return Icons.Icon({'Auroras', 'Aurora', img='Aurora', type='spellType'})
  elseif type == 'Ancient' then
+
elseif type == 'Ancient' then
    return Icons.Icon({'Ancient Magicks', 'Ancient', img='Ancient', type='spellType'})
+
return Icons.Icon({'Ancient Magicks', 'Ancient', img='Ancient', type='spellType'})
  elseif type == 'AltMagic' then
+
elseif type == 'AltMagic' then
    return Icons.Icon({'Alt. Magic', type='skill'})
+
return Icons.Icon({'Alt. Magic', type='skill'})
  end
+
end
  return ''
+
return ''
 +
end
 +
 
 +
function p._getSpellRow(spell, includeTypeColumn)
 +
local rowTxt = '\r\n|-\r\n|data-sort-value="'..spell.name..'"|'
 +
if spell.type == 'Auroras' then
 +
rowTxt = rowTxt..Icons.Icon({spell.name, type='aurora', notext=true, size=50})
 +
elseif spell.type == 'Curses' then
 +
rowTxt = rowTxt..Icons.Icon({spell.name, type='curse', notext=true, size=50})
 +
else
 +
rowTxt = rowTxt..Icons.Icon({spell.name, type='spell', notext=true, size=50})
 +
end
 +
rowTxt = rowTxt..'||'..Icons.Icon({spell.name, type='spell', noicon=true})
 +
rowTxt = rowTxt..'||data-sort-value="'..spell.level..'"|'..Icons._SkillReq('Magic', spell.level)
 +
--Handle required items/dungeon clears
 +
if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
 +
local reqItem = Items.getItemByID(spell.requiredItem)
 +
rowTxt = rowTxt..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
 +
end
 +
if spell.requiredDungeonCompletion ~= nil then
 +
local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
 +
rowTxt = rowTxt..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
 +
end
 +
 
 +
if includeTypeColumn then
 +
rowTxt = rowTxt..'||data-sort-value="'..p.getSpellTypeIndex(spell.type)..'"|'
 +
rowTxt = rowTxt..p.getSpellTypeLink(spell.type)
 +
end
 +
--8/20/21: Changed to just getting the spell's description outright
 +
rowTxt = rowTxt..'||'..p._getSpellStat(spell, 'description')
 +
--1/4/22: haha just kidding. Now we're also getting delay between attacks for spells with special attacks
 +
if spell.specialAttack ~= nil then
 +
local spAtt = spell.specialAttack
 +
local interval = spAtt.attackInterval ~= nil and spAtt.attackInterval or -1
 +
if interval ~= -1 then
 +
local hits = spAtt.attackCount ~= nil and spAtt.attackCount or 1
 +
rowTxt = rowTxt..'<br/>('
 +
rowTxt = rowTxt..Shared.round(interval / 1000, 2, 2)..'s delay between attacks.'
 +
if hits > 2 then
 +
rowTxt = rowTxt..' '..Shared.round(interval * (hits - 1) / 1000, 2, 2)..'s total duration.'
 +
end
 +
rowTxt = rowTxt..')'
 +
end
 +
end
 +
if p.getSpellTypeIndex(spell.type) == 4 then
 +
rowTxt = rowTxt..'||'..spell.baseExperience
 +
end
 +
rowTxt = rowTxt..'||style="text-align:center"|'
 +
rowTxt = rowTxt..p._getSpellRunes(spell)
 +
return rowTxt
 +
end
 +
 
 +
function p.getStandardSpellsTable(frame)
 +
local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
 +
result = result..'!!Requirements'
 +
result = result..'!!style="width:275px"|Description'
 +
result = result..'!!Runes'
 +
local spellList = {}
 +
for i, spell in Shared.skpairs(MagicData.Spells) do
 +
local rowTxt = p._getSpellRow(processSpell('Spells', i), false)
 +
result = result..rowTxt
 +
end
 +
result = result..'\r\n|}'
 +
return result
 +
end
 +
 
 +
function p.getCurseTable(frame)
 +
local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
 +
result = result..'!!Requirements'
 +
result = result..'!!style="width:275px"|Description'
 +
result = result..'!!Runes'
 +
local spellList = {}
 +
for i, spell in Shared.skpairs(MagicData.Curses) do
 +
local rowTxt = p._getSpellRow(processSpell('Curses', i), false)
 +
result = result..rowTxt
 +
end
 +
result = result..'\r\n|}'
 +
return result
 +
end
 +
 
 +
function p.getAuroraTable(frame)
 +
local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
 +
result = result..'!!Requirements'
 +
result = result..'!!style="width:275px"|Description'
 +
result = result..'!!Runes'
 +
for i, spell in Shared.skpairs(MagicData.Auroras) do
 +
local rowTxt = p._getSpellRow(processSpell('Auroras', i), false)
 +
result = result..rowTxt
 +
end
 +
result = result..'\r\n|}'
 +
return result
 +
end
 +
 
 +
function p.getAncientTable(frame)
 +
local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
 +
result = result..'!!Requirements'
 +
result = result..'!!style="width:275px"|Description'
 +
result = result..'!!Runes'
 +
for i, spell in Shared.skpairs(MagicData.Ancient) do
 +
local rowTxt = p._getSpellRow(processSpell('Ancient', i), false)
 +
result = result..rowTxt
 +
end
 +
result = result..'\r\n|}'
 +
return result
 +
end
 +
 
 +
function p.getAltSpellsTable(frame)
 +
local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
 +
result = result..'!!Requirements'
 +
result = result..'!!style="width:275px"|Description'
 +
result = result..'!!Experience'
 +
result = result..'!!Runes'
 +
local spellList = {}
 +
for i, spell in Shared.skpairs(MagicData.AltMagic) do
 +
local rowTxt = p._getSpellRow(processSpell('AltMagic', i), false)
 +
result = result..rowTxt
 +
end
 +
result = result..'\r\n|}'
 +
return result
 
end
 
end
  
 
return p
 
return p

Latest revision as of 20:40, 10 May 2022

Data pulled from Module:Magic/data


local p = {}

local MagicData = mw.loadData('Module:Magic/data')
local SkillData = mw.loadData('Module:Skills/data')

local Areas = require('Module:CombatAreas')
local Shared = require('Module:Shared')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Constants = require('Module:Constants')

function processSpell(section, index)
	local result = Shared.clone(MagicData[section][index])
	result.id = index - 1
	result.type = section
	return result
end

function p.getSpell(name, type)
	local section = type
	if type == 'Spell' or type == 'Standard' then
		section = 'Spells'
	elseif type == 'Curse' then
		section = 'Curses'
	elseif type == 'Aurora' then
		section = 'Auroras'
	elseif type == 'Alt Magic' or type == 'Alternative Magic' then
		section = 'AltMagic'
	end

	name = Shared.fixPagename(name)
	if section == nil then
		for i, section in Shared.skpairs(MagicData) do
			for j, spell in Shared.skpairs(section) do
				if spell.name == name then
					return processSpell(i, j)
				end
			end
		end
	elseif section ~= nil and MagicData[section] ~= nil then
		for i, spell in Shared.skpairs(MagicData[section]) do
			if spell.name == name then
				return processSpell(section, i)
			end
		end
	else
		return nil
	end
end

function p.getSpellByID(type, id)
	local section = type
	if type == nil or type == 'Spell' or type == 'Standard' then
		section = 'Spells'
	elseif type == 'Curse' then
		section = 'Curses'
	elseif type == 'Aurora' then
		section = 'Auroras'
	elseif type == 'Alt Magic' or type == 'Alternative Magic' then
		section = 'AltMagic'
	end

	if MagicData[section] ~= nil then
		return processSpell(section, id + 1)
	else
		return nil
	end
end

function p.getTypeString(type)
	if type == 'Auroras' then
		return 'Aurora'
	elseif type == 'Curses' then
		return 'Curse'
	elseif type == 'AltMagic' then
		return 'Alt. Magic'
	elseif type == 'Spells' then
		return "Combat Spell"
	elseif type == 'Ancient' then
		return 'Ancient Magick'
	end
end

function p._getSpellIcon(spell, size)
	if size == nil then size = 50 end
	if spell.type == 'Auroras' then
		return Icons.Icon({spell.name, type='aurora', notext=true, size=size})
	elseif spell.type == 'Curses' then
		return Icons.Icon({spell.name, type='curse', notext=true, size=size})
	else
		return Icons.Icon({spell.name, type='spell', notext=true, size=size})
	end
end

function p._getSpellRequirements(spell)
	local result = ''
	result = result..Icons._SkillReq('Magic', spell.level)
	if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
		local reqItem = Items.getItemByID(spell.requiredItem)
		result = result..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
	end
	if spell.requiredDungeonCompletion ~= nil then
		local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
		result = result..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
	end
	return result
end

function p._getSpellRunes(spell)
	local formatRuneList = function(runes)
			local runeList = {}
			for i, req in ipairs(runes) do
				local rune = Items.getItemByID(req.id)
				if rune ~= nil then
					table.insert(runeList, Icons.Icon({rune.name, type='item', notext=true, qty=req.qty}))
				end
			end
			return table.concat(runeList, ', ')
		end
	if type(spell.runesRequired) == 'table' then
		local resultPart  = {}
		table.insert(resultPart, formatRuneList(spell.runesRequired))
		if spell.runesRequiredAlt ~= nil and not Shared.tablesEqual(spell.runesRequired, spell.runesRequiredAlt) then
			table.insert(resultPart, "<br/>'''OR'''<br/>" .. formatRuneList(spell.runesRequiredAlt))
		end
		return table.concat(resultPart)
	else
		return ''
	end
end

function p.getSpellRunes(frame)
	local spellName = frame.args ~= nil and frame.args[1] or frame
	local spell = p.getSpell(spellName)
	if spell == nil then
		return "ERROR: No spell named "..spellName.." exists in the data module"
	end
	return p._getSpellRunes(spell)
end

function p._getSpellDescription(spell)
	local result = ''
	if spell.description ~= nil then
		if p.getSpellTypeIndex(spell.type) == 4 and string.find(spell.description, "<br>") ~= nil then
			result = string.sub(spell.description, 0, string.find(spell.description, "<br>")-1)
		else
			result = spell.description
		end
	elseif (spell.modifiers ~= nil or spell.enemyModifiers ~= nil) then
		if spell.modifiers ~= nil then
			local playerModText = Constants.getModifiersText(spell.modifiers, false)
			result = playerModText
		end
		if spell.enemyModifiers ~= nil then
			local enemyModText = Constants.getModifiersText(spell.enemyModifiers, false)
			result = result .. (string.len(result) > 0 and '<br/>' or '') .. 'Enemies are inflicted with:<br/>' .. enemyModText
		end
	elseif spell.type == 'Spells' then
		result = 'Combat spell with a max hit of '..(spell.maxHit * 10)
	end

	return result
end

function p._getSpellStat(spell, stat)
	if stat == 'bigIcon' then
		return p._getSpellIcon(spell, 250)
	elseif stat == 'description' then
		return p._getSpellDescription(spell)
	elseif stat == 'icon' then
		return p._getSpellIcon(spell)
	elseif stat == 'requirements' then
		return p._getSpellRequirements(spell)
	elseif stat == 'runes' then
		return p._getSpellRunes(spell)
	elseif stat == 'type' then
		return p.getTypeString(spell.type)
	end
	return spell[stat]
end

function p.getSpellStat(frame)
	local spellName = frame.args ~= nil and frame.args[1] or frame[1]
	local statName = frame.args ~= nil and frame.args[2] or frame[2]
	local spell = p.getSpell(spellName)
	if spell == nil then
		return "ERROR: No spell named "..spellName.." found"
	end
	return p._getSpellStat(spell, statName)
end

function p.getOtherSpellBoxText(frame)
	local spellName = frame.args ~= nil and frame.args[1] or frame
	local spell = p.getSpell(spellName)
	if spell == nil then
		return "ERROR: No spell named "..spellName.." found"
	end

	local result = ''

	--8/20/21: Changed to using the new getSpellDescription function
	result = result.."\r\n|-\r\n|'''Description:'''<br/>"..p._getSpellStat(spell, 'description')

	return result
end

function p._getSpellCategories(spell)
	local result = '[[Category:Spells]]'
	result = result..'[[Category:'..p.getTypeString(spell.type)..']]'
	return result
end

function p.getSpellCategories(frame)
	local spellName = frame.args ~= nil and frame.args[1] or frame
	local spell = p.getSpell(spellName)
	if spell == nil then
		return "ERROR: No spell named "..spellName.." found"
	end
	return p._getSpellCategories(spell)
end

-- If includeConsumes = true, then checks for Alt Magic spell resource consumptions as well as
-- the rune cost of spells
function p.getSpellsForItem(itemID, includeConsumes)
	if type(includeConsumes) ~= 'boolean' then
		includeConsumes = false
	end
	-- The superheat table is built later as & when needed
	local superheatCosts, coalID = nil, nil
	local spellList = {}
	for secName, secArray in pairs(MagicData) do
		for i, spell in ipairs(secArray) do
			local foundSpell = false
			for j, req in pairs(spell.runesRequired) do
				if req.id == itemID then
					foundSpell = true
					break
				end
			end
			if not foundSpell and spell.runesRequiredAlt ~= nil then
				for j, req in pairs(spell.runesRequiredAlt) do
					if req.id == itemID then
						foundSpell = true
						break
					end
				end
			end
			if includeConsumes and not foundSpell and spell.consumes ~= nil and spell.consumes > 0 then
				-- The consumes property of Alt Magic spells is as follows:
				-- 0 = Any item
				-- 1 = Junk item
				-- 2 = Superheat/ores with Coal
				-- 3 = Superheat/ores without Coal
				-- 4 = Nothing
				-- 5 = Coal ore
				-- We ignore 4 (no resource consumption) and 0 (as this would flag every item unhelpfully)

				-- Determine the coal ID for the first time if we need it
				if coalID == nil and Shared.contains({2, 3, 5}, spell.consumes) then
					local coalItem = Items.getItem('Coal Ore')
					if coalItem ~= nil then
						coalID = coalItem.id
					end
				end
				if spell.consumes == 1 and Shared.contains(SkillData.Fishing.JunkItems, itemID) then
					foundSpell = true
				elseif spell.consumes == 2 or spell.consumes == 3 then
					if superheatCosts == nil then
						-- Initialize the superheatItems table
						superheatCosts = {}
						for j, recipe in ipairs(SkillData.Smithing.Recipes) do
							if recipe.category == 0 then
								-- Bar recipe
								for k, itemCost in ipairs(recipe.itemCosts) do
									if itemCost.id ~= coalID then
										superheatCosts[itemCost.id] = true
									end
								end
							end
						end
					end
					local ignoreCoal = (spell.consumes == 3)
					if superheatCosts[itemID] or (not ignoreCoal and itemID == coalID) then
						foundSpell = true
					end
				elseif spell.consumes == 5 and itemID == coalID then
					foundSpell = true
				end
			end
			if foundSpell then
				table.insert(spellList, processSpell(secName, i))
			end
		end
	end
	table.sort(spellList, function(a, b)
		if a.type ~= b.type then
			return p.getSpellTypeIndex(a.type) < p.getSpellTypeIndex(b.type)
		else
			return a.level < b.level
		end
	end)
	return spellList
end

-- The below function is included for backwards compatibility
function p.getSpellsForRune(runeID)
	return p.getSpellsForItem(runeID, false)
end

function p.getSpellTypeIndex(type)
	if type == 'Spells' then
		return 0
	elseif type == 'Curses' then
		return 1
	elseif type == 'Auroras' then
		return 2
	elseif type == 'Ancient' then
		return 3
	elseif type == 'AltMagic' then
		return 4
	end
	return -1
end

function p.getSpellTypeLink(type)
	if type == 'Spells' then
		return Icons.Icon({'Standard Magic', 'Standard', img='Standard', type='spellType'})
	elseif type == 'Curses' then
		return Icons.Icon({'Curses', 'Curse', img='Curse', type='spellType'})
	elseif type == 'Auroras' then
		return Icons.Icon({'Auroras', 'Aurora', img='Aurora', type='spellType'})
	elseif type == 'Ancient' then
		return Icons.Icon({'Ancient Magicks', 'Ancient', img='Ancient', type='spellType'})
	elseif type == 'AltMagic' then
		return Icons.Icon({'Alt. Magic', type='skill'})
	end
	return ''
end

function p._getSpellRow(spell, includeTypeColumn)
	local rowTxt = '\r\n|-\r\n|data-sort-value="'..spell.name..'"|'
	if spell.type == 'Auroras' then
		rowTxt = rowTxt..Icons.Icon({spell.name, type='aurora', notext=true, size=50})
	elseif spell.type == 'Curses' then
		rowTxt = rowTxt..Icons.Icon({spell.name, type='curse', notext=true, size=50})
	else
		rowTxt = rowTxt..Icons.Icon({spell.name, type='spell', notext=true, size=50})
	end
	rowTxt = rowTxt..'||'..Icons.Icon({spell.name, type='spell', noicon=true})
	rowTxt = rowTxt..'||data-sort-value="'..spell.level..'"|'..Icons._SkillReq('Magic', spell.level)
	--Handle required items/dungeon clears
	if spell.requiredItem ~= nil and spell.requiredItem >= 0 then
		local reqItem = Items.getItemByID(spell.requiredItem)
		rowTxt = rowTxt..'<br/>'..Icons.Icon({reqItem.name, type='item', notext=true})..' equipped'
	end
	if spell.requiredDungeonCompletion ~= nil then
		local dung = Areas.getAreaByID('dungeon', spell.requiredDungeonCompletion[1])
		rowTxt = rowTxt..'<br/>'..Icons.Icon({dung.name, type='dungeon', notext=true, qty=spell.requiredDungeonCompletion[2]})..' Clears'
	end

	if includeTypeColumn then
		rowTxt = rowTxt..'||data-sort-value="'..p.getSpellTypeIndex(spell.type)..'"|'
		rowTxt = rowTxt..p.getSpellTypeLink(spell.type)
	end
	--8/20/21: Changed to just getting the spell's description outright
	rowTxt = rowTxt..'||'..p._getSpellStat(spell, 'description')
	--1/4/22: haha just kidding. Now we're also getting delay between attacks for spells with special attacks
	if spell.specialAttack ~= nil then
		local spAtt = spell.specialAttack
		local interval = spAtt.attackInterval ~= nil and spAtt.attackInterval or -1
		if interval ~= -1 then
			local hits = spAtt.attackCount ~= nil and spAtt.attackCount or 1
			rowTxt = rowTxt..'<br/>('
			rowTxt = rowTxt..Shared.round(interval / 1000, 2, 2)..'s delay between attacks.'
			if hits > 2 then
				rowTxt = rowTxt..' '..Shared.round(interval * (hits - 1) / 1000, 2, 2)..'s total duration.'
			end
			rowTxt = rowTxt..')'
		end
	end
	if p.getSpellTypeIndex(spell.type) == 4 then
		rowTxt = rowTxt..'||'..spell.baseExperience
	end
	rowTxt = rowTxt..'||style="text-align:center"|'
	rowTxt = rowTxt..p._getSpellRunes(spell)
	return rowTxt
end

function p.getStandardSpellsTable(frame)
	local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
	result = result..'!!Requirements'
	result = result..'!!style="width:275px"|Description'
	result = result..'!!Runes'
	local spellList = {}
	for i, spell in Shared.skpairs(MagicData.Spells) do
		local rowTxt = p._getSpellRow(processSpell('Spells', i), false)
		result = result..rowTxt
	end
	result = result..'\r\n|}'
	return result
end

function p.getCurseTable(frame)
	local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
	result = result..'!!Requirements'
	result = result..'!!style="width:275px"|Description'
	result = result..'!!Runes'
	local spellList = {}
	for i, spell in Shared.skpairs(MagicData.Curses) do
		local rowTxt = p._getSpellRow(processSpell('Curses', i), false)
		result = result..rowTxt
	end
	result = result..'\r\n|}'
	return result
end

function p.getAuroraTable(frame)
	local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
	result = result..'!!Requirements'
	result = result..'!!style="width:275px"|Description'
	result = result..'!!Runes'
	for i, spell in Shared.skpairs(MagicData.Auroras) do
		local rowTxt = p._getSpellRow(processSpell('Auroras', i), false)
		result = result..rowTxt
	end
	result = result..'\r\n|}'
	return result
end

function p.getAncientTable(frame)
	local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
	result = result..'!!Requirements'
	result = result..'!!style="width:275px"|Description'
	result = result..'!!Runes'
	for i, spell in Shared.skpairs(MagicData.Ancient) do
		local rowTxt = p._getSpellRow(processSpell('Ancient', i), false)
		result = result..rowTxt
	end
	result = result..'\r\n|}'
	return result
end

function p.getAltSpellsTable(frame)
	local result = '{|class="wikitable sortable"\r\n!colspan="2"|Spell'
	result = result..'!!Requirements'
	result = result..'!!style="width:275px"|Description'
	result = result..'!!Experience'
	result = result..'!!Runes'
	local spellList = {}
	for i, spell in Shared.skpairs(MagicData.AltMagic) do
		local rowTxt = p._getSpellRow(processSpell('AltMagic', i), false)
		result = result..rowTxt
	end
	result = result..'\r\n|}'
	return result
end

return p