Module:Items/SourceTables: Difference between revisions

_getItemSources/_getItemLootSourceTable: Refactor monster drops code
m (Remove redundant item source overrides)
(_getItemSources/_getItemLootSourceTable: Refactor monster drops code)
Line 19: Line 19:
local sourceOverrides = {
local sourceOverrides = {
   ['Dungeon'] = {
   ['Dungeon'] = {
  [361] = 'Volcanic Cave', -- Fire Cape
  [941] = 'Infernal Stronghold', -- Infernal Cape
     [950] = 'Volcanic Cave', -- A Tale of the Past, a future's prophecy
     [950] = 'Volcanic Cave', -- A Tale of the Past, a future's prophecy
     [951] = 'Fire God Dungeon', -- The First Hero and an Unknown Evil
     [951] = 'Fire God Dungeon', -- The First Hero and an Unknown Evil
Line 149: Line 151:
         break
         break
       end
       end
     
 
       --If this is a perfect item, need to find the original
       --If this is a perfect item, need to find the original
       if item2.perfectItem == item.id and item2.recipeRequirements ~= nil then
       if item2.perfectItem == item.id and item2.recipeRequirements ~= nil then
Line 321: Line 323:
   --Alright, time to go through all the ways you can get an item...
   --Alright, time to go through all the ways you can get an item...
   --First up: Can we kill somebody and take theirs?
   --First up: Can we kill somebody and take theirs?
   local killStr = ''
   local killStrPart = {}
   local dungeonStr = ''
   local dungeonStrPart = {}
  local count1 = 0
   for i, monster in ipairs(MonsterData.Monsters) do
   for i, monster in Shared.skpairs(MonsterData.Monsters) do
  local isDrop = false
    local isDrop = false
  if monster.bones == item.id and ((monster.lootTable ~= nil and not monster.isBoss) or Shared.contains(item.name, 'Shard')) then
    if monster.bones == item.id and ((monster.lootTable ~= nil and not monster.isBoss) or Shared.contains(item.name, "Shard")) then
  -- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
      isDrop = true
  isDrop = true
    elseif monster.lootTable ~= nil then
      elseif monster.lootTable ~= nil then
      for j, loot in pairs(monster.lootTable) do
      -- If the monster has a loot table, check if the item we are looking for is in there
        if loot[1] == item.id then
          for j, loot in ipairs(monster.lootTable) do
          isDrop = true
              if loot[1] == item.id then
          break
            isDrop = true
            break
            end
         end
         end
       end
       end
      if isDrop and Monsters.isDungeonOnlyMonster({args={monster.name}}) then
  if isDrop then
        -- 2021-05-24 Additional checks for dungeon exclusive monsters: Loot is not rolled on
  if not Shared.contains(item.name, 'Shard') and Monsters._isDungeonOnlyMonster(monster) then
        -- dungeon exclusive monsters unless they are the boss/last enemy of that dungeon
  -- For dungeon exclusive monsters, loot is only rolled when they are the last
        local rollForLoot = false
  -- monster within that dungeon (unless it is a shard)
        for k, area in pairs(Areas.getMonsterAreas(i - 1)) do
  if monster.isBoss then
          if not (area.type == 'dungeon') or (area.type == 'dungeon' and area.monsters[Shared.tableCount(area.monsters)] == i - 1) then
  local areaList = Areas.getMonsterAreas(monster.id)
            -- Either monster isn't dungeon exclusive, or is the boss/last enemy of a dungeon
  for k, area in ipairs(areaList) do
            rollForLoot = true
  if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
            break
  table.insert(dungeonStrPart, Icons.Icon({area.name, type='dungeon', notext=true}))
          end
  end
        end
  end
        isDrop = rollForLoot
  end
      end
  else
    end
  -- Item is not an end of dungeon reward, and drops when the monster is killed
    if isDrop then
  table.insert(killStrPart, Icons.Icon({monster.name, type='monster', notext=true}))
      if monster.isBoss and not Shared.contains(item.name, "Shard") then
  end
        local areaList = Areas.getMonsterAreas(i - 1)
  end
        --If this is a boss then we actually are completing dungeons for this and need to figure out which one
        for j, dung in pairs(areaList) do
          if string.len(dungeonStr) > 0 then
            dungeonStr = dungeonStr..','
          else
            dungeonStr = 'Completing: '
          end
          dungeonStr = dungeonStr..Icons.Icon({dung.name, type="dungeon", notext=true})
        end
      else
        count1 = count1 + 1
        if string.len(killStr) > 0 then
          killStr = killStr..','
          --if count1 % 3 == 1 and count1 > 1 then killStr = killStr..'<br/>' end
          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
   -- Is the item dropped from a cycle of the Impending Darkness event?
   -- Is the item dropped from a cycle of the Impending Darkness event?
   for i, eventItemID in ipairs(Areas.eventData.rewards) do
   for i, eventItemID in ipairs(Areas.eventData.rewards) do
  if item.id == eventItemID then
      if item.id == eventItemID then
    if string.len(dungeonStr) > 0 then
  local dungPrefix = (i == Shared.tableCount(Areas.eventData.rewards) and '' or i .. ' ' .. (i == 1 and 'cycle' or 'cycles') .. ' of ')
    dungeonStr = dungeonStr .. ','
          table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({'Impending Darkness Event', type='dungeon', notext=true}))
    else
  break
    dungeonStr = 'Completing: '
  end
    end
    local dungPrefix = (i == Shared.tableCount(Areas.eventData.rewards) and '' or i .. ' ' .. (i == 1 and 'cycle' or 'cycles') .. ' of ')
    dungeonStr = dungeonStr .. dungPrefix .. Icons.Icon({'Impending Darkness Event', type='dungeon', notext=true})
    break
  end
   end
   end
   -- Special exceptions for Fire/Infernal Cape and first two lore books
   -- Special exceptions for Fire/Infernal Cape and first two lore books
   if sourceOverrides['Dungeon'][item.id] ~= nil then
   if sourceOverrides['Dungeon'][item.id] ~= nil then
     if string.len(dungeonStr) > 0 then
     table.insert(dungeonStrPart, Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon', notext=true}))
      dungeonStr = dungeonStr .. ','
    else
      dungeonStr = 'Completing: '
    end
    dungeonStr = dungeonStr .. Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon', notext=true})
   end
   end


   if string.len(dungeonStr) > 0 then table.insert(lineArray, dungeonStr) end
   if Shared.tableCount(dungeonStrPart) > 0 then
   if string.len(killStr) > 0 then table.insert(lineArray, killStr) end
  table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, ','))
  end
   if Shared.tableCount(killStrPart) > 0 then
  table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, ','))
  end


   --Next: Can we find it in a box?
   --Next: Can we find it in a box?
Line 405: Line 383:
   local cookStr = ''
   local cookStr = ''
   local growStr = ''
   local growStr = ''
  local count1 = 0
   local count2 = 0
   local count2 = 0
  count1 = 0
   for i, item2 in pairs(ItemData.Items) do
   for i, item2 in pairs(ItemData.Items) do
     if item2.dropTable ~= nil then
     if item2.dropTable ~= nil then
Line 473: Line 451:
   table.insert(lineArray, Icons.Icon({"Astrology", type="skill"}))
   table.insert(lineArray, Icons.Icon({"Astrology", type="skill"}))
   end
   end
 
 
   --SmithCheck:
   --SmithCheck:
   if item.smithingLevel ~= nil then
   if item.smithingLevel ~= nil then
Line 493: Line 471:
     table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
     table.insert(lineArray, Icons._SkillReq("Runecrafting", item.runecraftingLevel))
   end
   end
 
 
   --CookCheck
   --CookCheck
   if item.cookingLevel ~= nil then
   if item.cookingLevel ~= nil then
Line 659: Line 637:
   --Alright, time to go through a few ways to get the item
   --Alright, time to go through a few ways to get the item
   --First up: Can we kill somebody and take theirs?
   --First up: Can we kill somebody and take theirs?
   for i, monster in pairs(MonsterData.Monsters) do
   for i, monster in ipairs(MonsterData.Monsters) do
     local minqty = 1
     local minqty = 1
     local qty = 1
     local qty = 1
Line 666: Line 644:
     --Only add bones if this monster has loot (ie appears outside a dungeon) and isn't a boss
     --Only add bones if this monster has loot (ie appears outside a dungeon) and isn't a boss
     --... unless we're looking for Shards of course, at which point we'll take any monster with the right bones
     --... unless we're looking for Shards of course, at which point we'll take any monster with the right bones
     if ((monster.lootTable ~= nil and not monster.isBoss) or Shared.contains(item.name, 'Shard')) and monster.bones == item.id then
     if monster.bones == item.id and ((monster.lootTable ~= nil and not monster.isBoss) or Shared.contains(item.name, 'Shard')) then
       qty = monster.boneQty ~= nil and monster.boneQty or 1
       qty = monster.boneQty ~= nil and monster.boneQty or 1
       minqty = qty
       minqty = qty
Line 672: Line 650:
       totalWt = 1
       totalWt = 1
     elseif monster.lootTable ~= nil then
     elseif monster.lootTable ~= nil then
       for j, loot in pairs(monster.lootTable) do
  -- If the monster has a loot table, check if the item we are looking for is in there
       for j, loot in ipairs(monster.lootTable) do
         totalWt = totalWt + loot[2]
         totalWt = totalWt + loot[2]
         if loot[1] == item.id then
         if loot[1] == item.id then
Line 682: Line 661:
     local lootChance = monster.lootChance ~= nil and monster.bones ~= item.id and monster.lootChance or 100
     local lootChance = monster.lootChance ~= nil and monster.bones ~= item.id and monster.lootChance or 100


    if wt > 0 and lootChance > 0 and not Shared.contains(item.name, 'Shard') and Monsters.isDungeonOnlyMonster({args={monster.name}}) then
if wt > 0 and lootChance > 0 then
      -- 2021-05-24 Additional checks for dungeon exclusive monsters: Loot is not rolled on
  if not Shared.contains(item.name, 'Shard') and Monsters._isDungeonOnlyMonster(monster) then
      -- dungeon exclusive monsters unless they are the boss/last enemy of that dungeon
-- For dungeon exclusive monsters, loot is only rolled when they are the last
      local rollForLoot = false
  -- monster within that dungeon (unless it is a shard)
      for k, area in pairs(Areas.getMonsterAreas(i - 1)) do
if monster.isBoss then
        if area.type ~= 'dungeon' or (area.type == 'dungeon' and area.monsters[Shared.tableCount(area.monsters)] == i - 1) then
  local areaList = Areas.getMonsterAreas(monster.id)
          -- Either monster isn't dungeon exclusive, or is the boss/last enemy of a dungeon
  for k, area in ipairs(areaList) do
          rollForLoot = true
            if area.type == 'dungeon' and area.monsters[#area.monsters] == monster.id then
          break
  table.insert(dropRows, {source = Icons.Icon({area.name, type='dungeon'}), type = '[[Dungeon]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
        end
    end
      end
  end
      if not rollForLoot then
    end
        wt = 0
  else
      end
         -- Item is not an end of dungeon reward, and drops when the monster is killed
    end
table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
    if wt > 0 and lootChance > 0 then
  end
      local sourceTxt = nil
end
      local typeTxt = nil
      --If we're dealing with a boss, this is a Dungeon row instead
      if monster.isBoss and not Shared.contains(item.name, 'Shard') then
        local dung = Areas.getMonsterAreas(i - 1)[1]
        sourceTxt = Icons.Icon({dung.name, type='dungeon'})
        typeTxt = '[[Dungeon]]'
      else
         sourceTxt = Icons.Icon({monster.name, type='monster'})
        typeTxt = '[[Monster]]'
      end
      table.insert(dropRows, {source = sourceTxt, type = typeTxt, minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
    end
   end
   end
   -- Is the item dropped from a cycle of the Impending Darkness event?
   -- Is the item dropped from a cycle of the Impending Darkness event?
   for i, eventItemID in ipairs(Areas.eventData.rewards) do
   for i, eventItemID in ipairs(Areas.eventData.rewards) do
Line 721: Line 687:
   end
   end
   end
   end
   --Special exception for the Fire/Infernal Cape and first two lore books as bonus dungeon drops
   --Special exception for the Fire/Infernal Cape and first two lore books as bonus dungeon drops
   if sourceOverrides['Dungeon'][item.id] ~= nil then
   if sourceOverrides['Dungeon'][item.id] ~= nil then
     local sourceTxt = Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon'})
     local sourceTxt = Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon'})
     table.insert(dropRows, {source=sourceTxt, type='[[Dungeon]]', minqty=1, qty=1, weight = 1, totalWeight = 1})
     table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
   end
   end


Line 966: Line 931:
   table.sort(itemArray, function(a, b) return a.id < b.id end)
   table.sort(itemArray, function(a, b) return a.id < b.id end)


   for i, item in Shared.skpairs(itemArray) do
   for i, item in ipairs(itemArray) do
     table.insert(resultPart, '|-\r\n')
     table.insert(resultPart, '|-\r\n')
     table.insert(resultPart, '! '..Icons.Icon({item.name, type='item', notext='true'})..'\r\n! '..Icons.Icon({item.name, type='item', noicon=true})..'\r\n')
     table.insert(resultPart, '! '..Icons.Icon({item.name, type='item', notext='true'})..'\r\n! '..Icons.Icon({item.name, type='item', noicon=true})..'\r\n')