Module:Items/ComparisonTables: Difference between revisions

Add two-handed indicator column for weapon tables
No edit summary
(Add two-handed indicator column for weapon tables)
 
(11 intermediate revisions by 3 users not shown)
Line 23: Line 23:


local slotOverrides = {
local slotOverrides = {
['2hWeapons'] = 'melvorD:2hWeapons',
['Enhancement'] = 'melvorD:Enhancement'
['Enhancement'] = 'melvorD:Enhancement'
}
}
local itemStyleOverrides = {
['Melee'] = {
'melvorF:Slayer_Helmet_Basic',
'melvorF:Slayer_Platebody_Basic',
'melvorF:Paladin_Gloves',
'melvorF:Desert_Wrappings',
'melvorF:Knights_Defender',
},
['Ranged'] = {
'melvorF:Slayer_Cowl_Basic',
'melvorF:Slayer_Leather_Body_Basic',
'melvorD:Cape_Of_Prat',
'melvorD:Ice_Arrows',
},
['Magic'] = {
'melvorF:Slayer_Wizard_Hat_Basic',
'melvorF:Slayer_Wizard_Robes_Basic',
'melvorF:Skull_Cape',
'melvorD:Enchanted_Shield',
'melvorF:Elementalist_Gloves',
'melvorTotH:Freezing_Touch_Body',
'melvorTotH:Lightning_Boots',
},
['Other'] = {
}
}
-- Categorise equipment by:
-- - Equipment slot ID
-- - Style (Melee, Ranged, Magic, Other)
p.SlotEquipment = {}
function p.populateSlotEquipment()
-- Populate from item data
local hiddenItems = {}
for _, itemID in ipairs(Items.HiddenItems) do
hiddenItems[itemID] = true
end
-- Transform style overrides into a form where items are indexed by ID
local styleOverrides = {}
for overType, itemIDs in pairs(itemStyleOverrides) do
for _, itemID in ipairs(itemIDs) do
styleOverrides[itemID] = overType
end
end
for _, item in ipairs(GameData.rawData.items) do
if (
-- Item is not hidden (includes debug items)
hiddenItems[item.id] == nil
-- Item isn't exclusive to Golbin Raid minigame
and (item.golbinRaidExclusive == nil or not item.golbinRaidExclusive)
-- Item can be equipped to any slot
and item.validSlots ~= nil
) then
-- Item is equippment to be included within equipment tables
local equipEntry = {
['item'] = item,
['slots'] = {},
['isWeapon'] = nil,
['style'] = nil
}
-- Determine which slots the equipment can be equipped to
local occupiesSlots = item.occupiesSlots or {}
for _, slotLocalID in ipairs(item.validSlots) do
local newSlotID = slotLocalID
-- Classify javelins and throwing knives as weapons
if slotLocalID == 'Quiver' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
newSlotID = 'Weapon'
-- Combine all enhancements
elseif Shared.contains({'Enhancement1', 'Enhancement2', 'Enhancement3'}, slotLocalID) then
newSlotID = 'Enhancement'
end
equipEntry.slots[newSlotID] = true
end
equipEntry.isWeapon = equipEntry.slots['Weapon'] or Shared.contains(occupiesSlots, 'Weapon')
-- Determine the style of the item (Melee, Ranged, Magic, Other)
local function hasSkillReq(reqs, localSkillID)
if reqs ~= nil then
local skillID = Shared.getNamespacedID('melvorD', localSkillID)
for levelType, typeReqs in pairs(reqs) do
if typeReqs[skillID] ~= nil then
return true
end
end
end
return false
end
local levelReqs = Items._getItemLevelReqs(item)
-- Apply any overrides first
if styleOverrides[item.id] ~= nil then
equipEntry.style = styleOverrides[item.id]
-- Weapon styles can be checked using the attackType property
elseif equipEntry.isWeapon and Shared.contains({'melee', 'ranged', 'magic'}, item.attackType) then
equipEntry.style = Shared.titleCase(item.attackType)
-- Magic
elseif hasSkillReq(levelReqs, 'Magic') then
equipEntry.style = 'Magic'
-- Ranged
elseif (
hasSkillReq(levelReqs, 'Ranged')
or equipEntry.slots.Quiver ~= nil
or item.ammoType ~= nil
) then
equipEntry.style = 'Ranged'
-- Melee
elseif (
hasSkillReq(levelReqs, 'Attack')
or hasSkillReq(levelReqs, 'Defence')
) then
equipEntry.style = 'Melee'
-- Other, default style if unmatched
else
equipEntry.style = 'Other'
end
-- Finally, add the entry into the slotEquipment table
table.insert(p.SlotEquipment, equipEntry)
end
end
end
p.populateSlotEquipment()
local function getEquipItemList(filter)
local equip = GameData.getEntities(p.SlotEquipment, function(x) return filter(x) end)
local items = {}
for _, entry in ipairs(equip) do
table.insert(items, entry.item)
end
return items
end


local function getSlotID(slot)
local function getSlotID(slot)
Line 56: Line 190:
end
end


local function getAttackSpeed(item)
local function getItems(slotID, style)
if item.equipmentStats ~= nil and item.equipmentStats['attackSpeed'] ~= nil then
return item.equipmentStats['attackSpeed'] or 4000
end
return 0
end
 
local function getItems(slotID)
local _, slotLocalID = Shared.getLocalID(slotID)
local _, slotLocalID = Shared.getLocalID(slotID)


local sortFunc = function(item)
return getEquipItemList(
-- Exclude the debug item
function(entry)
if item.id == 'melvorD:DEBUG_ITEM' then
return (
return false
entry.slots[slotLocalID] ~= nil
and (style == nil or style == '' or style == entry.style)
)
end
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
if item.name == 'Gods Enhancement (Superior)' then
mw.logObject(item)
end
if not Shared.contains(item.validSlots, slotLocalID) then
-- Special case for when the slot is Weapon but the data slot is Quiver (throwables)
if slotLocalID == 'Weapon' and Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
return true
end
-- Special case when the slot ID is for two handed weapons (this ID doesn't actually exist in game)
if slotLocalID == '2hWeapons' then
return Items._getItemStat(item, 'isTwoHanded')
end
if slotLocalID == 'Enhancement' then
return Shared.contains(item.validSlots, 'Enhancement1') or
  Shared.contains(item.validSlots, 'Enhancement2') or
  Shared.contains(item.validSlots, 'Enhancement3')
end
return false
end
-- Don't leap in one handed weapons with two handed.
-- Include throwables with one handed weapons
if slotLocalID == 'Weapon' then
if Items._getItemStat(item, 'isTwoHanded') then
return false
end
-- Exclude throwables from ammo.
elseif slotLocalID == 'Quiver' then
if Shared.contains({'Javelins', 'ThrowingKnives'}, item.ammoType) then
return false
end
end
 
return true
end
 
return Items.getItems(sortFunc)
end
end


Line 125: Line 210:
elseif statVal < 0 then
elseif statVal < 0 then
cell:addClass('table-negative')
cell:addClass('table-negative')
end
if math.abs(statVal) >= 1000 then
cell:attr('data-sort-value', statVal)
end
end
cell:css('text-align', 'right')
cell:css('text-align', 'right')
Line 137: Line 225:
return createStatCell(row, statVal)
return createStatCell(row, statVal)
:wikitext(statVal)
:wikitext(Num.formatnum(statVal))
end
end


Line 210: Line 298:
      
      
     if Shared.tableIsEmpty(abyssalSkills) == false then
     if Shared.tableIsEmpty(abyssalSkills) == false then
     highestLvReq = highestLvReq + 99
     highestLvReq = highestLvReq + 200
     end
     end


Line 219: Line 307:
end
end


function p.getEquipmentTable(itemList, slot)
function p.getEquipmentTable(itemList, slot, style)
local iconSize = 20
local iconSize = 20
 
local fl = FL.new(itemList)
-- If slot is nil, find out if we have any weapons.
-- If slot is nil, find out if we have any weapons.
if slot == nil then
if slot == nil then
local fl = FL.new(itemList)
if fl:any(function(x) return Shared.contains(x.validSlots, 'Weapon') end) then
if fl:any(function(x) return Shared.contains(item.validSlots, 'Weapon') end) then
slot = 'Weapon'
slot = 'Weapon'
end
end
end
end
local isWeapon = (slot == 'Weapon' or slot == '2hWeapons')
-- Sort itemList by name
local itemColspan = 3
itemList = fl:sortBy(function(x) return x.name end)
if isWeapon == true then itemColspan = 4 end
:sort()
:toTable()
local isWeapon = (slot == 'Weapon')
local itemColspan = (isWeapon and 5) or 3
local html = mw.html.create('table')
local html = mw.html.create('table')
Line 248: Line 340:


header0:tag('th'):wikitext("DR/AR")
header0:tag('th'):wikitext("DR/AR")
header0:tag('th'):wikitext()
local header1 = html:tag('tr'):addClass('headerRow-1')
local header1 = html:tag('tr'):addClass('headerRow-1')
Line 254: Line 347:
header1:tag('th'):wikitext('DLC')
header1:tag('th'):wikitext('DLC')
if isWeapon == true then
if isWeapon == true then
header1:tag('th'):wikitext('Two<br>Handed')
header1:tag('th'):wikitext('Attack<br>Speed')
header1:tag('th'):wikitext('Attack<br>Speed')
end
end
Line 290: Line 384:
:attr('data-sort-value', Icons.getExpansionID(item.id))
:attr('data-sort-value', Icons.getExpansionID(item.id))


-- Add attack speed.
-- Add two-handed & attack speed.
if isWeapon == true then
if isWeapon == true then
local atkSpeed = getAttackSpeed(item)
local twoHandText = (Items._getItemStat(item, 'isTwoHanded') and 'Yes') or 'No'
row:tag('td'):wikitext(Num.round(atkSpeed / 1000, 3, 1) .. 's')
row:tag('td'):wikitext(twoHandText)
:attr('data-sort-value', atkSpeed)
:css('text-align', 'center')
:css('text-align', 'right')
local atkSpeed = Items._getItemStat(item, 'attackSpeed') or 0
if atkSpeed > 0 then
row:tag('td'):wikitext(Num.round(atkSpeed / 1000, 3, 1) .. 's')
:attr('data-sort-value', atkSpeed)
:css('text-align', 'right')
else
row:tag('td'):wikitext('N/A')
:addClass('table-na')
end
end
end
Line 332: Line 435:


function p.getCategoryTable(frame)
function p.getCategoryTable(frame)
local slot = frame.args ~= nil and frame.args[1] or frame[1]
local args = frame.args ~= nil and frame.args or frame
local slot, style = args[1], Shared.titleCase(args[2] or '')
local slotID = getSlotID(slot)
local slotID = getSlotID(slot)
if slotID == nil then
if slotID == nil then
return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
return Shared.printError('Invalid slot ID: ' .. (slot or 'nil'))
elseif style ~= nil and style ~= '' and itemStyleOverrides[style] == nil then
return Shared.printError('Invalid style: ' .. (style or 'nil'))
end
end


-- Always sort by name.
return p.getEquipmentTable(getItems(slotID, style), slot)
local itemList = getItems(slotID)
table.sort(itemList, function(a, b) return a.name < b.name end)
 
return p.getEquipmentTable(itemList, slot)
end
end


Line 351: Line 453:


local itemList, errItems = {}, {}
local itemList, errItems = {}, {}
for i, rawItemName in ipairs({}) do
for i, rawItemName in ipairs(frameArgs) do
local itemName = Shared.trim(rawItemName)
local itemName = Shared.trim(rawItemName)
local item = Items.getItem(itemName)
local item = Items.getItem(itemName)
Line 366: Line 468:
return p.getEquipmentTable(itemList)
return p.getEquipmentTable(itemList)
end
end
end
function p.getDoubleLootTable(frame)
local modsDL = {
["id"] = {
'melvorD:doubleItemsSkill',
'melvorD:doubleItemsChanceAgainstDamageType'
},
["alias"] = {
'increasedChanceToDoubleLootCombat',
'decreasedChanceToDoubleLootCombat',
'increasedChanceToDoubleItemsGlobal',
'decreasedChanceToDoubleItemsGlobal'
}
}
local matchCriteria = Modifiers.getMatchCriteriaFromIDs(modsDL.id, modsDL.alias)
local itemMatchedMods = {}
local itemList = Items.getItems(
function(item)
if item.modifiers ~= nil then
local matchedMods = Modifiers.getMatchingModifiers(item.modifiers, matchCriteria)
if Shared.tableIsEmpty(matchedMods.matched) then
return false
else
itemMatchedMods[item.id] = matchedMods.matched
return true
end
end
end
)
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
table.insert(resultPart, '\r\n!colspan="2"|Name!!Bonus!!Description')
for i, item in Shared.skpairs(itemList) do
local lootValue = Modifiers.getModifierValue(itemMatchedMods[item.id])
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|data-sort-value="'..item.name..'"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '||' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..lootValue..'"|'..lootValue..'%')
table.insert(resultPart, '||' .. getItemDesc(item))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
end


Line 528: Line 584:
local PR = item.providedRunes
local PR = item.providedRunes
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="text-align: centre;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|style="text-align: center;"|'..Icons.Icon({item.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
table.insert(resultPart, '\r\n|' .. Icons.getExpansionIcon(item.id) .. Icons.Icon({item.name, type='item', noicon=true}))
local runeLines = {}
local runeLines = {}
Line 588: Line 644:
function p.getDRTable(frame)
function p.getDRTable(frame)
local style = frame.args ~= nil and frame.args[1] or frame
local style = frame.args ~= nil and frame.args[1] or frame
local SlotNames = {}
local slotNames = {}
local ItemList = {}
if style == 'Other' then
if style == 'Other' then
SlotNames = {'Helmet', 'Platelegs', 'Gloves', 'Shield', 'Cape', 'Amulet', 'Ring'}
slotNames = {'Helmet', 'Platelegs', 'Gloves', 'Shield', 'Cape', 'Amulet', 'Ring'}
else
else
SlotNames = {'Helmet', 'Platebody', 'Platelegs', 'Boots', 'Gloves', 'Weapon', 'Shield'}
slotNames = {'Helmet', 'Platebody', 'Platelegs', 'Boots', 'Gloves', 'Weapon', 'Shield'}
end
end
ItemList = Items.getItems(function(item)
local itemList = getEquipItemList(
local isMatch = true
function(entry)
if Items._getItemStat(item, 'damageReduction', true) <= 0 then
if Items._getItemStat(entry.item, 'damageReduction', true) <= 0 then
return false
-- Item provides no DR: Exclude
end
return false
-- Exclude Golbin raid exclusives for now, such that they don't
end
-- pollute various equipment tables
 
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
-- Check equipment slot matches
return false
local slotMatch = false
end
for _, slotName in ipairs(slotNames) do
--Using the same checks for Melee/Ranged/Magic that the Equipment Tables use
if entry.slots[slotName] ~= nil then
if style == 'Melee' then
slotMatch = true
if ((Items._getItemStat(item, 'defenceLevelRequired') == nil and Items._getItemStat(item, 'attackLevelRequired') == nil) and not Shared.contains(styleOverrides.Melee, item.name)) or Shared.contains(styleOverrides.NotMelee, item.name) then isMatch = false end
break
elseif style == 'Ranged' then
end
if (Items._getItemStat(item, 'rangedLevelRequired') == nil and not Shared.contains(styleOverrides.Ranged, item.name)) or Shared.contains(styleOverrides.NotRanged, item.name)  then isMatch = false end
end
elseif style == 'Magic' then
if not slotMatch then
if (Items._getItemStat(item, 'magicLevelRequired') == nil and not Shared.contains(styleOverrides.Magic, item.name)) or Shared.contains(styleOverrides.NotMagic, item.name) then isMatch = false end
return false
else
end
if (Items._getItemStat(item, 'attackLevelRequired') ~= nil or Items._getItemStat(item, 'defenceLevelRequired') ~= nil or Items._getItemStat(item, 'rangedLevelRequired') ~= nil or Items._getItemStat(item, 'magicLevelRequired') ~= nil or
 
Shared.contains(styleOverrides.Melee, item.name) or Shared.contains(styleOverrides.Ranged, item.name) or Shared.contains(styleOverrides.Magic, item.name)) and
-- Finally, ensure the style matches. If no style specified then return evertyhing
not Shared.contains(styleOverrides.None, item.name) then
return (style == nil or style == '' or style == entry.style)
isMatch = false
end
end
)
end
 
if isMatch and not Shared.contains(SlotNames, Items._getItemEquipSlot(item)) then
return p._getDRTable(slotNames, itemList)
isMatch = false
end
return isMatch
end)
return p._getDRTable(SlotNames, ItemList)
end
end


return p
return p