|
|
(23 intermediate revisions by 3 users not shown) |
Line 8: |
Line 8: |
| local Items = require('Module:Items') | | local Items = require('Module:Items') |
| local Num = require('Module:Number') | | local Num = require('Module:Number') |
| | local FL = require('Module:FunList') |
|
| |
|
| local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'} | | local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'} |
Line 20: |
Line 21: |
| NotMagic = {'Torrential Blast Crossbow', 'Spectral Ice Sword', 'Lightning Strike 1H Sword', 'FrostSpark 1H Sword'} | | NotMagic = {'Torrential Blast Crossbow', 'Spectral Ice Sword', 'Lightning Strike 1H Sword', 'FrostSpark 1H Sword'} |
| } | | } |
| | |
| | local slotOverrides = { |
| | ['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) |
| -- If slot is a slot name, convert it to the slot ID instead
| |
| local slotID = Shared.getNamespacedID('melvorD', slot) | | local slotID = Shared.getNamespacedID('melvorD', slot) |
| local slotData = GameData.getEntityByID('equipmentSlots', slotID) | | local slotData = GameData.getEntityByID('equipmentSlots', slotID) |
| -- Validate slotID
| | |
| if slotData == nil then | | if slotData == nil then |
| | if slotOverrides[slot] ~= nil then |
| | return slotOverrides[slot] |
| | end |
| | |
| -- slotID invalid, check if user provided a slot name | | -- slotID invalid, check if user provided a slot name |
| slotData = GameData.getEntityByProperty('equipmentSlots', 'emptyName', slot) | | slotData = GameData.getEntityByProperty('equipmentSlots', 'emptyName', slot) |
Line 47: |
Line 190: |
| end | | end |
|
| |
|
| function p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName) | | local function getItems(slotID, style) |
| if includeModifiers == nil then includeModifiers = false end | | local _, slotLocalID = Shared.getLocalID(slotID) |
| if sortByName == nil then sortByName = false end
| |
|
| |
|
| --Getting some lists set up here that will be used later | | return getEquipItemList( |
| --First, the list of columns used by both weapons & armour
| | function(entry) |
| local statColumns = {
| | return ( |
| 'stabAttackBonus', 'slashAttackBonus', 'blockAttackBonus', | | entry.slots[slotLocalID] ~= nil |
| 'rangedAttackBonus', 'magicAttackBonus', 'meleeStrengthBonus',
| | and (style == nil or style == '' or style == entry.style) |
| 'rangedStrengthBonus', 'magicDamageBonus', 'meleeDefenceBonus',
| | ) |
| 'rangedDefenceBonus', 'magicDefenceBonus', 'damageReduction',
| | end |
| 'resistance', 'attackLevelRequired', 'strengthLevelRequired',
| | ) |
| 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired',
| | end |
| 'attackAbyssalLevel', 'strengthAbyssalLevel','defenceAbyssalLevel',
| |
| 'rangedAbyssalLevel', 'magicAbyssalLevel' | |
| } | |
|
| |
|
| if Shared.tableIsEmpty(itemList) then | | --== Helper Functions for getCategoryTable ==-- |
| return Shared.printError('You must select at least one item to get stats for') | | local function createStatCell(row, statVal) |
| | local cell = row:tag('td') |
| | if statVal > 0 then |
| | cell:addClass('table-positive') |
| | elseif statVal < 0 then |
| | cell:addClass('table-negative') |
| end | | end |
| | if math.abs(statVal) >= 1000 then |
| | cell:attr('data-sort-value', statVal) |
| | end |
| | cell:css('text-align', 'right') |
| | return cell |
| | end |
|
| |
|
| local isWeaponType = ((itemList[1].validSlots ~= nil and Shared.contains(itemList[1].validSlots, 'Weapon')) | | local function addStatCell(row, item, stat) |
| or (itemList[1].occupiesSlots ~= nil and Shared.contains(itemList[1].occupiesSlots, 'Weapon'))) and Shared.contains(weaponTypes, itemList[1].type)
| | local statVal = 0 |
| | if item.equipmentStats ~= nil then |
| | statVal = item.equipmentStats[stat] or 0 |
| | end |
| | |
| | return createStatCell(row, statVal) |
| | :wikitext(Num.formatnum(statVal)) |
| | end |
|
| |
|
| --Now that we have a preliminary list, let's figure out which columns are irrelevant (IE are zero for all items in the selection)
| | local function addDRCell(row, item) |
| local ignoreColumns = Shared.clone(statColumns) | | local dr = 0 |
| for i, item in pairs(itemList) do | | local icon = nil |
| local ndx = 1
| | |
| while Shared.tableCount(ignoreColumns) >= ndx do
| | -- Grab damage reduction figure |
| if Items._getItemStat(item, ignoreColumns[ndx], true) ~= 0 then
| | if item.equipmentStats ~= nil then |
| table.remove(ignoreColumns, ndx)
| | if item.equipmentStats.damageReduction then |
| else | | dr, icon = item.equipmentStats.damageReduction, 'Damage Reduction' |
| ndx = ndx + 1
| | elseif item.equipmentStats.resistanceAbyssal then |
| end
| | dr, icon = item.equipmentStats.resistanceAbyssal, 'Abyssal Resistance' |
| | elseif item.equipmentStats.resistanceEternal then |
| | dr, icon = item.equipmentStats.resistanceEternal, 'Eternal Resistance' |
| end | | end |
| end | | end |
| | | |
| --Now to remove the ignored columns (and also we need to track groups like defence bonuses to see how many remain) | | local cell = createStatCell(row, dr) |
| local attBonusCols = 5 | | |
| local strBonusCols = 2 | | -- Add DR icons, if there's any value |
| local defBonusCols = 3 | | if dr ~= 0 then |
| local lvlReqCols = 5 | | cell:wikitext(Icons.Icon({icon, size=15, notext='true'}) .. ' ') |
| local abyssalLvlReqCols = 5
| |
| local ndx = 1
| |
| while Shared.tableCount(statColumns) >= ndx do
| |
| local colName = statColumns[ndx]
| |
| if Shared.contains(ignoreColumns, colName) then | |
| if Shared.contains(colName, 'AttackBonus') then attBonusCols = attBonusCols - 1 end
| |
| if Shared.contains(colName, 'trengthBonus') then strBonusCols = strBonusCols - 1 end
| |
| if Shared.contains(colName, 'efenceBonus') then defBonusCols = defBonusCols - 1 end
| |
| if Shared.contains(colName, 'AbyssalLevel') then abyssalLvlReqCols = abyssalLvlReqCols - 1 end
| |
| if Shared.contains(colName, 'LevelRequired') then lvlReqCols = lvlReqCols - 1 end
| |
| table.remove(statColumns, ndx)
| |
| else
| |
| ndx = ndx + 1
| |
| end
| |
| end | | end |
| | |
| | -- Add DR value |
| | cell:wikitext(dr .. '%') |
| | return cell |
| | end |
|
| |
|
| --Alright, let's start the table by building the shared header
| | local function getRequirements(item) |
| local resultPart = {}
| | if item.equipRequirements == nil then |
| table.insert(resultPart, '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"')
| | return nil |
| if isWeaponType then | |
| --Weapons have extra columns here for Attack Speed and "Two Handed?"
| |
| table.insert(resultPart, '\r\n!colspan="5"|')
| |
| else
| |
| table.insert(resultPart, '\r\n!colspan="3"|') | |
| end | | end |
| if attBonusCols > 0 then | | |
| table.insert(resultPart, '\r\n!colspan="'..attBonusCols..'"|Attack Bonus') | | local function getSkillName(skillID) |
| | local _, localSkillID = GameData.getLocalID(skillID) |
| | return localSkillID |
| end | | end |
| if strBonusCols > 0 then | | |
| table.insert(resultPart, '\r\n!colspan="'..strBonusCols..'"|Str. Bonus') | | local iconFuncs = { |
| | ['AbyssalLevel'] = function(x) |
| | return Icons._SkillRealmIcon(getSkillName(x.skillID), 'melvorItA:Abyssal') .. ' ' .. x.level |
| | end, |
| | ['SkillLevel'] = function(x) |
| | return Icons._SkillRealmIcon(getSkillName(x.skillID)) .. ' ' .. x.level |
| | end, |
| | } |
| | |
| | local reqs = {} |
| | local abyssalSkills = {} |
| | local highestLvReq = 0 |
| | |
| | -- Filter out all Abyssal Levels |
| | for _, req in ipairs(item.equipRequirements) do |
| | if req.type == 'AbyssalLevel' then abyssalSkills[req.skillID] = true end |
| | end |
| | |
| | -- If the req is a SkillLevel, but the skillID is already an AbyssalLevel, skip the entry |
| | -- These are likely 99 Level requirements in addition to the AbyssalLevel requirement. |
| | for _, req in ipairs(item.equipRequirements) do |
| | if not (req.type == 'SkillLevel' and abyssalSkills[req.skillID] == true) then |
| | -- Add requirement via factory function. |
| | local func = iconFuncs[req.type] |
| | if func then table.insert(reqs, func(req)) end |
| | |
| | -- Track highest level for data sorting. |
| | local lv = req.level or 0 |
| | if lv > highestLvReq then highestLvReq = lv end |
| | end |
| | end |
| | |
| | if Shared.tableIsEmpty(abyssalSkills) == false then |
| | highestLvReq = highestLvReq + 200 |
| | end |
| | |
| | return { |
| | ['datasortvalue'] = highestLvReq, |
| | ['requirements'] = table.concat(reqs, '<br>') |
| | } |
| | end |
| | |
| | function p.getEquipmentTable(itemList, slot, style) |
| | local iconSize = 20 |
| | local fl = FL.new(itemList) |
| | |
| | -- If slot is nil, find out if we have any weapons. |
| | if slot == nil then |
| | if fl:any(function(x) return Shared.contains(x.validSlots, 'Weapon') end) then |
| | slot = 'Weapon' |
| | end |
| end | | end |
| if Shared.contains(statColumns, 'magicDamageBonus') then | | |
| table.insert(resultPart, '\r\n!colspan="1"|% Dmg Bonus')
| | -- Sort itemList by name |
| end | | itemList = fl:sortBy(function(x) return x.name end) |
| if defBonusCols > 0 then | | :sort() |
| table.insert(resultPart, '\r\n!colspan="'..defBonusCols..'"|Defence Bonus')
| | :toTable() |
| end
| | |
| if Shared.contains(statColumns, 'damageReduction') then
| | local isWeapon = (slot == 'Weapon') |
| table.insert(resultPart, '\r\n!colspan="1"|DR') | | local itemColspan = (isWeapon and 5) or 3 |
| end
| | |
| if Shared.contains(statColumns, 'resistance') then | | local html = mw.html.create('table') |
| table.insert(resultPart, '\r\n!colspan="1"|AR')
| | :addClass('wikitable sortable stickyHeader') |
| end | | :addClass('col-1-center col-3-center') |
| if lvlReqCols > 0 then
| | |
| table.insert(resultPart, '\r\n!colspan="'..lvlReqCols..'"|Lvl Req')
| | local header0 = html:tag('tr'):addClass('headerRow-0') |
| end | | header0:tag('th'):attr('colspan', itemColspan) |
| if abyssalLvlReqCols > 0 then
| | header0:tag('th'):attr('colspan', 5) |
| table.insert(resultPart, '\r\n!colspan="'..abyssalLvlReqCols..'"|A. Lvl Req')
| | :wikitext("Attack Bonus") |
| end
| | header0:tag('th'):attr('colspan', 3) |
| if includeModifiers and includeDescription then | | :wikitext("Strength Bonus") |
| table.insert(resultPart, '\r\n!colspan="2"|')
| | header0:tag('th'):attr('colspan', 3) |
| elseif includeModifiers or includeDescription then
| | :wikitext("Defence Bonus") |
| table.insert(resultPart, '\r\n!colspan="1"|')
| | |
| end
| | header0:tag('th'):wikitext("DR/AR") |
| --One header row down, one to go
| | header0:tag('th'):wikitext() |
| table.insert(resultPart, '\r\n|-class="headerRow-1"')
| | |
| table.insert(resultPart, '\r\n!Item') | | local header1 = html:tag('tr'):addClass('headerRow-1') |
| table.insert(resultPart, '\r\n!Name')
| | header1:tag('th'):wikitext('Name') |
| table.insert(resultPart, '\r\n![[DLC]]')
| | :attr('colspan', 2) |
| --Weapons have Attack Speed here
| | header1:tag('th'):wikitext('DLC') |
| if isWeaponType then | | if isWeapon == true then |
| table.insert(resultPart, '\r\n!Attack Speed')
| | header1:tag('th'):wikitext('Two<br>Handed') |
| table.insert(resultPart, '\r\n!Two Handed?')
| | header1:tag('th'):wikitext('Attack<br>Speed') |
| end | |
| --Attack bonuses | |
| if Shared.contains(statColumns, 'slashAttackBonus') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
| |
| end
| |
| if Shared.contains(statColumns, 'stabAttackBonus') then | |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
| |
| end | |
| if Shared.contains(statColumns, 'blockAttackBonus') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
| |
| end
| |
| if Shared.contains(statColumns, 'rangedAttackBonus') then | |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'})) | |
| end
| |
| if Shared.contains(statColumns, 'magicAttackBonus') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'})) | |
| end | | 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 | | --Strength bonuses |
| if Shared.contains(statColumns, 'meleeStrengthBonus') then | | header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'})) |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
| | header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'})) |
| end | | header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'})) |
| if Shared.contains(statColumns, 'rangedStrengthBonus') then
| | |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
| |
| end | |
| if Shared.contains(statColumns, 'magicDamageBonus') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
| |
| end
| |
| --Defence bonuses | | --Defence bonuses |
| if Shared.contains(statColumns, 'meleeDefenceBonus') then | | header1:tag('th'):wikitext(Icons.Icon({'Strength', type='skill', size=iconSize, notext='true'})) |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'}))
| | header1:tag('th'):wikitext(Icons.Icon({'Ranged', type='skill', size=iconSize, notext='true'})) |
| end | | header1:tag('th'):wikitext(Icons.Icon({'Magic', type='skill', size=iconSize, notext='true'})) |
| if Shared.contains(statColumns, 'rangedDefenceBonus') then
| | |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'}))
| | -- Damage reduction |
| end | | header1:tag('th'):wikitext(Icons.Icon({'Damage Reduction', size=iconSize, notext='true'})) |
| if Shared.contains(statColumns, 'magicDefenceBonus') then
| | |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
| |
| end | |
| if Shared.contains(statColumns, 'damageReduction') then | |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Damage Reduction', notext='true'}))
| |
| end
| |
| if Shared.contains(statColumns, 'resistance') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Abyssal Resistance', notext='true'}))
| |
| end | |
| --Level requirements | | --Level requirements |
| if Shared.contains(statColumns, 'attackLevelRequired') then | | header1:tag('th'):wikitext('Equip Req') |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', notext='true'}))
| | |
| end
| | -- Fill the table with all items |
| if Shared.contains(statColumns, 'strengthLevelRequired') then | | for _, item in ipairs(itemList) do |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', notext='true'}))
| | local row = html:tag('tr') |
| end | | row:tag('td'):wikitext(Icons.Icon({item.name, type='item', notext=true})) |
| if Shared.contains(statColumns, 'defenceLevelRequired') then
| | :attr('data-sort-value', item.name) |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', notext='true'})) | | row:tag('td'):wikitext(Icons.Icon({item.name, type='item', noicon=true})) |
| end
| | :attr('data-sort-value', item.name) |
| if Shared.contains(statColumns, 'rangedLevelRequired') then
| | row:tag('td'):wikitext(Icons.getDLCColumnIcon(item.id)) |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', notext='true'})) | | :attr('data-sort-value', Icons.getExpansionID(item.id)) |
| end
| |
| if Shared.contains(statColumns, 'magicLevelRequired') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', notext='true'}))
| |
| end
| |
| if Shared.contains(statColumns, 'attackAbyssalLevel') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Attack', type='skill', class='abyss-icon', notext='true'}))
| |
| end
| |
| if Shared.contains(statColumns, 'strengthAbyssalLevel') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Strength', type='skill', class='abyss-icon', notext='true'}))
| |
| end
| |
| if Shared.contains(statColumns, 'defenceAbyssalLevel') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Defence', type='skill', class='abyss-icon', notext='true'}))
| |
| end
| |
| if Shared.contains(statColumns, 'rangedAbyssalLevel') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Ranged', type='skill', class='abyss-icon', notext='true'})) | |
| end
| |
| if Shared.contains(statColumns, 'magicAbyssalLevel') then
| |
| table.insert(resultPart, '\r\n!'..Icons.Icon({'Magic', type='skill', class='abyss-icon', notext='true'}))
| |
| end
| |
| --If includeModifiers is set to 'true', add the Modifiers column
| |
| if includeModifiers then
| |
| table.insert(resultPart, '\r\n!Modifiers')
| |
| end
| |
| --If includeDescription is set to 'true', add the Description column
| |
| if includeDescription then
| |
| table.insert(resultPart, '\r\n!Description')
| |
| end
| |
|
| |
|
| if sortByName then
| | -- Add two-handed & attack speed. |
| table.sort(itemList, function(a, b) return a.name < b.name end) | | if isWeapon == true then |
| end
| | local twoHandText = (Items._getItemStat(item, 'isTwoHanded') and 'Yes') or 'No' |
| for i, item in ipairs(itemList) do
| | row:tag('td'):wikitext(twoHandText) |
| if isWeaponType then | | :css('text-align', 'center') |
| --Building rows for weapons
| | |
| local atkSpeed = Items._getItemStat(item, 'attackSpeed', true) | | local atkSpeed = Items._getItemStat(item, 'attackSpeed') or 0 |
| table.insert(resultPart, '\r\n|-')
| | if atkSpeed > 0 then |
| table.insert(resultPart, '\r\n|style="text-align: centre;"|'..Icons.Icon({item.name, type='item', size=32, notext=true}))
| | row:tag('td'):wikitext(Num.round(atkSpeed / 1000, 3, 1) .. 's') |
| table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', noicon=true})) | | :attr('data-sort-value', atkSpeed) |
| table.insert(resultPart, '\r\n|'..Icons.getDLCColumnIcon(item.id))
| | :css('text-align', 'right') |
| table.insert(resultPart, '\r\n| data-sort-value="' .. atkSpeed .. '" style="text-align:right;" |'..Num.round(atkSpeed / 1000, 3, 1) .. 's')
| | else |
| --That's the first list out of the way, now for 2-Handed | | row:tag('td'):wikitext('N/A') |
| table.insert(resultPart, '\r\n| style="text-align: right;"|') | | :addClass('table-na') |
| table.insert(resultPart, Items._getItemStat(item, 'isTwoHanded') and 'Yes' or 'No')
| |
| for j, statName in pairs(statColumns) do | |
| local statValue = Items._getItemStat(item, statName, true) | |
| table.insert(resultPart, '\r\n| style="text-align:right;" class="')
| |
| if string.find(statName, '^(.+)LevelRequired$') == nil or string.find(statName, '^(.+)Abyssallevel$') == nil then
| |
| if statValue > 0 then
| |
| table.insert(resultPart, 'table-positive')
| |
| elseif statValue < 0 then
| |
| table.insert(resultPart, 'table-negative')
| |
| end
| |
| end
| |
| table.insert(resultPart, '"|'..Num.formatnum(statValue))
| |
| if statName == 'magicDamageBonus' or statName == 'damageReduction' or statName == 'resistance' then table.insert(resultPart, '%') end
| |
| end
| |
| --If requested, add the item Modifiers | |
| if includeModifiers then
| |
| table.insert(resultPart, '\r\n| ') | |
| local txtLines = {}
| |
| if item.modifiers ~= nil then
| |
| table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
| |
| end
| |
| --For items with a special attack, show the details
| |
| if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
| |
| table.insert(txtLines, "'''Special Attack:'''")
| |
| for i, spAttID in ipairs(item.specialAttacks) do
| |
| local spAtt = GameData.getEntityByID('attacks', spAttID)
| |
| local attChance = spAtt.defaultChance
| |
| if item.overrideSpecialChances ~= nil then
| |
| attChance = item.overrideSpecialChances[i]
| |
| end
| |
| table.insert(txtLines, attChance .. '% chance for ' .. spAtt.name .. ':')
| |
| table.insert(txtLines, spAtt.description)
| |
| end
| |
| end
| |
| table.insert(resultPart, table.concat(txtLines, '<br/>'))
| |
| end
| |
| --If requested, add description
| |
| if includeDescription then
| |
| table.insert(resultPart, '\r\n| ' .. getItemDesc(item))
| |
| end | | end |
| | 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') |
| | |
| | -- Add Damage Reduction / Abyssal Resistance |
| | addDRCell(row, item) |
| | |
| | local reqs = getRequirements(item) |
| | if reqs == nil then |
| | row:tag('td'):wikitext('None') |
| | :attr('data-sort-value', 0) |
| else | | else |
| --Building rows for armour | | row:tag('td'):wikitext(reqs.requirements) |
| table.insert(resultPart, '\r\n|-')
| | :attr('data-sort-value', reqs.datasortvalue) |
| table.insert(resultPart, '\r\n|'..Icons.Icon({(item.name or 'Unknown'), type='item', size=32, notext=true}))
| |
| table.insert(resultPart, '\r\n|'..Icons.Icon({item.name, type='item', noicon=true}))
| |
| table.insert(resultPart, '\r\n|'..Icons.getDLCColumnIcon(item.id))
| |
| for j, statName in pairs(statColumns) do
| |
| local statValue = Items._getItemStat(item, statName, true)
| |
| table.insert(resultPart, '\r\n|style="text-align:right;" class="')
| |
| if statValue > 0 then
| |
| table.insert(resultPart, 'table-positive')
| |
| elseif statValue < 0 then
| |
| table.insert(resultPart, 'table-negative')
| |
| end
| |
| table.insert(resultPart, '"|'..Num.formatnum(statValue))
| |
| if statName == 'magicDamageBonus' or statName == 'damageReduction' or statName == 'resistance' then table.insert(resultPart, '%') end
| |
| end
| |
| --If requested, add the item Modifiers
| |
| if includeModifiers then
| |
| table.insert(resultPart, '\r\n| ')
| |
| local txtLines = {}
| |
| if item.modifiers ~= nil then
| |
| table.insert(txtLines, Modifiers.getModifiersText(item.modifiers, true, false, 10))
| |
| end
| |
| --For items with a special attack, show the details
| |
| if item.specialAttacks ~= nil and not Shared.tableIsEmpty(item.specialAttacks) then
| |
| table.insert(txtLines, "'''Special Attack:'''")
| |
| for i, spAttID in ipairs(item.specialAttacks) do
| |
| local spAtt = GameData.getEntityByID('attacks', spAttID)
| |
| local attChance = spAtt.defaultChance
| |
| if item.overrideSpecialChances ~= nil then
| |
| attChance = item.overrideSpecialChances[i]
| |
| end
| |
| table.insert(txtLines, attChance .. '% chance for ' .. spAtt.name .. ':')
| |
| table.insert(txtLines, spAtt.description)
| |
| end
| |
| end
| |
| table.insert(resultPart, table.concat(txtLines, '<br/>'))
| |
| end
| |
| --If requested, add description
| |
| if includeDescription then
| |
| table.insert(resultPart, '\r\n| ' .. getItemDesc(item))
| |
| end
| |
| end | | end |
| end | | end |
|
| |
|
| table.insert(resultPart, '\r\n|}')
| | return tostring(html) |
| | |
| return table.concat(resultPart) | |
| end | | end |
|
| |
|
| function p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName) | | function p.getCategoryTable(frame) |
| -- If slot is a slot name, convert it to the slot ID instead | | 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 |
| local slotNS, slotLocalID = Shared.getLocalID(slotID)
| |
|
| |
|
| local itemList = Items.getItems(function(item) | | return p.getEquipmentTable(getItems(slotID, style), slot) |
| -- 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
| |
| if style == 'Melee' then
| |
| 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
| |
| elseif style == 'Ranged' then
| |
| 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
| |
| elseif style == 'Magic' 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
| |
| elseif style == 'None' then
| |
| if (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
| |
| not Shared.contains(styleOverrides.None, item.name) then
| |
| isMatch = false
| |
| end
| |
| end
| |
| local sID = slotLocalID
| |
| if sID == nil or not Shared.contains(item.validSlots, sID) then isMatch = false end
| |
| | |
| if isMatch and other ~= nil then
| |
| if 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 p._getEquipmentTable(itemList, includeModifiers, includeDescription, sortByName)
| |
| end
| |
| | |
| function p.getCategoryTable(frame)
| |
| local style = frame.args ~= nil and frame.args[1] or frame[1]
| |
| local slot = frame.args ~= nil and frame.args[2] or frame[2]
| |
| local other = frame.args ~= nil and frame.args[3] or frame[3]
| |
| local includeModifiers = frame.args ~= nil and frame.args.includeModifiers or frame.includeModifiers
| |
| local includeDescription = frame.args ~= nil and frame.args.includeDescription or frame.includeDescription
| |
| local sortByName = frame.args ~= nil and frame.args.sortByName or frame.sortByName
| |
| | |
| includeModifiers = includeModifiers ~= nil and string.upper(includeModifiers) == 'TRUE' or false
| |
| includeDescription = includeDescription ~= nil and string.upper(includeDescription) == 'TRUE' or false
| |
| sortByName = sortByName ~= nil and string.upper(sortByName) == 'TRUE' or false
| |
| | |
| return p._getCategoryTable(style, slot, other, includeModifiers, includeDescription, sortByName)
| |
| end | | end |
|
| |
|
Line 423: |
Line 453: |
|
| |
|
| local itemList, errItems = {}, {} | | local itemList, errItems = {}, {} |
| local errMsg = 'Some items not found in database: '
| |
| local hasErr = false
| |
| for i, rawItemName in ipairs(frameArgs) do | | for i, rawItemName in ipairs(frameArgs) do |
| local itemName = Shared.trim(rawItemName) | | local itemName = Shared.trim(rawItemName) |
Line 434: |
Line 462: |
| end | | end |
| end | | end |
| | | |
| if not Shared.tableIsEmpty(errItems) then | | if not Shared.tableIsEmpty(errItems) then |
| return Shared.printError('Some items not found in database: ' .. table.concat(errItems, ', ')) | | return Shared.printError('Some items not found in database: ' .. table.concat(errItems, ', ')) |
| else | | else |
| return p._getEquipmentTable(itemList, includeModifiers) | | return p.getEquipmentTable(itemList) |
| 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 | | end |
|
| |
| table.insert(resultPart, '\r\n|}')
| |
| return table.concat(resultPart)
| |
| end | | end |
|
| |
|
Line 602: |
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 662: |
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 |