Module:Items: Difference between revisions
From Melvor Idle
(getItemDataExport: Implement) |
(Add blocked/occupied slot) |
||
(80 intermediate revisions by 4 users not shown) | |||
Line 6: | Line 6: | ||
local p = {} | local p = {} | ||
local | local GameData = require('Module:GameData') | ||
local Constants = require('Module:Constants') | local Constants = require('Module:Constants') | ||
local Shared = require('Module:Shared') | local Shared = require('Module:Shared') | ||
local Modifiers = require('Module:Modifiers') | |||
local Icons = require('Module:Icons') | local Icons = require('Module:Icons') | ||
local Num = require('Module:Number') | |||
-- Used by p._getItemLevelReqs() | |||
local LevelReqCache = {} | |||
p.EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon', 'Easter Egg', | p.EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon', 'Easter Egg', | ||
Line 24: | Line 28: | ||
'Holiday Scarf', 'Gingerbread House', 'Gingerbread Man', 'Edible Candy Cane', | 'Holiday Scarf', 'Gingerbread House', 'Gingerbread Man', 'Edible Candy Cane', | ||
'Locked Chest', 'Locked Chest Key', 'Event Token (Holiday 2021)'} | 'Locked Chest', 'Locked Chest Key', 'Event Token (Holiday 2021)'} | ||
-- | -- List of item IDs that should typically not be included within outputs, usually | ||
p. | -- because they are not fully implemented despite existing within the game data | ||
p.HiddenItems = {} | |||
local function populateHiddenItems() | |||
local hiddenItems = GameData.getEntities('items', | |||
function(item) | |||
return item.name == nil or Shared.contains({'melvorTotH:Meteorite_Dust'}, item.id) | |||
end | |||
) | |||
for _, item in ipairs(hiddenItems) do | |||
table.insert(p.HiddenItems, item.id) | |||
end | |||
end | |||
populateHiddenItems() | |||
function p.getItemByID(ID) | function p.getItemByID(ID) | ||
return GameData.getEntityByID('items', ID) | |||
if | end | ||
function p.getItem(name) | |||
name = Shared.fixPagename(name) | |||
return GameData.getEntityByName('items', name) | |||
end | |||
function p.getItems(checkFunc) | |||
return GameData.getEntities('items', | |||
function(obj) | |||
return not Shared.contains(p.HiddenItems, obj.id) and checkFunc(obj) | |||
end | |||
) | |||
end | |||
function p._canItemUseSlot(item, equipSlot) | |||
--Function to easily check if an item can fit in a given equipment slot | |||
--Ex: p._canItemUseSlot({Bronze Platebody}, 'Platebody') returns true | |||
if type(item) == 'string' then | |||
item = p.getItem(item) | |||
end | end | ||
return | return item.validSlots ~= nil and Shared.contains(item.validSlots, equipSlot) | ||
end | end | ||
function p. | function p._getItemEquipSlot(item) | ||
--Function to return the (non-Passive) equipment slot that an item occupies | |||
if type(item) == 'string' then | |||
item = p.getItem(item) | |||
for i, | end | ||
if item == nil or item.validSlots == nil then | |||
return 'Invalid' | |||
end | |||
for i, slot in pairs(item.validSlots) do | |||
if slot ~= 'Passive' then | |||
return slot | |||
end | end | ||
end | end | ||
end | end | ||
function p. | -- Given an item, returns a table containing the item's level requirements (if any) | ||
local | -- The table structure is as follows: | ||
local | -- { ['normal'] = { SkillID = SkillLevel, ... }, ['abyssal'] = { SkillID = SkillLevel, ... } } | ||
for | -- If optional parameter 'condense' is specified as true, any normal requirements are excluded | ||
-- from the output if there should exist an abyssal level requirement for the same skill | |||
function p._getItemLevelReqs(item, condense) | |||
local condenseResult = (type(condense) == 'boolean' and condense) or false | |||
local reqTypeMap = { | |||
['SkillLevel'] = 'normal', | |||
['AbyssalLevel'] = 'abyssal' | |||
} | |||
local levelReqs = LevelReqCache[item.id] | |||
if levelReqs == nil then | |||
-- Item not in cache - obtain level requirements data | |||
if item.equipRequirements == nil then | |||
return nil | |||
end | |||
levelReqs = { ['normal'] = {}, ['abyssal'] = {} } | |||
for _, requirement in ipairs(item.equipRequirements) do | |||
local reqTypeKey = reqTypeMap[requirement.type] | |||
if reqTypeKey ~= nil then | |||
-- Requirement is a level requirement | |||
local reqSkillID, reqLevel = requirement.skillID, requirement.level | |||
local reqCurrentLevel = levelReqs[reqTypeKey][reqSkillID] | |||
if reqCurrentLevel == nil or reqLevel > reqCurrentLevel then | |||
levelReqs[reqTypeKey][reqSkillID] = reqLevel | |||
end | |||
end | |||
end | |||
-- Cache level requirements data for any subsequent lookups | |||
LevelReqCache[item.id] = levelReqs | |||
end | |||
if not condenseResult then | |||
-- Return requirements as is | |||
return levelReqs | |||
else | |||
-- Exclude any normal level requirements should an abyssal requirement exist for | |||
-- the same skill | |||
local levelReqsCondensed = { ['normal'] = {}, ['abyssal'] = levelReqs.abyssal } | |||
for skillID, reqLevel in pairs(levelReqs.normal) do | |||
if levelReqs.abyssal[skillID] == nil then | |||
levelReqsCondensed.normal[skillID] = reqLevel | |||
end | |||
end | end | ||
return levelReqsCondensed | |||
end | end | ||
end | end | ||
Line 75: | Line 138: | ||
--Special Overrides: | --Special Overrides: | ||
-- Equipment stats first | -- Equipment stats first | ||
if | if item.equipmentStats ~= nil and item.equipmentStats[StatName] ~= nil then | ||
result = item.equipmentStats[StatName] | result = item.equipmentStats[StatName] | ||
elseif StatName == 'attackSpeed' and item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon') then | |||
-- Item can be equipped as a weapon but has no attack speed, so use default of 4000ms | |||
result = 4000 | |||
elseif StatName == 'isTwoHanded' then | elseif StatName == 'isTwoHanded' then | ||
if item.validSlots ~= nil and item.occupiesSlots ~= nil then | if item.validSlots ~= nil and item.occupiesSlots ~= nil then | ||
Line 83: | Line 149: | ||
result = false | result = false | ||
end | end | ||
elseif string.find(StatName, '^(.+)LevelRequired$') ~= nil | elseif ( | ||
local skillName = | string.find(StatName, '^(.+)LevelRequired$') ~= nil | ||
or string.find(StatName, '^(.+)AbyssalLevel$') ~= nil | |||
) and item.equipRequirements ~= nil then | |||
local levelType = 'normal' | |||
local skillName = string.match(StatName, '^(.+)LevelRequired$') | |||
if skillName == nil then | |||
levelType = 'abyssal' | |||
skillName = string.match(StatName, '^(.+)AbyssalLevel$') | |||
end | |||
if skillName ~= nil then | if skillName ~= nil then | ||
local skillID = Constants.getSkillID(skillName) | local skillID = Constants.getSkillID(Shared.titleCase(skillName)) | ||
if skillID ~= nil then | if skillID ~= nil then | ||
result = | local levelReqs = p._getItemLevelReqs(item) | ||
result = levelReqs ~= nil and levelReqs[levelType][skillID] | |||
end | end | ||
end | end | ||
Line 94: | Line 169: | ||
result = p._getWeaponAttackType(item) | result = p._getWeaponAttackType(item) | ||
elseif StatName == 'description' then | elseif StatName == 'description' then | ||
result = item. | result = item.customDescription | ||
if result == nil or result == '' then result = 'No Description' end | if result == nil or result == '' then result = 'No Description' end | ||
elseif StatName == 'completionReq' then | elseif StatName == 'completionReq' then | ||
Line 106: | Line 181: | ||
elseif StatName == 'hasCombatStats' then | elseif StatName == 'hasCombatStats' then | ||
return tostring(p.hasCombatStats(item) or p._hasLevelRequirements(item)) | return tostring(p.hasCombatStats(item) or p._hasLevelRequirements(item)) | ||
elseif StatName == 'category' then | |||
-- Some categories have a namespace for some reason, remove it | |||
local _, localID = GameData.getLocalID(result) | |||
return localID | |||
end | end | ||
if result == nil and ZeroIfNil then result = 0 end | if result == nil and ZeroIfNil then result = 0 end | ||
return result | return result | ||
end | |||
function p.getItemValueByID(itemID) | |||
local item = p.getItemByID(itemID) | |||
if item == nil then | |||
return 0 | |||
end | |||
return p.getItemValue(item['name']) | |||
end | |||
function p.getItemValue(item) | |||
if type(item) == 'string' then | |||
-- Specific check if the item is GP (value of 1) | |||
if Shared.compareString('GP', item, true) | |||
or Shared.compareString('Gold Pieces', item, true) then | |||
return 1 | |||
end | |||
item = p.getItem(item) | |||
end | |||
if item then | |||
return item.sellsFor | |||
end | |||
return nil | |||
end | |||
function p.getValueText(item, minQuantity, maxQuantity) | |||
local minQ, maxQ = 1, 1 | |||
if type(minQuantity) == 'number' then | |||
minQ = minQuantity | |||
end | |||
if type(maxQuantity) == 'number' then | |||
maxQ = maxQuantity | |||
else | |||
maxQ = minQ | |||
end | |||
local amt = item.sellsFor or 0 | |||
local currID = item.sellsForCurrency or 'melvorD:GP' | |||
return Icons._Currency(currID, amt * minQ, amt * maxQ) | |||
end | |||
-- Function already exists, but without frame. | |||
-- Giving it a slightly different name since function overloading doesn't exist | |||
function p.getItemSellsFor(frame) | |||
local args = frame:getParent().args | |||
return p._getItemSellsFor(args[1], args[2], args.round) | |||
end | |||
function p._getItemSellsFor(itemName, multiplier, rounding) | |||
local itemValue = p.getItemValue(itemName) | |||
multiplier = tonumber(multiplier) or 1 | |||
rounding = tonumber(rounding) or 0 | |||
if itemValue == nil then | |||
error('No item named "' .. itemName .. '" exists in the data module') | |||
end | |||
return Num.round2(itemValue * multiplier, rounding) | |||
end | end | ||
Line 119: | Line 260: | ||
local item = p.getItem(ItemName) | local item = p.getItem(ItemName) | ||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named "' .. ItemName .. '" exists in the data module') | ||
end | end | ||
local result = p._getItemStat(item, StatName, ZeroIfNil) | local result = p._getItemStat(item, StatName, ZeroIfNil) | ||
if formatNum then result = | if formatNum then result = Num.formatnum(result) end | ||
return result | return result | ||
end | end | ||
Line 128: | Line 269: | ||
--Gets the value of a given modifier for a given item | --Gets the value of a given modifier for a given item | ||
--asString is false by default, when true it writes the full bonus text | --asString is false by default, when true it writes the full bonus text | ||
function p._getItemModifier(item, modifier, | function p._getItemModifier(item, modifier, skillID, asString) | ||
if asString == nil then asString = false end | if asString == nil then asString = false end | ||
if | if skillID == '' then | ||
skillID = nil | |||
elseif | elseif string.find(skillID, ':') == nil then | ||
skill = Constants.getSkillID( | -- Try to find a skill ID if it looks like a skill name has been passed | ||
skillID = Constants.getSkillID(skillID) | |||
end | end | ||
Line 141: | Line 283: | ||
if type(item.modifiers[modifier]) == 'table' then | if type(item.modifiers[modifier]) == 'table' then | ||
for i, subVal in Shared.skpairs(item.modifiers[modifier]) do | for i, subVal in Shared.skpairs(item.modifiers[modifier]) do | ||
if subVal[1] == | if subVal[1] == skillID then | ||
result = subVal[2] | result = subVal[2] | ||
break | break | ||
Line 152: | Line 294: | ||
if asString then | if asString then | ||
if | if skillID ~= nil then | ||
return Constants._getModifierText(modifier, { | return Constants._getModifierText(modifier, {skillID, result}) | ||
else | else | ||
return Constants._getModifierText(modifier, result) | return Constants._getModifierText(modifier, result) | ||
Line 163: | Line 305: | ||
function p.hasCombatStats(item) | function p.hasCombatStats(item) | ||
if item. | -- Checks if the combat stat is a valid, non-zero combat stat | ||
-- Ensure that, only in the case where the item is a Familar AND | |||
-- the checked stat is summoningMaxhit, the result is ignored. | |||
local function isNonZeroStat(statName, statVal) | |||
if statName == 'summoningMaxhit' and (p._canItemUseSlot(item, 'Summon1') or p._canItemUseSlot(item, 'Summon2')) then | |||
return false | |||
end | |||
return statVal ~= 0 | |||
end | |||
if item.equipmentStats ~= nil then | |||
-- Ensure at least one stat has a non-zero value | -- Ensure at least one stat has a non-zero value | ||
for statName, statVal in pairs(item.equipmentStats) do | for statName, statVal in pairs(item.equipmentStats) do | ||
if statVal | if isNonZeroStat(statName, statVal) then | ||
return true | |||
end | |||
end | end | ||
end | end | ||
return false | return false | ||
end | end | ||
Line 174: | Line 329: | ||
function p._hasLevelRequirements(item) | function p._hasLevelRequirements(item) | ||
--Function true if an item has at least one level requirement to equip | --Function true if an item has at least one level requirement to equip | ||
local levelReqs = p._getItemLevelReqs(item) | |||
for | if levelReqs ~= nil then | ||
if | for levelType, reqs in pairs(levelReqs) do | ||
if not Shared.tableIsEmpty(reqs) then | |||
return true | return true | ||
end | end | ||
end | end | ||
end | end | ||
return false | |||
end | end | ||
Line 197: | Line 351: | ||
local item = p.getItem(itemName) | local item = p.getItem(itemName) | ||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | ||
end | end | ||
Line 204: | Line 358: | ||
function p._getWeaponAttackType(item) | function p._getWeaponAttackType(item) | ||
if | if (item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon')) or | ||
(item.occupiesSlots ~= nil and Shared.contains(item.occupiesSlots, 'Weapon')) then | |||
if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then | if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then | ||
local iconType = item.attackType ~= 'melee' and 'skill' or nil | local iconType = item.attackType ~= 'melee' and 'skill' or nil | ||
Line 218: | Line 372: | ||
local item = p.getItem(itemName) | local item = p.getItem(itemName) | ||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | ||
end | end | ||
return p._getWeaponAttackType(item) | return p._getWeaponAttackType(item) | ||
end | end | ||
local statChangeDefs = { | |||
{ | |||
stat = 'stabAttackBonus', | |||
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Stab Bonus' | |||
}, | |||
{ | |||
stat = 'slashAttackBonus', | |||
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Slash Bonus' | |||
}, | |||
{ | |||
stat = 'blockAttackBonus', | |||
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Block Bonus' | |||
}, | |||
{ | |||
stat = 'meleeStrengthBonus', | |||
suffix = ' ' .. Icons.Icon({'Strength', type='skill', notext=true}) .. ' Strength Bonus' | |||
}, | |||
{ | |||
stat = 'rangedStrengthBonus', | |||
suffix = ' ' .. Icons.Icon({'Ranged', type='skill', notext=true}) .. ' Strength Bonus' | |||
}, | |||
{ | |||
stat = 'magicStrengthBonus', | |||
suffix = '% ' .. Icons.Icon({'Magic', type='skill', notext=true}) .. ' Damage Bonus' | |||
}, | |||
{ | |||
stat = 'meleeDefenceBonus', | |||
suffix = ' ' .. Icons.Icon({'Defence', type='skill', notext=true}) .. ' Defence Bonus' }, | |||
{ | |||
stat = 'rangedDefenceBonus', | |||
suffix = ' ' .. Icons.Icon({'Ranged', type='skill', notext=true}) .. ' Defence Bonus' | |||
}, | |||
{ | |||
stat = 'magicDefenceBonus', | |||
suffix = ' ' .. Icons.Icon({'Magic', type='skill', notext=true}) .. ' Defence Bonus' | |||
}, | |||
{ | |||
stat = 'damageReduction', | |||
suffix = '% Damage Reduction' | |||
}, | |||
{ | |||
stat = 'levelRequired', | |||
suffix = ' Level Required' | |||
} | |||
} | |||
-- Produces a list of stat & modifier changes between two items of equipmednt | |||
function p.getStatChangeString(item1, item2) | |||
local changeArray = {} | |||
local | local equipStats = { | ||
type(item1.equipmentStats) == 'table' and item1.equipmentStats or {}, | |||
type(item2.equipmentStats) == 'table' and item2.equipmentStats or {} | |||
} | |||
for i, statDef in ipairs(statChangeDefs) do | |||
local val1, val2 = 0, 0 | |||
if statDef.stat == 'levelRequired' then | |||
-- Iterate over equipment stats for both items, determining level requirements | |||
local levelReqs = {} | |||
for itemNum, item in ipairs({item1, item2}) do | |||
levelReqs[itemNum] = {} | |||
if item.equipRequirements ~= nil then | |||
for j, req in ipairs(item.equipRequirements) do | |||
if req.type == 'SkillLevel' then | |||
levelReqs[itemNum][req.skillID] = req.level | |||
end | |||
end | |||
end | |||
end | |||
-- Iterate over all skills, checking if there are requirements for these in either skill | |||
for j, skillData in ipairs(GameData.rawData.skillData) do | |||
local skillID = skillData.skillID | |||
val1, val2 = levelReqs[1][skillID] or 0, levelReqs[2][skillID] or 0 | |||
if val1 ~= val2 then | |||
table.insert(changeArray, Num.numStrWithSign(val1 - val2) .. ' ' .. Icons.Icon({skillData.data.name, type='skill', notext=true}) .. (statDef.suffix or '')) | |||
end | |||
end | |||
else | |||
-- Equipment stats | |||
val1, val2 = equipStats[1][statDef.stat] or 0, equipStats[2][statDef.stat] or 0 | |||
if val1 ~= val2 then | |||
table.insert(changeArray, Num.numStrWithSign(val1 - val2) .. (statDef.suffix or '')) | |||
end | |||
end | |||
end | end | ||
-- Include differences in modifiers | |||
-- TODO Implement getModifiersDifference | |||
--local modDiff = Constants.getModifiersText(Constants.getModifiersDifference(item2.modifiers, item1.modifiers)) | |||
local modDiff = nil | |||
if modDiff ~= nil and modDiff ~= '' then | |||
table.insert(changeArray, modDiff) | |||
end | end | ||
table. | return table.concat(changeArray, '<br/>') | ||
end | end | ||
function p._getOtherItemBoxText(item) | function p._getOtherItemBoxText(item) | ||
resultPart = {} | local resultPart = {} | ||
--For equipment, show the slot they go in | --For equipment, show the slot they go in | ||
local isPassive = false | |||
if item.validSlots ~= nil then | if item.validSlots ~= nil then | ||
local slotLinkMap = { | local slotLinkMap = { | ||
["Helmet"] = ' | ["Helmet"] = 'Helmets', | ||
["Platebody"] = ' | ["Platebody"] = 'Platebodies', | ||
["Platelegs"] = ' | ["Platelegs"] = 'Platelegs', | ||
["Boots"] = ' | ["Boots"] = 'Boots', | ||
["Weapon"] = ' | ["Weapon"] = 'Weapons', | ||
["Shield"] = ' | ["Shield"] = 'Shields', | ||
["Amulet"] = ' | ["Amulet"] = 'Amulets', | ||
["Ring"] = ' | ["Ring"] = 'Rings', | ||
["Gloves"] = ' | ["Gloves"] = 'Gloves', | ||
["Quiver"] = ' | ["Quiver"] = 'Ammunition', | ||
["Cape"] = ' | ["Cape"] = 'Capes', | ||
["Consumable"] = 'Consumables', | |||
["Passive"] = 'Combat Passive Slot', | ["Passive"] = 'Combat Passive Slot', | ||
["Summon1"] = 'Summoning', | ["Summon1"] = 'Summoning', | ||
["Summon2"] = 'Summoning' | ["Summon2"] = 'Summoning', | ||
["Gem"] = "Gems_(Equipment)" | |||
} | } | ||
local slotText = {} | function getSlotText(slotList) | ||
local slotText = {} | |||
for i, slot in ipairs(slotList) do | |||
local slotLink = slotLinkMap[slot] | |||
if slotLink == nil then | |||
table.insert(slotText, slot) | |||
else | |||
table.insert(slotText, '[[' .. slotLink .. '|' .. slot .. ']]') | |||
end | |||
if slot == 'Passive' then | |||
isPassive = true | |||
end | |||
end | end | ||
return slotText | |||
end | end | ||
slotText = getSlotText(item.validSlots) | |||
table.insert(resultPart, "\r\n|-\r\n|'''Equipment Slot:''' "..table.concat(slotText, ', ')) | table.insert(resultPart, "\r\n|-\r\n|'''Equipment Slot:''' "..table.concat(slotText, ', ')) | ||
if item.occupiesSlots ~= nil then | |||
slotText = getSlotText(item.occupiesSlots) | |||
table.insert(resultPart, "\r\n|-\r\n|'''Blocked Slot:''' "..table.concat(slotText, ', ')) | |||
end | |||
end | end | ||
--For weapons with a special attack, show the details | --For weapons with a special attack, show the details | ||
if item. | if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then | ||
table.insert(resultPart, "\r\n|-\r\n|'''Special Attack:'''") | table.insert(resultPart, "\r\n|-\r\n|'''Special Attack:'''") | ||
for i, | for i, spAttID in ipairs(item.specialAttacks) do | ||
table.insert(resultPart, '\r\n* ' .. | local spAtt = GameData.getEntityByID('attacks', spAttID) | ||
if spAtt ~= nil then | |||
local spAttChance = spAtt.defaultChance | |||
if type(item.overrideSpecialChances) == 'table' and item.overrideSpecialChances[i] ~= nil then | |||
spAttChance = item.overrideSpecialChances[i] | |||
end | |||
local spAttDesc = string.gsub(spAtt.description, '<Attack> ', '') | |||
table.insert(resultPart, '\r\n* ' .. spAttChance .. '% chance for ' .. spAtt.name .. ':') | |||
table.insert(resultPart, '\r\n** ' .. spAttDesc) | |||
end | |||
end | end | ||
end | |||
-- For Summoning combat familiars, show the max hit | |||
if item.equipmentStats ~= nil and item.equipmentStats.summoningMaxhit ~= nil then | |||
table.insert(resultPart, "\r\n|-\r\n|'''Max Hit:''' " .. Num.formatnum(item.equipmentStats.summoningMaxhit * 10)) | |||
end | end | ||
--For potions, show the number of charges | --For potions, show the number of charges | ||
if item. | if item.charges ~= nil then | ||
table.insert(resultPart, "\r\n|-\r\n|'''Charges:''' "..item. | table.insert(resultPart, "\r\n|-\r\n|'''Charges:''' "..item.charges) | ||
end | end | ||
--For food, show how much it heals for | --For food, show how much it heals for | ||
Line 300: | Line 551: | ||
if item.prayerPoints ~= nil then | if item.prayerPoints ~= nil then | ||
table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', type='skill'}).." Points:''' "..item.prayerPoints) | table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', type='skill'}).." Points:''' "..item.prayerPoints) | ||
end | |||
if item.soulPoints ~= nil then | |||
table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', 'Soul', type='item', img='Lesser Soul'}).." Points:''' "..item.soulPoints) | |||
end | |||
--For items that provide runes, show which runes are provided | |||
if item.providedRunes ~= nil then | |||
table.insert(resultPart, "\r\n|-\r\n|'''Runes Provided:''' ") | |||
local runeLines = {} | |||
local sortVal = '' | |||
for j, runePair in pairs(item.providedRunes) do | |||
local runeID = runePair.id | |||
local qty = runePair.quantity | |||
local rune = p.getItemByID(runeID) | |||
sortVal = sortVal..rune.name..qty | |||
table.insert(runeLines, Icons.Icon({rune.name, type='item', qty=qty})) | |||
end | |||
table.insert(resultPart, table.concat(runeLines, ', ')) | |||
end | end | ||
--For items with modifiers, show what those are | --For items with modifiers, show what those are | ||
if item.modifiers ~= nil and Shared. | if item.modifiers ~= nil and not Shared.tableIsEmpty(item.modifiers) then | ||
table.insert(resultPart, "\r\n|-\r\n|'''Modifiers:'''\r\n".. | table.insert(resultPart, "\r\n|-\r\n|'''Modifiers:'''\r\n") | ||
if isPassive then | |||
table.insert(resultPart, '<span style="color:green">Passive:</span><br/>') | |||
end | |||
table.insert(resultPart, Modifiers.getModifiersText(item.modifiers, true, false, 10)) | |||
end | end | ||
return table.concat(resultPart) | return table.concat(resultPart) | ||
Line 311: | Line 583: | ||
local itemName = frame.args ~= nil and frame.args[1] or frame | local itemName = frame.args ~= nil and frame.args[1] or frame | ||
local item = p.getItem(itemName) | local item = p.getItem(itemName) | ||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | end | ||
return p._getOtherItemBoxText(item) | |||
end | |||
function p._getCurrencyItemBoxText(item) | |||
local span = mw.html.create('span') | |||
:wikitext("'''Sells For:''' ") | |||
:wikitext(Icons._Currency((item.sellsForCurrency or 'melvorD:GP'), item.sellsFor)) | |||
return span | |||
end | |||
function p.getCurrencyItemBoxText(frame) | |||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = p.getItem(itemName) | |||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | ||
end | end | ||
return p. | return p._getCurrencyItemBoxText(item) | ||
end | end | ||
function p._getItemCategories(item) | function p._getItemCategories(item) | ||
local resultPart = {} | local resultPart = {} | ||
local isEquipment = item.validSlots ~= nil or item.occupiesSlots ~= nil or item.equipmentStats ~= nil | |||
if item.type ~= nil then table.insert(resultPart, '[[Category:'..item.type..']]') end | local category = p._getItemStat(item, 'category', false) | ||
if item.tier ~= nil then table.insert(resultPart, '[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]') end | if category ~= nil and category ~= 'Skills' then | ||
if item. | table.insert(resultPart, '[[Category:'..category..']]') | ||
end | |||
if item.type ~= nil then | |||
table.insert(resultPart, '[[Category:'..item.type..']]') | |||
end | |||
if isEquipment and item.tier ~= nil then | |||
table.insert(resultPart, '[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]') | |||
end | |||
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then | |||
table.insert(resultPart, '[[Category:Items With Special Attacks]]') | |||
end | |||
if item.validSlots ~= nil then | if item.validSlots ~= nil then | ||
local slotRemap = { | local slotRemap = { | ||
Line 363: | Line 658: | ||
local item = p.getItem(itemName) | local item = p.getItem(itemName) | ||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | ||
end | end | ||
Line 369: | Line 664: | ||
end | end | ||
function p. | function p.getItemGrid(frame) | ||
local | --melvorF, melvorD, melvorTotH, melvorAoD | ||
local | local dlcFunc = function(item, dlc) | ||
if | local itemDLC = Shared.getLocalID(item.id) | ||
return '' | if dlc == nil then | ||
return true | |||
end | |||
if dlc == 'base' then | |||
return itemDLC == 'melvorD' or itemDLC == 'melvorF' | |||
end | |||
if itemDLC == dlc then | |||
return true | |||
end | |||
return false | |||
end | |||
-- Convert list of hidden items into a key/value structure, such that | |||
-- lookups are more efficient than repeated calls to Shared.contains() | |||
local hiddenItemIDs = {} | |||
for i, itemID in ipairs(p.HiddenItems) do | |||
hiddenItemIDs[itemID] = 1 | |||
end | |||
local args = frame:getParent().args | |||
local dlc = args[1] or args.DLC or args.dlc or nil | |||
local columns = tonumber(args[2] or args.Columns or args.columns) or 17 | |||
local html = mw.html.create('table') | |||
:addClass('wikitable lighttable individual table-img-grid') | |||
local curRow = html:tag('tr') | |||
local i = 0 | |||
for _, v in pairs(GameData.rawData.items) do | |||
if hiddenItemIDs[v.id] == nil and dlcFunc(v, dlc) == true then | |||
if i >= columns then | |||
curRow = html:tag('tr') | |||
i = 0 | |||
end | |||
local cell = curRow:tag('td') | |||
:wikitext(Icons.Icon({v.name, type='item', notext=true, size='32'})) | |||
-- Mod operator is slow. We use this instead | |||
i = i + 1 | |||
end | |||
end | |||
return tostring(html) | |||
end | |||
function p.getEquipRequirementRow(req) | |||
local result = "" | |||
if (req.type == "SkillLevel" or req.type == "AbyssalLevel") then | |||
local pre = (req.type == "AbyssalLevel" and ' Abyssal') or '' | |||
local skillName = Constants.getSkillName(req.skillID) | |||
local skillIcon = Icons.Icon({skillName, type='skill', notext=true}) | |||
result = '\r\n!style="text-align:right;"| '..skillIcon..pre..' Level Required' | |||
result = result..'\r\n|style="text-align:right;"| '..req.level | |||
elseif (req.type == "DungeonCompletion" or req.type == "AbyssDepthCompletion") then | |||
local reqDefns = { | |||
["DungeonCompletion"] = { | |||
["dataKey"] = 'dungeons', | |||
["IDKey"] = 'dungeonID', | |||
["imgType"] = 'dungeon' | |||
}, | |||
["AbyssDepthCompletion"] = { | |||
["dataKey"] = 'abyssDepths', | |||
["IDKey"] = 'depthID', | |||
["imgType"] = 'combatArea' | |||
} | |||
} | |||
local reqDefn = reqDefns[req.type] | |||
if reqDefn ~= nil then | |||
local area = GameData.getEntityByID(reqDefn.dataKey, req[reqDefn.IDKey]) | |||
if area == nil then | |||
result = '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid area for requirement type "' .. req.type .. '"') | |||
else | |||
local areaIcon = Icons.Icon({area.name, type=reqDefn.imgType, notext=true}) | |||
result = '\r\n!style="text-align:right;"| '..areaIcon..' Completions' | |||
result = result..'\r\n|style="text-align:right;"| '..Num.formatnum(req.count) | |||
end | |||
end | |||
elseif req.type == "Completion" then | |||
local ns = GameData.getEntityByName('namespaces', req.namespace) | |||
if ns == nil then | |||
return '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid namespace for completion requirement "' .. req.namespace .. '"') | |||
else | |||
result = '\r\n!style="text-align:right;"| ' .. ns.displayName .. ' Completion' | |||
result = result .. '\r\n|style="text-align:right;"| ' .. req.percent .. '%' | |||
end | |||
else | else | ||
return '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid equip requirement type "' .. req.type .. '"') | |||
end | end | ||
return result | |||
end | end | ||
function p. | function p._getItemResistance(item) | ||
local | local namespace = Shared.getLocalID(item.id) | ||
if namespace == 'melvorItA' then | |||
local eternalResistance = p._getItemStat(item, 'resistanceEternal', false) | |||
if eternalResistance ~= nil or item.damageType ~= nil and item.damageType == 'melvorItA:Eternal' then | |||
return eternalResistance or 0, 'Eternal Resistance' | |||
else | else | ||
return p._getItemStat(item, 'resistanceAbyssal', true), 'Abyssal Resistance' | |||
end | end | ||
else | |||
return p._getItemStat(item, 'damageReduction', true), 'Damage Reduction' | |||
end | end | ||
end | end | ||
Line 404: | Line 780: | ||
local item = p.getItem(itemName) | local item = p.getItem(itemName) | ||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | ||
end | |||
local resistance, resistanceText = p._getItemResistance(item) | |||
local ns, damageType = nil, nil | |||
if item.damageType ~= nil then | |||
ns, damageType = Shared.getLocalID(p._getItemStat(item, 'damageType')) | |||
else | |||
damageType = 'Normal' | |||
end | end | ||
Line 414: | Line 798: | ||
["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}), | ["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}), | ||
["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}), | ["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}), | ||
["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true}) | ["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true}), | ||
["Resistance"] = Icons.Icon({resistanceText, notext=true}), | |||
["DamageType"] = Icons.Icon({(damageType .. ' Damage'), type="damage", notext=true}) | |||
} | } | ||
local reqCount = item.equipRequirements ~= nil and Shared.tableCount(item.equipRequirements) or 0 | |||
local emptyRow = '\r\n!colspan="2"|' | |||
local resultPart = {} | local resultPart = {} | ||
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Weapon Stats') | table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Weapon Stats') | ||
Line 422: | Line 812: | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Speed') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Speed') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.round(p._getItemStat(item, 'attackSpeed', true) / 1000, 3, 1) .. 's') | ||
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus') | table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true)) | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Damage Type') | |||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. ico['DamageType'] .. ' ' .. damageType) | |||
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Resistance'] .. ' ' .. resistanceText) | |||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. resistance .. '%') | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Type') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Type') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackType')) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackType')) | ||
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus') | table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true)) | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico[' | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, ' | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'meleeStrengthBonus', true))) | ||
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus') | table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true)) | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus') | |||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'stabAttackBonus', true))) | |||
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Equip Requirements') | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'slashAttackBonus', true))) | ||
table.insert(resultPart, '\r\n | if reqCount > 0 then | ||
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[1])) | |||
else | |||
table.insert(resultPart, '\r\n|colspan=2 style="text-align:right"|None') | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'blockAttackBonus', true))) | ||
table.insert(resultPart, | if reqCount > 1 then | ||
table.insert(resultPart, | table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[2])) | ||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedAttackBonus', true))) | ||
table.insert(resultPart, | if reqCount > 2 then | ||
table.insert(resultPart, | table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[3])) | ||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedStrengthBonus', true))) | ||
table.insert(resultPart, | if reqCount > 3 then | ||
table.insert(resultPart, | table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[4])) | ||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'magicAttackBonus', true))) | ||
table.insert(resultPart, | if reqCount > 4 then | ||
table.insert(resultPart, | table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[5])) | ||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%') | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'magicDamageBonus', true)) .. '%') | ||
table.insert(resultPart, '\r\n!style="text-align:right;"| Two Handed?') | if reqCount > 5 then | ||
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[6])) | |||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Two Handed?') | |||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. (p._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. (p._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')) | ||
if reqCount > 6 then | |||
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[6])) | |||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
--Add extra rows at the end for items that have more than 3 different requirements | |||
if reqCount > 7 then | |||
for i = 8, reqCount, 1 do | |||
table.insert(resultPart,"\r\n|-") | |||
table.insert(resultPart, emptyRow) | |||
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[i])) | |||
end | |||
end | |||
table.insert(resultPart, '\r\n|}') | table.insert(resultPart, '\r\n|}') | ||
Line 478: | Line 908: | ||
local item = p.getItem(itemName) | local item = p.getItem(itemName) | ||
if item == nil then | if item == nil then | ||
return | return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | ||
end | end | ||
local resistance, resistanceText = p._getItemResistance(item) | |||
local ico = { | local ico = { | ||
Line 488: | Line 920: | ||
["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}), | ["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}), | ||
["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}), | ["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}), | ||
["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true}) | ["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true}), | ||
["Resistance"] = Icons.Icon({resistanceText, notext=true}), | |||
} | } | ||
local reqCount = item.equipRequirements ~= nil and Shared.tableCount(item.equipRequirements) or 0 | |||
local emptyRow = '\r\n!colspan="2"|' | |||
local resultPart = {} | local resultPart = {} | ||
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Armour Stats') | table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Armour Stats') | ||
Line 496: | Line 933: | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeStrengthBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'meleeStrengthBonus', true))) | ||
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus') | table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'meleeDefenceBonus', true))) | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'stabAttackBonus', | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'stabAttackBonus', true))) | ||
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico[' | table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Resistance'] .. ' ' .. resistanceText) | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. resistance .. '%') | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'slashAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'slashAttackBonus', true))) | ||
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus') | table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedDefenceBonus', true))) | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'blockAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'blockAttackBonus', true))) | ||
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus') | table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true)) | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedAttackBonus', true))) | ||
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| | table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Equip Requirements') | ||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedStrengthBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedStrengthBonus', true))) | ||
table.insert(resultPart, | if reqCount > 0 then | ||
table.insert(resultPart, '\r\n|style="text-align:right | table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[1])) | ||
else | |||
table.insert(resultPart, '\r\n|colspan=2 style="text-align:right"|None') | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicAttackBonus', true)) | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'magicAttackBonus', true))) | ||
table.insert(resultPart, | if reqCount > 1 then | ||
table.insert(resultPart, | table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[2])) | ||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus') | table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus') | ||
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%') | table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%') | ||
table.insert(resultPart, | if reqCount > 2 then | ||
table.insert(resultPart, | table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[3])) | ||
else | |||
table.insert(resultPart, emptyRow) | |||
end | |||
--Add extra rows at the end for items that have more than 3 different requirements | |||
if reqCount > 3 then | |||
for i = 4, reqCount, 1 do | |||
table.insert(resultPart, "\r\n|-") | |||
table.insert(resultPart, emptyRow) | |||
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[i])) | |||
end | |||
end | |||
table.insert(resultPart, '\r\n|}') | table.insert(resultPart, '\r\n|}') | ||
Line 549: | Line 1,000: | ||
:tag('th'):wikitext('ItemName'):done() | :tag('th'):wikitext('ItemName'):done() | ||
:tag('th'):wikitext('GPValue'):done() | :tag('th'):wikitext('GPValue'):done() | ||
for i, item in ipairs( | for i, item in ipairs(GameData.rawData.items) do | ||
resultTable:tag('tr') | resultTable:tag('tr') | ||
:tag('td'):wikitext(item.id):done() | :tag('td'):wikitext(item.id):done() | ||
Line 557: | Line 1,008: | ||
end | end | ||
return tostring(resultTable) | return tostring(resultTable) | ||
end | |||
--Returns the expansion icon for the item if it has one | |||
function p.getExpansionIcon(frame) | |||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = p.getItem(itemName) | |||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | |||
return Icons.getExpansionIcon(item.id) | |||
end | |||
function p.buildSmithableArmourNav(frame) | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:auto; clear:both; width: 100%"') | |||
table.insert(resultPart, '\r\n!colspan = 2 style="background-color:#275C87;color:#FFFFFF;min-width:730px;"|') | |||
table.insert(resultPart, Icons.Icon({'Smithing', type='skill', notext=true})) | |||
table.insert(resultPart, ' Smithable Armour Sets') | |||
local metalTypes = {'Bronze', 'Iron', 'Steel', 'Mithril', {'Adamant', 'Adamantite'}, {'Rune', 'Runite'}, {'Dragon', 'Dragonite'}, | |||
{'Corundum', 'Corundumite', TotH = true}, {'Augite', 'Augite', TotH = true}, {'Divine', 'Divinite', TotH = true}} | |||
local pieces = {"Helmet", "Platebody", "Platelegs", "Boots", "Shield"} | |||
for i, metal in ipairs(metalTypes) do | |||
local metalName, barName | |||
local isTotH = false | |||
if type(metal) == 'table' then | |||
metalName = metal[1] | |||
barName = metal[2]..' Bar' | |||
isTotH = metal.TotH ~= nil and metal.TotH | |||
else | |||
metalName = metal | |||
barName = metal..' Bar' | |||
end | |||
table.insert(resultPart, '\r\n|-\r\n!') | |||
if isTotH then | |||
table.insert(resultPart, Icons.TotH()) | |||
end | |||
table.insert(resultPart, Icons.Icon({barName, type="item", notext=true})) | |||
table.insert(resultPart, " "..metalName) | |||
table.insert(resultPart, "\r\n|") | |||
for j, piece in ipairs(pieces) do | |||
if j > 1 then | |||
table.insert(resultPart, ' • ') | |||
end | |||
table.insert(resultPart, '<span style="display:inline-block">') | |||
table.insert(resultPart, Icons.Icon({metalName..' '..piece, piece, type='item'})) | |||
if isTotH then | |||
table.insert(resultPart, ' '..Icons.Icon({'(I) '..metalName..' '..piece, '(I)', type='item'})) | |||
table.insert(resultPart, ' '..Icons.Icon({'(P) '..metalName..' '..piece, '(P)', type='item'})) | |||
else | |||
table.insert(resultPart, ' '..Icons.Icon({'(S) '..metalName..' '..piece, '(S)', type='item'})) | |||
table.insert(resultPart, ' '..Icons.Icon({'(G) '..metalName..' '..piece, '(G)', type='item'})) | |||
end | |||
table.insert(resultPart, '</span>') | |||
end | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.buildCraftableArmourNav(frame) | |||
local resultPart = {} | |||
table.insert(resultPart, '{| class="wikitable mw-collapsible"') | |||
table.insert(resultPart, '\r\n!colspan = 2 style="background-color:#275C87;color:#FFFFFF;min-width:730px;"|') | |||
table.insert(resultPart, Icons.Icon({'Crafting', type='skill', notext=true})) | |||
table.insert(resultPart, ' Craftable Armour Sets') | |||
local leatherTypes = {'Leather', 'Hard Leather'} | |||
local leatherPieces = {"Cowl", "Body", "Chaps", "Gloves", "Vambraces", "Boots"} | |||
table.insert(resultPart, '\r\n|-\r\n!') | |||
table.insert(resultPart, Icons.Icon({'Leather', type='item', notext=true})) | |||
table.insert(resultPart, ' Leather') | |||
for i, material in pairs(leatherTypes) do | |||
if i > 1 then table.insert(resultPart, '\r\n|-\r\n!Hard Leather') end | |||
table.insert(resultPart, '\r\n|') | |||
for j, piece in ipairs(leatherPieces) do | |||
if j > 1 then | |||
table.insert(resultPart, ' • ') | |||
end | |||
table.insert(resultPart, Icons.Icon({material..' '..piece, piece, type='item'})) | |||
end | |||
end | |||
local materialTypes = {{'Green D-hide', 'Green Dragonhide'}, {'Blue D-hide', 'Blue Dragonhide'}, {'Red D-hide', 'Red Dragonhide'}, {'Black D-hide', 'Black Dragonhide'}, | |||
{'Elderwood', 'Elderwood Logs', TotH = true}, {'Revenant', 'Revenant Logs', TotH = true}, {'Carrion', 'Carrion Logs', TotH = true}} | |||
local pieces = {"Body", "Chaps", "Vambraces", "Shield"} | |||
for i, material in ipairs(materialTypes) do | |||
local isTotH = false | |||
local craftName = material[1] | |||
local matName = material[2] | |||
isTotH = material.TotH ~= nil and material.TotH | |||
table.insert(resultPart, '\r\n|-\r\n!') | |||
if isTotH then | |||
table.insert(resultPart, Icons.TotH()) | |||
end | |||
table.insert(resultPart, Icons.Icon({matName, type="item", notext=true})) | |||
table.insert(resultPart, " "..craftName) | |||
table.insert(resultPart, "\r\n|") | |||
for j, piece in ipairs(pieces) do | |||
if j > 1 then | |||
table.insert(resultPart, ' • ') | |||
end | |||
table.insert(resultPart, '<span style="display:inline-block">') | |||
table.insert(resultPart, Icons.Icon({craftName..' '..piece, piece, type='item'})) | |||
table.insert(resultPart, ' '..Icons.Icon({'(U) '..craftName..' '..piece, '(U)', type='item'})) | |||
table.insert(resultPart, '</span>') | |||
end | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | |||
end | |||
function p.getLifestealWeapons() | |||
local items = p.getItems(function(item) | |||
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then | |||
for i, spAttID in ipairs(item.specialAttacks) do | |||
local spAtt = GameData.getEntityByID('attacks', spAttID) | |||
if spAtt ~= nil then | |||
return spAtt.lifesteal > 0 | |||
end | |||
end | |||
end | |||
return false | |||
end) | |||
for i, item in ipairs(items) do | |||
mw.log(item.name) | |||
end | |||
end | end | ||
return p | return p |
Latest revision as of 03:10, 6 September 2024
Lua module containing all sorts of functions for getting data on items. Pulls data from Module:GameData/data.
Some functions were split to submodules:
--This module contains all sorts of functions for getting data on items
--Several functions related to use tables can be found at Module:Items/UseTables
--Functions related to source tables can be found at Module:Items/SourceTables
--Other functions moved to Module:Items/ComparisonTables
local p = {}
local GameData = require('Module:GameData')
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Modifiers = require('Module:Modifiers')
local Icons = require('Module:Icons')
local Num = require('Module:Number')
-- Used by p._getItemLevelReqs()
local LevelReqCache = {}
p.EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon', 'Easter Egg',
'Abnormal Log', 'Red Herring', 'Cool Glasses'}
p.EventItems = {'Christmas Cracker', 'Christmas Coal', 'Christmas Sweater',
'Christmas Wreath', 'Candy Cane', 'Santa Hat',
'Friendship Bracelet', 'Event Clue 1', 'Event Clue 2',
'Event Clue 3', 'Event Clue 4', 'Candle', 'Cake Base',
'Magical Flavouring', 'Magical Icing', 'Birthday Cake',
'Purple Party Hat', 'Birthday Token', 'Christmas Present (Yellow)',
'Christmas Present (Blue)', 'Christmas Present (Green)', 'Christmas Present (White)',
'Christmas Present (Purple)', 'Christmas Present (Standard)', 'Event Token - Holiday 2021',
'Holiday Scarf', 'Gingerbread House', 'Gingerbread Man', 'Edible Candy Cane',
'Locked Chest', 'Locked Chest Key', 'Event Token (Holiday 2021)'}
-- List of item IDs that should typically not be included within outputs, usually
-- because they are not fully implemented despite existing within the game data
p.HiddenItems = {}
local function populateHiddenItems()
local hiddenItems = GameData.getEntities('items',
function(item)
return item.name == nil or Shared.contains({'melvorTotH:Meteorite_Dust'}, item.id)
end
)
for _, item in ipairs(hiddenItems) do
table.insert(p.HiddenItems, item.id)
end
end
populateHiddenItems()
function p.getItemByID(ID)
return GameData.getEntityByID('items', ID)
end
function p.getItem(name)
name = Shared.fixPagename(name)
return GameData.getEntityByName('items', name)
end
function p.getItems(checkFunc)
return GameData.getEntities('items',
function(obj)
return not Shared.contains(p.HiddenItems, obj.id) and checkFunc(obj)
end
)
end
function p._canItemUseSlot(item, equipSlot)
--Function to easily check if an item can fit in a given equipment slot
--Ex: p._canItemUseSlot({Bronze Platebody}, 'Platebody') returns true
if type(item) == 'string' then
item = p.getItem(item)
end
return item.validSlots ~= nil and Shared.contains(item.validSlots, equipSlot)
end
function p._getItemEquipSlot(item)
--Function to return the (non-Passive) equipment slot that an item occupies
if type(item) == 'string' then
item = p.getItem(item)
end
if item == nil or item.validSlots == nil then
return 'Invalid'
end
for i, slot in pairs(item.validSlots) do
if slot ~= 'Passive' then
return slot
end
end
end
-- Given an item, returns a table containing the item's level requirements (if any)
-- The table structure is as follows:
-- { ['normal'] = { SkillID = SkillLevel, ... }, ['abyssal'] = { SkillID = SkillLevel, ... } }
-- If optional parameter 'condense' is specified as true, any normal requirements are excluded
-- from the output if there should exist an abyssal level requirement for the same skill
function p._getItemLevelReqs(item, condense)
local condenseResult = (type(condense) == 'boolean' and condense) or false
local reqTypeMap = {
['SkillLevel'] = 'normal',
['AbyssalLevel'] = 'abyssal'
}
local levelReqs = LevelReqCache[item.id]
if levelReqs == nil then
-- Item not in cache - obtain level requirements data
if item.equipRequirements == nil then
return nil
end
levelReqs = { ['normal'] = {}, ['abyssal'] = {} }
for _, requirement in ipairs(item.equipRequirements) do
local reqTypeKey = reqTypeMap[requirement.type]
if reqTypeKey ~= nil then
-- Requirement is a level requirement
local reqSkillID, reqLevel = requirement.skillID, requirement.level
local reqCurrentLevel = levelReqs[reqTypeKey][reqSkillID]
if reqCurrentLevel == nil or reqLevel > reqCurrentLevel then
levelReqs[reqTypeKey][reqSkillID] = reqLevel
end
end
end
-- Cache level requirements data for any subsequent lookups
LevelReqCache[item.id] = levelReqs
end
if not condenseResult then
-- Return requirements as is
return levelReqs
else
-- Exclude any normal level requirements should an abyssal requirement exist for
-- the same skill
local levelReqsCondensed = { ['normal'] = {}, ['abyssal'] = levelReqs.abyssal }
for skillID, reqLevel in pairs(levelReqs.normal) do
if levelReqs.abyssal[skillID] == nil then
levelReqsCondensed.normal[skillID] = reqLevel
end
end
return levelReqsCondensed
end
end
function p._getItemStat(item, StatName, ZeroIfNil)
local result = item[StatName]
--Special Overrides:
-- Equipment stats first
if item.equipmentStats ~= nil and item.equipmentStats[StatName] ~= nil then
result = item.equipmentStats[StatName]
elseif StatName == 'attackSpeed' and item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon') then
-- Item can be equipped as a weapon but has no attack speed, so use default of 4000ms
result = 4000
elseif StatName == 'isTwoHanded' then
if item.validSlots ~= nil and item.occupiesSlots ~= nil then
result = Shared.contains(item.validSlots, 'Weapon') and Shared.contains(item.occupiesSlots, 'Shield')
else
result = false
end
elseif (
string.find(StatName, '^(.+)LevelRequired$') ~= nil
or string.find(StatName, '^(.+)AbyssalLevel$') ~= nil
) and item.equipRequirements ~= nil then
local levelType = 'normal'
local skillName = string.match(StatName, '^(.+)LevelRequired$')
if skillName == nil then
levelType = 'abyssal'
skillName = string.match(StatName, '^(.+)AbyssalLevel$')
end
if skillName ~= nil then
local skillID = Constants.getSkillID(Shared.titleCase(skillName))
if skillID ~= nil then
local levelReqs = p._getItemLevelReqs(item)
result = levelReqs ~= nil and levelReqs[levelType][skillID]
end
end
elseif StatName == 'attackType' then
result = p._getWeaponAttackType(item)
elseif StatName == 'description' then
result = item.customDescription
if result == nil or result == '' then result = 'No Description' end
elseif StatName == 'completionReq' then
if item.ignoreCompletion == nil or not item.ignoreCompletion then
result = 'Yes'
else
result = 'No'
end
elseif StatName == 'slayerBonusXP' then
return p._getItemModifier(item, 'increasedSkillXP', 'Slayer', false)
elseif StatName == 'hasCombatStats' then
return tostring(p.hasCombatStats(item) or p._hasLevelRequirements(item))
elseif StatName == 'category' then
-- Some categories have a namespace for some reason, remove it
local _, localID = GameData.getLocalID(result)
return localID
end
if result == nil and ZeroIfNil then result = 0 end
return result
end
function p.getItemValueByID(itemID)
local item = p.getItemByID(itemID)
if item == nil then
return 0
end
return p.getItemValue(item['name'])
end
function p.getItemValue(item)
if type(item) == 'string' then
-- Specific check if the item is GP (value of 1)
if Shared.compareString('GP', item, true)
or Shared.compareString('Gold Pieces', item, true) then
return 1
end
item = p.getItem(item)
end
if item then
return item.sellsFor
end
return nil
end
function p.getValueText(item, minQuantity, maxQuantity)
local minQ, maxQ = 1, 1
if type(minQuantity) == 'number' then
minQ = minQuantity
end
if type(maxQuantity) == 'number' then
maxQ = maxQuantity
else
maxQ = minQ
end
local amt = item.sellsFor or 0
local currID = item.sellsForCurrency or 'melvorD:GP'
return Icons._Currency(currID, amt * minQ, amt * maxQ)
end
-- Function already exists, but without frame.
-- Giving it a slightly different name since function overloading doesn't exist
function p.getItemSellsFor(frame)
local args = frame:getParent().args
return p._getItemSellsFor(args[1], args[2], args.round)
end
function p._getItemSellsFor(itemName, multiplier, rounding)
local itemValue = p.getItemValue(itemName)
multiplier = tonumber(multiplier) or 1
rounding = tonumber(rounding) or 0
if itemValue == nil then
error('No item named "' .. itemName .. '" exists in the data module')
end
return Num.round2(itemValue * multiplier, rounding)
end
function p.getItemStat(frame)
local args = frame.args ~= nil and frame.args or frame
local ItemName = args[1]
local StatName = args[2]
local ZeroIfNil = args.ForceZero ~= nil and args.ForceZero ~= '' and args.ForceZero ~= 'false'
local formatNum = args.formatNum ~= nil and args.formatNum ~= '' and args.formatNum ~= 'false'
local item = p.getItem(ItemName)
if item == nil then
return Shared.printError('No item named "' .. ItemName .. '" exists in the data module')
end
local result = p._getItemStat(item, StatName, ZeroIfNil)
if formatNum then result = Num.formatnum(result) end
return result
end
--Gets the value of a given modifier for a given item
--asString is false by default, when true it writes the full bonus text
function p._getItemModifier(item, modifier, skillID, asString)
if asString == nil then asString = false end
if skillID == '' then
skillID = nil
elseif string.find(skillID, ':') == nil then
-- Try to find a skill ID if it looks like a skill name has been passed
skillID = Constants.getSkillID(skillID)
end
local result = 0
if item.modifiers ~= nil and item.modifiers[modifier] ~= nil then
if type(item.modifiers[modifier]) == 'table' then
for i, subVal in Shared.skpairs(item.modifiers[modifier]) do
if subVal[1] == skillID then
result = subVal[2]
break
end
end
else
result = item.modifiers[modifier]
end
end
if asString then
if skillID ~= nil then
return Constants._getModifierText(modifier, {skillID, result})
else
return Constants._getModifierText(modifier, result)
end
else
return result
end
end
function p.hasCombatStats(item)
-- Checks if the combat stat is a valid, non-zero combat stat
-- Ensure that, only in the case where the item is a Familar AND
-- the checked stat is summoningMaxhit, the result is ignored.
local function isNonZeroStat(statName, statVal)
if statName == 'summoningMaxhit' and (p._canItemUseSlot(item, 'Summon1') or p._canItemUseSlot(item, 'Summon2')) then
return false
end
return statVal ~= 0
end
if item.equipmentStats ~= nil then
-- Ensure at least one stat has a non-zero value
for statName, statVal in pairs(item.equipmentStats) do
if isNonZeroStat(statName, statVal) then
return true
end
end
end
return false
end
function p._hasLevelRequirements(item)
--Function true if an item has at least one level requirement to equip
local levelReqs = p._getItemLevelReqs(item)
if levelReqs ~= nil then
for levelType, reqs in pairs(levelReqs) do
if not Shared.tableIsEmpty(reqs) then
return true
end
end
end
return false
end
function p.getItemModifier(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame[1]
local modName = frame.args ~= nil and frame.args[2] or frame[2]
local skillName = frame.args ~= nil and frame.args[3] or frame[3]
local asString = frame.args ~= nil and frame.args[4] or frame[4]
if asString ~= nil then
asString = (string.upper(asString) ~= 'FALSE')
end
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
return p._getItemModifier(item, modName, skillName, asString)
end
function p._getWeaponAttackType(item)
if (item.validSlots ~= nil and Shared.contains(item.validSlots, 'Weapon')) or
(item.occupiesSlots ~= nil and Shared.contains(item.occupiesSlots, 'Weapon')) then
if Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then
local iconType = item.attackType ~= 'melee' and 'skill' or nil
return Icons.Icon({Shared.titleCase(item.attackType), type=iconType, nolink='true'})
end
end
return 'Invalid'
end
function p.getWeaponAttackType(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
return p._getWeaponAttackType(item)
end
local statChangeDefs = {
{
stat = 'stabAttackBonus',
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Stab Bonus'
},
{
stat = 'slashAttackBonus',
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Slash Bonus'
},
{
stat = 'blockAttackBonus',
suffix = ' ' .. Icons.Icon({'Melee', notext=true}) .. ' Block Bonus'
},
{
stat = 'meleeStrengthBonus',
suffix = ' ' .. Icons.Icon({'Strength', type='skill', notext=true}) .. ' Strength Bonus'
},
{
stat = 'rangedStrengthBonus',
suffix = ' ' .. Icons.Icon({'Ranged', type='skill', notext=true}) .. ' Strength Bonus'
},
{
stat = 'magicStrengthBonus',
suffix = '% ' .. Icons.Icon({'Magic', type='skill', notext=true}) .. ' Damage Bonus'
},
{
stat = 'meleeDefenceBonus',
suffix = ' ' .. Icons.Icon({'Defence', type='skill', notext=true}) .. ' Defence Bonus' },
{
stat = 'rangedDefenceBonus',
suffix = ' ' .. Icons.Icon({'Ranged', type='skill', notext=true}) .. ' Defence Bonus'
},
{
stat = 'magicDefenceBonus',
suffix = ' ' .. Icons.Icon({'Magic', type='skill', notext=true}) .. ' Defence Bonus'
},
{
stat = 'damageReduction',
suffix = '% Damage Reduction'
},
{
stat = 'levelRequired',
suffix = ' Level Required'
}
}
-- Produces a list of stat & modifier changes between two items of equipmednt
function p.getStatChangeString(item1, item2)
local changeArray = {}
local equipStats = {
type(item1.equipmentStats) == 'table' and item1.equipmentStats or {},
type(item2.equipmentStats) == 'table' and item2.equipmentStats or {}
}
for i, statDef in ipairs(statChangeDefs) do
local val1, val2 = 0, 0
if statDef.stat == 'levelRequired' then
-- Iterate over equipment stats for both items, determining level requirements
local levelReqs = {}
for itemNum, item in ipairs({item1, item2}) do
levelReqs[itemNum] = {}
if item.equipRequirements ~= nil then
for j, req in ipairs(item.equipRequirements) do
if req.type == 'SkillLevel' then
levelReqs[itemNum][req.skillID] = req.level
end
end
end
end
-- Iterate over all skills, checking if there are requirements for these in either skill
for j, skillData in ipairs(GameData.rawData.skillData) do
local skillID = skillData.skillID
val1, val2 = levelReqs[1][skillID] or 0, levelReqs[2][skillID] or 0
if val1 ~= val2 then
table.insert(changeArray, Num.numStrWithSign(val1 - val2) .. ' ' .. Icons.Icon({skillData.data.name, type='skill', notext=true}) .. (statDef.suffix or ''))
end
end
else
-- Equipment stats
val1, val2 = equipStats[1][statDef.stat] or 0, equipStats[2][statDef.stat] or 0
if val1 ~= val2 then
table.insert(changeArray, Num.numStrWithSign(val1 - val2) .. (statDef.suffix or ''))
end
end
end
-- Include differences in modifiers
-- TODO Implement getModifiersDifference
--local modDiff = Constants.getModifiersText(Constants.getModifiersDifference(item2.modifiers, item1.modifiers))
local modDiff = nil
if modDiff ~= nil and modDiff ~= '' then
table.insert(changeArray, modDiff)
end
return table.concat(changeArray, '<br/>')
end
function p._getOtherItemBoxText(item)
local resultPart = {}
--For equipment, show the slot they go in
local isPassive = false
if item.validSlots ~= nil then
local slotLinkMap = {
["Helmet"] = 'Helmets',
["Platebody"] = 'Platebodies',
["Platelegs"] = 'Platelegs',
["Boots"] = 'Boots',
["Weapon"] = 'Weapons',
["Shield"] = 'Shields',
["Amulet"] = 'Amulets',
["Ring"] = 'Rings',
["Gloves"] = 'Gloves',
["Quiver"] = 'Ammunition',
["Cape"] = 'Capes',
["Consumable"] = 'Consumables',
["Passive"] = 'Combat Passive Slot',
["Summon1"] = 'Summoning',
["Summon2"] = 'Summoning',
["Gem"] = "Gems_(Equipment)"
}
function getSlotText(slotList)
local slotText = {}
for i, slot in ipairs(slotList) do
local slotLink = slotLinkMap[slot]
if slotLink == nil then
table.insert(slotText, slot)
else
table.insert(slotText, '[[' .. slotLink .. '|' .. slot .. ']]')
end
if slot == 'Passive' then
isPassive = true
end
end
return slotText
end
slotText = getSlotText(item.validSlots)
table.insert(resultPart, "\r\n|-\r\n|'''Equipment Slot:''' "..table.concat(slotText, ', '))
if item.occupiesSlots ~= nil then
slotText = getSlotText(item.occupiesSlots)
table.insert(resultPart, "\r\n|-\r\n|'''Blocked Slot:''' "..table.concat(slotText, ', '))
end
end
--For weapons with a special attack, show the details
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
table.insert(resultPart, "\r\n|-\r\n|'''Special Attack:'''")
for i, spAttID in ipairs(item.specialAttacks) do
local spAtt = GameData.getEntityByID('attacks', spAttID)
if spAtt ~= nil then
local spAttChance = spAtt.defaultChance
if type(item.overrideSpecialChances) == 'table' and item.overrideSpecialChances[i] ~= nil then
spAttChance = item.overrideSpecialChances[i]
end
local spAttDesc = string.gsub(spAtt.description, '<Attack> ', '')
table.insert(resultPart, '\r\n* ' .. spAttChance .. '% chance for ' .. spAtt.name .. ':')
table.insert(resultPart, '\r\n** ' .. spAttDesc)
end
end
end
-- For Summoning combat familiars, show the max hit
if item.equipmentStats ~= nil and item.equipmentStats.summoningMaxhit ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Max Hit:''' " .. Num.formatnum(item.equipmentStats.summoningMaxhit * 10))
end
--For potions, show the number of charges
if item.charges ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Charges:''' "..item.charges)
end
--For food, show how much it heals for
if item.healsFor ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Heals for:''' "..Icons.Icon({"Hitpoints", type="skill", notext="true"})..' '..(item.healsFor * 10))
end
--For Prayer Points, show how many you get
if item.prayerPoints ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', type='skill'}).." Points:''' "..item.prayerPoints)
end
if item.soulPoints ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''"..Icons.Icon({'Prayer', 'Soul', type='item', img='Lesser Soul'}).." Points:''' "..item.soulPoints)
end
--For items that provide runes, show which runes are provided
if item.providedRunes ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Runes Provided:''' ")
local runeLines = {}
local sortVal = ''
for j, runePair in pairs(item.providedRunes) do
local runeID = runePair.id
local qty = runePair.quantity
local rune = p.getItemByID(runeID)
sortVal = sortVal..rune.name..qty
table.insert(runeLines, Icons.Icon({rune.name, type='item', qty=qty}))
end
table.insert(resultPart, table.concat(runeLines, ', '))
end
--For items with modifiers, show what those are
if item.modifiers ~= nil and not Shared.tableIsEmpty(item.modifiers) then
table.insert(resultPart, "\r\n|-\r\n|'''Modifiers:'''\r\n")
if isPassive then
table.insert(resultPart, '<span style="color:green">Passive:</span><br/>')
end
table.insert(resultPart, Modifiers.getModifiersText(item.modifiers, true, false, 10))
end
return table.concat(resultPart)
end
function p.getOtherItemBoxText(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
return p._getOtherItemBoxText(item)
end
function p._getCurrencyItemBoxText(item)
local span = mw.html.create('span')
:wikitext("'''Sells For:''' ")
:wikitext(Icons._Currency((item.sellsForCurrency or 'melvorD:GP'), item.sellsFor))
return span
end
function p.getCurrencyItemBoxText(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
return p._getCurrencyItemBoxText(item)
end
function p._getItemCategories(item)
local resultPart = {}
local isEquipment = item.validSlots ~= nil or item.occupiesSlots ~= nil or item.equipmentStats ~= nil
local category = p._getItemStat(item, 'category', false)
if category ~= nil and category ~= 'Skills' then
table.insert(resultPart, '[[Category:'..category..']]')
end
if item.type ~= nil then
table.insert(resultPart, '[[Category:'..item.type..']]')
end
if isEquipment and item.tier ~= nil then
table.insert(resultPart, '[[Category:'..Shared.titleCase(item.tier)..' '..item.type..']]')
end
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
table.insert(resultPart, '[[Category:Items With Special Attacks]]')
end
if item.validSlots ~= nil then
local slotRemap = {
['Passive'] = 'Passive Items',
['Summon1'] = 'Summoning Familiars',
['Summon2'] = ''
}
for i, slotName in ipairs(item.validSlots) do
local slotRemapName = slotName
if slotRemap[slotName] ~= nil then slotRemapName = slotRemap[slotName] end
if slotRemapName ~= '' then table.insert(resultPart, '[[Category:' .. slotRemapName .. ']]') end
end
end
if item.modifiers ~= nil then
local modsDL = {
'increasedChanceToDoubleLootCombat',
'decreasedChanceToDoubleLootCombat',
'increasedChanceToDoubleLootThieving',
'decreasedChanceToDoubleLootThieving',
'increasedChanceToDoubleItemsGlobal',
'decreasedChanceToDoubleItemsGlobal'
}
for modName, val in pairs(item.modifiers) do
if Shared.contains(modsDL, modName) then
table.insert(resultPart, '[[Category:Double Loot Chance Items]]')
break
end
end
end
return table.concat(resultPart)
end
function p.getItemCategories(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
return p._getItemCategories(item)
end
function p.getItemGrid(frame)
--melvorF, melvorD, melvorTotH, melvorAoD
local dlcFunc = function(item, dlc)
local itemDLC = Shared.getLocalID(item.id)
if dlc == nil then
return true
end
if dlc == 'base' then
return itemDLC == 'melvorD' or itemDLC == 'melvorF'
end
if itemDLC == dlc then
return true
end
return false
end
-- Convert list of hidden items into a key/value structure, such that
-- lookups are more efficient than repeated calls to Shared.contains()
local hiddenItemIDs = {}
for i, itemID in ipairs(p.HiddenItems) do
hiddenItemIDs[itemID] = 1
end
local args = frame:getParent().args
local dlc = args[1] or args.DLC or args.dlc or nil
local columns = tonumber(args[2] or args.Columns or args.columns) or 17
local html = mw.html.create('table')
:addClass('wikitable lighttable individual table-img-grid')
local curRow = html:tag('tr')
local i = 0
for _, v in pairs(GameData.rawData.items) do
if hiddenItemIDs[v.id] == nil and dlcFunc(v, dlc) == true then
if i >= columns then
curRow = html:tag('tr')
i = 0
end
local cell = curRow:tag('td')
:wikitext(Icons.Icon({v.name, type='item', notext=true, size='32'}))
-- Mod operator is slow. We use this instead
i = i + 1
end
end
return tostring(html)
end
function p.getEquipRequirementRow(req)
local result = ""
if (req.type == "SkillLevel" or req.type == "AbyssalLevel") then
local pre = (req.type == "AbyssalLevel" and ' Abyssal') or ''
local skillName = Constants.getSkillName(req.skillID)
local skillIcon = Icons.Icon({skillName, type='skill', notext=true})
result = '\r\n!style="text-align:right;"| '..skillIcon..pre..' Level Required'
result = result..'\r\n|style="text-align:right;"| '..req.level
elseif (req.type == "DungeonCompletion" or req.type == "AbyssDepthCompletion") then
local reqDefns = {
["DungeonCompletion"] = {
["dataKey"] = 'dungeons',
["IDKey"] = 'dungeonID',
["imgType"] = 'dungeon'
},
["AbyssDepthCompletion"] = {
["dataKey"] = 'abyssDepths',
["IDKey"] = 'depthID',
["imgType"] = 'combatArea'
}
}
local reqDefn = reqDefns[req.type]
if reqDefn ~= nil then
local area = GameData.getEntityByID(reqDefn.dataKey, req[reqDefn.IDKey])
if area == nil then
result = '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid area for requirement type "' .. req.type .. '"')
else
local areaIcon = Icons.Icon({area.name, type=reqDefn.imgType, notext=true})
result = '\r\n!style="text-align:right;"| '..areaIcon..' Completions'
result = result..'\r\n|style="text-align:right;"| '..Num.formatnum(req.count)
end
end
elseif req.type == "Completion" then
local ns = GameData.getEntityByName('namespaces', req.namespace)
if ns == nil then
return '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid namespace for completion requirement "' .. req.namespace .. '"')
else
result = '\r\n!style="text-align:right;"| ' .. ns.displayName .. ' Completion'
result = result .. '\r\n|style="text-align:right;"| ' .. req.percent .. '%'
end
else
return '\r\n!style="text-align:right;" colspan=2|' .. Shared.printError('Invalid equip requirement type "' .. req.type .. '"')
end
return result
end
function p._getItemResistance(item)
local namespace = Shared.getLocalID(item.id)
if namespace == 'melvorItA' then
local eternalResistance = p._getItemStat(item, 'resistanceEternal', false)
if eternalResistance ~= nil or item.damageType ~= nil and item.damageType == 'melvorItA:Eternal' then
return eternalResistance or 0, 'Eternal Resistance'
else
return p._getItemStat(item, 'resistanceAbyssal', true), 'Abyssal Resistance'
end
else
return p._getItemStat(item, 'damageReduction', true), 'Damage Reduction'
end
end
function p.getWeaponStatsBox(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
local resistance, resistanceText = p._getItemResistance(item)
local ns, damageType = nil, nil
if item.damageType ~= nil then
ns, damageType = Shared.getLocalID(p._getItemStat(item, 'damageType'))
else
damageType = 'Normal'
end
local ico = {
["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
["Combat"] = Icons.Icon({'Combat', notext=true}),
["Defence"] = Icons.Icon({'Defence', type='skill', notext=true}),
["Magic"] = Icons.Icon({'Magic', type='skill', notext=true}),
["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}),
["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}),
["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true}),
["Resistance"] = Icons.Icon({resistanceText, notext=true}),
["DamageType"] = Icons.Icon({(damageType .. ' Damage'), type="damage", notext=true})
}
local reqCount = item.equipRequirements ~= nil and Shared.tableCount(item.equipRequirements) or 0
local emptyRow = '\r\n!colspan="2"|'
local resultPart = {}
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Weapon Stats')
table.insert(resultPart, '\r\n|-\r\n!colspan="2" style="border-bottom:solid thin black;"| Offensive Stats')
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Defensive Stats')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Speed')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.round(p._getItemStat(item, 'attackSpeed', true) / 1000, 3, 1) .. 's')
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'meleeDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Damage Type')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. ico['DamageType'] .. ' ' .. damageType)
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Resistance'] .. ' ' .. resistanceText)
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. resistance .. '%')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Attack Type')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'attackType'))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'rangedDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'meleeStrengthBonus', true)))
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'stabAttackBonus', true)))
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Equip Requirements')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'slashAttackBonus', true)))
if reqCount > 0 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[1]))
else
table.insert(resultPart, '\r\n|colspan=2 style="text-align:right"|None')
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'blockAttackBonus', true)))
if reqCount > 1 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[2]))
else
table.insert(resultPart, emptyRow)
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedAttackBonus', true)))
if reqCount > 2 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[3]))
else
table.insert(resultPart, emptyRow)
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedStrengthBonus', true)))
if reqCount > 3 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[4]))
else
table.insert(resultPart, emptyRow)
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'magicAttackBonus', true)))
if reqCount > 4 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[5]))
else
table.insert(resultPart, emptyRow)
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'magicDamageBonus', true)) .. '%')
if reqCount > 5 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[6]))
else
table.insert(resultPart, emptyRow)
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| Two Handed?')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. (p._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No'))
if reqCount > 6 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[6]))
else
table.insert(resultPart, emptyRow)
end
--Add extra rows at the end for items that have more than 3 different requirements
if reqCount > 7 then
for i = 8, reqCount, 1 do
table.insert(resultPart,"\r\n|-")
table.insert(resultPart, emptyRow)
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[i]))
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getArmourStatsBox(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
local resistance, resistanceText = p._getItemResistance(item)
local ico = {
["Attack"] = Icons.Icon({'Attack', type='skill', notext=true}),
["Combat"] = Icons.Icon({'Combat', notext=true}),
["Defence"] = Icons.Icon({'Defence', type='skill', notext=true}),
["Magic"] = Icons.Icon({'Magic', type='skill', notext=true}),
["Ranged"] = Icons.Icon({'Ranged', type='skill', notext=true}),
["Strength"] = Icons.Icon({'Strength', type='skill', notext=true}),
["Slayer"] = Icons.Icon({'Slayer', type='skill', notext=true}),
["Resistance"] = Icons.Icon({resistanceText, notext=true}),
}
local reqCount = item.equipRequirements ~= nil and Shared.tableCount(item.equipRequirements) or 0
local emptyRow = '\r\n!colspan="2"|'
local resultPart = {}
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="4" style="border-bottom:solid medium black;"| Armour Stats')
table.insert(resultPart, '\r\n|-\r\n!colspan="2" style="border-bottom:solid thin black;"| Offensive Stats')
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Defensive Stats')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Strength'] .. ' Strength Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'meleeStrengthBonus', true)))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Defence'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'meleeDefenceBonus', true)))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Stab Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'stabAttackBonus', true)))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Resistance'] .. ' ' .. resistanceText)
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. resistance .. '%')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Slash Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'slashAttackBonus', true)))
table.insert(resultPart, '\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedDefenceBonus', true)))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Combat'] .. ' Block Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'blockAttackBonus', true)))
table.insert(resultPart, '\r\n!style="text-align:right;border-bottom:solid thin black;"| ' .. ico['Magic'] .. ' Defence Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;border-bottom:solid thin black;"| ' .. p._getItemStat(item, 'magicDefenceBonus', true))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedAttackBonus', true)))
table.insert(resultPart, '\r\n!colspan="2" style="border-bottom:solid thin black;"| Equip Requirements')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Ranged'] .. ' Strength Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'rangedStrengthBonus', true)))
if reqCount > 0 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[1]))
else
table.insert(resultPart, '\r\n|colspan=2 style="text-align:right"|None')
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' Attack Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. Num.formatnum(p._getItemStat(item, 'magicAttackBonus', true)))
if reqCount > 1 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[2]))
else
table.insert(resultPart, emptyRow)
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"| ' .. ico['Magic'] .. ' % Damage Bonus')
table.insert(resultPart, '\r\n|style="text-align:right;"| ' .. p._getItemStat(item, 'magicDamageBonus', true) .. '%')
if reqCount > 2 then
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[3]))
else
table.insert(resultPart, emptyRow)
end
--Add extra rows at the end for items that have more than 3 different requirements
if reqCount > 3 then
for i = 4, reqCount, 1 do
table.insert(resultPart, "\r\n|-")
table.insert(resultPart, emptyRow)
table.insert(resultPart, p.getEquipRequirementRow(item.equipRequirements[i]))
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getItemDataExport(frame)
local resultTable = mw.html.create('table')
resultTable:addClass('wikitable')
resultTable:tag('tr'):addClass('headerRow-0')
:tag('th'):wikitext('ItemID'):done()
:tag('th'):wikitext('ItemName'):done()
:tag('th'):wikitext('GPValue'):done()
for i, item in ipairs(GameData.rawData.items) do
resultTable:tag('tr')
:tag('td'):wikitext(item.id):done()
:tag('td'):wikitext(item.name):done()
:tag('td'):wikitext(item.sellsFor):done()
end
return tostring(resultTable)
end
--Returns the expansion icon for the item if it has one
function p.getExpansionIcon(frame)
local itemName = frame.args ~= nil and frame.args[1] or frame
local item = p.getItem(itemName)
if item == nil then
return Shared.printError('No item named "' .. itemName .. '" exists in the data module')
end
return Icons.getExpansionIcon(item.id)
end
function p.buildSmithableArmourNav(frame)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable mw-collapsible navigation-not-searchable" style="margin:auto; clear:both; width: 100%"')
table.insert(resultPart, '\r\n!colspan = 2 style="background-color:#275C87;color:#FFFFFF;min-width:730px;"|')
table.insert(resultPart, Icons.Icon({'Smithing', type='skill', notext=true}))
table.insert(resultPart, ' Smithable Armour Sets')
local metalTypes = {'Bronze', 'Iron', 'Steel', 'Mithril', {'Adamant', 'Adamantite'}, {'Rune', 'Runite'}, {'Dragon', 'Dragonite'},
{'Corundum', 'Corundumite', TotH = true}, {'Augite', 'Augite', TotH = true}, {'Divine', 'Divinite', TotH = true}}
local pieces = {"Helmet", "Platebody", "Platelegs", "Boots", "Shield"}
for i, metal in ipairs(metalTypes) do
local metalName, barName
local isTotH = false
if type(metal) == 'table' then
metalName = metal[1]
barName = metal[2]..' Bar'
isTotH = metal.TotH ~= nil and metal.TotH
else
metalName = metal
barName = metal..' Bar'
end
table.insert(resultPart, '\r\n|-\r\n!')
if isTotH then
table.insert(resultPart, Icons.TotH())
end
table.insert(resultPart, Icons.Icon({barName, type="item", notext=true}))
table.insert(resultPart, " "..metalName)
table.insert(resultPart, "\r\n|")
for j, piece in ipairs(pieces) do
if j > 1 then
table.insert(resultPart, ' • ')
end
table.insert(resultPart, '<span style="display:inline-block">')
table.insert(resultPart, Icons.Icon({metalName..' '..piece, piece, type='item'}))
if isTotH then
table.insert(resultPart, ' '..Icons.Icon({'(I) '..metalName..' '..piece, '(I)', type='item'}))
table.insert(resultPart, ' '..Icons.Icon({'(P) '..metalName..' '..piece, '(P)', type='item'}))
else
table.insert(resultPart, ' '..Icons.Icon({'(S) '..metalName..' '..piece, '(S)', type='item'}))
table.insert(resultPart, ' '..Icons.Icon({'(G) '..metalName..' '..piece, '(G)', type='item'}))
end
table.insert(resultPart, '</span>')
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.buildCraftableArmourNav(frame)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable mw-collapsible"')
table.insert(resultPart, '\r\n!colspan = 2 style="background-color:#275C87;color:#FFFFFF;min-width:730px;"|')
table.insert(resultPart, Icons.Icon({'Crafting', type='skill', notext=true}))
table.insert(resultPart, ' Craftable Armour Sets')
local leatherTypes = {'Leather', 'Hard Leather'}
local leatherPieces = {"Cowl", "Body", "Chaps", "Gloves", "Vambraces", "Boots"}
table.insert(resultPart, '\r\n|-\r\n!')
table.insert(resultPart, Icons.Icon({'Leather', type='item', notext=true}))
table.insert(resultPart, ' Leather')
for i, material in pairs(leatherTypes) do
if i > 1 then table.insert(resultPart, '\r\n|-\r\n!Hard Leather') end
table.insert(resultPart, '\r\n|')
for j, piece in ipairs(leatherPieces) do
if j > 1 then
table.insert(resultPart, ' • ')
end
table.insert(resultPart, Icons.Icon({material..' '..piece, piece, type='item'}))
end
end
local materialTypes = {{'Green D-hide', 'Green Dragonhide'}, {'Blue D-hide', 'Blue Dragonhide'}, {'Red D-hide', 'Red Dragonhide'}, {'Black D-hide', 'Black Dragonhide'},
{'Elderwood', 'Elderwood Logs', TotH = true}, {'Revenant', 'Revenant Logs', TotH = true}, {'Carrion', 'Carrion Logs', TotH = true}}
local pieces = {"Body", "Chaps", "Vambraces", "Shield"}
for i, material in ipairs(materialTypes) do
local isTotH = false
local craftName = material[1]
local matName = material[2]
isTotH = material.TotH ~= nil and material.TotH
table.insert(resultPart, '\r\n|-\r\n!')
if isTotH then
table.insert(resultPart, Icons.TotH())
end
table.insert(resultPart, Icons.Icon({matName, type="item", notext=true}))
table.insert(resultPart, " "..craftName)
table.insert(resultPart, "\r\n|")
for j, piece in ipairs(pieces) do
if j > 1 then
table.insert(resultPart, ' • ')
end
table.insert(resultPart, '<span style="display:inline-block">')
table.insert(resultPart, Icons.Icon({craftName..' '..piece, piece, type='item'}))
table.insert(resultPart, ' '..Icons.Icon({'(U) '..craftName..' '..piece, '(U)', type='item'}))
table.insert(resultPart, '</span>')
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getLifestealWeapons()
local items = p.getItems(function(item)
if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
for i, spAttID in ipairs(item.specialAttacks) do
local spAtt = GameData.getEntityByID('attacks', spAttID)
if spAtt ~= nil then
return spAtt.lifesteal > 0
end
end
end
return false
end)
for i, item in ipairs(items) do
mw.log(item.name)
end
end
return p