Module:Magic
From Melvor Idle
Revision as of 20:40, 10 May 2022 by Falterfire (talk | contribs) (Okay for real this should be a fix for standard magic spell linking)
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