Module:Magic: Difference between revisions

2,755 bytes removed ,  15 November 2023
m
Fix typo
(Excluding Runestone from the superior gems list when determining which spells can make an item)
m (Fix typo)
 
(7 intermediate revisions by the same user not shown)
Line 5: Line 5:
local GameData = require('Module:GameData')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local SkillData = GameData.skillData
local Common = require('Module:Common')
local Attacks = require('Module:Attacks')
local Attacks = require('Module:Attacks')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
Line 10: Line 11:


p.spellBooks = {
p.spellBooks = {
    { id = 'standard', dataID = 'standardSpells', name = 'Standard Magic', imgType = 'spell' },
{ id = 'standard', dataID = 'standardSpells', name = 'Standard Magic', imgType = 'spell' },
    { id = 'ancient', dataID = 'ancientSpells', name = 'Ancient Magick', imgType = 'spell' },
{ id = 'ancient', dataID = 'ancientSpells', name = 'Ancient Magick', imgType = 'spell' },
    { id = 'archaic', dataID = 'archaicSpells', name = 'Archaic Magick', imgType = 'spell' },
{ id = 'archaic', dataID = 'archaicSpells', name = 'Archaic Magick', imgType = 'spell' },
    { id = 'curse', dataID = 'curseSpells', name = 'Curse', imgType = 'curse' },
{ id = 'curse', dataID = 'curseSpells', name = 'Curse', imgType = 'curse' },
    { id = 'aurora', dataID = 'auroraSpells', name = 'Aurora', imgType = 'aurora' },
{ id = 'aurora', dataID = 'auroraSpells', name = 'Aurora', imgType = 'aurora' },
    { id = 'altMagic', dataID = 'altSpells', name = 'Alt. Magic', imgType = 'spell', dataBySkill = true }
{ id = 'altMagic', dataID = 'altSpells', name = 'Alt. Magic', imgType = 'spell', dataBySkill = true }
}
}


p.spellBookIndex = {}
p.spellBookIndex = {}
for i, spellBook in ipairs(p.spellBooks) do
for i, spellBook in ipairs(p.spellBooks) do
    p.spellBookIndex[spellBook.id] = i
p.spellBookIndex[spellBook.id] = i
end
end


function p.getSpellBookID(sectionName)
function p.getSpellBookID(sectionName)
    if sectionName == 'Spell' or sectionName == 'Standard' then
if sectionName == 'Spell' or sectionName == 'Standard' then
        return 'standard'
return 'standard'
    elseif sectionName == 'Ancient' then
elseif sectionName == 'Ancient' then
        return 'ancient'
return 'ancient'
    elseif sectionName == 'Archaic' then
elseif sectionName == 'Archaic' then
        return 'archaic'
return 'archaic'
    elseif sectionName == 'Curse' then
elseif sectionName == 'Curse' then
        return 'curse'
return 'curse'
    elseif sectionName == 'Aurora' then
elseif sectionName == 'Aurora' then
        return 'aurora'
return 'aurora'
    elseif Shared.contains({'Alt Magic', 'Alt. Magic', 'Alternative Magic'}, sectionName) then
elseif Shared.contains({'Alt Magic', 'Alt. Magic', 'Alternative Magic'}, sectionName) then
        return 'altMagic'
return 'altMagic'
    else
else
        return sectionName
return sectionName
    end
end
end
end


-- Retrieves all spells within the given spellbook
-- Retrieves all spells within the given spellbook
function p.getSpellsBySpellBook(spellBookID)
function p.getSpellsBySpellBook(spellBookID)
    if type(spellBookID) == 'string' then
if type(spellBookID) == 'string' then
        local spellBook = GameData.getEntityByID(p.spellBooks, spellBookID)
local spellBook = GameData.getEntityByID(p.spellBooks, spellBookID)
        if spellBook ~= nil then
if spellBook ~= nil then
            if spellBook.dataBySkill then
if spellBook.dataBySkill then
                -- Data is part of the Magic skill object
-- Data is part of the Magic skill object
                local magicData = GameData.getSkillData('melvorD:Magic')
local magicData = GameData.getSkillData('melvorD:Magic')
                if magicData ~= nil then
if magicData ~= nil then
                    return magicData[spellBook.dataID]
return magicData[spellBook.dataID]
                end
end
            else
else
                -- Data is at the root of GameData
-- Data is at the root of GameData
                return GameData.rawData[spellBook.dataID]
return GameData.rawData[spellBook.dataID]
            end
end
        end
end
    end
end
end
end


Line 64: Line 65:
name = Shared.fixPagename(name)
name = Shared.fixPagename(name)


    for i, spellBook in ipairs(p.spellBooks) do
for i, spellBook in ipairs(p.spellBooks) do
        if spellBookID == nil or spellBookID == spellBook.id then
if spellBookID == nil or spellBookID == spellBook.id then
            local spells = p.getSpellsBySpellBook(spellBook.id)
local spells = p.getSpellsBySpellBook(spellBook.id)
            local spell = GameData.getEntityByName(spells, name)
local spell = GameData.getEntityByName(spells, name)
            if spell ~= nil then
if spell ~= nil then
                return spell
return spell
            end
end
        end
end
    end
end
end
end


Line 78: Line 79:
local spellBookID = p.getSpellBookID(spellType)
local spellBookID = p.getSpellBookID(spellType)


    if spellType == nil or spellBookID ~= nil then
if spellType == nil or spellBookID ~= nil then
        for i, spellBook in ipairs(p.spellBooks) do
for i, spellBook in ipairs(p.spellBooks) do
            if spellType == nil or spellBookID == spellBook.id then
if spellType == nil or spellBookID == spellBook.id then
                if spellBook.dataBySkill then
if spellBook.dataBySkill then
                    return GameData.getEntityByID(p.getSpellsBySpellBook(spellBook.id), id)
return GameData.getEntityByID(p.getSpellsBySpellBook(spellBook.id), id)
                else
else
                    return GameData.getEntityByID(spellBook.dataID, id)
return GameData.getEntityByID(spellBook.dataID, id)
                end
end
            end
end
        end
end
    end
end
end
end


Line 96: Line 97:
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[[Category:Pages with script errors]]"
return Shared.printError('No spell named "' .. spellName .. '" exists in the data module')
end
end
Line 103: Line 104:


function p.getTypeString(spellType)
function p.getTypeString(spellType)
    local spellBookID = p.getSpellBookID(spellType)
local spellBookID = p.getSpellBookID(spellType)
    if spellBookID ~= nil then
if spellBookID ~= nil then
        local spellBook = GameData.getEntityByID(p.spellBooks, spellBookID)
local spellBook = GameData.getEntityByID(p.spellBooks, spellBookID)
        if spellBook ~= nil then
if spellBook ~= nil then
            return spellBook.name
return spellBook.name
        end
end
    end
end
end
end


function p._getSpellIconType(spell)
function p._getSpellIconType(spell)
    local spellBook = GameData.getEntityByID(p.spellBooks, spell.spellBook)
local spellBook = GameData.getEntityByID(p.spellBooks, spell.spellBook)
    if spellBook == nil then
if spellBook == nil then
        -- Pick a suitable default
-- Pick a suitable default
        return 'spell'
return 'spell'
    else
else
        return spellBook.imgType
return spellBook.imgType
    end
end
end
end


Line 134: Line 135:
function p._getSpellIcon(spell, size)
function p._getSpellIcon(spell, size)
if size == nil then size = 50 end
if size == nil then size = 50 end
    local imgType = p._getSpellIconType(spell)
local imgType = p._getSpellIconType(spell)
    return Icons.Icon({spell.name, type=imgType, notext=true, size=size})
return Icons.Icon({spell.name, type=imgType, notext=true, size=size})
end
end


function p._getSpellRequirements(spell)
function p._getSpellRequirements(spell)
    -- All spells have a Magic level requirement
-- All spells have a Magic level requirement
    local resultPart = { Icons._SkillReq('Magic', spell.level) }
local extraReqs = {
{ ['type'] = 'SkillLevel', ['skillID'] = 'melvorD:Magic', ['level'] = spell.level }
}
if spell.requiredItemID ~= nil then
table.insert(extraReqs, { ['type'] = 'SlayerItem', ['itemID'] = spell.requiredItemID })
end


    if spell.requiredItemID ~= nil then
local resultPart = {}
        local item = Items.getItemByID(spell.requiredItemID)
for i, reqs in ipairs({ extraReqs, spell.requirements }) do
        if item ~= nil then
local reqStr = Common.getRequirementString(reqs)
            table.insert(resultPart, Icons.Icon({item.name, type='item', notext=true}) .. ' Equipped')
if reqStr ~= nil then
        end
table.insert(resultPart, reqStr)
    end
end
    if spell.requirements ~= nil then
end
        for i, req in ipairs(spell.requirements) do
            if req.type == 'DungeonCompletion' then
                local dung = GameData.getEntityByID('dungeons', req.dungeonID)
                if dung ~= nil then
                    table.insert(resultPart, Icons.Icon({dung.name, type='dungeon', qty=req.count, notext=true}) .. ' Clears')
                end
            elseif req.type == 'MonsterKilled' then
                local monster = GameData.getEntityByID('monsters', req.monsterID)
                if monster ~= nil then
                    table.insert(resultPart, Icons.Icon({monster.name, type='monster', qty=req.count, notext=true}) .. ' Kills')
                end
            else
                table.insert(resultPart, 'ERROR: Unknown requirement: ' .. (req.type or 'nil') .. '[[Category:Pages with script errors]]')
            end
        end
    end


    return table.concat(resultPart, '<br/>')
if Shared.tableIsEmpty(resultPart) then
return 'None'
else
return table.concat(resultPart, '<br/>')
end
end
end


local function formatRuneList(runes)
local function formatRuneList(runes)
    local runeList = {}
local runeList = {}
    for i, req in ipairs(runes) do
for i, req in ipairs(runes) do
        local rune = Items.getItemByID(req.id)
local rune = Items.getItemByID(req.id)
        if rune ~= nil then
if rune ~= nil then
            table.insert(runeList, Icons.Icon({rune.name, type='item', notext=true, qty=req.quantity}))
table.insert(runeList, Icons.Icon({rune.name, type='item', notext=true, qty=req.quantity}))
        end
end
    end
end
    return table.concat(runeList, ', ')  
return table.concat(runeList, ', ')  
end
end


Line 200: Line 194:
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 Shared.printError('No spell named "' .. spellName .. '" exists in the data module')
end
end
return p._getSpellItems(spell)
return p._getSpellItems(spell)
Line 222: Line 216:
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 Shared.printError('No spell named "' .. spellName .. '" exists in the data module')
end
end
return p._getSpellRunes(spell)
return p._getSpellRunes(spell)
Line 229: Line 223:
-- Generates description template data. See: altMagic.js, description()
-- Generates description template data. See: altMagic.js, description()
function p._getSpellTemplateData(spell)
function p._getSpellTemplateData(spell)
    local templateData = nil
local templateData = nil
    if spell.spellBook == 'altMagic' then
if spell.spellBook == 'altMagic' then
        if spell.produces ~= nil then
if spell.produces ~= nil then
            -- Item produced varies depending on items consumed
-- Item produced varies depending on items consumed
            if spell.produces == 'Bar' then
if spell.produces == 'Bar' then
                templateData = {
templateData = {
                    ["barAmount"] = spell.productionRatio,
["barAmount"] = spell.productionRatio,
                    ["oreAmount"] = spell.specialCost.quantity
["oreAmount"] = spell.specialCost.quantity
                }
}
            elseif spell.produces == 'GP' then
elseif spell.produces == 'GP' then
                templateData = {
templateData = {
                    ["percent"] = spell.productionRatio * 100
["percent"] = spell.productionRatio * 100
                }
}
            else
else
                local itemProduced = Items.getItemByID(spell.produces)
local itemProduced = Items.getItemByID(spell.produces)
                if itemProduced ~= nil and itemProduced.prayerPoints ~= nil and type(spell.fixedItemCosts) == 'table' then
local spellNS, spellLocalID = GameData.getLocalID(spell.id)
                    -- Item produced is a bone
if itemProduced ~= nil and itemProduced.prayerPoints ~= nil and type(spell.fixedItemCosts) == 'table' and Shared.tableCount(spell.fixedItemCosts) == 1 and spellNS ~= 'melvorAoD' then
                    local costItem = Items.getItemByID(spell.fixedItemCosts[1].id)
-- Item produced is a bone and spell is not from AoD (logic from altMagic.js)
                    if costItem ~= nil then
local costItem = Items.getItemByID(spell.fixedItemCosts[1].id)
                        templateData = {
if costItem ~= nil then
                            ["itemName"] = costItem.name,
templateData = {
                            ["qty1"] = spell.fixedItemCosts[1].quantity,
["itemName"] = costItem.name,
                            ["qty2"] = itemProduced.prayerPoints
["qty1"] = spell.fixedItemCosts[1].quantity,
                        }
["qty2"] = itemProduced.prayerPoints
                    end
}
                end
end
            end
end
        end
end
        if templateData == nil then
end
            templateData = {
if templateData == nil then
                ["amount"] = spell.productionRatio,
templateData = {
                ["percent"] = spell.productionRatio * 100,
["amount"] = spell.productionRatio,
                ["specialCostQty"] = spell.specialCost.quantity
["percent"] = spell.productionRatio * 100,
            }
["specialCostQty"] = spell.specialCost.quantity
}
if type(spell.fixedItemCosts) == 'table' then
if type(spell.fixedItemCosts) == 'table' then
for i, fixedCost in ipairs(spell.fixedItemCosts) do
for i, fixedCost in ipairs(spell.fixedItemCosts) do
Line 272: Line 267:
end
end
end
end
        end
end
    end
end
    return (templateData or {})
return (templateData or {})
end
end


Line 281: Line 276:
local connector = inline and '<br/>' or ' and '
local connector = inline and '<br/>' or ' and '
if spell.description ~= nil then
if spell.description ~= nil then
        return Shared.applyTemplateData(spell.description, p._getSpellTemplateData(spell))
return Shared.applyTemplateData(spell.description, p._getSpellTemplateData(spell))
    elseif spell.modifiers ~= nil or spell.targetModifiers ~= nil then
elseif spell.modifiers ~= nil or spell.targetModifiers ~= nil then
        local resultPart = {}
local resultPart = {}
        if spell.modifiers ~= nil then
if spell.modifiers ~= nil then
            table.insert(resultPart, Constants.getModifiersText(spell.modifiers, false))
table.insert(resultPart, Constants.getModifiersText(spell.modifiers, false))
        end
end
        if spell.targetModifiers ~= nil then
if spell.targetModifiers ~= nil then
            local targetModText = Constants.getModifiersText(spell.targetModifiers, false, inline)
local targetModText = Constants.getModifiersText(spell.targetModifiers, false, inline)
            if inline then  
if inline then  
            table.insert(resultPart, targetModText)
table.insert(resultPart, targetModText)
            else
else
            table.insert(resultPart, 'Enemies are inflicted with:<br/>' .. targetModText)
table.insert(resultPart, 'Enemies are inflicted with:<br/>' .. targetModText)
            end
end
        end
end
        return table.concat(resultPart, connector)
return table.concat(resultPart, connector)
    elseif spell.specialAttackID ~= nil or spell.specialAttack ~= nil then
elseif spell.specialAttackID ~= nil or spell.specialAttack ~= nil then
    local spAtt = Attacks.getAttackByID(spell.specialAttackID or spell.specialAttack)
local spAtt = Attacks.getAttackByID(spell.specialAttackID or spell.specialAttack)
    if spAtt ~= nil then
if spAtt ~= nil then
    return spAtt.description
return spAtt.description
    end
end
    elseif spell.spellBook == 'standard' then
elseif spell.spellBook == 'standard' then
        return 'Combat spell with a max hit of ' .. Shared.formatnum(spell.maxHit * 10)
return 'Combat spell with a max hit of ' .. Shared.formatnum(spell.maxHit * 10)
    else
else
        return ''
return ''
    end
end
end
end


Line 322: Line 317:
return p.getTypeString(spell.spellBook)
return p.getTypeString(spell.spellBook)
elseif stat == 'spellDamage' then
elseif stat == 'spellDamage' then
if spell.maxHit ~= undefined then  
if spell.maxHit ~= nil then  
return spell.maxHit * 10
return spell.maxHit * 10
else
else
Line 336: Line 331:
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 Shared.printError('No spell named "' .. spellName .. '" exists in the data module')
end
end
return p._getSpellStat(spell, statName)
return p._getSpellStat(spell, statName)
Line 345: Line 340:
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 Shared.printError('No spell named "' .. spellName .. '" exists in the data module')
end
end


Line 370: Line 365:
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 Shared.printError('No spell named "' .. spellName .. '" exists in the data module')
end
end
return p._getSpellCategories(spell)
return p._getSpellCategories(spell)
Line 376: Line 371:


function p._getAltSpellCostText(spell)
function p._getAltSpellCostText(spell)
    if spell.specialCost ~= nil then
if spell.specialCost ~= nil then
        local costType = spell.specialCost.type
local costType = spell.specialCost.type
        if costType == nil or costType == 'None' then
if costType == nil or costType == 'None' then
            if type(spell.fixedItemCosts) == 'table' then
if type(spell.fixedItemCosts) == 'table' then
                local costText = {}
local costText = {}
                for i, itemCost in ipairs(spell.fixedItemCosts) do
for i, itemCost in ipairs(spell.fixedItemCosts) do
                    local item = Items.getItemByID(itemCost.id)
local item = Items.getItemByID(itemCost.id)
                    if item ~= nil then
if item ~= nil then
                        table.insert(costText, Icons.Icon({item.name, type='item', qty=itemCost.quantity}))
table.insert(costText, Icons.Icon({item.name, type='item', qty=itemCost.quantity}))
                    end
end
                end
end
                if not Shared.tableIsEmpty(costText) then
if not Shared.tableIsEmpty(costText) then
                    return table.concat(costText, ', ')
return table.concat(costText, ', ')
                end
end
            else
else
                return nil
return nil
            end
end
        else
else
            local qty = Shared.formatnum(spell.specialCost.quantity)
local qty = Shared.formatnum(spell.specialCost.quantity)
            local typeString = {
local typeString = {
                ['AnyItem'] = qty .. ' of any item',
['AnyItem'] = qty .. ' of any item',
                ['BarIngredientsWithCoal'] = qty .. ' x required ores for the chosen bar',
['BarIngredientsWithCoal'] = qty .. ' x required ores for the chosen bar',
                ['BarIngredientsWithoutCoal'] = qty .. ' x required ores (except ' .. Icons.Icon({'Coal Ore', type='item'}) .. ') for the chosen bar',
['BarIngredientsWithoutCoal'] = qty .. ' x required ores (except ' .. Icons.Icon({'Coal Ore', type='item'}) .. ') for the chosen bar',
                ['JunkItem'] = qty .. ' of any [[Fishing#Junk|Junk]] item',
['JunkItem'] = qty .. ' of any [[Fishing#Junk|Junk]] item',
                ['SuperiorGem'] = qty .. ' of any superior gem',
['SuperiorGem'] = qty .. ' of any superior gem',
                ['AnyNormalFood'] = qty .. ' x non-perfect food'
['AnyNormalFood'] = qty .. ' x non-perfect food'
            }
}
            return typeString[costType]
return typeString[costType]
        end
end
    end
end
end
end


function p.getSpellsProducingItem(itemID)
function p.getSpellsProducingItem(itemID)
    -- Only need to check Alt. Magic spells
-- Only need to check Alt. Magic spells
    local spellList = {}
local spellList = {}


    -- Classify whether the item fits into various categories
-- Classify whether the item fits into various categories
    local isBar, isShard, isGem, isSuperiorGem, isPerfectFood = false, false, false, false, false
local isBar, isShard, isGem, isSuperiorGem, isPerfectFood = false, false, false, false, false
    local item = Items.getItemByID(itemID)
local item = Items.getItemByID(itemID)
    if item ~= nil then
if item ~= nil then
        isBar = item.type == 'Bar'
isBar = not Shared.tableIsEmpty(GameData.getEntities(SkillData.Smithing.recipes,
        isShard = GameData.getEntityByProperty(SkillData.Magic.randomShards, 'itemID', item.id) ~= nil
function(recipe)
        isGem = GameData.getEntityByProperty('randomGems', 'itemID', itemID) ~= nil
return recipe.categoryID == 'melvorD:Bars' and recipe.productID == item.id
        --Runestone can't be created by Alt Magic spells that make random superior gems.
end))
        isSuperiorGem = item.type == 'Superior Gem' and item.id ~= SkillData.Mining.runestoneItemID
isShard = GameData.getEntityByProperty(SkillData.Magic.randomShards, 'itemID', item.id) ~= nil
        if item.healsFor ~= nil then
isGem = GameData.getEntityByProperty('randomGems', 'itemID', itemID) ~= nil
            -- Item is food, but is it a product of perfect cooking?
--Runestone can't be created by Alt Magic spells that make random superior gems.
            local cookData = GameData.getSkillData('melvorD:Cooking')
isSuperiorGem = item.type == 'Superior Gem' and item.id ~= SkillData.Mining.runestoneItemID
            if cookData ~= nil and cookData.recipes ~= nil then
if item.healsFor ~= nil then
                isPerfectFood = GameData.getEntityByProperty(cookData.recipes, 'perfectCookID', itemID) ~= nil
-- Item is food, but is it a product of perfect cooking?
            end
local cookData = GameData.getSkillData('melvorD:Cooking')
        end
if cookData ~= nil and cookData.recipes ~= nil then
    end
isPerfectFood = GameData.getEntityByProperty(cookData.recipes, 'perfectCookID', itemID) ~= nil
end
end
end


    for i, spell in ipairs(p.getSpellsBySpellBook('altMagic')) do
for i, spell in ipairs(p.getSpellsBySpellBook('altMagic')) do
        local includeSpell = false
local includeSpell = false
        if spell.produces ~= nil then
if spell.produces ~= nil then
            if spell.produces == itemID then
if spell.produces == itemID then
                includeSpell = true
includeSpell = true
            else
else
                includeSpell = ((isBar and spell.produces == 'Bar') or
includeSpell = ((isBar and spell.produces == 'Bar') or
                    (isShard and spell.produces == 'RandomShards') or
(isShard and spell.produces == 'RandomShards') or
                    (isGem and spell.produces == 'RandomGem') or
(isGem and spell.produces == 'RandomGem') or
                    (isSuperiorGem and spell.produces == 'RandomSuperiorGem') or
(isSuperiorGem and spell.produces == 'RandomSuperiorGem') or
                    (isPerfectFood and spell.produces == 'PerfectFood'))
(isPerfectFood and spell.produces == 'PerfectFood'))
            end
end
            if includeSpell then
if includeSpell then
                table.insert(spellList, spell)
table.insert(spellList, spell)
            end
end
        end
end
    end
end


    table.sort(spellList, function(a, b) return a.level < b.level end)
table.sort(spellList, function(a, b) return a.level < b.level end)
return spellList
return spellList
end
end
Line 458: Line 456:
includeConsumes = false
includeConsumes = false
end
end
    local runeKeys = { 'runesRequired', 'runesRequiredAlt' }
local runeKeys = { 'runesRequired', 'runesRequiredAlt' }
    local spellList = {}
local spellList = {}
   
    -- Initialize some vars & only populate if we're including resource consumptions
-- Initialize some vars & only populate if we're including resource consumptions
    local isJunkItem, isSuperiorGem, isNormalFood, isCoal, isBarIngredient = false, false, false, false, false
local isJunkItem, isSuperiorGem, isNormalFood, isCoal, isBarIngredient = false, false, false, false, false
    if includeConsumes then
if includeConsumes then
        local thisItem = Items.getItemByID(itemID)
local thisItem = Items.getItemByID(itemID)
        local junkItemIDs = GameData.getSkillData('melvorD:Fishing').junkItemIDs
local junkItemIDs = GameData.getSkillData('melvorD:Fishing').junkItemIDs
        isJunkItem = Shared.contains(junkItemIDs, itemID)
isJunkItem = Shared.contains(junkItemIDs, itemID)
        isSuperiorGem = thisItem.type == 'Superior Gem'
isSuperiorGem = thisItem.type == 'Superior Gem'
        if thisItem.healsFor ~= nil then
if thisItem.healsFor ~= nil then
            -- Item is food, but is it from cooking & is it normal or perfect?
-- Item is food, but is it from cooking & is it normal or perfect?
            local cookData = GameData.getSkillData('melvorD:Cooking')
local cookData = GameData.getSkillData('melvorD:Cooking')
            if cookData ~= nil and cookData.recipes ~= nil then
if cookData ~= nil and cookData.recipes ~= nil then
                isNormalFood = GameData.getEntityByProperty(cookData.recipes, 'productID', itemID) ~= nil
isNormalFood = GameData.getEntityByProperty(cookData.recipes, 'productID', itemID) ~= nil
            end
end
        end
end
        isCoal = itemID == 'melvorD:Coal_Ore'
isCoal = itemID == 'melvorD:Coal_Ore'
        if not isCoal then
if not isCoal then
            -- Don't need to check if the item is another bar ingredient if we already know it is coal
-- Don't need to check if the item is another bar ingredient if we already know it is coal
            local smithingRecipes = GameData.getSkillData('melvorD:Smithing').recipes
local smithingRecipes = GameData.getSkillData('melvorD:Smithing').recipes
            for i, recipe in ipairs(smithingRecipes) do
for i, recipe in ipairs(smithingRecipes) do
                if recipe.categoryID == 'melvorD:Bars' then
if recipe.categoryID == 'melvorD:Bars' then
                    for k, itemCost in ipairs(recipe.itemCosts) do
for k, itemCost in ipairs(recipe.itemCosts) do
                        if itemCost.id == itemID then
if itemCost.id == itemID then
                            isBarIngredient = true
isBarIngredient = true
                            break
break
                        end
end
                    end
end
                    if isBarIngredient then
if isBarIngredient then
                        break
break
                    end
end
                end
end
            end
end
        end
end
    end
end


    -- Find applicable spells
-- Find applicable spells
    for i, spellBook in ipairs(p.spellBooks) do
for i, spellBook in ipairs(p.spellBooks) do
        local spells = p.getSpellsBySpellBook(spellBook.id)
local spells = p.getSpellsBySpellBook(spellBook.id)
        for j, spell in ipairs(spells) do
for j, spell in ipairs(spells) do
        local foundSpell = false
local foundSpell = false
            -- Check runes first
-- Check runes first
            for k, runeKey in ipairs(runeKeys) do
for k, runeKey in ipairs(runeKeys) do
                if spell[runeKey] ~= nil then
if spell[runeKey] ~= nil then
                    for m, req in ipairs(spell[runeKey]) do
for m, req in ipairs(spell[runeKey]) do
                        if req.id == itemID then
if req.id == itemID then
                            foundSpell = true
foundSpell = true
                            break
break
                        end
end
                    end
end
                end
end
                if foundSpell then
if foundSpell then
                    break
break
                end
end
            end
end
            if includeConsumes and not foundSpell then
if includeConsumes and not foundSpell then
                -- Check items consumed by the spell
-- Check items consumed by the spell
                -- Fixed costs first, as that is a well-defined list of item IDs
-- Fixed costs first, as that is a well-defined list of item IDs
                if spell.fixedItemCosts ~= nil then
if spell.fixedItemCosts ~= nil then
                    for k, itemCost in ipairs(spell.fixedItemCosts) do
for k, itemCost in ipairs(spell.fixedItemCosts) do
                        if itemCost.id == itemID then
if itemCost.id == itemID then
                            foundSpell = true
foundSpell = true
                            break
break
                        end
end
                    end
end
                end
end
                if not foundSpell and spell.specialCost ~= nil then
if not foundSpell and spell.specialCost ~= nil then
                    local costType = spell.specialCost.type
local costType = spell.specialCost.type
                    foundSpell = (isJunkItem and costType == 'JunkItem') or
foundSpell = (isJunkItem and costType == 'JunkItem') or
                        (isSuperiorGem and costType == 'AnySuperiorGem') or
(isSuperiorGem and costType == 'AnySuperiorGem') or
                        (isNormalFood and costType == 'AnyNormalFood') or
(isNormalFood and costType == 'AnyNormalFood') or
                        ((isCoal or isBarIngredient) and costType == 'BarIngredientsWithCoal') or
((isCoal or isBarIngredient) and costType == 'BarIngredientsWithCoal') or
                        (isBarIngredient and costType == 'BarIngredientsWithoutCoal')
(isBarIngredient and costType == 'BarIngredientsWithoutCoal')
                end
end
            end
end
           
            if foundSpell then
if foundSpell then
                table.insert(spellList, spell)
table.insert(spellList, spell)
            end
end
        end
end
    end
end


    table.sort(spellList, function(a, b)
table.sort(spellList, function(a, b)
        if a.spellBook ~= b.spellBook then
if a.spellBook ~= b.spellBook then
            return p.spellBookIndex[a.spellBook] < p.spellBookIndex[b.spellBook]
return p.spellBookIndex[a.spellBook] < p.spellBookIndex[b.spellBook]
        else
else
            return a.level < b.level
return a.level < b.level
        end
end
    end)
end)
return spellList
return spellList
end
end
Line 557: Line 555:


function p.getSpellTypeLink(spellBookID)
function p.getSpellTypeLink(spellBookID)
    if spellBookID == 'standard' then
if spellBookID == 'standard' then
return Icons.Icon({'Standard Magic', 'Standard', img='Standard', type='spellType'})
return Icons.Icon({'Standard Magic', 'Standard', img='Standard', type='spellType'})
    elseif spellBookID == 'ancient' then
elseif spellBookID == 'ancient' then
        return Icons.Icon({'Ancient Magicks', 'Ancient', img='Ancient', type='spellType'})
return Icons.Icon({'Ancient Magicks', 'Ancient', img='Ancient', type='spellType'})
    elseif spellBookID == 'archaic' then
elseif spellBookID == 'archaic' then
        return Icons.Icon({'Archaic Magicks', 'Archaic', img='Archaic', type='spellType'})
return Icons.Icon({'Archaic Magicks', 'Archaic', img='Archaic', type='spellType'})
    elseif spellBookID == 'curse' then
elseif spellBookID == 'curse' then
return Icons.Icon({'Curses', 'Curse', img='Curse', type='spellType'})
return Icons.Icon({'Curses', 'Curse', img='Curse', type='spellType'})
    elseif spellBookID == 'aurora' then
elseif spellBookID == 'aurora' then
return Icons.Icon({'Auroras', 'Aurora', img='Aurora', type='spellType'})
return Icons.Icon({'Auroras', 'Aurora', img='Aurora', type='spellType'})
    elseif spellBookID == 'altMagic' then
elseif spellBookID == 'altMagic' then
return Icons.Icon({'Alt. Magic', type='skill'})
return Icons.Icon({'Alt. Magic', type='skill'})
end
end
Line 595: Line 593:


function p._getSpellRow(spell, includeTypeColumn, includeItems, includeDamage, includeExperience)
function p._getSpellRow(spell, includeTypeColumn, includeItems, includeDamage, includeExperience)
    local spellBookIdx = p.spellBookIndex[spell.spellBook]
local spellBookIdx = p.spellBookIndex[spell.spellBook]
    local spellBook = p.spellBooks[spellBookIdx]
local spellBook = p.spellBooks[spellBookIdx]
    local rowPart = {'\n|-\n|data-sort-value="' .. spell.name .. '"| '}
local rowPart = {'\n|-\n|data-sort-value="' .. spell.name .. '"| '}
    table.insert(rowPart, Icons.Icon({spell.name, type=spellBook.imgType, notext=true, size=50}))
table.insert(rowPart, Icons.Icon({spell.name, type=spellBook.imgType, notext=true, size=50}))
table.insert(rowPart, '|| ' .. Icons.getExpansionIcon(spell.id) .. Icons.Icon({spell.name, type=spellBook.imgType, noicon=true}))
table.insert(rowPart, '|| ' .. Icons.getExpansionIcon(spell.id) .. Icons.Icon({spell.name, type=spellBook.imgType, noicon=true}))
if includeTypeColumn then
if includeTypeColumn then
Line 620: Line 618:
local hits = spAtt.attackCount ~= nil and spAtt.attackCount or 1
local hits = spAtt.attackCount ~= nil and spAtt.attackCount or 1
if interval ~= nil and hits > 1 then
if interval ~= nil and hits > 1 then
            table.insert(rowPart, '<br/>(' .. Shared.round(interval / 1000, 2, 2) .. 's delay between attacks.')
table.insert(rowPart, '<br/>(' .. Shared.round(interval / 1000, 2, 2) .. 's delay between attacks.')
if hits > 2 then
if hits > 2 then
table.insert(rowPart, ' ' .. Shared.round(interval * (hits - 1) / 1000, 2, 2) .. 's total duration.')
table.insert(rowPart, ' ' .. Shared.round(interval * (hits - 1) / 1000, 2, 2) .. 's total duration.')
Line 657: Line 655:
end
end


local spellListSorted = Shared.shallowClone(spellList)
table.sort(spellListSorted, function(a, b) return a.level < b.level end)
local resultPart = {p._getSpellHeader(includeSpellbook, includeItems, includeDamage, includeExperience)}
local resultPart = {p._getSpellHeader(includeSpellbook, includeItems, includeDamage, includeExperience)}
for i, spell in ipairs(spellList) do
for i, spell in ipairs(spellListSorted) do
table.insert(resultPart, p._getSpellRow(spell, includeSpellbook, includeItems, includeDamage, includeExperience))
table.insert(resultPart, p._getSpellRow(spell, includeSpellbook, includeItems, includeDamage, includeExperience))
end
end


table.insert(resultPart, '\n|}')
table.insert(resultPart, '\n|}')
        return table.concat(resultPart)
return table.concat(resultPart)
end
end
end
end
Line 676: Line 676:
local spell = p.getSpell(spellName)
local spell = p.getSpell(spellName)
if spell == nil then
if spell == nil then
return 'ERROR: Unknown spell "' .. spellName .. '"[[Category:Pages with script errors]]'
return Shared.printError('No spell named "' .. spellName .. '" exists in the data module')
else
else
table.insert(spellList, spell)
table.insert(spellList, spell)
Line 692: Line 692:
-- Included below for backwards compatibility
-- Included below for backwards compatibility
function p.getStandardSpellsTable(frame)
function p.getStandardSpellsTable(frame)
    return p._getSpellTable(p.getSpellsBySpellBook('standard'), false)
return p._getSpellTable(p.getSpellsBySpellBook('standard'), false)
end
end


function p.getAncientTable(frame)
function p.getAncientTable(frame)
    return p._getSpellTable(p.getSpellsBySpellBook('ancient'), false)
return p._getSpellTable(p.getSpellsBySpellBook('ancient'), false)
end
end


function p.getCurseTable(frame)
function p.getCurseTable(frame)
    return p._getSpellTable(p.getSpellsBySpellBook('curse'), false)
return p._getSpellTable(p.getSpellsBySpellBook('curse'), false)
end
end


function p.getAuroraTable(frame)
function p.getAuroraTable(frame)
    return p._getSpellTable(p.getSpellsBySpellBook('aurora'), false)
return p._getSpellTable(p.getSpellsBySpellBook('aurora'), false)
end
end


function p.getAltSpellsTable(frame)
function p.getAltSpellsTable(frame)
    return p._getSpellTable(p.getSpellsBySpellBook('altMagic'), false)
return p._getSpellTable(p.getSpellsBySpellBook('altMagic'), false)
end
end


return p
return p