Module:Items: Difference between revisions

Added soul points to ItemBox
(getStatChangeString: Re-structure for potential performance benefits, and correct issue where certain skills may be omitted)
(Added soul points to ItemBox)
(24 intermediate revisions by 3 users not shown)
Line 9: Line 9:
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')


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 23: Line 25:
'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
-- 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)
Line 29: Line 46:


function p.getItem(name)
function p.getItem(name)
name = string.gsub(name, "%%27", "'")
name = Shared.fixPagename(name)
name = string.gsub(name, "'", "'")
return GameData.getEntityByName('items', name)
return GameData.getEntityByName('items', name)
end
end


function p.getItems(checkFunc)
function p.getItems(checkFunc)
return GameData.getEntities('items', checkFunc)
return GameData.getEntities('items',
function(obj)
return not Shared.contains(p.HiddenItems, obj.id) and checkFunc(obj)
end
)
end
end


Line 112: Line 132:
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 125: Line 207:
end
end
local result = p._getItemStat(item, StatName, ZeroIfNil)
local result = p._getItemStat(item, StatName, ZeroIfNil)
if formatNum then result = Shared.formatnum(result) end
if formatNum then result = Num.formatnum(result) end
return result
return result
end
end


--Gets the value of a given modifier for a given itemg
--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, skillID, asString)
function p._getItemModifier(item, modifier, skillID, asString)
Line 167: Line 249:


function p.hasCombatStats(item)
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
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 ~= 0 then
if isNonZeroStat(statName, statVal) then
return true
return true
end
end
end
end
end
end
return false
return false
end
end
Line 301: Line 394:
val1, val2 = levelReqs[1][skillID] or 0, levelReqs[2][skillID] or 0
val1, val2 = levelReqs[1][skillID] or 0, levelReqs[2][skillID] or 0
if val1 ~= val2 then
if val1 ~= val2 then
table.insert(changeArray, Shared.numStrWithSign(val1 - val2) .. ' ' .. Icons.Icon({skillData.data.name, type='skill', notext=true}) .. (statDef.suffix or ''))
table.insert(changeArray, Num.numStrWithSign(val1 - val2) .. ' ' .. Icons.Icon({skillData.data.name, type='skill', notext=true}) .. (statDef.suffix or ''))
end
end
end
end
Line 308: Line 401:
val1, val2 = equipStats[1][statDef.stat] or 0, equipStats[2][statDef.stat] or 0
val1, val2 = equipStats[1][statDef.stat] or 0, equipStats[2][statDef.stat] or 0
if val1 ~= val2 then
if val1 ~= val2 then
table.insert(changeArray, Shared.numStrWithSign(val1 - val2) .. (statDef.suffix or ''))
table.insert(changeArray, Num.numStrWithSign(val1 - val2) .. (statDef.suffix or ''))
end
end
end
end
Line 314: Line 407:


-- Include differences in modifiers
-- Include differences in modifiers
local modDiff = Constants.getModifiersText(Constants.getModifiersDifference(item2.modifiers, item1.modifiers))
-- TODO Implement getModifiersDifference
--local modDiff = Constants.getModifiersText(Constants.getModifiersDifference(item2.modifiers, item1.modifiers))
local modDiff = nil
if modDiff ~= nil and modDiff ~= '' then
if modDiff ~= nil and modDiff ~= '' then
table.insert(changeArray, modDiff)
table.insert(changeArray, modDiff)
Line 342: Line 437:
["Passive"] = 'Combat Passive Slot',
["Passive"] = 'Combat Passive Slot',
["Summon1"] = 'Summoning',
["Summon1"] = 'Summoning',
["Summon2"] = 'Summoning'
["Summon2"] = 'Summoning',
["Gem"] = "Gems_(Equipment)"
}
}
local slotText = {}
local slotText = {}
Line 377: Line 473:
-- For Summoning combat familiars, show the max hit
-- For Summoning combat familiars, show the max hit
if item.equipmentStats ~= nil and item.equipmentStats.summoningMaxhit ~= nil then
if item.equipmentStats ~= nil and item.equipmentStats.summoningMaxhit ~= nil then
table.insert(resultPart, "\r\n|-\r\n|'''Max Hit:''' " .. Shared.formatnum(item.equipmentStats.summoningMaxhit * 10))
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
Line 390: Line 486:
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
end
--For items that provide runes, show which runes are provided
--For items that provide runes, show which runes are provided
Line 411: Line 510:
table.insert(resultPart, '<span style="color:green">Passive:</span><br/>')
table.insert(resultPart, '<span style="color:green">Passive:</span><br/>')
end
end
table.insert(resultPart, Constants.getModifiersText(item.modifiers, true, false, 10))
table.insert(resultPart, Modifiers.getModifiersText(item.modifiers, true, false, 10))
end
end
return table.concat(resultPart)
return table.concat(resultPart)
Line 484: Line 583:


function p.getItemGrid(frame)
function p.getItemGrid(frame)
local resultPart = {}
--melvorF, melvorD, melvorTotH, melvorAoD
table.insert(resultPart, '{|')
local dlcFunc = function(item, dlc)
for i, item in ipairs(GameData.rawData.items) do
local itemDLC = Shared.getLocalID(item.id)
if i % 17 == 1 then
if dlc == nil then
table.insert(resultPart, '\r\n|-\r\n|')
return true
else
end
table.insert(resultPart, '||')
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')
 
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')
:css('height', '48px')
:css('width', '48px')
:css('padding', '0px')
cell:tag('div')
:css('padding', '8px')
:wikitext(Icons.Icon({v.name, type='item', notext=true, size='32'}))
-- Mod operator is slow. We use this instead
i = i + 1
end
end
table.insert(resultPart, 'style="padding:3px"|'..Icons.Icon({item.name, type='item', notext=true, size='40'}))
end
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
return tostring(html)
end
end


function p.getEquipRequirementRow(req)
function p.getEquipRequirementRow(req)
local result = ""
local result = ""
if req.type == "SkillLevel" then
if (req.type == "SkillLevel" or req.type == "AbyssalLevel") then
local pre = (req.type == "AbyssalLevel" and ' Abyssal') or ''
local skillName = Constants.getSkillName(req.skillID)
local skillName = Constants.getSkillName(req.skillID)
local skillIcon = Icons.Icon({skillName, type='skill', notext=true})
local skillIcon = Icons.Icon({skillName, type='skill', notext=true})
result = '\r\n!style="text-align:right;"| '..skillIcon..' Level Required'
result = '\r\n!style="text-align:right;"| '..skillIcon..pre..' Level Required'
result = result..'\r\n|style="text-align:right;"| '..req.level
result = result..'\r\n|style="text-align:right;"| '..req.level
elseif req.type == "DungeonCompletion" then
elseif (req.type == "DungeonCompletion" or req.type == "AbyssDepthCompletion") then
local dungeonName = GameData.getEntityByID("dungeons", req.dungeonID).name
local reqDefns = {
local dungeonIcon = Icons.Icon({dungeonName, type="dungeon", notext=true})
["DungeonCompletion"] = {
result = '\r\n!style="text-align:right;"| '..dungeonIcon..' Completions'
["dataKey"] = 'dungeons',
result = result..'\r\n|style="text-align:right;"| '..req.count
["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
elseif req.type == "Completion" then
local ns = GameData.getEntityByName('namespaces', req.namespace)
local ns = GameData.getEntityByName('namespaces', req.namespace)
Line 550: Line 712:


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;"| ' .. Shared.round(p._getItemStat(item, 'attackSpeed', true) / 1000, 3, 1) .. 's')
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))
Line 850: Line 1,012:
table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
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