Module:Items/ComparisonTables: Difference between revisions

no edit summary
(getStatChangeString: Move to Module:Items)
No edit summary
(14 intermediate revisions by 3 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', "Knight's Defender", "Ward of Flame Platebody"},
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', 'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
Magic = {'Slayer Wizard Hat (Basic)', 'Slayer Wizard Robes (Basic)', 'Enchanted Shield', 'Elementalist Gloves', 'Frostspark Boots', 'Freezing Touch Body', 'Lightning Boots'},
Line 18: Line 20:
NotMagic = {'Torrential Blast Crossbow', 'Spectral Ice Sword', 'Lightning Strike 1H Sword', 'FrostSpark 1H Sword'}
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 188: Line 216:
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: 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}))
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;" |'..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;"|')
Line 202: Line 230:
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|')
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 218: Line 245:
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 226: Line 257:
--If requested, add description
--If requested, add description
if includeDescription then
if includeDescription then
table.insert(resultPart, '\r\n| ')
table.insert(resultPart, '\r\n| ' .. getItemDesc(item))
table.insert(resultPart, Constants.getDescription(item.customDescription, item.modifiers) 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|'..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|' .. Icons.getExpansionIcon(item.id) .. 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
Line 242: Line 272:
table.insert(resultPart, 'table-negative')
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
Line 249: Line 279:
table.insert(resultPart, '\r\n| ')
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 258: 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 266: Line 299:
--If requested, add description
--If requested, add description
if includeDescription then
if includeDescription then
table.insert(resultPart, '\r\n| ')
table.insert(resultPart, '\r\n| ' .. getItemDesc(item))
table.insert(resultPart, Constants.getDescription(item.customDescription, item.modifiers) or '')
end
end
end
end
Line 279: Line 311:
function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
-- If slot is a slot name, convert it to the slot ID instead
-- If slot is a slot name, convert it to the slot ID instead
slot = Constants.getEquipmentSlotID(slot) or (Constants.getEquipmentSlotName(slot) ~= nil and slot)
local slotID = getSlotID(slot)
if slotID == nil then
return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
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 301: 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
Line 313: 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
isMatch = other == item.ammoTypeRequired
isMatch = other == item.ammoTypeRequired
elseif slot == 'Quiver' then
elseif slotLocalID == 'Quiver' then
if other == 'Thrown' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
if other == 'Thrown' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
isMatch = true
isMatch = true
Line 346: 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 = 'Some items not found in database: '
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 Shared.printError(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 374: 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 400: 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.getExpansionIcon(item.id) .. 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, '||'..(Constants.getDescription(item.customDescription, item.modifiers) or ''))
table.insert(resultPart, '||' .. getItemDesc(item))
end
end


Line 416: Line 458:


function p.getItemUpgradeTable(frame)
function p.getItemUpgradeTable(frame)
local category = frame.args ~= nil and frame.args[1] or frame
local args = frame.args ~= nil and frame.args or frame
local category, usedItemName = args[1], args.usedItem
local upgradeArray = {}
local upgradeArray = {}
local isEquipment = false
local isEquipment = false
local usedItemID = nil
if usedItemName ~= nil and usedItemName ~= '' then
local usedItem = Items.getItem(usedItemName)
if usedItem == nil then
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
return false
end


if string.upper(category) == 'POTION' then
if string.upper(category) == 'POTION' then
upgradeArray = GameData.getEntities('itemUpgrades',
upgradeArray = GameData.getEntities('itemUpgrades',
function(upgrade) return Shared.contains(upgrade.upgradedItemID, 'Potion') end
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
upgradeArray = GameData.getEntities('itemUpgrades',
upgradeArray = GameData.getEntities('itemUpgrades',
function(upgrade)
function(upgrade)
if not Shared.contains(upgrade.upgradedItemID, 'Potion') then
if not Shared.contains(upgrade.upgradedItemID, 'Potion') and upgradeConsumesItem(upgrade, usedItemID) then
local item = Items.getItemByID(upgrade.upgradedItemID)
local item = Items.getItemByID(upgrade.upgradedItemID)
if item ~= nil then
if item ~= nil then
Line 438: Line 504:
else
else
-- If category is a slot name, convert it to the slot ID instead
-- If category is a slot name, convert it to the slot ID instead
category = Constants.getEquipmentSlotID(category) or (Constants.getEquipmentSlotName(category) ~= nil and category)
local slotID = getSlotID(category)
if slotID ~= nil then
local slotNS, slotLocalID = Shared.getLocalID(slotID)
category = slotLocalID
end
 
if category == nil then
if category == nil then
return Shared.printError('Invalid option. Choose either an equipment slot, Potion, or Other')
return Shared.printError('Invalid option. Choose either an equipment slot, Potion, or Other')
Line 445: Line 516:
upgradeArray = GameData.getEntities('itemUpgrades',
upgradeArray = GameData.getEntities('itemUpgrades',
function(upgrade)
function(upgrade)
local item = Items.getItemByID(upgrade.upgradedItemID)
if upgradeConsumesItem(upgrade, usedItemID) then
if item ~= nil then
local item = Items.getItemByID(upgrade.upgradedItemID)
return item.validSlots ~= nil and Shared.contains(item.validSlots, category)
if item ~= nil then
return item.validSlots ~= nil and Shared.contains(item.validSlots, category)
end
end
end
return false
return false
Line 454: Line 527:
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, upgrade in ipairs(upgradeArray) do
for i, upgrade in ipairs(upgradeArray) do
Line 466: Line 540:
table.insert(resultPart, '||' .. Icons.getExpansionIcon(item.id) .. 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, itemCost in ipairs(upgrade.itemCosts) do
if useStatChange then
local mat = Items.getItemByID(itemCost.id)
-- Generate stat change column
if mat ~= nil then
local statChangeString = ''
table.insert(matArray, Icons.Icon({mat.name, type='item', qty=itemCost.quantity}))
if not Shared.tableIsEmpty(upgrade.rootItemIDs) then
if isEquipment and item.validSlots ~= nil and mat.validSlots ~= nil and statChangeString == '' then
-- Some items (e.g. FEZ) may have multiple root items. Simply use the first one
statChangeString = Items.getStatChangeString(item, mat)
local rootItem = Items.getItemByID(upgrade.rootItemIDs[1])
if rootItem ~= nil then
statChangeString = Items.getStatChangeString(item, rootItem)
end
end
end
end
end
table.insert(resultPart, '|| '..statChangeString)
if upgrade.gpCost ~= nil and upgrade.gpCost > 0 then
table.insert(matArray, Icons.GP(upgrade.gpCost))
end
if upgrade.scCost ~= nil and upgrade.scCost > 0 then
table.insert(matArray, Icons.SC(upgrade.scCost))
end
table.insert(resultPart, '||'..table.concat(matArray, '<br/>'))
 
if isEquipment then
table.insert(resultPart, '||'..statChangeString)
end
end
end
end
2,647

edits