Module:Items: Difference between revisions

From Melvor Idle
(Improvements to the getItemSources code to account for more things. Probably not everything quite yet, but getting there)
(Tweaks to the getEquipmentTable code)
Line 384: Line 384:
   local ammoTypeStr = args.ammoType
   local ammoTypeStr = args.ammoType
   local category = args.category ~= nil and args.category or 'Combat'
   local category = args.category ~= nil and args.category or 'Combat'
  local i = 0
  local j = 0


   --Find out what Ammo Type we're working with
   --Find out what Ammo Type we're working with
Line 410: Line 408:


   --Getting some lists set up here that will be used later
   --Getting some lists set up here that will be used later
   --First, the list of columns used by both weapons & armor
   --First, the list of columns used by both weapons & armour
   local statColumns = {'slashAttackBonus', 'stabAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'strengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'defenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus'}
   local statColumns = {'slashAttackBonus', 'stabAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'strengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'defenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus'}
   --Then the lists for just weapons/just armor
   --Then the lists for just weapons/just armour
   local weaponStatColumns = {'attackLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}  
   local weaponStatColumns = {'attackLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}  
   local armorStatColumns = {'damageReduction', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
   local armourStatColumns = {'damageReduction', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
   --Then the list of weapon types
   --Then the list of weapon types
   local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}
   local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}
Line 436: Line 434:
     result = result..'\r\n!colspan="1"|'
     result = result..'\r\n!colspan="1"|'
   else
   else
     --Only armor pieces have DR right now, so ignore that column for weapons
     --Only armour pieces have DR right now, so ignore that column for weapons
     result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|Damage Reduction'
     result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|Damage Reduction'
   end
   end
Line 463: Line 461:
   result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
   result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
   result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
   result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
   --Damage Reduction/Defence Req for armor, 2-handed/Attack Req for weapons
   --Damage Reduction/Defence Req for armour, 2-handed/Attack Req for weapons
   if isWeaponType then
   if isWeaponType then
     result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?'
     result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?'
Line 478: Line 476:


   --And with all the header out of the way, finally time to actually build the table itself.
   --And with all the header out of the way, finally time to actually build the table itself.
  local itemList = {}
   for i, itemBase in pairs(ItemData) do
   for i, itemBase in pairs(ItemData) do
     local item = Shared.clone(itemBase)
     local item = Shared.clone(itemBase)
Line 483: Line 482:
     local listItem = false
     local listItem = false
     if isWeaponType then
     if isWeaponType then
      listItem = item.type == type and item.category == category
    listItem = item.type == type and item.category == category
       if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
       if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
       if listItem then
    else
         result = result..'\r\n|-'
      --Now for handling armour
        result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
       if type == "Armour" or type == "Melee" then
        result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
         listItem = item.defenceLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Armour')
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.formatnum(item.attackSpeed)
      elseif type == "Ranged Armour" or type == "Ranged" then
         for j, statName in pairs(statColumns) do
         listItem = item.rangedLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
          local statValue = p._getItemStat(item, statName, true)
      elseif type == "Magic Armour" or type == "Magic" then
          result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
         listItem = item.magicLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Magic Armour')
          if statValue > 0 then
      else
            result = result..'background-color:lightgreen;'
        listItem = item.type == type and item.category ~= 'Combat'
          elseif statValue < 0 then
            result = result..'background-color:lightpink;'
          end
          result = result..'"|'..Shared.formatnum(statValue)
          if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
         end
        --That's the first list out of the way, now for 2-Handed
        result = result..'\r\n| style ="text-align: right;"|'
        if item.isTwoHanded then result = result..'Yes' else result = result..'No' end
        --Now the weapon exclusive columns
        for j, statName in pairs(weaponStatColumns) do
          local statValue = p._getItemStat(item, statName, true)
          result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
          result = result..'"|'..Shared.formatnum(statValue)
          if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
        end
        --Finally, the Sources
        result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
        result = result..p._getItemSources(item)
       end
       end
    else
      --Now for handling armor
      listItem = item.type == type and item.category == category
       if ammoType ~= nil then listItem = listItem and item.ammoType == ammoType end
       if ammoType ~= nil then listItem = listItem and item.ammoType == ammoType end
       if slot ~= nil then listItem = listItem and item.equipmentSlot == slot end
       if slot ~= nil then listItem = listItem and item.equipmentSlot == slot end
    end
    if listItem then
      table.insert(itemList, item)
    end
  end


       if listItem then
  table.sort(itemList, function(a, b) return a.id < b.id end)
         result = result..'\r\n|-'
  for i, item in pairs(itemList) do
         result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
    if isWeaponType then
        result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
      --Building rows for weapons
        for j, statName in pairs(statColumns) do
      result = result..'\r\n|-'
           local statValue = p._getItemStat(item, statName, true)
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
          result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
      result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.formatnum(item.attackSpeed)
      for j, statName in pairs(statColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if statValue > 0 then
          result = result..'background-color:lightgreen;'
        elseif statValue < 0 then
          result = result..'background-color:lightpink;'
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --That's the first list out of the way, now for 2-Handed
      result = result..'\r\n| style ="text-align: right;"|'
       if item.isTwoHanded then result = result..'Yes' else result = result..'No' end
      --Now the weapon exclusive columns
      for j, statName in pairs(weaponStatColumns) do
        local statValue = p._getItemStat(item, statName, true)
         result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
         result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --Finally, the Sources
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
      result = result..p._getItemSources(item)
    else
      --Building rows for armour
      result = result..'\r\n|-'
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
      for j, statName in pairs(statColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if statValue > 0 then
           result = result..'background-color:lightgreen;'
        elseif statValue < 0 then
          result = result..'background-color:lightpink;'
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --That's the first list out of the way, now for armour specific things
      for j, statName in pairs(armourStatColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if j == 1 then
           if statValue > 0 then
           if statValue > 0 then
             result = result..'background-color:lightgreen;'
             result = result..'background-color:lightgreen;'
Line 533: Line 561:
             result = result..'background-color:lightpink;'
             result = result..'background-color:lightpink;'
           end
           end
          result = result..'"|'..Shared.formatnum(statValue)
          if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
        end
        --That's the first list out of the way, now for armor specific things
        for j, statName in pairs(armorStatColumns) do
          local statValue = p._getItemStat(item, statName, true)
          result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
          if j == 1 then
            if statValue > 0 then
              result = result..'background-color:lightgreen;'
            elseif statValue < 0 then
              result = result..'background-color:lightpink;'
            end
          end
          result = result..'"|'..Shared.formatnum(statValue)
          if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
         end
         end
        --Finally, the Sources
         result = result..'"|'..Shared.formatnum(statValue)
         result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
         if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
         result = result..p._getItemSources(item)
       end
       end
      --Finally, the Sources
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
      result = result..p._getItemSources(item)
     end
     end
   end
   end

Revision as of 21:45, 23 September 2020

Lua module containing all sorts of functions for getting data on items. Pulls data from Module:GameData/data.

Some functions were split to submodules:


local p = {}

local MonsterData = mw.loadData('Module:Monsters/data')
local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')
local Constants = mw.loadData('Module:Constants/data')

local Shared = require('Module:Shared')
local Icons = require('Module:Icons')

local EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon'}
local OtherShopItems = {'Cooking Gloves', 'Mining Gloves', 'Gem Gloves', 'Smithing Gloves', 'Thieving Gloves'}

function p.getItemByID(ID)
  local result = Shared.clone(ItemData[ID + 1])
  if result ~= nil then
    result.id = ID
  end
  return result
end

function p.getItem(name)
  local result = nil
  for i, item in pairs(ItemData) do
    if(item.name == name) then
      result = Shared.clone(item)
      --Make sure every item has an id, and account for Lua being 1-index
      result.id = i -1
    end
  end
  return result
end

function p._getItemStat(item, StatName, ZeroIfNil)
  local result = item[StatName]
  --Special Overrides:
  if StatName == 'stabAttackBonus' then
    if item.attacBonus == nil then 
      result = nil
    else
      result = item.attackBonus[1]
    end
  elseif StatName == 'slashAttackBonus' then
    if item.attackBonus == nil then 
      result = nil
    else
      result = item.attackBonus[2]
    end
  elseif StatName == 'blockAttackBonus' then
    if item.attackBonus == nil then 
      result = nil
    else
      result = item.attackBonus[3]
    end
  elseif StatName == 'attackType' then
    result = p._getWeaponAttackType(item)
  end
  if result == nil and ZeroIfNil then result = 0 end
  return result
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 item = p.getItem(ItemName)
  if item == nil then
    return "ERROR: No item named "..ItemName.." exists in the data module"
  end
  return p._getItemStat(item, StatName, ZeroIfNil)
end

function p._getWeaponAttackType(item)
  if item.type == 'Weapon' then
    return Icons.Icon({'Melee', nolink='true'})
  elseif item.type == 'Ranged Weapon' then
    return Icons.Icon({'Ranged', type='skill', nolink='true'})
  elseif item.type == 'Magic Staff' or item.type == 'Magic Wand' then
    return Icons.Icon({'Magic', type='skill', nolink='true'})
  else
    return "Invalid"
  end
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 "ERROR: No item named "..ItemName.." exists in the data module"
  end
  return p._getWeaponAttackType(item)
end

function p.getPotionTable(frame)
  local potionName = frame.args ~= nil and frame.args[1] or frame
  local tiers = {'I', 'II', 'III', 'IV'}

  local result = '{| class="wikitable"'
  result = result..'\r\n!Potion!!Tier!!Charges!!Effect'

  local tier1potion = p.getItem(potionName..' I')
  for i, tier in pairs(tiers) do
    local tierName = potionName..' '..tier
    local potion = p.getItemByID(tier1potion.id + i - 1)
    if potion == nil then
       mw.log("Failed to get tier "..tier)
    else
      result = result..'\r\n|-'
      result = result..'\r\n|'..Icons.Icon({tierName, type='item', notext='true', size='60'})
      result = result..'||'..'[['..tierName..'|'..tier..']]'
      result = result..'||'..potion.potionCharges..'||'..potion.description
    end
  end

  result = result..'\r\n|}'
  return result
end

function p.getCreationTable(frame)
  local itemName = frame.args ~= nil and frame.args[1] or frame
  local item = p.getItem(itemName)
  if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module"
  end

  local skill = ''
  local time = 0
  local lvl = 0
  local xp = 0
  local qty = nil
  local req = {}
  --First figure out what skill is used to make this...
  if item.smithingLevel ~= nil then
    skill = 'Smithing'
    lvl = item.smithingLevel
    xp = item.smithingXP
    req = item.smithReq
    qty = item.smithingQty
    time = 2
  elseif item.craftingLevel ~= nil then
    skill = 'Crafting'
    lvl = item.craftingLevel
    xp = item.craftingXP
    req = item.craftReq
    qty = item.craftQty
    time = 3
  elseif item.runecraftingLevel ~= nil then
    skill = 'Runecrafting'
    lvl = item.runecraftingLevel
    xp = item.runecraftingXP
    req = item.runecraftReq
    qty = item.runecraftQty
    time = 2
  elseif item.fletchingLevel ~= nil then
    skill = 'Fletching'
    lvl = item.fletchingLevel
    xp = item.fletchingXP
    req = item.fletchReq
    qty = item.fletchQty
    time = 2
  elseif item.herbloreReq ~= nil then
    skill = 'Herblore'
    req = item.herbloreReq
    --Currently using 'herbloreMasteryID' as shorthand to find details, could be a better method
    local potionID = item.herbloreMasteryID
    local potionData = SkillData.Herblore.ItemData[potionID + 1]
    lvl = potionData.herbloreLevel
    xp = potionData.herbloreXP
    time = 2
  else
    return "Failed to find creation requirements for this (Possibly the module isn't properly updated for this skill)"
  end
  if qty == nil then qty = 1 end

  local result = '{|class="wikitable"'
  result = result..'\r\n!colspan="2"|Item Creation\r\n|-'
  result = result..'\r\n|-\r\n!style="text-align: right;"|Requirements'
  result = result..'\r\n|'..Icons.Icon({skill, type="skill", notext="true"}).." '''"..lvl.."'''"
  result = result..'\r\n|-\r\n!style="text-align: right;"|Materials\r\n|'
  for i, mat in pairs(req) do
    if i > 1 then result = result..'<br/>' end
    local matItem = p.getItemByID(mat.id)
    if matItem == nil then
      result = result..mat.qty..'x ?????'
    else
      result = result..Icons.Icon({matItem.name, type='item', qty=mat.qty})
    end
  end
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base Quantity'
  result = result..'\r\n|'..qty
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base Experience'
  result = result..'\r\n|'..xp..' XP'
  result = result..'\r\n|-\r\n!style="text-align:right;"|Base Creation Time'
  result = result..'\r\n|'..time..'.0 s'
  result = result..'\r\n|}'

  return result
end

function p._getItemSources(item)
  local result = nil
  local lineArray = {}

  --Alright, time to go through all the ways you can get an item...
  --First up: Can we kill somebody and take theirs?
  local killStr = ''
  for i, monster in pairs(MonsterData.Monsters) do
    if monster.lootTable ~= nil then
      for j, loot in pairs(monster.lootTable) do
        if loot[1] == item.id then
          if string.len(killStr) > 0 then
            killStr = killStr..','..Icons.Icon({monster.name, type="monster", notext="true"})
          else
            killStr = killStr..'Killing: '..Icons.Icon({monster.name, type="monster", notext="true"})
          end
        end
      end
    end
  end
  if string.len(killStr) > 0 then table.insert(lineArray, killStr) end

  --Next: Can we find it in a box?
  --While we're here, check for upgrades, cooking, and growing
  local lootStr = ''
  local upgradeStr = ''
  local cookStr = ''
  local burnStr = ''
  local growStr = ''
  for i, item2 in pairs(ItemData) do
    if item2.dropTable ~= nil then
      for j, loot in pairs(item2.dropTable) do
        if loot[1] == item.id then
          if string.len(lootStr) > 0 then
            lootStr = lootStr..','..Icons.Icon({item2.name, type="item", notext="true"})
          else
            lootStr = lootStr..'Opening: '..Icons.Icon({item2.name, type="item", notext="true"})
          end
        end
      end
    end
    if item2.trimmedItemID == item.id then
        if string.len(upgradeStr) > 0 then
          upgradeStr = upgradeStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          upgradeStr = upgradeStr..'Upgrading: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.cookedItemID == item.id then
        if string.len(cookStr) > 0 then
          cookStr = cookStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          cookStr = cookStr..'Cooking: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.burntItemID == item.id then
        if string.len(burnStr) > 0 then
          burnStr = burnStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          burnStr = burnStr..'Burning: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
    if item2.grownItemID == item.id then
        if string.len(growStr) > 0 then
          growStr = growStr..','..Icons.Icon({item2.name, type="item", notext="true"})
        else
          growStr = growStr..'Growing: '..Icons.Icon({item2.name, type="item", notext="true"})
        end
    end
  end
  if string.len(lootStr) > 0 then table.insert(lineArray, lootStr) end
  if string.len(upgradeStr) > 0 then table.insert(lineArray, upgradeStr) end
  if string.len(cookStr) > 0 then table.insert(lineArray, cookStr) end
  if string.len(burnStr) > 0 then table.insert(lineArray, burnStr) end
  if string.len(growStr) > 0 then table.insert(lineArray, growStr) end

  --Next: Can we take it from somebody else -without- killing them?
  local thiefStr = ''
  for i, npc in pairs(SkillData.Thieving) do
    if npc.lootTable ~= nil then
      for j, loot in pairs(npc.lootTable) do
        if loot[1] == item.id then
          if string.len(thiefStr) > 0 then
            thiefStr = thiefStr..','..Icons.Icon({npc.name, type="item", notext="true"})
          else
            thiefStr = thiefStr..'Pickpocketing: '..Icons.Icon({npc.name, type="item", notext="true"})
          end
        end
      end
    end
  end
  if string.len(thiefStr) > 0 then table.insert(lineArray, thiefStr) end

  --If all else fails, I guess we should check if we can make it ourselves
  --SmithCheck:
  if item.smithingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Smithing", item.smithingLevel))
  end

  --CraftCheck:
  if item.craftingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Crafting", item.craftingLevel))
  end

  --FletchCheck:
  if item.fletchingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Fletching", item.fletchingLevel))
  end

  --RunecraftCheck:
  if item.runecraftingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
  end

  --MineCheck:
  if item.miningLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Mining", item.miningLevel))
  end

  --FishCheck:
  if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) then
    table.insert(lineArray, Icons._SkillReq("Fishing", 1))
  elseif item.fishingLevel ~= nil then
    table.insert(lineArray, Icons._SkillReq("Fishing", item.fishingLevel))
  end

  --HerbCheck:
  if item.herbloreMasteryID ~= nil then
    local potionData = SkillData.Herblore.ItemData[item.herbloreMasteryID + 1].herbloreLevel
    table.insert(lineArray, Icons._SkillReq("Herblore", potionData))
  end

  --Finally there are some weird exceptions:
  --Coal can be acquired via firemaking
  if item.name == "Coal Ore" then
    table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
  end

  --Gems can be acquired from both mining & fishing
  if item.type == 'Gem' then
    table.insert(lineArray, Icons._SkillReq("Fishing", 1))
    table.insert(lineArray, Icons._SkillReq("Mining", 1))
  end

  --Tokens are from the appropriate skill
  if item.isToken then
    for skill, id in pairs(Constants.skill) do
      if id == item.skill then
        table.insert(lineArray, Icons._SkillReq(skill, 1))
      end
    end
  end

  --Shop items (including special items like gloves that aren't otherwise listed)
  if item.slayerCost ~= nil or item.buysFor ~= nil or Shared.contains(OtherShopItems, item.name) then
    table.insert(lineArray, '[[Shop]]')
  end

  --Easter Eggs (manual list 'cause don't have a better way to do that)
  if Shared.contains(EasterEggs, item.name) then
    table.insert(lineArray, '[[Easter Eggs]]')
  end

  return table.concat(lineArray, "<br/>")
end


function p.getItemSources(frame)
  local itemName = frame.args ~= nil and frame[1] or frame
  local item = p.getItem(itemName)
  if item == nil then
    return "ERROR: No item named "..itemName.." exists in the data module"
  end

  return p._getItemSources(item)
end

function p.getEquipmentTable(frame)
  local args = frame.args ~= nil and frame.args or frame
  local type = args.type
  local tier = args.tier
  local slotStr = args.slot
  local ammoTypeStr = args.ammoType
  local category = args.category ~= nil and args.category or 'Combat'

   --Find out what Ammo Type we're working with
  local ammoType = nil
  if ammoTypeStr ~= nil then
    if ammoTypeStr == "Arrows" then
      ammoType = 0
    elseif ammoTypeStr == 'Bolts' then
      ammoType = 1
    elseif ammoTypeStr == 'Javelins' then
      ammoType = 2
    elseif ammoTypeStr == 'Throwing Knives' then
      ammoType = 3
    end
  end

  --Find out what slot we're working with
  local slot = nil
  if slotStr ~= nil then
    slot = Constants.equipmentSlot[slotStr]
  end
  mw.log("Type = "..(type ~= nil and type or '')..", Slot = "..(slot ~= nil and slot or '')..", AmmoType = "..(ammoType ~= nil and ammoType or ''))
  

  --Getting some lists set up here that will be used later
  --First, the list of columns used by both weapons & armour
  local statColumns = {'slashAttackBonus', 'stabAttackBonus','blockAttackBonus','rangedAttackBonus', 'magicAttackBonus', 'strengthBonus', 'rangedStrengthBonus', 'magicDamageBonus', 'defenceBonus', 'rangedDefenceBonus', 'magicDefenceBonus'}
  --Then the lists for just weapons/just armour
  local weaponStatColumns = {'attackLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'} 
  local armourStatColumns = {'damageReduction', 'defenceLevelRequired', 'rangedLevelRequired', 'magicLevelRequired'}
  --Then the list of weapon types
  local weaponTypes = {'Magic Staff', 'Magic Wand', 'Ranged Weapon', 'Weapon'}

  local isWeaponType = Shared.contains(weaponTypes, type)
  
  --Alright, let's start the table by building the shared header
  local result = '{| class="wikitable sortable stickyHeader"\r\n|-class="headerRow-0"'
  if isWeaponType then
    --Weapons have an extra column here for Attack Speed
    result = result..'\r\n!colspan="3"|'
  else
    result = result..'\r\n!colspan="2"|'
  end
  result = result..'\r\n!colspan="5"style="padding:0 0.5em 0 0.5em;"|Attack Bonus'
  result = result..'\r\n!colspan="2"style="padding:0 0.5em 0 0.5em;"|Strength Bonus'
  result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|% Damage Bonus'
  result = result..'\r\n!colspan="3"style="padding:0 0.5em 0 0.5em;"|Defence Bonus'
  if isWeaponType then
    --Weapons have an extra columns here for "Two Handed?"
    result = result..'\r\n!colspan="1"|'
  else
    --Only armour pieces have DR right now, so ignore that column for weapons
    result = result..'\r\n!colspan="1"style="padding:0 0.5em 0 0.5em;"|Damage Reduction'
  end
  result = result..'\r\n!colspan="3"style="padding:0 0.5em 0 0.5em;"|Levels Required'
  result = result..'\r\n!colspan="1"|'
  --One header row down, one to go
  result = result..'\r\n|-class="headerRow-1"'
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Item'
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Name'
  --Weapons have Attack Speed here
  if isWeaponType then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Attack Speed'
  end
  --Attack bonuses
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --Strength bonuses
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Strength', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --Defence bonuses
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --Damage Reduction/Defence Req for armour, 2-handed/Attack Req for weapons
  if isWeaponType then
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Two Handed?'
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Attack', type='skill', notext='true'})
  else
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
    result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Defence', type='skill', notext='true'})
  end
  --Then Ranged/Magic requirements
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Ranged', type='skill', notext='true'})
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|'..Icons.Icon({'Magic', type='skill', notext='true'})
  --And finally Sources
  result = result..'\r\n!style="padding:0 1em 0 0.5em;"|Sources'

  --And with all the header out of the way, finally time to actually build the table itself.
  local itemList = {}
  for i, itemBase in pairs(ItemData) do
    local item = Shared.clone(itemBase)
    item.id = i - 1
    local listItem = false
    if isWeaponType then
    listItem = item.type == type and item.category == category
      if ammoType ~= nil then listItem = listItem and item.ammoTypeRequired == ammoType end
    else
      --Now for handling armour
      if type == "Armour" or type == "Melee" then
        listItem = item.defenceLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Armour')
      elseif type == "Ranged Armour" or type == "Ranged" then
        listItem = item.rangedLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Ranged Armour')
      elseif type == "Magic Armour" or type == "Magic" then
        listItem = item.magicLevelRequired ~= nil or (item.category == 'Combat' and item.type == 'Magic Armour')
      else
        listItem = item.type == type and item.category ~= 'Combat'
      end
      if ammoType ~= nil then listItem = listItem and item.ammoType == ammoType end
      if slot ~= nil then listItem = listItem and item.equipmentSlot == slot end
    end
    if listItem then
      table.insert(itemList, item)
    end
  end

  table.sort(itemList, function(a, b) return a.id < b.id end)
  for i, item in pairs(itemList) do
    if isWeaponType then
      --Building rows for weapons
      result = result..'\r\n|-'
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
      result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;" |'..Shared.formatnum(item.attackSpeed)
      for j, statName in pairs(statColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if statValue > 0 then
          result = result..'background-color:lightgreen;'
        elseif statValue < 0 then
          result = result..'background-color:lightpink;'
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --That's the first list out of the way, now for 2-Handed
      result = result..'\r\n| style ="text-align: right;"|'
      if item.isTwoHanded then result = result..'Yes' else result = result..'No' end
      --Now the weapon exclusive columns
      for j, statName in pairs(weaponStatColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --Finally, the Sources
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
      result = result..p._getItemSources(item)
    else
      --Building rows for armour
      result = result..'\r\n|-'
      result = result..'\r\n|style ="text-align: left;padding: 0 0 0 0;"|'..Icons.Icon({item.name, type='item', size=50, notext=true})
      result = result..'\r\n|style ="text-align: left;padding: 0 0.5em 0 0.5em;"|[['..item.name..']]'
      for j, statName in pairs(statColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if statValue > 0 then
          result = result..'background-color:lightgreen;'
        elseif statValue < 0 then
          result = result..'background-color:lightpink;'
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --That's the first list out of the way, now for armour specific things
      for j, statName in pairs(armourStatColumns) do
        local statValue = p._getItemStat(item, statName, true)
        result = result..'\r\n| style ="text-align: right;padding: 0 0.5em 0 0;'
        if j == 1 then
          if statValue > 0 then
            result = result..'background-color:lightgreen;'
          elseif statValue < 0 then
            result = result..'background-color:lightpink;'
          end
        end
        result = result..'"|'..Shared.formatnum(statValue)
        if statName == 'magicDamageBonus' or statName == 'damageReduction' then result = result..'%' end
      end
      --Finally, the Sources
      result = result..'\r\n| style ="text-align: right;white-space: nowrap;padding: 0 0.5em 0 0.5em;" |'
      result = result..p._getItemSources(item)
    end
  end

  result = result..'\r\n|}'
  return result
end

return p