Module:Items/ComparisonTables: Difference between revisions

no edit summary
(Fix check for empty special attacks table)
No edit summary
(43 intermediate revisions by 4 users not shown)
Line 1: Line 1:
local p = {}
local p = {}


local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local GameData = require('Module:GameData')
local Common = require('Module:Common')
local Modifiers = require('Module:Modifiers')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Items = require('Module:Items')
local Num = require('Module:Number')


local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}
local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}


local styleOverrides = {
local styleOverrides = {
Melee = {'Slayer Helmet (Basic)', 'Slayer Platebody (Basic)', 'Paladin Gloves', 'Desert Wrappings', 'Almighty Lute', 'Candy Cane', 'Bob's Rake'},
Melee = {'Slayer Helmet (Basic)', 'Slayer Platebody (Basic)', 'Paladin Gloves', 'Desert Wrappings', 'Almighty Lute', 'Candy Cane', "Bob's Rake", "Knight's Defender", "Ward of Flame Platebody"},
Ranged = {'Slayer Cowl (Basic)', 'Slayer Leather Body (Basic)', 'Ice Arrows'},
Ranged = {'Slayer Cowl (Basic)', 'Slayer Leather Body (Basic)', 'Ice Arrows'},
Magic = {'Slayer Wizard Hat (Basic)', 'Slayer Wizard Robes (Basic)', 'Enchanted Shield', 'Elementalist Gloves'},
Magic = {'Slayer Wizard Hat (Basic)', 'Slayer Wizard Robes (Basic)', 'Enchanted Shield', 'Elementalist Gloves', 'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
None = {},
None = {},
NotMelee = {'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
NotRanged = {},
NotMagic = {'Torrential Blast Crossbow', 'Spectral Ice Sword', 'Lightning Strike 1H Sword', 'FrostSpark 1H Sword'}
}
}
local function getSlotID(slot)
-- If slot is a slot name, convert it to the slot ID instead
local slotID = Shared.getNamespacedID('melvorD', slot)
local slotData = GameData.getEntityByID('equipmentSlots', slotID)
-- Validate slotID
if slotData == nil then
-- slotID invalid, check if user provided a slot name
slotData = GameData.getEntityByProperty('equipmentSlots', 'emptyName', slot)
if slotData == nil then
return nil
end
slotID = slotData.id
end
return slotID
end
local function getItemDesc(item)
if item.customDescription ~= nil then
return item.customDescription
elseif item.modifiers ~= nil then
return Modifiers.getModifiersText(item.modifiers, false, false)
else
return ''
end
end


function p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
function p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
Line 22: Line 53:
--Getting some lists set up here that will be used later
--Getting some lists set up here that will be used later
--First, the list of columns used by both weapons & armour
--First, the list of columns used by both weapons & armour
local statColumns = {'stabAttackBonus', 'slashAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'meleeStrengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'meleeDefenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction', 'attackLevelRequired', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
local statColumns = {
'stabAttackBonus', 'slashAttackBonus', 'blockAttackBonus',  
'rangedAttackBonus', 'magicAttackBonus', 'meleeStrengthBonus',
'rangedStrengthBonus', 'magicDamageBonus', 'meleeDefenceBonus',
'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction',
'attackLevelRequired', 'strengthLevelRequired', 'defenceLevelRequired',
'rangedLevelRequired', 'magicLevelRequired'
}


if Shared.tableIsEmpty(itemList) then
if Shared.tableIsEmpty(itemList) then
return 'ERROR: you must select at least one item to get stats for[[Category:Pages with script errors]]'
return Shared.printError('You must select at least one item to get stats for')
end
end


Line 48: Line 86:
local strBonusCols = 2
local strBonusCols = 2
local defBonusCols = 3
local defBonusCols = 3
local lvlReqCols = 4
local lvlReqCols = 5
local ndx = 1
local ndx = 1
while Shared.tableCount(statColumns) >= ndx do
while Shared.tableCount(statColumns) >= ndx do
Line 73: Line 111:
end
end
if attBonusCols > 0 then
if attBonusCols > 0 then
table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Attack Bonus')
table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"|Attack Bonus')
end
end
if strBonusCols > 0 then
if strBonusCols > 0 then
table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Str. Bonus')
table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"|Str. Bonus')
end
end
if Shared.contains(statColumns, 'magicDamageBonus') then
if Shared.contains(statColumns, 'magicDamageBonus') then
table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Dmg Bonus')
table.insert(resultPart, '\r\n!colspan="1"|% Dmg Bonus')
end
end
if defBonusCols > 0 then
if defBonusCols > 0 then
table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"style="padding:0 0.5em 0 0.5em;"|Defence Bonus')
table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"|Defence Bonus')
end
end
if Shared.contains(statColumns, 'damageReduction') then
if Shared.contains(statColumns, 'damageReduction') then
table.insert(resultPart, '\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|DR')
table.insert(resultPart, '\r\n!colspan="1"|DR')
end
end
if lvlReqCols > 0 then
if lvlReqCols > 0 then
table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"style="padding:0 0.5em 0 0.5em;"|Lvl Req')
table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"|Lvl Req')
end
end
if includeModifiers and includeDescription then
if includeModifiers and includeDescription then
Line 97: Line 135:
--One header row down, one to go
--One header row down, one to go
table.insert(resultPart, '\r\n|-class="headerRow-1"')
table.insert(resultPart, '\r\n|-class="headerRow-1"')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Item')
table.insert(resultPart, '\r\n!Item')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Name')
table.insert(resultPart, '\r\n!Name')
--Weapons have Attack Speed here
--Weapons have Attack Speed here
if isWeaponType then
if isWeaponType then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed')
table.insert(resultPart, '\r\n!Attack Speed')
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?')
table.insert(resultPart, '\r\n!Two Handed?')
end
end
--Attack bonuses
--Attack bonuses
if Shared.contains(statColumns, 'slashAttackBonus') then
if Shared.contains(statColumns, 'slashAttackBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'stabAttackBonus') then
if Shared.contains(statColumns, 'stabAttackBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'blockAttackBonus') then
if Shared.contains(statColumns, 'blockAttackBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'rangedAttackBonus') then
if Shared.contains(statColumns, 'rangedAttackBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'magicAttackBonus') then
if Shared.contains(statColumns, 'magicAttackBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
end
end
--Strength bonuses
--Strength bonuses
if Shared.contains(statColumns, 'meleeStrengthBonus') then
if Shared.contains(statColumns, 'meleeStrengthBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'rangedStrengthBonus') then
if Shared.contains(statColumns, 'rangedStrengthBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'magicDamageBonus') then
if Shared.contains(statColumns, 'magicDamageBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
end
end
--Defence bonuses
--Defence bonuses
if Shared.contains(statColumns, 'meleeDefenceBonus') then
if Shared.contains(statColumns, 'meleeDefenceBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'rangedDefenceBonus') then
if Shared.contains(statColumns, 'rangedDefenceBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'magicDefenceBonus') then
if Shared.contains(statColumns, 'magicDefenceBonus') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'damageReduction') then
if Shared.contains(statColumns, 'damageReduction') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
end
--Level requirements
--Level requirements
if Shared.contains(statColumns, 'attackLevelRequired') then
if Shared.contains(statColumns, 'attackLevelRequired') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
end
if Shared.contains(statColumns, 'strengthLevelRequired') then
table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'defenceLevelRequired') then
if Shared.contains(statColumns, 'defenceLevelRequired') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'rangedLevelRequired') then
if Shared.contains(statColumns, 'rangedLevelRequired') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
end
end
if Shared.contains(statColumns, 'magicLevelRequired') then
if Shared.contains(statColumns, 'magicLevelRequired') then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'}))
table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
end
end
--If includeModifiers is set to 'true', add the Modifiers column
--If includeModifiers is set to 'true', add the Modifiers column
if includeModifiers then
if includeModifiers then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Modifiers')
table.insert(resultPart, '\r\n!Modifiers')
end
end
--If includeDescription is set to 'true', add the Description column
--If includeDescription is set to 'true', add the Description column
if includeDescription then
if includeDescription then
table.insert(resultPart, '\r\n!style="padding:0 1em 0 0.5em;"|Description')
table.insert(resultPart, '\r\n!Description')
end
end


Line 173: Line 214:
local atkSpeed = Items._getItemStat(item, 'attackSpeed', true)
local atkSpeed = Items._getItemStat(item, 'attackSpeed', true)
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|style="text-align: centre;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.round(atkSpeed / 1000, 3, 1) .. 's')
table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style="text-align:right;" |'..Num.round(atkSpeed / 1000, 3, 1) .. 's')
--That's the first list out of the way, now for 2-Handed
--That's the first list out of the way, now for 2-Handed
table.insert(resultPart, '\r\n| style ="text-align: right;"|')
table.insert(resultPart, '\r\n| style="text-align: right;"|')
table.insert(resultPart, Items._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')
table.insert(resultPart, Items._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')
for j, statName in pairs(statColumns) do
for j, statName in pairs(statColumns) do
local statValue = Items._getItemStat(item, statName, true)
local statValue = Items._getItemStat(item, statName, true)
table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
table.insert(resultPart, '\r\n| style="text-align:right;" class="')
if string.find(statName, '^(.+)LevelRequired$') == nil then
if string.find(statName, '^(.+)LevelRequired$') == nil then
if statValue > 0 then
if statValue > 0 then
table.insert(resultPart, 'background-color:lightgreen;')
table.insert(resultPart, 'table-positive')
elseif statValue < 0 then
elseif statValue < 0 then
table.insert(resultPart, 'background-color:lightpink;')
table.insert(resultPart, 'table-negative')
end
end
end
end
table.insert(resultPart, '"|'..Shared.formatnum(statValue))
table.insert(resultPart, '"|'..Num.formatnum(statValue))
if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
end
end
--If requested, add the item Modifiers
--If requested, add the item Modifiers
if includeModifiers then
if includeModifiers then
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
table.insert(resultPart, '\r\n| ')
local txtLines = {}
local txtLines = {}
local modTxt = Constants.getModifiersText(item.modifiers, true)
if item.modifiers ~= nil then
if modTxt ~= '' then
table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
table.insert(txtLines, modTxt)
end
end
--For items with a special attack, show the details
--For items with a special attack, show the details
if not Shared.tableIsEmpty(item.specialAttacks) then
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
table.insert(txtLines, "'''Special Attack:'''")
table.insert(txtLines, "'''Special Attack:'''")
for i, spAttID in ipairs(item.specialAttacks) do
for i, spAttID in ipairs(item.specialAttacks) do
local spAtt = GameData.getEntityByID('attacks', spAttID)
local spAtt = GameData.getEntityByID('attacks', spAttID)
table.insert(txtLines, spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
local attChance = spAtt.defaultChance
if item.overrideSpecialChances ~= nil then
attChance = item.overrideSpecialChances[i]
end
table.insert(txtLines, attChance .. '% chance for ' .. spAtt.name .. ':')
table.insert(txtLines, spAtt.description)
table.insert(txtLines, spAtt.description)
end
end
Line 213: Line 257:
--If requested, add description
--If requested, add description
if includeDescription then
if includeDescription then
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
table.insert(resultPart, '\r\n| ' .. getItemDesc(item))
table.insert(resultPart, item.customDescription ~= nil and item.customDescription or '')
end
end
else
else
--Building rows for armour
--Building rows for armour
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|'..Icons.Icon({(item.name or 'Unknown'), type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|'..Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
for j, statName in pairs(statColumns) do
for j, statName in pairs(statColumns) do
local statValue = Items._getItemStat(item, statName, true)
local statValue = Items._getItemStat(item, statName, true)
table.insert(resultPart, '\r\n| style ="text-align: right;padding: 0 0.5em 0 0;')
table.insert(resultPart, '\r\n|style="text-align:right;" class="')
if statValue > 0 then
if statValue > 0 then
table.insert(resultPart, 'background-color:lightgreen;')
table.insert(resultPart, 'table-positive')
elseif statValue < 0 then
elseif statValue < 0 then
table.insert(resultPart, 'background-color:lightpink;')
table.insert(resultPart, 'table-negative')
end
end
table.insert(resultPart, '"|'..Shared.formatnum(statValue))
table.insert(resultPart, '"|'..Num.formatnum(statValue))
if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
if statName == 'magicDamageBonus' or statName == 'damageReduction' then table.insert(resultPart, '%') end
end
end
--If requested, add the item Modifiers
--If requested, add the item Modifiers
if includeModifiers then
if includeModifiers then
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
table.insert(resultPart, '\r\n| ')
local txtLines = {}
local txtLines = {}
local modTxt = Constants.getModifiersText(item.modifiers, true)
if item.modifiers ~= nil then
if modTxt ~= '' then
table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
table.insert(txtLines, modTxt)
end
end
--For items with a special attack, show the details
--For items with a special attack, show the details
Line 245: Line 287:
for i, spAttID in ipairs(item.specialAttacks) do
for i, spAttID in ipairs(item.specialAttacks) do
local spAtt = GameData.getEntityByID('attacks', spAttID)
local spAtt = GameData.getEntityByID('attacks', spAttID)
table.insert(txtLines, spAtt.defaultChance .. '% chance for ' .. spAtt.name .. ':')
local attChance = spAtt.defaultChance
if item.overrideSpecialChances ~= nil then
attChance = item.overrideSpecialChances[i]
end
table.insert(txtLines, attChance .. '% chance for ' .. spAtt.name .. ':')
table.insert(txtLines, spAtt.description)
table.insert(txtLines, spAtt.description)
end
end
Line 253: Line 299:
--If requested, add description
--If requested, add description
if includeDescription then
if includeDescription then
table.insert(resultPart, '\r\n|style="text-align:left;padding:0 0.5em 0 0.5em;"|')
table.insert(resultPart, '\r\n| ' .. getItemDesc(item))
table.insert(resultPart, item.customDescription ~= nil and item.customDescription or '')
end
end
end
end
Line 265: Line 310:


function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
if type(slot) == 'number' then
-- If slot is a slot name, convert it to the slot ID instead
slot = Constants.getEquipmentSlotName(slot)
local slotID = getSlotID(slot)
if slotID == nil then
return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
end
end
local slotNS, slotLocalID = Shared.getLocalID(slotID)


local itemList = Items.getItems(function(item)
local itemList = Items.getItems(function(item)
-- Exclude the debug item
if item.id == 'melvorD:DEBUG_ITEM' then
return false
end
-- Exclude Golbin raid exclusives for now, such that they don't
-- Exclude Golbin raid exclusives for now, such that they don't
-- pollute various equipment tables
-- pollute various equipment tables
Line 277: Line 329:
local isMatch = true
local isMatch = true
if style == 'Melee' then
if style == 'Melee' then
if (Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name) then isMatch = false end
if ((Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name)) or Shared.contains(styleOverrides.NotMelee, item.name) then isMatch = false end
elseif style == 'Ranged' then
elseif style == 'Ranged' then
if Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name) then isMatch = false end
if (Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name)) or Shared.contains(styleOverrides.NotRanged, item.name)  then isMatch = false end
elseif style == 'Magic' then
elseif style == 'Magic' then
if Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name) then isMatch = false end
if (Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name)) or Shared.contains(styleOverrides.NotMagic, item.name) then isMatch = false end
elseif style == 'None' then
elseif style == 'None' then
if (Items._getItemStat(item, 'defenceLevelRequired') ~= nil or Items._getItemStat(item, 'rangedLevelRequired') ~= nil or Items._getItemStat(item, 'magicLevelRequired') ~= nil or
if (Items._getItemStat(item, 'defenceLevelRequired') ~= nil or Items._getItemStat(item, 'rangedLevelRequired') ~= nil or Items._getItemStat(item, 'magicLevelRequired') ~= nil or
Line 289: Line 341:
end
end
end
end
if slot == nil or not Shared.contains(item.validSlots, slot) then isMatch = false end
local sID = slotLocalID
if sID == nil or not Shared.contains(item.validSlots, sID) then isMatch = false end


if isMatch and other ~= nil then
if isMatch and other ~= nil then
if slot == 'Cape' then
if slot == 'Cape' then
local isSkillcape = Shared.contains(item.name, 'Skillcape') or item.name == 'Cape of Completion'
-- TODO Would be more reliable if based on items appearing within the relevant shop categories instead
local isSkillcape = Shared.contains(item.name, 'Skillcape') or Shared.contains(item.name, 'Cape of Completion')
if other == 'Skillcapes' then
if other == 'Skillcapes' then
isMatch = isSkillcape
isMatch = isSkillcape
Line 300: Line 354:
end
end
end
end
if slot == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
if slotLocalID == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
if other == 'Arrows' then
isMatch = other == item.ammoTypeRequired
if item.ammoTypeRequired ~= 0 then isMatch = false end
elseif slotLocalID == 'Quiver' then
elseif other == 'Bolts' then
if other == 'Thrown' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
if item.ammoTypeRequired ~= 1 then isMatch = false end
isMatch = true
end
else
elseif slot == 'Quiver' then
isMatch = other == item.ammoType
if other == 'Arrows' then
if item.ammoType ~= 0 then isMatch = false end
elseif other == 'Bolts' then
if item.ammoType ~= 1 then isMatch = false end
elseif other == 'Javelins' then
if item.ammoType ~= 2 then isMatch = false end
elseif other == 'Throwing Knives' then
if item.ammoType ~= 3 then isMatch = false end
elseif other == 'Thrown' then
if item.ammoType ~= 2 and item.ammoType ~= 3 then isMatch = false end
end
end
end
end
Line 343: Line 387:


function p.getTableForList(frame)
function p.getTableForList(frame)
local stuffString = frame.args ~= nil and frame.args[1] or frame[1]
local pFrame = frame:getParent()
local itemNames = Shared.splitString(stuffString, ',')
local frameArgs = pFrame.args ~= nil and pFrame.args or frame
 
local includeModifiers = frameArgs.includeModifiers ~= nil and string.upper(frameArgs.includeModifiers) == 'TRUE' or false
local includeModifiers = frame.args ~= nil and frame.args[2] or frame[2]
includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false


local itemList = {}
local itemList, errItems = {}, {}
local errMsg = 'ERROR: Some items not found in database: [[Category:Pages with script errors]]'
local errMsg = 'Some items not found in database: '
local hasErr = false
local hasErr = false
for i, name in Shared.skpairs(itemNames) do
for i, rawItemName in ipairs(frameArgs) do
local nextItem = Items.getItem(Shared.trim(name))
local itemName = Shared.trim(rawItemName)
if nextItem == nil then
local item = Items.getItem(itemName)
errMsg = errMsg.." '"..name.."'"
if item == nil then
hasErr = true
table.insert(errItems, "'" .. itemName .. "'")
else
else
table.insert(itemList, nextItem)
table.insert(itemList, item)
end
end
end
end


if hasErr then
if not Shared.tableIsEmpty(errItems) then
return errMsg
return Shared.printError('Some items not found in database: ' .. table.concat(errItems, ', '))
else
else
return p._getEquipmentTable(itemList, includeModifiers)
return p._getEquipmentTable(itemList, includeModifiers)
Line 371: Line 413:
function p.getDoubleLootTable(frame)
function p.getDoubleLootTable(frame)
local modsDL = {
local modsDL = {
'increasedChanceToDoubleLootCombat',
["id"] = {
'decreasedChanceToDoubleLootCombat',
'melvorD:doubleItemsSkill',
'increasedChanceToDoubleLootThieving',
'melvorD:doubleItemsChanceAgainstDamageType'
'decreasedChanceToDoubleLootThieving',
},
'increasedChanceToDoubleItemsGlobal',
["alias"] = {
'decreasedChanceToDoubleItemsGlobal'
'increasedChanceToDoubleLootCombat',
'decreasedChanceToDoubleLootCombat',
'increasedChanceToDoubleItemsGlobal',
'decreasedChanceToDoubleItemsGlobal'
}
}
}
local modDetail = {}
local matchCriteria = Modifiers.getMatchCriteriaFromIDs(modsDL.id, modsDL.alias)
for i, modName in pairs(modsDL) do
local itemMatchedMods = {}
local mName, mText, mSign, mIsNeg, mValUnsigned = Constants.getModifierDetails(modName)
local itemList = Items.getItems(
modDetail[modName] = { mult = (mSign == "+" and 1 or -1) }
function(item)
end
 
local itemList = Items.getItems(function(item)
if item.modifiers ~= nil then
if item.modifiers ~= nil then
for modName, val in pairs(item.modifiers) do
local matchedMods = Modifiers.getMatchingModifiers(item.modifiers, matchCriteria)
if Shared.contains(modsDL, modName) then return true end
if Shared.tableIsEmpty(matchedMods.matched) then
return false
else
itemMatchedMods[item.id] = matchedMods.matched
return true
end
end
return false
end
end
end)
end
)


local resultPart = {}
local resultPart = {}
Line 397: Line 444:
table.insert(resultPart, '\r\n!colspan="2"|Name!!Bonus!!Description')
table.insert(resultPart, '\r\n!colspan="2"|Name!!Bonus!!Description')
for i, item in Shared.skpairs(itemList) do
for i, item in Shared.skpairs(itemList) do
local lootValue = 0
local lootValue = Modifiers.getModifierValue(itemMatchedMods[item.id])
for modName, modDet in pairs(modDetail) do
 
lootValue = lootValue + (item.modifiers[modName] or 0) * modDet.mult
end
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '||' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
table.insert(resultPart, '||'..(item.customDescription or ''))
table.insert(resultPart, '||' .. getItemDesc(item))
end
end


Line 412: Line 457:
end
end


function p.getStatChangeString(item1, item2)
function p.getItemUpgradeTable(frame)
--Used by getItemUpgradeTable to get the stat change between two items
local args = frame.args ~= nil and frame.args or frame
local changeArray = {}
local category, usedItemName = args[1], args.usedItem
 
local upgradeArray = {}
local getSpecificStatString = function(val1, val2, subStr)
local isEquipment = false
if val1 == nil then val1 = 0 end
if val2 == nil then val2 = 0 end
local usedItemID = nil
 
if usedItemName ~= nil and usedItemName ~= '' then
if val1 ~= val2 then
local usedItem = Items.getItem(usedItemName)
local txt = string.gsub(subStr, '{V}', Shared.numStrWithSign(val1 - val2))
if usedItem == nil then
table.insert(changeArray, txt)
return Shared.printError('Used item not found: ' .. usedItemName)
end
usedItemID = usedItem.id
end
local function upgradeConsumesItem(itemUpgrade, itemID)
if itemID == nil then
return true
end
for i, itemCost in ipairs(itemUpgrade.itemCosts) do
if itemCost.id == itemID then
return true
end
end
end
end
 
return false
--Unfortunately just gonna have to manually check all the changes I think...
local statList = {
-- {'statName', 'statDescription'}
{'stabAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Stab Bonus'},
{'slashAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Slash Bonus'},
{'blockAttackBonus', '{V} '..Icons.Icon({'Melee', notext=true})..' Block Bonus'},
{'meleeStrengthBonus', '{V} '..Icons.Icon({'Strength', type='skill', notext=true})..' Strength Bonus'},
{'rangedStrengthBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Strength Bonus'},
{'magicStrengthBonus', '{V}% '..Icons.Icon({'Magic', type='skill', notext=true})..' Damage Bonus'},
{'meleeDefenceBonus', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Defence Bonus'},
{'rangedDefenceBonus', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Defence Bonus'},
{'magicDefenceBonus', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Defence Bonus'},
{'damageReduction', '{V}% Damage Reduction'},
{'increasedSlayerXP', '{V}% '..Icons.Icon({'Slayer', type='skill', notext=true})..' Bonus XP'},
{'attackLevelRequired', '{V} '..Icons.Icon({'Attack', type='skill', notext=true})..' Level Required'},
{'defenceLevelRequired', '{V} '..Icons.Icon({'Defence', type='skill', notext=true})..' Level Required'},
{'rangedLevelRequired', '{V} '..Icons.Icon({'Ranged', type='skill', notext=true})..' Level Required'},
{'magicLevelRequired', '{V} '..Icons.Icon({'Magic', type='skill', notext=true})..' Level Required'},
}
for i, stat in ipairs(statList) do
if stat[1] == 'increasedSlayerXP' then
getSpecificStatString(Items._getItemModifier(item1, stat[1], 'Slayer'), Items._getItemModifier(item2, stat[1], 'Slayer'), stat[2])
else
getSpecificStatString(Items._getItemStat(item1, stat[1]), Items._getItemStat(item2, stat[1]), stat[2])
end
end
end
return table.concat(changeArray, '<br/>')
end
function p.getItemUpgradeTable(frame)
local category = frame.args ~= nil and frame.args[1] or frame
local itemArray = {}
local isEquipment = false


if string.upper(category) == 'POTION' then
if string.upper(category) == 'POTION' then
itemArray = Items.getItems(function(item) return Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil end)
upgradeArray = GameData.getEntities('itemUpgrades',
function(upgrade)
return Shared.contains(upgrade.upgradedItemID, 'Potion') and upgradeConsumesItem(upgrade, usedItemID)
end
)
elseif string.upper(category) == 'OTHER' then
elseif string.upper(category) == 'OTHER' then
itemArray = Items.getItems(function(item)
upgradeArray = GameData.getEntities('itemUpgrades',
return not Shared.contains(item.name, 'Potion') and item.itemsRequired ~= nil and item.validSlots == nil
function(upgrade)
end)
if not Shared.contains(upgrade.upgradedItemID, 'Potion') and upgradeConsumesItem(upgrade, usedItemID) then
local item = Items.getItemByID(upgrade.upgradedItemID)
if item ~= nil then
return item.validSlots == nil or Shared.tableIsEmpty(item.validSlots)
end
end
return false
end
)
else
else
if Constants.getEquipmentSlotID(category) == nil then
-- If category is a slot name, convert it to the slot ID instead
return 'ERROR: Invalid option. Choose either an equipment slot, Potion, or Other[[Category:Pages with script errors]]'
local slotID = getSlotID(category)
if slotID ~= nil then
local slotNS, slotLocalID = Shared.getLocalID(slotID)
category = slotLocalID
end
 
if category == nil then
return Shared.printError('Invalid option. Choose either an equipment slot, Potion, or Other')
end
end
isEquipment = true
isEquipment = true
itemArray = Items.getItems(function(item)
upgradeArray = GameData.getEntities('itemUpgrades',
return item.itemsRequired ~= nil and Shared.contains(item.validSlots, category)
function(upgrade)
end)
if upgradeConsumesItem(upgrade, usedItemID) then
local item = Items.getItemByID(upgrade.upgradedItemID)
if item ~= nil then
return item.validSlots ~= nil and Shared.contains(item.validSlots, category)
end
end
return false
end
)
end
end


local useStatChange = isEquipment or (string.upper(category) == 'POTION')
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!colspan="2"|Upgraded Item!!Ingredients')
table.insert(resultPart, '\r\n!colspan="2"|Upgraded Item!!Ingredients')
if isEquipment then table.insert(resultPart, '!!Stat Change') end
if useStatChange then table.insert(resultPart, '!!Stat Change') end


for i, item in Shared.skpairs(itemArray) do
for i, upgrade in ipairs(upgradeArray) do
local item = Items.getItemByID(upgrade.upgradedItemID)
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true}))
table.insert(resultPart, '||'..Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '||' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))


local matArray = {}
table.insert(resultPart, '|| ' .. Common.getCostString({ items = upgrade.itemCosts, gp = upgrade.gpCost, sc = upgrade.scCost}, 'None'))
local statChangeString = ''
for i, row in Shared.skpairs(item.itemsRequired) do
local mat = Items.getItemByID(row[1])
table.insert(matArray, Icons.Icon({mat.name, type='item', qty=row[2]}))


if item.validSlots ~= nil and mat.validSlots ~= nil and statChangeString == '' then
if useStatChange then
statChangeString = p.getStatChangeString(item, mat)
-- Generate stat change column
local statChangeString = ''
if not Shared.tableIsEmpty(upgrade.rootItemIDs) then
-- Some items (e.g. FEZ) may have multiple root items. Simply use the first one
local rootItem = Items.getItemByID(upgrade.rootItemIDs[1])
if rootItem ~= nil then
statChangeString = Items.getStatChangeString(item, rootItem)
end
end
end
table.insert(resultPart, '|| '..statChangeString)
end
end
if item.trimmedGPCost ~= nil and item.trimmedGPCost > 0 then
end
table.insert(matArray, Icons.GP(item.trimmedGPCost))
 
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
 
function p.getRuneProvidingItemTable(frame)
local itemArray = Items.getItems(function(item) return item.providedRunes ~= nil end)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!colspan="2"|Item!!Runes Provided')
for i, item in pairs(itemArray) do
local PR = item.providedRunes
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="text-align: centre;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
local runeLines = {}
local sortVal = ''
for j, runePair in pairs(PR) do
local runeID = runePair.id
local qty = runePair.quantity
local rune = Items.getItemByID(runeID)
sortVal = sortVal..rune.name..qty
table.insert(runeLines, Icons.Icon({rune.name, type='item', qty=qty}))
end
end
table.insert(resultPart, '||'..table.concat(matArray, '<br/>'))
table.insert(resultPart, '\r\n|data-sort-value="'..sortVal..'"|'..table.concat(runeLines, '<br/>'))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end


if isEquipment then
function p._getDRTable(slots, items)
table.insert(resultPart, '||'..statChangeString)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable" style="width:90%;"')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n! style="width:4%"|DR%')
for i, slot in pairs(slots) do
table.insert(resultPart, '\r\n! '..slot)
end
local DRTable = {}
table.sort(items, function(a, b) return a.name < b.name end)
for i, item in pairs(items) do
local DR = Items._getItemStat(item, 'damageReduction', true)
local EquipSlot = Items._getItemEquipSlot(item)
if DRTable[DR] == nil then
DRTable[DR] = {}
end
if DRTable[DR][EquipSlot] == nil then
DRTable[DR][EquipSlot] = {}
end
table.insert(DRTable[DR][EquipSlot], Icons.Icon({item.name, type='item', expicon = Icons.getExpansionIcon(item.id)}))
end
for DR, SlotTables in Shared.skpairs(DRTable) do
table.insert(resultPart, '\r\n|-\r\n|'..DR..'%')
for i, SlotName in pairs(slots) do
table.insert(resultPart, '\r\n|')
if SlotTables[SlotName] ~= nil then
table.insert(resultPart, table.concat(SlotTables[SlotName], '<br/>'))
end
end
end
end
end
 
table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
return table.concat(resultPart)
end
function p.getDRTable(frame)
local style = frame.args ~= nil and frame.args[1] or frame
local SlotNames = {}
local ItemList = {}
if style == 'Other' then
SlotNames = {'Helmet', 'Platelegs', 'Gloves', 'Shield', 'Cape', 'Amulet', 'Ring'}
else
SlotNames = {'Helmet', 'Platebody', 'Platelegs', 'Boots', 'Gloves', 'Weapon', 'Shield'}
end
ItemList =  Items.getItems(function(item)
local isMatch = true
if Items._getItemStat(item, 'damageReduction', true) <= 0 then
return false
end
-- Exclude Golbin raid exclusives for now, such that they don't
-- pollute various equipment tables
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
return false
end
--Using the same checks for Melee/Ranged/Magic that the Equipment Tables use
if style == 'Melee' then
if ((Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name)) or Shared.contains(styleOverrides.NotMelee, item.name) then isMatch = false end
elseif style == 'Ranged' then
if (Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name)) or Shared.contains(styleOverrides.NotRanged, item.name)  then isMatch = false end
elseif style == 'Magic' then
if (Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name)) or Shared.contains(styleOverrides.NotMagic, item.name) then isMatch = false end
else
if (Items._getItemStat(item, 'attackLevelRequired') ~= nil or Items._getItemStat(item, 'defenceLevelRequired') ~= nil or Items._getItemStat(item, 'rangedLevelRequired') ~= nil or Items._getItemStat(item, 'magicLevelRequired') ~= nil or
Shared.contains(styleOverrides.Melee, item.name) or Shared.contains(styleOverrides.Ranged, item.name) or Shared.contains(styleOverrides.Magic, item.name)) and
not Shared.contains(styleOverrides.None, item.name) then
isMatch = false
end
end
if isMatch and not Shared.contains(SlotNames, Items._getItemEquipSlot(item)) then
isMatch = false
end
return isMatch
end)
return p._getDRTable(SlotNames, ItemList)
end
end


return p
return p
2,647

edits