Module:Items

From Melvor Idle
Revision as of 15:26, 23 September 2020 by Falterfire (talk | contribs) (Added function for compiling item sources. (Somewhat incomplete right now))

Lua module for generating various item tables. Pulls data from Module:GameData/data


local p = {}

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

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

local EasterEggs = {'Amulet of Calculated Promotion', 'Clue Chasers Insignia', '8', 'Lemon'}

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 = ''
  --Alright, time to go through all the ways you can get an item...
  --First up: Can we kill somebody and take theirs?
  local killFound = false
  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 killFound then
            result = result..','..Icons.Icon({monster.name, type="monster", notext="true"})
          else
            result = result..'Killing: '..Icons.Icon({monster.name, type="monster", notext="true"})
            killFound = true
          end
        end
      end
    end
  end

  --Next: Can we find it in a box?
  local boxFound = false
  for i, chest in pairs(ItemData) do
    if chest.dropTable ~= nil then
      for j, loot in pairs(chest.dropTable) do
        if loot[1] == item.id then
          if boxFound then
            result = result..','..Icons.Icon({chest.name, type="item", notext="true"})
          else
            if string.len(result) > 0 then result = result..'\r\n<br/>' end
            result = result..'Opening: '..Icons.Icon({chest.name, type="item", notext="true"})
            boxFound = true
          end
        end
      end
    end
  end

  --Next: Can we take it from somebody else -without- killing them?
  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(result) > 0 then result = result..'\r\n<br/>' end
          result = result..'Pickpocketing: '..Icons.Icon({npc.name, type="thieving", notext="true"})
          boxFound = true
        end
      end
    end
  end

  --Next: Can we get to it via upgrading?
  local upgradeFound = false
  if item.itemsRequired ~= nil then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..'Upgrading: '
    for i, req in pairs(item.itemsRequired) do
      local reqItem = p.getItemByID(req[1])
      if reqItem.canUpgrade then
        if upgradeFound then result = result..', ' else upgradeFound = true end
        result = result..Icons.Icon({reqItem.name, type='item', notext='true'})
      end
    end
  end

  --If all else fails, I guess we should check if we can make it ourselves
  --SmithCheck:
  if item.smithingLevel ~= nil then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..Icons._SkillReq("Smithing", item.smithingLevel)
  end

  --CraftCheck:
  if item.craftingLevel ~= nil then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..Icons._SkillReq("Crafting", item.craftingLevel)
  end

  --FletchCheck:
  if item.fletchingLevel ~= nil then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..Icons._SkillReq("Fletching", item.fletchingLevel)
  end

  --RunecraftCheck:
  if item.runecraftingLevel ~= nil then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..Icons._SkillReq("Runecrafting", item.runecraftingLevel)
  end

  --FishCheck:
  if (item.category == "Fishing" and (item.type == "Junk" or item.type == "Special")) or item.category == "Gem" then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..Icons._SkillReq("Fishing", 1)
  elseif item.category == 'Fishing' then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..Icons._SkillReq("Fishing", item.fishingLevel)
  end

  --Finally there are some weird exceptions:
  --Shop items
  if item.slayerCost ~= nil or item.buysFor ~= nil then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..'[[Shop]]'
  end

  --Easter Eggs (manual list 'cause don't have a better way to do that)
  if Shared.contains(EasterEggs, item.name) then
    if string.len(result) > 0 then result = result..'\r\n<br/>' end
    result = result..'[[Easter Eggs]]'
  end

  return result
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

return p