Module:Items/ComparisonTables/Sandbox
From Melvor Idle
Documentation for this module may be created at Module:Items/ComparisonTables/Sandbox/doc
local p = {}
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local Common = require('Module:Common')
local Modifiers = require('Module:Modifiers')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Num = require('Module:Number')
local Debug = require('Module:Debug')
local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}
local function getItemDesc(item)
if item.customDescription ~= nil then
return item.customDescription
elseif item.modifiers ~= nil then
return Modifiers.getModifiersText(item.modifiers, false, false)
else
return ''
end
end
local function getAttackSpeed(item)
if item.equipmentStats ~= nil and item.equipmentStats['attackSpeed'] ~= nil then
return item.equipmentStats['attackSpeed'] or 4000
end
return 0
end
local function getSlotID(slot)
-- If slot is a slot name, convert it to the slot ID instead
local slotID = Shared.getNamespacedID('melvorD', slot)
local slotData = GameData.getEntityByID('equipmentSlots', slotID)
-- Validate slotID
if slotData == nil then
-- slotID invalid, check if user provided a slot name
slotData = GameData.getEntityByProperty('equipmentSlots', 'emptyName', slot)
if slotData == nil then
return nil
end
slotID = slotData.id
end
return slotID
end
local function getItems(slotID)
local slotNS, slotLocalID = Shared.getLocalID(slotID)
local sortFunc = function(item)
-- Exclude the debug item
if item.id == 'melvorD:DEBUG_ITEM' then
return false
end
-- Exclude Golbin raid exclusives for now, such that they don't pollute various equipment tables
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
return false
end
local isMatch = true
local sID = slotLocalID
if not Shared.contains(item.validSlots, sID) then
isMatch = false
end
if isMatch and other ~= nil then
if slot == 'Cape' then
-- TODO Would be more reliable if based on items appearing within the relevant shop categories instead
local isSkillcape = Shared.contains(item.name, 'Skillcape') or Shared.contains(item.name, 'Cape of Completion')
if other == 'Skillcapes' then
isMatch = isSkillcape
elseif other == 'No Skillcapes' then
isMatch = not isSkillcape
end
end
if slotLocalID == 'Weapon' then --For quiver slot or weapon slot, 'other' is the ammo type
isMatch = other == item.ammoTypeRequired
elseif slotLocalID == 'Quiver' then
if other == 'Thrown' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
isMatch = true
else
isMatch = other == item.ammoType
end
end
end
return isMatch
end
return Items.getItems(sortFunc)
end
local function addStatCell(row, item, stat)
local statVal = 0
if item.equipmentStats ~= nil then
statVal = item.equipmentStats[stat] or 0
end
local cell = row:tag('td')
if statVal > 0 then
cell:addClass('table-positive')
elseif statVal < 0 then
cell:addClass('table-negative')
end
cell:css('text-align', 'right')
cell:wikitext(statVal)
return cell
end
function p._getCategoryTable(slot)
local iconSize = 20
local slotID = getSlotID(slot)
if slotID == nil then
return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
end
-- Always sort by name.
local itemList = getItems(slotID)
table.sort(itemList, function(a, b) return a.name < b.name end)
local isWeapon = (slot == 'Weapon')
local itemColspan = 3
if isWeapon == true then itemColspan = 4 end
local html = mw.html.create('table')
:addClass('wikitable sortable stickyHeader')
:addClass('col-1-center col-3-center')
local header0 = html:tag('tr'):addClass('headerRow-0')
header0:tag('th'):attr('colspan', itemColspan)
header0:tag('th'):attr('colspan', 5)
:wikitext("Attack Bonus")
header0:tag('th'):attr('colspan', 3)
:wikitext("Strength Bonus")
header0:tag('th'):attr('colspan', 3)
:wikitext("Defence Bonus")
-- TODO: if abyssal switch text
header0:tag('th'):wikitext("DR")
header0:tag('th') -- Empty row above "Equip Req"
local header1 = html:tag('tr'):addClass('headerRow-1')
header1:tag('th'):wikitext('Name')
:attr('colspan', 2)
header1:tag('th'):wikitext('DLC')
if isWeapon == true then
header1:tag('th'):wikitext('Attack<br>Speed')
end
-- Attack bonuses
header1:tag('th'):wikitext(Icons.Icon({'Attack', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Defence', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'}))
--Strength bonuses
header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'}))
--Defence bonuses
header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'}))
header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'}))
-- Damage reduction
-- TODO: Check if isabyssal
header1:tag('th'):wikitext(Icons.Icon({'Damage Reduction', size=iconSize, notext='true'}))
--header1:tag('th'):wikitext(Icons.Icon({'Abyssal Resistance', size=iconSize, notext='true'}))
--Level requirements
header1:tag('th'):wikitext('Equip Req')
-- Fill the table with all items
for _, item in ipairs(itemList) do
local row = html:tag('tr')
row:tag('td'):wikitext(Icons.Icon({item.name, type='item', notext=true}))
:attr('data-sort-value', item.name)
row:tag('td'):wikitext(Icons.Icon({item.name, type='item', noicon=true}))
:attr('data-sort-value', item.name)
row:tag('td'):wikitext(Icons.getDLCColumnIcon(item.id))
:attr('data-sort-value', Icons.getExpansionID(item.id))
-- Add attack speed.
if isWeapon == true then
local atkSpeed = getAttackSpeed(item)
row:tag('td'):wikitext(Num.round(atkSpeed / 1000, 3, 1) .. 's')
:attr('data-sort-value', atkSpeed)
:css('text-align', 'right')
end
-- Attack bonuses
addStatCell(row, item, 'stabAttackBonus')
addStatCell(row, item, 'slashAttackBonus')
addStatCell(row, item, 'blockAttackBonus')
addStatCell(row, item, 'rangedAttackBonus')
addStatCell(row, item, 'magicAttackBonus')
-- Strength bonuses
addStatCell(row, item, 'meleeStrengthBonus')
addStatCell(row, item, 'rangedStrengthBonus')
addStatCell(row, item, 'magicDamageBonus'):wikitext('%')
-- Defence bonuses
addStatCell(row, item, 'meleeDefenceBonus')
addStatCell(row, item, 'rangedDefenceBonus')
addStatCell(row, item, 'magicDefenceBonus')
-- TODO: Switch Abyss
addStatCell(row, item, 'damageReduction'):wikitext('%') -- resistance
-- TODO: Format requirements
row:tag('td'):wikitext('None')
--'attackLevelRequired', 'strengthLevelRequired',
--'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired',
--'attackAbyssalLevel', 'strengthAbyssalLevel','defenceAbyssalLevel',
--'rangedAbyssalLevel', 'magicAbyssalLevel'
end
return tostring(html)
end
-- TODO:
--- Split Melvor / Abyss items (based on realm?)
--- Split 1h and 2h weapons
--- When the category is 'Weapon', include every wieldable items (incl knives, javs, etc)
function p.getCategoryTable(frame)
local slot = frame.args ~= nil and frame.args[1] or frame[1]
return p._getCategoryTable(slot)
end
return p