Anonymous

Module:Items/SourceTables: Difference between revisions

From Melvor Idle
Update for v1.1
(Alt Magic -> Alt. Magic for consistency)
(Update for v1.1)
Line 1: Line 1:
local p = {}
local p = {}


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


local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Magic = require('Module:Magic')
local Magic = require('Module:Magic')
local Areas = require('Module:CombatAreas')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Items = require('Module:Items')
Line 15: Line 12:
local Monsters = require('Module:Monsters')
local Monsters = require('Module:Monsters')
local Skills = require('Module:Skills')
local Skills = require('Module:Skills')
local SkillEnum = mw.loadData('Module:Constants/data').skill
-- Implements overrides for sources which cannot be obtained from game data
-- Currently only overrides for dungeon sources are implemented here
local sourceOverrides = {
['Dungeon'] = {
[950] = 'Volcanic Cave', -- A Tale of the Past, a future's prophecy
[951] = 'Fire God Dungeon', -- The First Hero and an Unknown Evil
[1116] = 'Into the Mist' -- Beginning of the End
}
}


function p._getCreationTable(item)
function p._getCreationTable(item)
Line 38: Line 24:


local tables = {}
local tables = {}
local itemID = item.id
--First figure out what skill is used to make this...
--First figure out what skill is used to make this...
if type(item.masteryID) == 'table' then
 
local skillID, masteryID = item.masteryID[1], item.masteryID[2]
local skillIDs = {
skill = Constants.getSkillName(skillID)
['Gathering'] = {
if skillID == SkillEnum.Fishing then
['Woodcutting'] = { recipeKey = 'trees' },
-- Fishing
['Fishing'] = { recipeKey = 'fish' },
local fish = SkillData.Fishing.Fish[masteryID + 1]
['Mining'] = { recipeKey = 'rockData' },
if fish ~= nil and fish.itemID == item.id then
['Farming'] = { recipeKey = 'recipes' }
lvl = fish.level
},
xp = fish.baseXP
['Artisan'] = {
qty = 1
['Cooking'] = { },
time = fish.baseMinInterval / 1000
['Smithing'] = { },
maxTime = fish.baseMaxInterval / 1000
['Fletching'] = { },
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime))
['Crafting'] = { },
['Runecrafting'] = { },
['Herblore'] = { },
['Summoning'] = { }
}
}
 
-- Gathering skills
-- All follow a similar data structure
for localSkillID, dataProp in ipairs(skillIDs.Gathering) do
local skillData = SkillData[localSkillID]
local skill = skillData.name
local lvl, xp, qty, req, time, maxTime = 0, 0, 0, nil, 0, nil
for i, recipe in ipairs(skillData[dataProp.recipeKey]) do
if recipe.productId == itemID then
lvl = recipe.level
xp = recipe.baseExperience
qty = recipe.baseQuantity or 1
-- Action time
if recipe.baseMinInterval ~= nil then
time = recipe.baseMinInterval / 1000
if recipe.baseMaxInterval ~= nil then
maxTime = recipe.baseMaxInterval / 1000
end
elseif recipe.baseInterval ~= nil then
time = recipe.baseInterval /1000
elseif skillData.baseInterval ~= nil then
time = skillData.baseInterval / 1000
end
-- Special requirements
if recipe.totalMasteryRequired ~= nil then
specialReq = Icons.Icon({'Mastery', notext=true}) .. Shared.formatNum(recipe.totalMasteryRequired) .. 'total [[' .. skill .. ']] [[Mastery]]'
end
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq))
-- Assumes item has a single source per skill
break
end
end
elseif skillID == SkillEnum.Mining then
end
-- Mining
end
local rock = SkillData.Mining.Rocks[masteryID + 1]
 
if rock ~= nil then
-- Artisan skills
lvl = rock.levelRequired
-- Allow follow a similar data structure
xp = rock.baseExperience
for localSkillID, dataProp in ipairs(skillIDs.Artisan) do
qty = rock.baseQuantity
local skillData = SkillData[localSkillID]
time = 3
local skill = skillData.name
if item.name == 'Dragonite Ore' then
local lvl, xp, qty, req, time, maxTime = 0, 0, 0, nil, 0, nil
specialReq = Icons.Icon({"Mastery", notext='true'})..' 271 total [[Mining]] [[Mastery]]'
for i, recipe in ipairs(skillData.recipes) do
if recipe.productID == itemID or (localSkillID == 'Cooking' and recipe.perfectCookID == itemID) then
lvl = recipe.level
xp = recipe.baseExperience
qty = recipe.baseQuantity or 1
-- Action time
if recipe.baseMinInterval ~= nil then
time = recipe.baseMinInterval / 1000
if recipe.baseMaxInterval ~= nil then
maxTime = recipe.baseMaxInterval / 1000
end
elseif recipe.baseInterval ~= nil then
time = recipe.baseInterval /1000
elseif skillData.baseInterval ~= nil then
time = skillData.baseInterval / 1000
end
end
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, nil, specialReq))
-- Special requirements
end
-- Potions have a mastery level requirement depending on the tier
elseif Shared.contains({SkillEnum.Smithing, SkillEnum.Fletching, SkillEnum.Crafting,
if item.charges ~= nil and item.tier ~= nil then
SkillEnum.Runecrafting, SkillEnum.Herblore}, skillID) then
local levelUnlock = GameData.getEntityByProperty(skillData.masteryLevelUnlocks, 'descriptionID', item.tier + 1)
-- Smithing, Fletching, Crafting, Runecrafting, Herblore
if levelUnlock ~= nil then
-- All have somewhat consistent recipe data structures
specialReq = Icons._MasteryReq(item.name, levelUnlock.level, false)
local recipeKey = (skillID == SkillEnum.Herblore and 'Potions') or 'Recipes'
end
local recipe = SkillData[skill][recipeKey][masteryID + 1]
end
if recipe ~= nil then
-- Materials & output quantity
local masteryReq = nil
-- Special case for Summoning recipes
local itemMatch = (recipe.itemID == item.id)
if localSkillID == 'Summoning' then
if skillID == SkillEnum.Herblore then
local shardCostArray, otherCostArray = {}, {}
-- For Herblore, we need to check a table of potion IDs & determine the mastery requirement
local recipeGPCost = skillData.recipeGPCost
for i, potionID in ipairs(recipe.potionIDs) do
-- Shards
if potionID == item.id then
for j, itemCost in ipairs(recipe.itemCosts) do
itemMatch = true
local shard = Items.getItemByID(itemCost.id)
masteryReq = SkillData.Herblore.TierMasteryLevels[i]
if shard ~= nil then
break
table.insert(shardCostArray, Icons.Icon({shard.name, type='item', notext=true, qty=itemCost.quantity}))
end
end
end
end
end
-- Other costs
if itemMatch then
if recipe.gpCost > 0 then
local baseTime = {
table.insert(otherCostArray, Icons.GP(recipe.gpCost))
[SkillEnum.Smithing] = 2,
end
[SkillEnum.Fletching] = 2,
if recipe.scCost > 0 then
[SkillEnum.Crafting] = 3,
table.insert(otherCostArray, Icons.SC(recipe.scCost))
[SkillEnum.Runecrafting] = 2,
[SkillEnum.Herblore] = 2
}
local baseQty = recipe.baseQuantity or 1
lvl = recipe.level
xp = recipe.baseXP
time = baseTime[skillID]
if masteryReq ~= nil and masteryReq > 1 then
specialReq = Icons._MasteryReq(item.name, masteryReq, false)
end
end
-- Some items (such as Arrow shafts) have multiple recipes
for j, nonShardID in ipairs(recipe.nonShardItemCosts) do
if type(recipe.alternativeCosts) == 'table' then
local nonShard = Items.getItemByID(nonShardID)
local reqPart, qtyPart = {}, {}
if nonShard ~= nil then
for i, altCost in ipairs(recipe.alternativeCosts) do
local itemValue = math.max(nonShard.sellsFor, 20)
local reqSubPart = {}
local nonShardQty = math.max(1, math.floor(recipeGPCost / itemValue))
for j, itemCost in ipairs(altCost.itemCosts) do
table.insert(otherCostArray, Icons.Icon({nonShard.name, type='item', notext=true, qty=nonShardQty}))
local reqItem = Items.getItemByID(itemCost.id)
if reqItem == nil then
table.insert(reqSubPart, itemCost.qty .. 'x ?????')
else
table.insert(reqSubPart, Icons.Icon({reqItem.name, type='item', qty=itemCost.qty}))
end
end
if recipe.gpCost ~= nil and recipe.gpCost > 0 then
table.insert(reqSubPart, Icons.GP(recipe.gpCost))
end
if recipe.scCost ~= nil and recipe.scCost > 0 then
table.insert(reqSubPart, Icons.GP(recipe.scCost))
end
table.insert(reqPart, table.concat(reqSubPart, ', '))
table.insert(qtyPart, Shared.formatnum(baseQty * altCost.quantityMultiplier))
end
end
local sep = "<br/>'''OR''' "
req = table.concat(reqPart, sep)
qty = table.concat(qtyPart, sep)
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time, maxTime, specialReq))
elseif type(recipe.itemCosts) == 'table' and Shared.tableCount(recipe.itemCosts) > 0 then
req = recipe.itemCosts
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, baseQty, time, maxTime, specialReq, recipe.gpCost, recipe.scCost))
end
end
end
req = table.concat(shardCostArray, ', ')
end
if not Shared.tableIsEmpty(otherCostArray) then
elseif skillID == SkillEnum.Summoning then
req = req .. '<br/>and one of the following:<br/>' .. table.concat(otherCostArray, "<br/>'''OR''' ")
-- Summoning
local recipe = SkillData.Summoning.Marks[masteryID + 1]
if recipe ~= nil and recipe.itemID == item.id then
lvl = recipe.level
xp = recipe.baseXP
qty = recipe.baseQuantity
time = 5
-- Create item requirements text
local ShardCostArray, OtherCostArray = {}, {}
-- Shards
for j, cost in ipairs(recipe.itemCosts) do
local shard = Items.getItemByID(cost.id)
if shard ~= nil then
table.insert(ShardCostArray, Icons.Icon({shard.name, type='item', notext=true, qty=cost.qty}))
end
end
end
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
-- Other costs
-- Some items (such as Arrow shafts) have multiple recipes
local recipeGPCost = SkillData.Summoning.RecipeGPCost
elseif type(recipe.alternativeCosts) == 'table' then
if recipe.gpCost > 0 then
local reqPart, qtyPart = {}, {}
table.insert(OtherCostArray, Icons.GP(recipe.gpCost))
for j, altCost in ipairs(recipe.alternativeCosts) do
end
local reqSubPart = {}
if recipe.scCost > 0 then
for k, itemCost in ipairs(altCost.itemCosts) do
table.insert(OtherCostArray, Icons.SC(recipe.scCost))
local reqItem = Items.getItemByID(itemCost.id)
end
if reqItem == nil then
for j, nonShardID in ipairs(recipe.nonShardItemCosts) do
table.insert(reqSubPart, itemCost.qty .. 'x ?????')
local nonShard = Items.getItemByID(nonShardID)
else
if nonShard ~= nil then
table.insert(reqSubPart, Icons.Icon({reqItem.name, type='item', qty=itemCost.qty}))
local itemValue = math.max(nonShard.sellsFor, 20)
end
local nonShardQty = math.max(1, math.floor(recipeGPCost / itemValue))
end
table.insert(OtherCostArray, Icons.Icon({nonShard.name, type='item', notext=true, qty=nonShardQty}))
if recipe.gpCost ~= nil and recipe.gpCost > 0 then
table.insert(reqSubPart, Icons.GP(recipe.GPCost))
end
if recipe.scCost ~= nil and recipe.scCost > 0 then
table.insert(reqSubPart, Icons.SC(recipe.SCCost))
end
table.insert(reqPart, table.concat(reqSubPart, ', '))
table.insert(qtyPart, Shared.formatnum(qty * altCost.quantityMultiplier))
end
end
local sep = "<br/>'''OR'''"
req = table.concat(reqPart, sep)
local qtyText = table.concat(qtyPart, sep)
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qtyText, time, maxTime, specialReq))
-- Finally, normal recipes with a single set of item costs
elseif type(recipe.itemCosts) == 'table' and not Shared.tableIsEmpty(recipe.itemCosts) then
table.insert(tables, p.buildCreationTable(skill, lvl, xp, recipe.itemCosts, qty, time, maxTime, specialReq, recipe.gpCost, recipe.scCost))
end
end
req = table.concat(ShardCostArray, ', ')
if #OtherCostArray > 0 then
req = req .. '<br/>and one of the following:<br/>' .. table.concat(OtherCostArray, "<br/>'''OR''' ")
end
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
end
end
end
-- Woodcutting
if item.type == 'Logs' then
-- Determine which tree (if any) the log is from
for i, tree in ipairs(SkillData.Woodcutting.Trees) do
if tree.logID == item.id then
skill = 'Woodcutting'
lvl = tree.levelRequired
time = tree.baseInterval / 1000
xp = tree.baseExperience
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
break
end
end
end
-- Cooking
if item.canEat then
for i, recipe in ipairs(SkillData.Cooking.Recipes) do
if recipe.itemID == item.id or recipe.perfectCookID == item.id then
skill = 'Cooking'
lvl = recipe.level
xp = recipe.baseXP
req = recipe.itemCosts
qty = recipe.baseQuantity
time = recipe.baseInterval / 1000
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
break
end
end
end
-- Farming
if item.type == 'Herb' or item.type == 'Logs' or (item.type == 'Food' and item.category ~= 'Cooking') then
-- Herb means farming
-- Logs/Food might mean farming or might not. Depends on the item
for i, seed in ipairs(ItemData.Items) do
if seed.grownItemID ~= nil and seed.grownItemID == item.id then
skill = 'Farming'
lvl = seed.farmingLevel
xp = seed.farmingXP
time = seed.timeToGrow
if item.type == 'Logs' then
qty = 35
else
qty = 15
end
req = {{id = seed.id, qty = (seed.seedsRequired ~= nil and seed.seedsRequired or 1)}}
table.insert(tables, p.buildCreationTable(skill, lvl, xp, req, qty, time))
break
end
end
end
end
end
end


-- Alt. Magic, excludes Gems and Bars
-- Alt. Magic, excludes spells which can produce a variety of items, such as Gems and Bars
-- Bars are handled by getItemSuperheatTable()
-- Bars are handled by getItemSuperheatTable()
-- Gems are handled by _getItemLootSourceTable()
-- Gems are handled by _getItemLootSourceTable()
for i, altSpell in ipairs(MagicData.AltMagic) do
for i, altSpell in ipairs(p.getSpellsBySpellBook('altMagic')) do
if type(altSpell.produces) == 'number' and altSpell.produces == item.id then
if altSpell.produces == item.id then
table.insert(tables, p._buildAltMagicTable(altSpell))
table.insert(tables, p._buildAltMagicTable(altSpell))
end
end
end
end


if Shared.tableCount(tables) == 0 then
if Shared.tableIsEmpty(tables) then
return ""
return ''
else
else
return table.concat(tables, '\r\n')
return table.concat(tables, '\r\n')
Line 243: Line 192:
function p.getAltMagicTable(frame)
function p.getAltMagicTable(frame)
local spellName = frame.args ~= nil and frame.args[1] or frame
local spellName = frame.args ~= nil and frame.args[1] or frame
local spell = Magic.getSpell(spellName, 'AltMagic')
local spell = Magic.getSpell(spellName, 'altMagic')
if spell == nil then
if spell == nil then
return 'ERROR: Could not find Alt. Magic spell "' .. spellName .. '"[[Category:Pages with script errors]]'
return 'ERROR: Could not find Alt. Magic spell "' .. spellName .. '"[[Category:Pages with script errors]]'
Line 253: Line 202:
function p._buildAltMagicTable(spell)
function p._buildAltMagicTable(spell)
local resultPart = {}
local resultPart = {}
local imgType = p._getSpellIconType(spell)
table.insert(resultPart, '{|class="wikitable"\r\n|-')
table.insert(resultPart, '{|class="wikitable"\r\n|-')
table.insert(resultPart, '\r\n!colspan="2"|'..Icons.Icon({spell.name, type='spell'}))
table.insert(resultPart, '\r\n!colspan="2"|'..Icons.Icon({spell.name, type=imgType}))
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Requirements')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Requirements')
table.insert(resultPart, '\r\n|'..Icons._SkillReq('Magic', spell.level))
table.insert(resultPart, '\r\n|'..Icons._SkillReq('Magic', spell.level))


-- The produces property of Alt. Magic spells is as follows:
local costText = p._getAltSpellCostText(spell)
-- -3 = A random gem, using the same weights as Mining
if costText ~= nil then
-- -2 = A bar of the type being created (Superheat)
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
-- -1 = GP (Alchemy)
table.insert(resultPart, '\r\n| ' .. costText)
--  0 = Undefined
-- >0 = Item ID of the item being produced
-- The amount produced is determined by the productionRatio property
 
-- The consumes property of Alt. Magic spells is as follows:
-- 0 = Any item
-- 1 = Junk item
-- 2 = Superheat/ores with Coal
-- 3 = Superheat/ores without Coal
-- 4 = Nothing
-- 5 = Coal ore
-- Superheat (2, 3) is handled by _getItemSuperheatTable()
if spell.consumes ~= nil then
local consumeText = {
'1 of any item',
'1 of any [[Fishing#Junk|Junk]] item',
'1 x required ores for the chosen bar',
'1 x required ores (except ' .. Icons.Icon({'Coal Ore', type='item'}) .. ') for the chosen bar',
nil,
Icons.Icon({'Coal Ore', type='item', qty=1})
}
local consumeStr = consumeText[spell.consumes + 1]
if consumeStr ~= nil then
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials')
table.insert(resultPart, '\r\n| ' .. consumeStr)
end
end
end


Line 305: Line 229:
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{|class="wikitable"')
table.insert(resultPart, '{|class="wikitable"')
if req ~= nil then
table.insert(resultPart, '\r\n!colspan="2"|Item ' .. (req == nil and 'Creation' or 'Production'))
table.insert(resultPart, '\r\n!colspan="2"|Item Creation')
else
table.insert(resultPart, '\r\n!colspan="2"|Item Production')
end
table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Requirements')
table.insert(resultPart, '\r\n|-\r\n!style="text-align: right;"|Requirements')
table.insert(resultPart, '\r\n|'..Icons._SkillReq(skill, lvl))
table.insert(resultPart, '\r\n|'..Icons._SkillReq(skill, lvl))
Line 343: Line 263:
table.insert(resultPart, '\r\n|'..Shared.formatnum(xp)..' XP')
table.insert(resultPart, '\r\n|'..Shared.formatnum(xp)..' XP')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Creation Time')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Base Creation Time')
table.insert(resultPart, '\r\n|'..Shared.formatnum(Shared.round(time, 2, 0))..'s')
table.insert(resultPart, '\r\n|'..Shared.timeString(time, true))
if maxTime ~= nil and maxTime > time then table.insert(resultPart, ' - '..Shared.formatnum(Shared.round(maxTime, 2, 0))..'s') end
if maxTime ~= nil and maxTime > time then table.insert(resultPart, ' - '..Shared.timeString(maxTime, true)) end
table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')


Line 367: Line 287:
--First up: Can we kill somebody and take theirs?
--First up: Can we kill somebody and take theirs?
local killStrPart = {}
local killStrPart = {}
for i, monster in ipairs(MonsterData.Monsters) do
for i, monster in ipairs(GameData.rawData.monsters) do
local isDrop = false
local isDrop = false
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
Line 378: Line 298:
--  - A boss monster, whose drops are accounted for in data from Areas instead
--  - A boss monster, whose drops are accounted for in data from Areas instead
for j, loot in ipairs(monster.lootTable) do
for j, loot in ipairs(monster.lootTable) do
if loot[1] == item.id and not Monsters._isDungeonOnlyMonster(monster) then
if loot.itemID == item.id and not Monsters._isDungeonOnlyMonster(monster) then
isDrop = true
isDrop = true
break
break
Line 391: Line 311:
-- Is the item dropped from any dungeon?
-- Is the item dropped from any dungeon?
local dungeonStrPart = {}
local dungeonStrPart = {}
local dungeonList = Areas.getAreas(function(area) return area.type == 'dungeon' and type(area.rewards) == 'table' and Shared.contains(area.rewards, item.id) end)
for i, dungeon in ipairs(GameData.rawData.dungeons) do
if dungeonList ~= nil then
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
for i, dungeon in ipairs(dungeonList) do
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
table.insert(dungeonStrPart, Icons.Icon({dungeon.name, type='dungeon', notext=true}))
table.insert(dungeonStrPart, Icons.Icon({dungeon.name, type='dungeon', notext=true}))
elseif dungeon.eventID ~= nil then
-- Is the item dropped from a combat event (e.g. Impending Darkness event)?
local event = GameData.getEntityByID('combatEvents', dungeon.eventID)
if type(event) == 'table' and type(event.itemRewardIDs) == 'table' then
for j, itemRewardID in ipairs(event.itemRewardIDs) do
if item.id == itemRewardID then
local dungPrefix = (i == Shared.tableCount(event.itemRewardIDs) and '' or (i == 1 and 'cycle' or 'cycles') .. ' of ')
table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({dungeon.name, type='dungeon', notext=true}))
break
end
end
end
end
end
end
-- Is the item dropped from a cycle of the Impending Darkness event?
for i, eventItemID in ipairs(Areas.eventData.rewards) do
if item.id == eventItemID then
local dungPrefix = (i == Shared.tableCount(Areas.eventData.rewards) and '' or i .. ' ' .. (i == 1 and 'cycle' or 'cycles') .. ' of ')
table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({'Impending Darkness Event', type='dungeon', notext=true}))
break
end
end
-- Special exceptions for lore books
if sourceOverrides['Dungeon'][item.id] ~= nil then
table.insert(dungeonStrPart, Icons.Icon({sourceOverrides['Dungeon'][item.id], type='dungeon', notext=true}))
end
end


if Shared.tableCount(dungeonStrPart) > 0 then
if not Shared.tableIsEmpty(dungeonStrPart) then
table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, ','))
table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, ','))
end
end
if Shared.tableCount(killStrPart) > 0 then
if not Shared.tableIsEmpty(killStrPart) then
table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, ','))
table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, ','))
end
end


--Next: Can we find it in a box?
-- Can we find it in an openable item?
--While we're here, check for upgrades, and growing
local lootPart = {}
local lootPart, upgradePart, growPart = {}, {}, {}
for i, item2 in ipairs(GameData.rawData.items) do
for i, item2 in pairs(ItemData.Items) do
if item2.dropTable ~= nil then
if item2.dropTable ~= nil then
for j, loot in ipairs(item2.dropTable) do
for j, loot in ipairs(item2.dropTable) do
if loot[1] == item.id then
if loot.id == item.id then
table.insert(lootPart, Icons.Icon({item2.name, type='item', notext=true}))
table.insert(lootPart, Icons.Icon({item2.name, type='item', notext=true}))
break
break
end
end
end
end
end
if item2.trimmedItemID ~= nil and item2.trimmedItemID == item.id then
table.insert(upgradePart, Icons.Icon({item2.name, type='item', notext=true}))
end
if item2.grownItemID == item.id then
-- Farming
table.insert(growPart, Icons.Icon({item2.name, type='item', notext=true}))
end
end
end
end
if #lootPart > 0 then
 
if not Shared.tableIsEmpty(lootPart) then
table.insert(lineArray, 'Opening: ' .. table.concat(lootPart, ','))
table.insert(lineArray, 'Opening: ' .. table.concat(lootPart, ','))
end
end
if #upgradePart > 0 then
 
table.insert(categoryArray, '[[Category:Upgraded Items]]')
-- Is the item a result of upgrading/downgrading another item?
table.insert(lineArray, 'Upgrading: ' .. table.concat(upgradePart, ','))
local upgradePart = { up = {}, down = {} }
for i, upgrade in ipairs(GameData.rawData.itemUpgrades) do
if item.id == upgrade.upgradedItemID then
local key = (upgrade.isDowngrade and 'down' or 'up')
for j, rootItemID in ipairs(upgrade.rootItemIDs) do
local rootItem = Items.getItemByID(rootItemID)
if rootItem ~= nil then
table.insert(upgradePart[key], Icons.Icon({rootItem.name, type='item', notext=true}))
end
end
end
end
end
if #growPart > 0 then
 
table.insert(categoryArray, '[[Category:Harvestable Items]]')
local upgradeCat = false
table.insert(lineArray, 'Growing: ' .. table.concat(growPart, ','))
for k, parts in pairs(upgradePart) do
if not Shared.tableIsEmpty(parts) then
if not upgradeCat then
table.insert(categoryArray, '[[Category:Upgraded Items]]')
upgradeCat = true
end
local typeText = (k == 'up' and 'Upgrading') or 'Downgrading'
table.insert(lineArray, typeText .. ': ' .. table.concat(parts, ','))
end
end
end


Line 467: Line 398:


--Check if we can make it ourselves
--Check if we can make it ourselves
--AstrologyCheck (Just a brute force for now because only two items)
local skillIDs = {
if Shared.contains({'Stardust', 'Golden Stardust'}, item.name) then
['Gathering'] = {
table.insert(lineArray, Icons.Icon({'Astrology', type='skill'}))
['Woodcutting'] = { recipeKey = 'trees' },
end
['Fishing'] = { recipeKey = 'fish' },
 
['Mining'] = { recipeKey = 'rockData' },
-- Sources discoverable through mastery IDs
['Farming'] = { recipeKey = 'recipes' }
-- Does _not_ handle:
},
-- Fishing: Junk, special items
['Artisan'] = {
-- Cooking: perfect items
['Cooking'] = { },
if type(item.masteryID) == 'table' then
['Smithing'] = { },
local skillID, masteryID = item.masteryID[1], item.masteryID[2]
['Fletching'] = { },
local skill = Constants.getSkillName(skillID)
['Crafting'] = { },
local keyData = {
['Runecrafting'] = { },
[SkillEnum.Fishing] = { ["recipe"] = 'Fish' },
['Herblore'] = { },
[SkillEnum.Mining] = { ["recipe"] = 'Rocks', ["level"] = 'levelRequired', ["item"] = 'oreID' },
['Summoning'] = { }
[SkillEnum.Smithing] = {},
[SkillEnum.Fletching] = {},
[SkillEnum.Crafting] = {},
[SkillEnum.Runecrafting] = {},
[SkillEnum.Herblore] = { ["recipe"] = 'Potions', ["item"] = 'potionIDs', ["isItemList"] = true },
[SkillEnum.Summoning] = { ["recipe"] = 'Marks' }
}
}
local keys = keyData[skillID]
}
if type(keys) == 'table' then
if keys.recipe == nil then
keys.recipe = 'Recipes'
end
if keys.level == nil then
keys.level = 'level'
end
if keys.item == nil then
keys.item = 'itemID'
end
if keys.isItemList == nil then
keys.isItemList = false
end


local recipe = SkillData[skill][keys.recipe][masteryID + 1]
-- Gathering skills
if recipe ~= nil and (
for localSkillID, dataProp in ipairs(skillIDs.Gathering) do
(not keys.isItemList and recipe[keys.item] == item.id) or
local skillData = SkillData[localSkillID]
(keys.isItemList and Shared.contains(recipe[keys.item], item.id))) then
local skill = skillData.name
local levelReq = recipe[keys.level]
for i, recipe in ipairs(skillData[dataProp.recipeKey]) do
if levelReq ~= nil then
if recipe.productId == item.id then
table.insert(lineArray, Icons._SkillReq(skill, levelReq))
table.insert(lineArray, Icons._SkillReq(skill, recipe.level))
end
break
end
end
end
end
end
end


-- Woodcutting
-- Artisan skills
for i, tree in ipairs(SkillData.Woodcutting.Trees) do
for localSkillID, dataProp in ipairs(skillIDs.Artisan) do
if tree.logID == item.id then
local skillData = SkillData[localSkillID]
table.insert(lineArray, Icons._SkillReq('Woodcutting', tree.levelRequired))
local skill = skillData.name
break
for i, recipe in ipairs(skillData.recipes) do
if recipe.productID == item.id or (localSkillID == 'Cooking' and recipe.perfectCookID == item.id) then
table.insert(lineArray, Icons._SkillReq(skill, recipe.level))
break
end
end
end
end
end


-- Woodcutting: Nests
--AstrologyCheck (Just a brute force for now because only two items)
if item.name == 'Bird Nest' then
if Shared.contains({SkillData.Astrology.stardustItemID, SkillData.Astrology.goldenStardustItemID}, item.id) then
table.insert(lineArray, Icons._SkillReq('Woodcutting', 1))
table.insert(lineArray, Icons.Icon({SkillData.Astrology.name, type='skill'}))
end
end


-- Fishing: Junk & Specials
-- Woodcutting: Nests
if Shared.contains(SkillData.Fishing.JunkItems, item.id) then
-- TODO Ash
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Junk|Junk]]')
if item.id == SkillData.Woodcutting.nestItemID then
else
table.insert(lineArray, Icons._SkillReq(SkillData.Woodcutting.name, 1))
for i, specialItem in ipairs(SkillData.Fishing.SpecialItems) do
elseif item.id == SkillData.Woodcutting.ravenNestItemID then
if specialItem[1] == item.id then
local levelReq = nil
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Special|Special]]')
for i, tree in ipairs(SkillData.Woodcutting.trees) do
break
if tree.canDropRavenNest and (levelReq == nil or tree.level < levelReq) then
levelReq = tree.level
end
end
end
end
table.insert(lineArray, Icons._SkillReq(SkillData.Woodcutting.name, levelReq))
end
end


-- Cooking
-- Fishing: Junk & Specials
if item.canEat then
if Shared.contains(SkillData.Fishing.junkItemIDs, item.id) then
for i, recipe in ipairs(SkillData.Cooking.Recipes) do
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Junk|Junk]]')
if recipe.itemID == item.id or recipe.perfectCookID == item.id then
elseif GameData.getEntityByProperty(SkillData.Fishing.specialItems, 'itemID', item.id) ~= nil then
table.insert(lineArray, Icons._SkillReq('Cooking', recipe.level))
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Special|Special]]')
break
end
end
end
end


-- Alt. Magic
-- Firemaking: Coal
for i, altSpell in ipairs(MagicData.AltMagic) do
-- TODO Ash, Charcoal, Diamonds, Fire spirits
if type(altSpell.produces) == 'number' and
if item.id == SkillData.Firemaking.coalItemID then
((altSpell.produces == -3 and item.type == 'Gem' and item.name ~= 'Jadestone')
table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
or (altSpell.produces == -2 and item.type == 'Bar')
    or (altSpell.produces > 0 and altSpell.produces == item.id)) then
table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
break
end
end
end


--Finally there are some weird exceptions:
-- Mining: Gems
--Coal can be acquired via firemaking
if (GameData.getEntityByProperty('randomGems', 'itemID', item.id) ~= nil or
if item.name == "Coal Ore" then
GameData.getEntityByProperty('randomSuperiorGems', 'itemID', item.id) ~= nil) then
table.insert(lineArray, Icons._SkillReq("Firemaking", 1))
table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]')
end
end


--Gems can be acquired from Mining
-- Alt. Magic
if item.type == 'Gem' and item.name ~= 'Jadestone' then
if not Shared.tableIsEmpty(Magic.getSpellsProducingItem(item.id)) then
table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]')
table.insert(lineArray, Icons.Icon({"Alt. Magic", type='skill'}))
end
end


--Rhaelyx pieces are also special
--Finally there are some weird exceptions:
--Rhaelyx pieces
if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
if Shared.contains({'Circlet of Rhaelyx', 'Jewel of Rhaelyx', 'Mysterious Stone'}, item.name) then
local rhaSkills = {
local rhaSkills = {
Line 599: Line 510:


--Tokens are from the appropriate skill
--Tokens are from the appropriate skill
if item.isToken and item.skill ~= nil then
if item.modifiers ~= nil and item.modifiers.masteryToken ~= nil then
table.insert(lineArray, Icons._SkillReq(Constants.getSkillName(item.skill), 1))
for localSkillID, skillData in ipairs(SkillData) do
if skillData.masteryTokenID ~= nil and skillData.masteryTokenID == item.id then
table.insert(lineArray, Icons._SkillReq(skillData.name, 1))
break
end
end
end
end


Line 609: Line 525:


--Shop items (including special items like gloves that aren't otherwise listed)
--Shop items (including special items like gloves that aren't otherwise listed)
local shopSources = Shop.getItemSourceArray(item.id)
if not Shared.tableIsEmpty(Shop.getItemSourceArray(item.id)) then
if Shared.tableCount(shopSources) > 0 then
table.insert(lineArray, Icons.Icon({'Shop'}))
table.insert(lineArray, Icons.Icon({'Shop'}))
end
end
Line 625: Line 540:
--Gold Topaz Ring drops from any action (when not wearing a Gold Topaz Ring)
--Gold Topaz Ring drops from any action (when not wearing a Gold Topaz Ring)
--Also handling Signet Ring things here
--Also handling Signet Ring things here
if item.name == 'Gold Topaz Ring' then
if item.id == 'melvorD:Gold_Topaz_Ring' then
table.insert(lineArray, 'Any non-combat action if not worn (Instead of '..Icons.Icon({"Signet Ring Half (a)", type="item"})..')')
table.insert(lineArray, 'Any non-combat action if not worn (Instead of '..Icons.Icon({"Signet Ring Half (a)", type="item"})..')')
table.insert(lineArray, 'Killing any monster if not worn (Instead of '..Icons.Icon({"Signet Ring Half (b)", type="item"})..')')
table.insert(lineArray, 'Killing any monster if not worn (Instead of '..Icons.Icon({"Signet Ring Half (b)", type="item"})..')')
elseif item.name == 'Signet Ring Half (a)' then
elseif item.id == 'melvorD:Signet_Ring_Half_A' then
table.insert(lineArray, 'Any non-combat action while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
table.insert(lineArray, 'Any non-combat action while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
elseif item.name == 'Signet Ring Half (b)' then
elseif item.id == 'melvorD:Signet_Ring_Half_B' then
table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'}))
end
end
Line 679: Line 594:
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
local fmt = (chance < 0.10 and '%.2g') or '%.2f'
local fmt = (chance < 0.10 and '%.2g') or '%.2f'
chance = string.format(fmt, chance)
local chanceStr = string.format(fmt, chance)
if weight >= totalWeight then
if weight >= totalWeight then
-- Fraction would be 1/1, so only show the percentage
-- Fraction would be 1/1, so only show the percentage
chance = 100
chanceStr = '100'
table.insert(rowPart, '\r\n|colspan="2" ')
table.insert(rowPart, '\r\n|colspan="2" ')
else
else
Line 691: Line 606:
table.insert(rowPart, '\r\n|colspan="2" ')
table.insert(rowPart, '\r\n|colspan="2" ')
else
else
table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="' .. chance .. '"| ' .. Shared.fraction(weight, totalWeight) .. '\r\n|')
table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="' .. chanceStr .. '"| ' .. Shared.fraction(weight, totalWeight) .. '\r\n|')
end
end
end
end
Line 698: Line 613:
table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
else
else
table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chance .. '"|'..chance..'%')
table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chanceStr .. '"|'..chanceStr..'%')
end
end
return table.concat(rowPart)
return table.concat(rowPart)
Line 706: Line 621:
--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 ipairs(MonsterData.Monsters) do
for i, drop in ipairs(p._getItemMonsterSources(item)) do
local minqty = 1
local monster = GameData.getEntityByID('monsters', drop.id)
local qty = 1
if monster ~= nil then
local wt = 0
table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = drop.minQty, drop.maxQty, weight = drop.dropWt, totalWeight = drop.totalWt})
local totalWt = 0
--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
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
qty = monster.boneQty ~= nil and monster.boneQty or 1
minqty = qty
wt = 1
totalWt = 1
elseif monster.lootTable ~= nil then
-- If the monster has a loot table, check if the item we are looking for is in there
-- Dungeon exclusive monsters don't count as they are either:
--  - A monster before the boss, which doesn't drop anything except shards (checked above)
--  - A boss monster, whose drops are accounted for in data from Areas instead
for j, loot in ipairs(monster.lootTable) do
totalWt = totalWt + loot[2]
if loot[1] == item.id and not Monsters._isDungeonOnlyMonster(monster) then
wt = loot[2]
qty = loot[3]
end
end
end
end
local lootChance = monster.lootChance ~= nil and monster.bones ~= item.id and monster.lootChance or 100
end


if wt > 0 and lootChance > 0 then
-- Item drops when the monster is killed
table.insert(dropRows, {source = Icons.Icon({monster.name, type='monster'}), type = '[[Monster]]', minqty = minqty, qty = qty, weight = wt * lootChance, totalWeight = totalWt * 100})
end
end
-- Is the item dropped from any dungeon?
-- Is the item dropped from any dungeon?
local dungeonList = Areas.getAreas(function(area) return area.type == 'dungeon' and type(area.rewards) == 'table' and Shared.contains(area.rewards, item.id) end)
for i, dungeon in ipairs(GameData.rawData.dungeons) do
if dungeonList ~= nil then
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
for i, dungeon in ipairs(dungeonList) do
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
table.insert(dropRows, {source = Icons.Icon({dungeon.name, type='dungeon'}), type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
table.insert(dropRows, {source = Icons.Icon({dungeon.name, type='dungeon'}), type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
elseif dungeon.eventID ~= nil then
-- Is the item dropped from a combat event (e.g. Impending Darkness event)?
local event = GameData.getEntityByID('combatEvents', dungeon.eventID)
if type(event) == 'table' and type(event.itemRewardIDs) == 'table' then
for j, itemRewardID in ipairs(event.itemRewardIDs) do
if item.id == itemRewardID then
local sourceTxt = Icons.Icon({dungeon.name, type='dungeon', notext=true}) .. (i == Shared.tableCount(event.itemRewardIDs) and '' or ', Cycle ' .. i)
table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
break
end
end
end
end
end
end
-- Is the item dropped from a cycle of the Impending Darkness event?
for i, eventItemID in ipairs(Areas.eventData.rewards) do
if item.id == eventItemID then
sourceTxt = Icons.Icon({'Impending Darkness Event', type='dungeon'}) .. (i == Shared.tableCount(Areas.eventData.rewards) and '' or ', Cycle ' .. i)
table.insert(dropRows, {source = sourceTxt, type = '[[Dungeon]]', minqty = 1, qty = 1, weight = 1, totalWeight = 1})
break
end
end
--Special exception for the Fire/Infernal Cape and first two lore books as bonus dungeon drops
if sourceOverrides['Dungeon'][item.id] ~= nil then
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})
end
end


--Next: Can we find it by rummaging around in another item?
-- Can we find it in an openable item?
for i, item2 in ipairs(ItemData.Items) do
for i, item2 in ipairs(GameData.rawData.items) do
if item2.dropTable ~= nil then
if item2.dropTable ~= nil then
local qty = 1
local minQty, maxQty, wt, totalWt = 1, 1, 0, 0
local wt = 0
for j, loot in ipairs(item2.dropTable) do
local totalWt = 0
totalWt = totalWt + loot.weight
for j, loot in pairs(item2.dropTable) do
if loot.id == item.id then
totalWt = totalWt + loot[2]
wt = loot.weight
if loot[1] == item.id then
minQty = loot.minQuantity
wt = loot[2]
maxQty = loot.maxQuantity
if item2.dropQty ~= nil then qty = item2.dropQty[j] end
end
end
end
end
Line 775: Line 663:
if wt > 0 then
if wt > 0 then
local sourceTxt = Icons.Icon({item2.name, type='item'})
local sourceTxt = Icons.Icon({item2.name, type='item'})
table.insert(dropRows, {source = sourceTxt, type = '[[Chest]]', minqty = 1, qty = qty, weight = wt, totalWeight = totalWt})
table.insert(dropRows, {source = sourceTxt, type = '[[Chest]]', minqty = minQty, qty = maxQty, weight = wt, totalWeight = totalWt})
end
end
end
end
end
end


--Finally, let's try just stealing it
-- Can it be obtained from Thieving?
local thiefType = Icons.Icon({"Thieving", type='skill'})
local thiefItems = Skills.getThievingSourcesForItem(item.id)
local thiefItems = Skills.getThievingSourcesForItem(item.id)
for i, thiefRow in ipairs(thiefItems) do
for i, thiefRow in ipairs(thiefItems) do
local sourceTxt = ''
local sourceTxt = ''
if thiefRow.npc == 'all' then
if thiefRow.npc == 'all' then
sourceTxt = "Thieving Rare Drop"
sourceTxt = 'Thieving Rare Drop'
else
else
sourceTxt = Icons.Icon({thiefRow.npc, type='thieving'})
sourceTxt = Icons.Icon({thiefRow.npc, type='thieving'})
end
end
table.insert(dropRows, {source = sourceTxt, type = thiefType, minqty = thiefRow.minQty, qty = thiefRow.maxQty, weight = thiefRow.wt, totalWeight = thiefRow.totalWt})
table.insert(dropRows, {source = sourceTxt, type = Icons.Icon({SkillData.Thieving.name, type='skill'}), minqty = thiefRow.minQty, qty = thiefRow.maxQty, weight = thiefRow.wt, totalWeight = thiefRow.totalWt})
end
end


--Bonus overtime: Special Fishing table & mining gem table. Also Rags to Riches
-- Fishing: Junk & Specials
if Shared.contains(SkillData.Fishing.JunkItems, item.id) then
if Shared.contains(SkillData.Fishing.junkItems, item.id) then
local fishSource = '[[Fishing#Junk|Junk]]'
local fishSource = '[[Fishing#Junk|Junk]]'
local fishType = Icons.Icon({'Fishing', type='skill'})
local fishType = Icons.Icon({'Fishing', type='skill'})
Line 801: Line 688:
else
else
local fishTotWeight, fishItem = 0, nil
local fishTotWeight, fishItem = 0, nil
for i, specialItem in ipairs(SkillData.Fishing.SpecialItems) do
for i, specialItem in ipairs(SkillData.Fishing.specialItems) do
if specialItem[1] == item.id then
if specialItem.itemID == item.id then
fishItem = specialItem
fishItem = specialItem
end
end
fishTotWeight = fishTotWeight + specialItem[2]
fishTotWeight = fishTotWeight + specialItem.weight
end
end
if fishItem ~= nil then
if fishItem ~= nil then
local fishSource = '[[Fishing#Special|Special]]'
local fishSource = '[[Fishing#Special|Special]]'
local fishType = Icons.Icon({'Fishing', type='skill'})
local fishType = Icons.Icon({SkillData.Fishing.name, type='skill'})
table.insert(dropRows, {source = fishSource, type = fishType, minqty = fishItem[3], qty = fishItem[3], weight = fishItem[2], totalWeight = fishTotWeight})
table.insert(dropRows, {source = fishSource, type = fishType, minqty = fishItem.minQuantity, qty = fishItem.maxQuantity, weight = fishItem.weight, totalWeight = fishTotWeight})
end
end
end
end
--Jadestone is special and doesn't count
 
if item.type == 'Gem' and item.name ~= 'Jadestone' then
-- Mining: Gems, and also Alt. Magic spells producing random gems
local mineType = Icons.Icon({'Mining', type='skill'})
if Shared.contains({'Gem', 'Superior Gem'}, item.type) then
local thisGemChance = Items.GemTable[item.name].chance
local gemKeys = { 'randomGems', 'randomSuperiorGems' }
local totalGemChance = 0
for i, gemKey in ipairs(gemKeys) do
for i, gem in pairs(Items.GemTable) do
local thisGem, totalGemWeight = nil, 0
totalGemChance = totalGemChance + gem.chance
for j, gem in ipairs(GameData[gemKey]) do
end
totalGemWeight = totalGemWeight + gem.weight
table.insert(dropRows, {source = '[[Mining#Gems|Gem]]', type = mineType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
if gem.itemID == item.id then
local magicType = Icons.Icon({'Magic', type = 'skill'})
thisGem = gem
for i, altSpell in ipairs(MagicData.AltMagic) do
end
if type(altSpell.produces) == 'number' and altSpell.produces == -3 then
end
table.insert(dropRows, {source = Icons.Icon({altSpell.name, type='spell'}), type = magicType, minqty = 1, qty = 1, weight = thisGemChance, totalWeight = totalGemChance})
if thisGem ~= nil then
local mineType = Icons.Icon({SkillData.Mining.name, type='skill'})
table.insert(dropRows, {source = '[[Mining#Gems|Gem]]', type = mineType, minqty = thisGem.minQuantity, qty = thisGem.maxQuantity, weight = thisGem.weight, totalWeight = totalGemWeight})
-- Check for Alt. Magic spells also
local magicType = Icons.Icon({SkillData.Magic.name, type = 'skill'})
local producesKey = (gemKey == 'randomGems' and 'RandomGem') or 'RandomSuperiorGem'
for j, spell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
if spell.produces ~= nil and spell.produces == producesKey then
table.insert(dropRows, {source = Icons.Icon({spell.name, type=p._getSpellIconType(spell)}), type = magicType, minqty = thisGem.minQuantity, qty = thisGem.maxQuantity, weight = thisGem.weight, totalWeight = totalGemWeight})
end
end
end
end
end
end
Line 831: Line 729:


--Make sure to return nothing if there are no drop sources
--Make sure to return nothing if there are no drop sources
if Shared.tableCount(dropRows) == 0 then return '' end
if Shared.tableIsEmpty(dropRows) then return '' end


table.sort(dropRows, function(a, b)
table.sort(dropRows, function(a, b)
Line 844: Line 742:
end
end
end)
end)
for i, data in pairs(dropRows) do
for i, data in ipairs(dropRows) do
table.insert(resultPart, buildRow(data.source, data.type, data.minqty, data.qty, data.weight, data.totalWeight))
table.insert(resultPart, buildRow(data.source, data.type, data.minqty, data.qty, data.weight, data.totalWeight))
end
end
Line 864: Line 762:
function p._getItemUpgradeTable(item)
function p._getItemUpgradeTable(item)
local resultPart = {}
local resultPart = {}
if item.itemsRequired ~= nil then
local upgrade = GameData.getEntityByProperty('itemUpgrades', 'upgradedItemID', item.id)
--First, get details on all the required materials
if upgrade ~= nil then
local upgradeFrom = {}
local upgradeCost = {}
local materials = {}
for i, itemCost in ipairs(upgrade.itemCosts) do
for i, row in pairs(item.itemsRequired) do
local costItem = Items.getItemByID(itemCost.id)
local mat = Items.getItemByID(row[1])
if costItem ~= nil then
--Check to see if the source item can trigger the upgrade
table.insert(upgradeCost, Icons.Icon({costItem.name, type='item', qty=itemCost.quantity}))
if mat.canUpgrade or (mat.type == 'Armour' and mat.canUpgrade == nil) then
table.insert(upgradeFrom, Icons.Icon({mat.name, type='item'}))
end
end
table.insert(materials, Icons.Icon({mat.name, type='item', qty=row[2]}))
end
end
if type(item.trimmedGPCost) == 'number' and item.trimmedGPCost > 0 then
if type(upgrade.gpCost) == 'number' and upgrade.gpCost > 0 then
table.insert(materials, Icons.GP(item.trimmedGPCost))
table.insert(upgradeCost, Icons.GP(upgrade.gpCost))
end
if type(upgrade.scCost) == 'number' and upgrade.scCost > 0 then
table.insert(upgradeCost, Icons.SC(upgrade.scCost))
end
end
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Upgrading Items|Item Upgrade]]')
table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Upgrading Items|Item Upgrade]]')
--[[result = result..'\r\n|-\r\n!style="text-align:right;"|Upgrades From\r\n|'
result = result..table.concat(upgradeFrom, '<br/>')--]]
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials\r\n|')
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials\r\n|')
table.insert(resultPart, table.concat(materials, '<br/>'))
table.insert(resultPart, table.concat(upgradeCost, '<br/>'))
table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
end
end
Line 897: Line 793:


return p._getItemUpgradeTable(item)
return p._getItemUpgradeTable(item)
end
function p._getSuperheatSmithRecipe(item)
local smithRecipe = GameData.getEntityByID(SkillData.Smithing.recipes, 'productID', item.id)
if smithRecipe ~= nil and smithRecipe.category == 'melvorD:Bars' then
return smithRecipe
end
end
end


Line 902: Line 805:
--Manually build the Superheat Item table
--Manually build the Superheat Item table
-- Validate that the item can be superheated
-- Validate that the item can be superheated
local canSuperheat, smithRecipe = true, nil
local smithRecipe = p._getSuperheatSmithRecipe(item.id)
if type(item.masteryID) ~= 'table' then
if smithRecipe == nil then
canSuperheat = false
elseif item.masteryID[1] ~= SkillEnum.Smithing then
canSuperheat = false
else
smithRecipe = SkillData.Smithing.Recipes[item.masteryID[2] + 1]
if smithRecipe == nil or smithRecipe.category ~= 0 then
canSuperheat = false
end
end
if not canSuperheat then
return 'ERROR: The item "' .. item.name .. '" cannot be superheated[[Category:Pages with script errors]]'
return 'ERROR: The item "' .. item.name .. '" cannot be superheated[[Category:Pages with script errors]]'
end
end
Line 919: Line 812:
local oreStringPart, coalString = {}, ''
local oreStringPart, coalString = {}, ''
for i, mat in ipairs(smithRecipe.itemCosts) do
for i, mat in ipairs(smithRecipe.itemCosts) do
local thisMat = Items.getItemByID(mat.id)
local matItem = Items.getItemByID(mat.id)
if thisMat.name == 'Coal Ore' then
if mat.id == 'melvorD:Coal_Ore' then
coalString = Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty})
coalString = Icons.Icon({matItem.name, type='item', notext='true', qty=mat.quantity})
else
else
table.insert(oreStringPart, Icons.Icon({thisMat.name, type='item', notext='true', qty=mat.qty}))
table.insert(oreStringPart, Icons.Icon({matItem.name, type='item', notext='true', qty=mat.quantity}))
end
end
end
end
--Set up the header
--Set up the header
local superheatTable = {}
local superheatTable = {}
Line 934: Line 828:
table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
table.insert(superheatTable, '!!'..Icons.Icon({item.name, type='item', notext='true'})..' Bars')
table.insert(superheatTable, '!!Ore!!Runes')
table.insert(superheatTable, '!!Ore!!Runes')
--Loop through all the variants
 
local spellNames = {'Superheat I', 'Superheat II', 'Superheat III', 'Superheat IV'}
--Loop through all the variants
for i, sName in pairs(spellNames) do
local spells = Magic.getSpellsProducingItem(item.id)
local spell = Magic.getSpell(sName, 'AltMagic')
for i, spell in ipairs(spells) do
table.insert(superheatTable, '\r\n|-\r\n|'..Icons.Icon({spell.name, type='spell', notext=true, size=50}))
if spell.specialCost ~= nil and Shared.contains({ 'BarIngredientsWithCoal', 'BarIngredientsWithoutCoal' }, spell.specialCost.type) then
table.insert(superheatTable, '||'..Icons.Icon({spell.name, type='spell', noicon=true})..'||style="text-align:right;"|'..smithRecipe.level)
local imgType = p._getSpellIconType(spell)
table.insert(superheatTable, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.baseExperience)
table.insert(superheatTable, '\r\n|-\r\n|'..Icons.Icon({spell.name, type=imgType, notext=true, size=50}))
table.insert(superheatTable, '||style="text-align:right;"|'..spell.productionRatio)
table.insert(superheatTable, '||'..Icons.Icon({spell.name, type=imgType, noicon=true})..'||style="text-align:right;"|'..smithRecipe.level)
table.insert(superheatTable, '|| '..table.concat(oreStringPart, ', '))
table.insert(superheatTable, '||style="text-align:right;"|'..spell.level..'||style="text-align:right;"|'..spell.baseExperience)
if spell.consumes == 2 and coalString ~= '' then
table.insert(superheatTable, '||style="text-align:right;"|'..spell.productionRatio)
-- 2 = Superheat with coal, 3 = Superheat without coal
table.insert(superheatTable, '|| '..table.concat(oreStringPart, ', '))
table.insert(superheatTable, (Shared.tableCount(oreStringPart) > 0 and ', ' or '') .. coalString)
if spell.specialCost.type == 'BarIngredientsWithCoal' and coalString ~= '' then
table.insert(superheatTable, (not Shared.tableIsEmpty(oreStringPart) and ', ' or '') .. coalString)
end
table.insert(superheatTable, '||style="text-align:center"| ' .. Magic._getSpellRunes(spell))
end
end
table.insert(superheatTable, '||style="text-align:center"| ' .. Magic._getSpellRunes(spell))
end
end
--Add the table end and add the table to the result string
--Add the table end and add the table to the result string
table.insert(superheatTable, '\r\n|}')
table.insert(superheatTable, '\r\n|}')
Line 967: Line 864:
local resultPart = {}
local resultPart = {}
local shopTable = Shop._getItemShopTable(item)
local shopTable = Shop._getItemShopTable(item)
if string.len(shopTable) > 0 then
if shopTable ~= '' then
table.insert(resultPart, '===Shop===\r\n'..shopTable)
table.insert(resultPart, '===Shop===\r\n'..shopTable)
end
end


local creationTable = p._getCreationTable(item)
local creationTable = p._getCreationTable(item)
if string.len(creationTable) > 0 then
if creationTable ~= '' then
if #resultPart > 0 then table.insert(resultPart, '\r\n') end
if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
table.insert(resultPart, '===Creation===\r\n'..creationTable)
table.insert(resultPart, '===Creation===\r\n'..creationTable)
end
end


local upgradeTable = p._getItemUpgradeTable(item)
local upgradeTable = p._getItemUpgradeTable(item)
if string.len(upgradeTable) > 0 then
if upgradeTable ~= '' then
if #resultPart > 0 then table.insert(resultPart, '\r\n') end
if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
if string.len(creationTable) == 0 then table.insert(resultPart, '===Creation===\r\n') end
if creationTable ~= '' then table.insert(resultPart, '===Creation===\r\n') end
table.insert(resultPart, upgradeTable)
table.insert(resultPart, upgradeTable)
end
end


if type(item.masteryID) == 'table' and item.masteryID[1] == SkillEnum.Smithing then
if p._getSuperheatSmithRecipe(item.id) ~= nil then
local recipe = SkillData.Smithing.Recipes[item.masteryID[2] + 1]
table.insert(resultPart, '\r\n==='..Icons.Icon({'Alt. Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item))
if recipe ~= nil and recipe.category == 0 then
table.insert(resultPart, '\r\n==='..Icons.Icon({'Alt. Magic', type='skill'})..'===\r\n'..p._getItemSuperheatTable(item))
end
end
end


local lootTable = p._getItemLootSourceTable(item)
local lootTable = p._getItemLootSourceTable(item)
if string.len(lootTable) > 0 then
if lootTable ~= '' then
if #resultPart > 0 then table.insert(resultPart, '\r\n') end
if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
table.insert(resultPart, '===Loot===\r\n'..lootTable)
table.insert(resultPart, '===Loot===\r\n'..lootTable)
end
end
Line 1,015: Line 909:
table.insert(resultPart, '!colspan="2"|Item\r\n! Passive\r\n')
table.insert(resultPart, '!colspan="2"|Item\r\n! Passive\r\n')


local itemArray = Items.getItems(function(item) return item.validSlots ~= nil and Shared.contains(item.validSlots, 'Passive') end)
local itemArray = Items.getItems(function(item) return item.validSlots ~= nil and (item.golbinRaidExclusive == nil or not item.golbinRaidExclusive) and Shared.contains(item.validSlots, 'Passive') end)
 
table.sort(itemArray, function(a, b) return a.id < b.id end)


for i, item in ipairs(itemArray) do
for i, item in ipairs(itemArray) do
local passiveDesc = item.customDescription or Constants.getModifiersText(item.modifiers, false)
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')
table.insert(resultPart, '| '..item.description..'\r\n')
table.insert(resultPart, '| '..passiveDesc..'\r\n')
end
end


Line 1,032: Line 925:
function p._getItemMonsterSources(item)
function p._getItemMonsterSources(item)
local resultArray = {}
local resultArray = {}
for i, monster in ipairs(MonsterData.Monsters) do
for i, monster in ipairs(GameData.rawData.monsters) do
local chance = 0
local chance = 0
local weight = 0
local weight = 0
Line 1,039: Line 932:
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
if monster.bones == item.id and Monsters._getMonsterBones(monster) ~= nil then
-- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
-- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table
maxQty = (monster.bones.quantity ~= nil and monster.bones.quantity) or 1
minQty = maxQty
chance = 1
chance = 1
weight = 1
weight = 1
if monster.boneQty ~= nil then
elseif monster.lootTable ~= nil and not Monsters._isDungeonOnlyMonster(monster) then
minQty = monster.boneQty
maxQty = monster.boneQty
end
elseif monster.lootTable ~= nil then
-- If the monster has a loot table, check if the item we are looking for is in there
-- If the monster has a loot table, check if the item we are looking for is in there
-- Dungeon exclusive monsters don't count as they are either:
-- Dungeon exclusive monsters don't count as they are either:
Line 1,051: Line 942:
--  - A boss monster, whose drops are accounted for in data from Areas instead
--  - A boss monster, whose drops are accounted for in data from Areas instead
for j, loot in ipairs(monster.lootTable) do
for j, loot in ipairs(monster.lootTable) do
weight = weight + loot[2]
weight = weight + loot.weight
if loot[1] == item.id and not Monsters._isDungeonOnlyMonster(monster) then
if loot.itemID == item.id then
chance = loot[2]
chance = loot.weight
maxQty = loot[3]
minQty = loot.minQuantity
maxQty = loot.maxQuantity
end
end
end
end
local lootChance = monster.lootChance ~= nil and monster.lootChance or 100
local lootChance = monster.lootChance ~= nil and monster.bones ~= item.id and monster.lootChance or 100
chance = chance * lootChance
chance = chance * lootChance
weight = weight * 100
weight = weight * 100