Anonymous

Module:Items/SourceTables/Sandbox: Difference between revisions

From Melvor Idle
Added 1/x values to Chances; Use Recipe icon for source icons when available; Merge Shop and Upgrade sources into Creation Table; Changed 'Inputs' to 'Costs'; Add output quantity for upgraded items; Fix Ash requirements, apparently
mNo edit summary
(Added 1/x values to Chances; Use Recipe icon for source icons when available; Merge Shop and Upgrade sources into Creation Table; Changed 'Inputs' to 'Costs'; Add output quantity for upgraded items; Fix Ash requirements, apparently)
Line 10: Line 10:
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local Items = require('Module:Items')
local Shop = require('Module:Shop')
local Shop = require('Module:Sandbox/Shop') -- TODO: Remove Sandbox/ when pushing live
local Monsters = require('Module:Monsters')
local Monsters = require('Module:Monsters')
local Skills = require('Module:Skills')
local Skills = require('Module:Skills')
Line 19: Line 19:
}
}


function p.testCreationTables()
--[[
-- TODO:  
TODO:
-- Fix TODOs throughout file then remove this test function
-- Move Township Task and Cartography Disovery rewards under Item Sources (?)
-- (Major) Either add all the data from _getItemSourceTables into a single table or convert those over to a similar style table as well
-- Center Quantity & Chance in Loot Tables
local tables = {}
Creation-Tables:
local checkItems = {
-- Move anything that isn't Combat, Thieving, or Chests out of Loot Sources and into Creation Tables (Alt Magic Spells & Gems) (?)
'Dragonite Ore', -- Single source; This should have no change from the live version
Use-Tables:
'Arrow Shafts', -- Multi source (Costs, Qty)
-- Shrink icon size down (to 25px?)
'Rune Essence', -- Multi source (Category, Time, XP, Qty, Runes)
-- Or change "Item Created" to a single cell? (1 (IMG) Barrier Touch Potion I)
'Coal Ore', -- Multi source (Same Qty, everything else is different)
-- Change Type header to Source
'Charcoal',
-- Change Requirements header to Requires
'Ash', -- Requires a fix in _getCreationTable
-- Change XP header to Exp
'Withered Ash', -- Requires a fix in _getCreationTable
-- Change XP display to: Icon - Value - (' XP' or ' AXP' if abyssal)
'Eternal Ash',
-- Remove right alignment on XP, Requirements
'Generous Fire Spirit',
-- Add 'table-na' to cells with N/A
-- "Circlet of Rhaelyx", -- The following commented out items have no creation source data; This may appear later
--]]
-- "Jewel of Rhaelyx",
 
-- "Signet Ring Half (a)",
local function doesRecipeHaveItemID(recipe, itemID)
-- "Signet Ring Half (b)",
if recipe.productId == itemID then
"Gold Topaz Ring",
return true
-- "Astrology Lesser Relic",
elseif Shared.contains(recipe.primaryProducts, itemID) or Shared.contains(recipe.secondaryProducts, itemID) then
-- "Mysterious Stone",
return true
"Gold Bar",
elseif type(recipe.products) == 'table' then
"Raw Shrimp",
return GameData.getEntityByProperty(recipe.products, 'itemID', itemID) ~= nil
"Shrimp",
end
"Rune Platebody",
return false
"Yew Longbow",
end
"Water Rune",
 
"Steam Rune",
function p._getCreationTableData(item, tableData)
"Controlled Heat Potion II",
if tableData == nil then tableData = {} end
"Wolf", -- Melvor Tablet with multiple recipe costs
"Fox", -- Has a base shard cost and multi recipe cost that is also shards
"Leprechaun", -- Has a currency
"Void Wisp", -- Abyssal Tablet with a single recipe
"Redwood Logs",
"Carrot Cake",
"Carrot Cake (Perfect)",
"Mantalyme Herb",
"Carrot",
-- "Topaz",
-- "Sanguine Blade",
-- "Ring of Power",
-- "Infernal Claw",
-- "Chapeau Noir",
-- "Rope",
-- "Ancient Ring of Mastery",
-- "Mastery Token (Cooking)",
-- "Gem Gloves",
"Thief's Moneysack",
"Stardust",
"Golden Stardust",
"Abyssal Stardust",
-- "Golden Star",
-- "Slayer Deterer",
"Paper",
-- "Lemon",
-- "Aranite Brush",
"Barrier Dust",
-- "Shadow Raven Nest",
'Obsidian Tendril',
'Obsidian Bark',
'Obsidian Thorn',
'Obsidian Vein Seed',
}


for i, itemID in ipairs(checkItems) do
local skill = ''
table.insert(tables, p._getCreationTable(Items.getItem(itemID)))
local reqs = nil
end
local source = nil
return table.concat(tables, '\n')
local time = 0
end
local maxTime = nil
local lvl = 0
local isAbyssal = false
local xp = 0
local qty = 1
local costs = nil
local weight = nil
local totalWeight = nil


local function doesRecipeHaveItemID(recipe, itemID)
local itemID = item.id
if recipe.productId == itemID then
--First figure out what skill is used to make this...
return true
elseif Shared.contains(recipe.primaryProducts, itemID) or Shared.contains(recipe.secondaryProducts, itemID) then
return true
elseif type(recipe.products) == 'table' then
return GameData.getEntityByProperty(recipe.products, 'itemID', itemID) ~= nil
end
return false
end
 
function p._getCreationTable(item)
local skill = ''
local specialReq = nil
local source = nil
local time = 0
local maxTime = nil
local lvl = 0
local isAbyssal = false
local xp = 0
local qty = nil
local costs = nil
local chance = nil
 
local tableData = {}
local itemID = item.id
--First figure out what skill is used to make this...


local skillIDs = {
local skillIDs = {
['Gathering'] = {
['Gathering'] = {
['Farming'] = { recipeKey = 'recipes' },
['Farming'] = { recipeKey = 'recipes', recipeType = 'item' },
['Woodcutting'] = { recipeKey = 'trees' },
['Woodcutting'] = { recipeKey = 'trees', recipeType = 'tree' },
['Fishing'] = { recipeKey = 'fish' },
['Fishing'] = { recipeKey = 'fish', recipeType = 'item' },
['Firemaking'] = { recipeKey = 'logs' },
['Firemaking'] = { recipeKey = 'logs', recipeType = 'item' },
['Mining'] = { recipeKey = 'rockData' },
['Mining'] = { recipeKey = 'rockData', recipeType = 'rock' },
['Cartography'] = { recipeKey = 'paperRecipes' },
['Cartography'] = { recipeKey = 'paperRecipes', recipeType = 'item' },
['Harvesting'] = { recipeKey = 'veinData' }
['Harvesting'] = { recipeKey = 'veinData', recipeType = 'vein' }
},
},
['Artisan'] = {
['Artisan'] = {
Line 144: Line 92:
local skillData = SkillData[localSkillID]
local skillData = SkillData[localSkillID]
local skill = skillData.name
local skill = skillData.name
local lvl, isAbyssal, xp, qty, source, costs, time, maxTime, chance = 0, false, 0, 0, nil, nil, 0, nil, nil
local reqs, lvl, isAbyssal, xp, qty, source, costs, time, maxTime, weight, totalWeight = nil, 0, false, 0, 1, nil, nil, 0, nil, nil, nil
for i, recipe in ipairs(skillData[dataProp.recipeKey]) do
for i, recipe in ipairs(skillData[dataProp.recipeKey]) do
local hasProduct = doesRecipeHaveItemID(recipe, itemID)
local hasProduct = doesRecipeHaveItemID(recipe, itemID)
Line 151: Line 99:
xp = recipe.baseAbyssalExperience or recipe.baseExperience
xp = recipe.baseAbyssalExperience or recipe.baseExperience
qty = recipe.baseQuantity or 1
qty = recipe.baseQuantity or 1
source = Icons.Icon({ skill, type='skill', class=(isAbyssal and 'abyss-icon' or '') })
reqs = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil))
if localSkillID == 'Farming' then
-- Source Icon
costs = { recipe.seedCost }
if recipe.name ~= nil then
local catData = GameData.getEntityByID(skillData.categories, recipe.categoryID)
source = Icons.Icon({ skill, img=recipe.name, type=dataProp.recipeType })
qty = 5 * catData.harvestMultiplier
else
source = Icons.Icon({ skill, type='skill', class=(isAbyssal and 'abyss-icon' or nil) })
end
end
-- Action time
-- Action time
Line 167: Line 116:
elseif skillData.baseInterval ~= nil then
elseif skillData.baseInterval ~= nil then
time = skillData.baseInterval / 1000
time = skillData.baseInterval / 1000
elseif localSkillID == 'Cartography' then
time = 5
end
end
-- Item chance and recipe costs
-- Custom Chance, Qty, and Costs data
if localSkillID == 'Firemaking' then
if localSkillID == 'Farming' then
costs = { recipe.seedCost }
local catData = GameData.getEntityByID(skillData.categories, recipe.categoryID)
qty = 5 * catData.harvestMultiplier
elseif localSkillID == 'Firemaking' then
local itemChanceData = GameData.getEntityByProperty(SkillData.Firemaking.primaryProducts, 'itemID', itemID)
local itemChanceData = GameData.getEntityByProperty(SkillData.Firemaking.primaryProducts, 'itemID', itemID)
if itemChanceData ~= nil then
if itemChanceData ~= nil then
chance = itemChanceData.chance .. '%'
weight = itemChanceData.chance
elseif itemID == 'melvorD:Generous_Fire_Spirit' then
elseif itemID == 'melvorD:Generous_Fire_Spirit' then
chance = '0.1%'
weight = 0.1
end
end


Line 192: Line 143:
end
end
elseif localSkillID == 'Cartography' then
elseif localSkillID == 'Cartography' then
time = 5
local costItem = Items.getItemByID(recipe.costs.items[1].id)
local costItem = Items.getItemByID(recipe.costs.items[1].id)
costs = Icons.Icon({ costItem.name, type='item', qty=1 })
costs = Icons.Icon({ costItem.name, type='item', qty=1 })
elseif localSkillID == 'Harvesting' then
elseif localSkillID == 'Harvesting' then
local totalWeight = 0
local itemChanceData = nil
local itemChanceData = nil
totalWeight = 0


for i, product in ipairs(recipe.products) do
for i, product in ipairs(recipe.products) do
Line 205: Line 158:


if itemChanceData ~= nil then
if itemChanceData ~= nil then
chance = Num.round2((itemChanceData.weight / totalWeight * 100), 2) .. '%'
weight = itemChanceData.weight
specialReq = itemChanceData.minIntensityPercent .. '% ' .. Icons.Icon({ recipe.name, type='vein', notext=true }) .. ' Intensity'
reqs = reqs .. '<br>' .. itemChanceData.minIntensityPercent .. '% ' .. Icons.Icon({ recipe.name, type='vein', notext=true }) .. ' Intensity'
end
end
end
end
-- Special requirements
-- Special requirements
if recipe.totalMasteryRequired ~= nil then
if recipe.totalMasteryRequired ~= nil then
specialReq = Icons.Icon({ 'Mastery', notext=true }) .. ' ' .. Num.formatnum(recipe.totalMasteryRequired) .. ' total [[' .. skill .. ']] [[Mastery]]'
reqs = reqs .. '<br>' .. Icons.Icon({ 'Mastery', notext=true }) .. ' ' .. Num.formatnum(recipe.totalMasteryRequired) .. ' total [[' .. skill .. ']] [[Mastery]]'
end
end
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['lvl'] = lvl,
['reqs'] = reqs,
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = xp,
['xp'] = xp,
Line 223: Line 176:
['time'] = time,
['time'] = time,
['maxTime'] = maxTime,
['maxTime'] = maxTime,
['specialReq'] = specialReq,
['weight'] = weight,
['chance'] = chance
['totalWeight'] = totalWeight
})
})
-- Most recipes have a single item source or the item source data
-- Most recipes have a single item source or the item source data
Line 238: Line 191:
local skillData = SkillData[localSkillID]
local skillData = SkillData[localSkillID]
local skill = skillData.name
local skill = skillData.name
local lvl, isAbyssal, xp, qty, source, costs, time, maxTime = 0, false, 0, 0, Icons.Icon({ skill, type='skill' }), nil, 0, nil
local reqs, lvl, isAbyssal, xp, qty, source, costs, time, maxTime = nil, 0, false, 0, 1, Icons.Icon({ skill, type='skill' }), nil, 0, nil
for i, recipe in ipairs(skillData.recipes) do
for i, recipe in ipairs(skillData.recipes) do
if recipe.productID == itemID or
if recipe.productID == itemID or
Line 246: Line 199:
xp = recipe.baseAbyssalExperience or recipe.baseExperience
xp = recipe.baseAbyssalExperience or recipe.baseExperience
qty = recipe.baseQuantity or 1
qty = recipe.baseQuantity or 1
source = Icons.Icon({ skill, type='skill', class=(isAbyssal and 'abyss-icon' or '') })
reqs = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil))
source = Icons.Icon({ skill, type='skill', class=(isAbyssal and 'abyss-icon' or nil) })
-- Action time
-- Action time
if recipe.baseMinInterval ~= nil then
if recipe.baseMinInterval ~= nil then
Line 263: Line 217:
local levelUnlock = GameData.getEntityByProperty(skillData.masteryLevelUnlocks, 'descriptionID', item.tier + 1)
local levelUnlock = GameData.getEntityByProperty(skillData.masteryLevelUnlocks, 'descriptionID', item.tier + 1)
if levelUnlock ~= nil then
if levelUnlock ~= nil then
specialReq = Icons._MasteryReq(item.name, levelUnlock.level, false)
reqs = reqs .. '<br>' .. Icons._MasteryReq(item.name, levelUnlock.level, false)
end
end
end
end
Line 279: Line 233:
end
end
if categoryIconName ~= nil and categoryName ~= nil then
if categoryIconName ~= nil and categoryName ~= nil then
specialReq = Icons.Icon({'Cooking', categoryName, section = 'Cooking Upgrades', img = categoryIconName, type = 'upgrade'})
reqs = reqs .. '<br>' .. Icons.Icon({ 'Cooking', categoryName, section = 'Cooking Upgrades', img = categoryIconName, type = 'upgrade' })
end
end
end
end
Line 314: Line 268:
costs = costs .. '<br>' .. (costLen == 1 and '' or 'and one of the following:<br>') .. table.concat(otherCostArray, "<br>'''OR''' ")
costs = costs .. '<br>' .. (costLen == 1 and '' or 'and one of the following:<br>') .. table.concat(otherCostArray, "<br>'''OR''' ")
end
end
specialReq = 'At least 1 ' .. Icons.Icon({ 'Summoning%23Summoning Marks', item.name, img=item.name, type='mark' }) .. ' mark discovered'
reqs = reqs .. '<br>At least 1 ' .. Icons.Icon({ 'Summoning', item.name, img=item.name, type='mark', section='Summoning Marks' }) .. ' mark discovered'
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['lvl'] = lvl,
['reqs'] = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = xp,
['xp'] = xp,
Line 323: Line 277:
['qty'] = qty,
['qty'] = qty,
['source'] = source,
['source'] = source,
['time'] = time,
['time'] = time
['specialReq'] = specialReq
})
})
-- Some items (such as Arrow shafts) have multiple recipes
-- Some items (such as Arrow shafts) have multiple recipes
Line 340: Line 293:
end
end
end
end
costsStr = costsStr .. (Common.getCostString({ ["items"] = {}, ["currencies"] = recipe.currencyCosts }) or '')
costsStr = costsStr .. Common.getCostString({ ["items"] = {}, ["currencies"] = recipe.currencyCosts }, '')
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['lvl'] = lvl,
['reqs'] = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = xp,
['xp'] = xp,
['costs'] = costsStr,
['costs'] = costsStr,
['qty'] = Num.formatnum(qty * altCost.quantityMultiplier),
['qty'] = qty * altCost.quantityMultiplier,
['source'] = Icons.Icon({ skill, type='skill' }),
['source'] = Icons.Icon({ skill, type='skill' }),
['time'] = time,
['time'] = time,
['maxTime'] = maxTime,
['maxTime'] = maxTime
['specialReq'] = specialReq
})
})
end
end
Line 358: Line 310:
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['lvl'] = lvl,
['reqs'] = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = xp,
['xp'] = xp,
Line 366: Line 318:
['time'] = time,
['time'] = time,
['maxTime'] = maxTime,
['maxTime'] = maxTime,
['specialReq'] = specialReq,
['currencyCost'] = recipe.currencyCosts
['currencyCost'] = recipe.currencyCosts
})
})
Line 378: Line 329:
-- Gems are handled by _getItemLootSourceTable()
-- Gems are handled by _getItemLootSourceTable()
for i, altSpell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
for i, altSpell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
if altSpell.produces == item.id then
if altSpell.produces == itemID then
table.insert(tableData, {
table.insert(tableData, {
['skill'] = 'Magic',
['skill'] = 'Magic',
['lvl'] = altSpell.level,
['reqs'] = Icons._SkillReq('Magic', altSpell.level, false),
['isAbyssal'] = false,
['isAbyssal'] = false,
['xp'] = altSpell.baseExperience,
['xp'] = altSpell.baseExperience,
Line 401: Line 352:
table.insert(tableData, {
table.insert(tableData, {
['skill'] = 'Astrology',
['skill'] = 'Astrology',
['lvl'] = 1,
['reqs'] = Icons._SkillReq('Astrology', 1, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = (isAbyssal and 1238 or 5), -- Use the (A)XP value for the first (abyssal) constellation
['qty'] = qty,
['source'] = Icons.Icon({ 'Astrology', type='skill', class=(isAbyssal and 'abyss-icon' or '')}),
['xp'] = (isAbyssal and 1238 or 5), -- Use the XP value for the first constellation
['source'] = Icons.Icon({ 'Astrology', type='skill', class=(isAbyssal and 'abyss-icon' or nil) }),
['time'] = 3,
['time'] = 3,
['chance'] = stardustChanceData.chance .. '%'
['weight'] = stardustChanceData.chance
})
})
end
end


if Shared.tableIsEmpty(tableData) then
return tableData
return ''
else
return p.buildCreationTable(tableData, item)
end
end
end


function p.buildCreationTable(tableData, item)
function p.buildCreationTable(item, tableData)
if Shared.tableIsEmpty(tableData) then return '' end
 
local showSource = false
local showSource = false
local showRequirements = false
local showRequirements = false
Line 432: Line 382:
colspan = colspan + 1
colspan = colspan + 1
end
end
if not showRequirements and tableData[1].skill ~= tableData[i].skill then
if not showRequirements and tableData[1].reqs ~= tableData[i].reqs then
showRequirements = true
showRequirements = true
colspan = colspan + 1
colspan = colspan + 1
Line 452: Line 402:
colspan = colspan + 1
colspan = colspan + 1
end
end
if not showChance and tableData[1].chance ~= tableData[i].chance then
if not showChance and tableData[1].weight ~= tableData[i].weight then
showChance = true
showChance = true
colspan = colspan + 1
colspan = colspan + 2
end
end
end
end
Line 469: Line 419:
costsRow:wikitext(mat.quantity .. 'x ?????')
costsRow:wikitext(mat.quantity .. 'x ?????')
else
else
costsRow:wikitext(Icons.Icon({matItem.name, type='item', qty=mat.quantity}))
costsRow:wikitext(Icons.Icon({ matItem.name, type='item', qty=mat.quantity }))
end
end
end
end
if data.currencyCost ~= nil then
if data.currencyCost ~= nil then
costsRow:wikitext('<br>' .. (Common.getCostString({ ["items"] = {}, ["currencies"] = data.currencyCost }) or ''))
costsRow:wikitext('<br>' .. Common.getCostString({ ["items"] = {}, ["currencies"] = data.currencyCost }, ''))
end
end
else
else
Line 480: Line 430:
end
end


if data.altCosts ~= nil then
if type(data.altCosts) == 'string' then
costsRow:wikitext('<br>' .. data.altCosts:gsub(', ', '<br>'))
local costStr = data.altCosts:gsub(', ', '<br>')
costsRow:wikitext('<br>' .. costStr)
end
end
end
end
Line 495: Line 446:
if showXP then tableHeader:tag('th'):wikitext('Exp') end
if showXP then tableHeader:tag('th'):wikitext('Exp') end
if showTime then tableHeader:tag('th'):wikitext('Time') end
if showTime then tableHeader:tag('th'):wikitext('Time') end
if showChance then tableHeader:tag('th'):wikitext('Chance') end
if showChance then tableHeader:tag('th'):wikitext('Chance'):attr('colspan', 2) end


-- Populate table data with any unique entries (Ex: Ash's Inputs, Outputs, Exp, Time)
for i, data in ipairs(tableData) do
for i, data in ipairs(tableData) do
if data.qty == nil then data.qty = 1 end
local recipeRow = resultTable:tag('tr')
local recipeRow = resultTable:tag('tr')


if showSource then recipeRow:tag('td'):wikitext(data.source) end
if showSource then recipeRow:tag('td'):wikitext(data.source) end
if showRequirements then recipeRow:tag('td'):wikitext((data.reqs or 'None')) end
if showRequirements then
local tableReqs = recipeRow:tag('td'):wikitext(Icons._SkillReq(data.skill, data.lvl, false, (data.isAbyssal and "melvorItA:Abyssal" or nil)))
 
if data.specialReq ~= nil then
recipeRow:tag('br'):wikitext(data.specialReq)
end
end


if showInputs and data.costs ~= nil then
if showInputs and data.costs ~= nil then
Line 518: Line 461:
end
end


if showOutputs then recipeRow:tag('td'):wikitext(Icons.Icon({item.name, type='item', notext=true, qty=data.qty})):addClass('center') end
if showOutputs then
if showXP then recipeRow:tag('td'):wikitext(Icons.Icon({data.skill, (Num.formatnum(data.xp) .. (data.isAbyssal and ' AXP' or ' XP')), nolink=true, type='skill'})) end
if type(data.qty) == 'string' then
if showTime then recipeRow:tag('td'):wikitext(Shared.timeString(data.time, true)):addClass('center') end
local outputData = recipeRow:tag('td'):wikitext(data.qty)
if showChance then recipeRow:tag('td'):wikitext((data.chance or '100%')):addClass('center') end
if data.center then outputData:addClass('center') end
end
else
 
recipeRow:tag('td'):wikitext(Icons.Icon({ item.name, type='item', notext=true, qty=(data.qty or 1) })):addClass('center')
end
end
if showXP and data.skill ~= nil and data.xp ~= nil then
local iconClass = (data.isAbyssal and 'abyss-icon' or nil)
local xpText = (data.isAbyssal and ' AXP' or ' XP')
recipeRow:tag('td')
:wikitext(Icons.Icon({ data.skill, notext=true, type='skill', class=iconClass }))
:wikitext(' ' .. Num.formatnum(data.xp) .. xpText)
end
if showTime and data.time ~= nil then recipeRow:tag('td'):wikitext(Shared.timeString(data.time, true)):addClass('center') end
if showChance then
if data.weight ~= nil then
recipeRow:tag('td'):wikitext(Num.fraction(data.weight, (data.totalWeight or 100)))
recipeRow:tag('td'):wikitext(Num.round2(data.weight / (data.totalWeight or 100) * 100, 2) .. '%')
else
recipeRow:tag('td'):wikitext('100%'):addClass('center'):attr('colspan', 2)
end
end
end
 
-- Add all non-unique data below the table data (Ex: Ash's Source, Requires, Chance)
if not showSource and tableData[1].source ~= nil then
if not showSource and tableData[1].source ~= nil then
resultTable:tag('tr')
resultTable:tag('tr')
:tag('th')
:tag('th'):wikitext('Source'):css('text-align', 'right')
:wikitext('Source')
:tag('td'):attr('colspan', colspan):wikitext(tableData[1].source)
:css('text-align', 'right')
:tag('td'):attr('colspan', colspan):wikitext(tableData[1].source)
end
end
if not showRequirements and tableData[1].skill ~= nil and tableData[1].lvl ~= nil then
if not showRequirements and tableData[1].reqs ~= nil then
local reqRow = resultTable:tag('tr')
local reqRow = resultTable:tag('tr')
:tag('th')
:tag('th'):wikitext('Requires'):css('text-align', 'right')
:wikitext('Requires')
:tag('td'):wikitext(tableData[1].reqs):attr('colspan', colspan)
:css('text-align', 'right')
local reqData = reqRow:tag('td'):attr('colspan', colspan)
reqData:wikitext(Icons._SkillReq(tableData[1].skill, tableData[1].lvl, false, (tableData[1].isAbyssal and "melvorItA:Abyssal" or nil)))
 
if tableData[1].specialReq ~= nil then
reqData:wikitext('<br>' .. tableData[1].specialReq)
end
end
end
if not showInputs and tableData[1].costs ~= nil then
if not showInputs and tableData[1].costs ~= nil then
local costRow = resultTable:tag('tr')
local costRow = resultTable:tag('tr')
costRow:tag('th')
:tag('th'):wikitext('Costs'):css('text-align', 'right')
:wikitext('Inputs')
 
:css('text-align', 'right')
addCostsRow(costRow, tableData[1], colspan)
addCostsRow(costRow, tableData[1], colspan)
end
end
if not showOutputs and tableData[1].qty ~= nil then
if not showOutputs and tableData[1].qty ~= nil then
resultTable:tag('tr')
local outputRow = resultTable:tag('tr')
:tag('th')
:tag('th'):wikitext('Outputs'):css('text-align', 'right')
:wikitext('Outputs')
 
:css('text-align', 'right')
if type(tableData[1].qty) == 'string' then
:tag('td'):attr('colspan', colspan):wikitext(Icons.Icon({ item.name, type='item', qty=tableData[1].qty }))
outputRow:tag('td'):wikitext(tableData[1].qty)
else
outputRow:tag('td'):attr('colspan', colspan):wikitext(Icons.Icon({ item.name, type='item', qty=(tableData[1].qty or 1) }))
end
end
end
if not showXP and tableData[1].xp ~= nil then
if not showXP and tableData[1].xp ~= nil then
local xpText = (tableData[1].isAbyssal and ' AXP' or ' XP')
resultTable:tag('tr')
resultTable:tag('tr')
:tag('th')
:tag('th'):wikitext('Base Exp'):css('text-align', 'right')
:wikitext('Base Exp')
:tag('td'):attr('colspan', colspan):wikitext(Num.formatnum(tableData[1].xp) .. xpText)
:css('text-align', 'right')
:tag('td'):attr('colspan', colspan):wikitext(Num.formatnum(tableData[1].xp) .. (tableData[1].isAbyssal and ' AXP' or ' XP'))
end
end
if not showTime and tableData[1].time ~= nil then
if not showTime and tableData[1].time ~= nil then
resultTable:tag('tr')
resultTable:tag('tr')
local timeHeader = resultTable:tag('th')
local timeHeader = resultTable:tag('th'):wikitext('Base Time'):css('text-align', 'right')
timeHeader
:wikitext('Base Time')
:css('text-align', 'right')


local timeData = timeHeader:tag('td'):attr('colspan', colspan)
local timeData = timeHeader:tag('td'):attr('colspan', colspan)
Line 579: Line 531:
end
end
end
end
if not showChance and tableData[1].chance ~= nil then
if not showChance and tableData[1].weight ~= nil then
resultTable:tag('tr')
local chanceData = resultTable:tag('tr')
:tag('th')
:tag('th'):wikitext('Base Chance'):css('text-align', 'right')
:wikitext('Base Chance')
:tag('td'):attr('colspan', colspan)
:css('text-align', 'right')
:wikitext(Num.fraction(tableData[1].weight, (tableData[1].totalWeight or 100)))
:tag('td'):attr('colspan', colspan):wikitext(tableData[1].chance)
:wikitext(' (' .. Num.round2(tableData[1].weight / (tableData[1].totalWeight or 100) * 100, 2) .. '%)')
end
end


return '==' .. item.name .. '==\n' .. tostring(resultTable) -- TODO: Remove item.name header
return tostring(resultTable)
end
end


Line 597: Line 549:
end
end


return p._getCreationTable(item)
return p.buildCreationTable(p._getCreationTableData(item), item)
end
end


Line 644: Line 596:
}
}
for entity, dungeons in pairs(dungeonEntities) do
for entity, dungeons in pairs(dungeonEntities) do
local iconType = entity == 'Dungeon' and 'dungeon' or 'combatArea'
for i, dungeon in ipairs(dungeons) do
for i, dungeon in ipairs(dungeons) do
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
table.insert(dungeonStrPart, Icons.Icon({dungeon.name, type=iconType, notext=true}))
table.insert(dungeonStrPart, Icons.Icon({dungeon.name, type='combatArea', notext=true}))
elseif dungeon.eventID ~= nil then
elseif dungeon.eventID ~= nil then
-- Is the item dropped from a combat event (e.g. Impending Darkness event)?
-- Is the item dropped from a combat event (e.g. Impending Darkness event)?
Line 656: Line 607:
if item.id == itemRewardID then
if item.id == itemRewardID then
local dungPrefix = (eventCycle == Shared.tableCount(event.itemRewardIDs) and '' or eventCycle .. (eventCycle == 1 and ' cycle' or ' cycles') .. ' of ')
local dungPrefix = (eventCycle == Shared.tableCount(event.itemRewardIDs) and '' or eventCycle .. (eventCycle == 1 and ' cycle' or ' cycles') .. ' of ')
table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({dungeon.name, type=iconType, notext=true}))
table.insert(dungeonStrPart, dungPrefix .. Icons.Icon({dungeon.name, type='combatArea', notext=true}))
break
break
end
end
Line 983: Line 934:
-- Mining: Gems
-- Mining: Gems
if (GameData.getEntityByProperty('randomGems', 'itemID', item.id) ~= nil or
if (GameData.getEntityByProperty('randomGems', 'itemID', item.id) ~= nil or
GameData.getEntityByProperty('randomSuperiorGems', 'itemID', item.id) ~= nil) then
GameData.getEntityByProperty('randomSuperiorGems', 'itemID', item.id) ~= nil or
GameData.getEntityByProperty('randomAbyssalGems', 'itemID', item.id) ~= nil) then
table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]')
table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]')
elseif item.id == SkillData.Mining.runestoneItemID then
elseif item.id == SkillData.Mining.runestoneItemID then
Line 1,144: Line 1,096:
levelValue = level:match('%[%[.-%]%]%s*(%w+)$') or ''
levelValue = level:match('%[%[.-%]%]%s*(%w+)$') or ''
end
end
table.insert(rowPart, '\r\n|style="text-align: left;" data-sort-value="'..levelValue..'"|'..expIcon.. level)
table.insert(rowPart, '\r\n|style="text-align: left;" data-sort-value="'..levelValue..'"|'..expIcon..' '..level)
table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="'..qty..'"|'..Num.formatnum(minqty))
table.insert(rowPart, '\r\n|style="text-align: right;" data-sort-value="'..qty..'"|'..Num.formatnum(minqty))
if qty ~= minqty then table.insert(rowPart, ' - '..Num.formatnum(qty)) end
if qty ~= minqty then table.insert(rowPart, ' - '..Num.formatnum(qty)) end
Line 1,220: Line 1,172:
}
}
for entity, dungeons in pairs(dungeonEntities) do
for entity, dungeons in pairs(dungeonEntities) do
local iconType = entity == 'Dungeon' and 'dungeon' or 'combatArea'
for i, dungeon in ipairs(dungeons) do
for i, dungeon in ipairs(dungeons) do
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then
table.insert(dropRows, {
table.insert(dropRows, {
source = Icons.Icon({dungeon.name, type=iconType}),  
source = Icons.Icon({dungeon.name, type='combatArea'}),  
level = '[['..entity..']]',
level = '[['..entity..']]',
minqty = 1,  
minqty = 1,  
Line 1,238: Line 1,189:
for eventCycle, itemRewardID in ipairs(event.itemRewardIDs) do
for eventCycle, itemRewardID in ipairs(event.itemRewardIDs) do
if item.id == itemRewardID then
if item.id == itemRewardID then
local sourceTxt = Icons.Icon({dungeon.name, type=iconType}) .. (eventCycle == Shared.tableCount(event.itemRewardIDs) and '' or ', Cycle ' .. eventCycle)
local sourceTxt = Icons.Icon({dungeon.name, type='combatArea'}) .. (eventCycle == Shared.tableCount(event.itemRewardIDs) and '' or ', Cycle ' .. eventCycle)
table.insert(dropRows, {
table.insert(dropRows, {
source = sourceTxt,  
source = sourceTxt,  
Line 1,361: Line 1,312:


-- Mining: Gems, and also Alt. Magic spells producing random gems
-- Mining: Gems, and also Alt. Magic spells producing random gems
if Shared.contains({'Gem', 'Superior Gem'}, item.type) then
if Shared.contains({'Gem', 'Superior Gem', 'Abyssal Gem'}, item.type) then
local gemKeys = { 'randomGems', 'randomSuperiorGems' }
local gemKeys = { 'randomGems', 'randomSuperiorGems', 'randomAbyssalGems' }
for i, gemKey in ipairs(gemKeys) do
for i, gemKey in ipairs(gemKeys) do
local thisGem, totalGemWeight = nil, 0
local thisGem, totalGemWeight = nil, 0
Line 1,396: Line 1,347:
-- Check for Alt. Magic spells also
-- Check for Alt. Magic spells also
local producesKey = (gemKey == 'randomGems' and 'RandomGem') or 'RandomSuperiorGem'
local producesKey = (gemKey == 'randomGems' and 'RandomGem') or (gemKey == 'randomSuperiorGems' and 'RandomSuperiorGem') or nil
for j, spell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
if producesKey ~= nil then
if spell.produces ~= nil and spell.produces == producesKey then
for j, spell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
table.insert(dropRows, {
if spell.produces ~= nil and spell.produces == producesKey then
source = Icons.Icon({spell.name, type=Magic._getSpellIconType(spell)}),  
table.insert(dropRows, {
level = Icons.Icon({'Alternative Magic', type='skill', img='Magic', notext=true}) .. ' Level ' .. spell.level,
source = Icons.Icon({spell.name, type=Magic._getSpellIconType(spell)}),  
levelNum = spell.level,
level = Icons.Icon({'Alternative Magic', type='skill', img='Magic', notext=true}) .. ' Level ' .. spell.level,
minqty = thisGem.minQuantity,  
levelNum = spell.level,
qty = thisGem.maxQuantity,
minqty = thisGem.minQuantity,  
weight = thisGem.weight,  
qty = thisGem.maxQuantity,
totalWeight = totalGemWeight,
weight = thisGem.weight,  
expIcon = Icons.getExpansionIcon(spell.id)})
totalWeight = totalGemWeight,
expIcon = Icons.getExpansionIcon(spell.id)})
end
end
end
end
end
Line 1,446: Line 1,399:
end
end


function p._getItemUpgradeTable(item)
function p._getItemUpgradeTableData(item, tableData)
local resultPart = {}
if tableData == nil then tableData = {} end
 
local upgrade = GameData.getEntityByProperty('itemUpgrades', 'upgradedItemID', item.id)
local upgrade = GameData.getEntityByProperty('itemUpgrades', 'upgradedItemID', item.id)
if upgrade ~= nil then
if upgrade ~= nil then
Line 1,455: Line 1,409:
})
})


table.insert(resultPart, '{| class="wikitable"\r\n|-\r\n!colspan="2"|[[Upgrading Items|Item Upgrade]]')
table.insert(tableData, {
table.insert(resultPart, '\r\n|-\r\n!style="text-align:right;"|Materials\r\n|')
['costs'] = upgradeCost,
table.insert(resultPart, upgradeCost)
['qty'] = 1,
table.insert(resultPart, '\r\n|}')
['source'] = '[[Upgrading Items|Item Upgrade]]'
})
end
end
return table.concat(resultPart)
 
return tableData
end
end


Line 1,470: Line 1,426:
end
end


return p._getItemUpgradeTable(item)
return p.buildCreationTable(p._getItemUpgradeTableData(item), item)
end
end


Line 1,563: Line 1,519:
return ''
return ''
end
end
-- TODO: Remove this maybe?
function p._getItemShopTableData(item, tableData)
if tableData == nil then tableData = {} end
local purchaseArray = Shop.getItemSourceArray(item.id)


function p._getItemSourceTables(item)
for i, purchaseData in ipairs(purchaseArray) do
local resultPart = {}
local purchase = purchaseData.purchase
local shopTable = Shop._getItemShopTable(item)
local namespace, localID = Shared.getLocalID(purchase.id)
if shopTable ~= '' then
local source = nil
table.insert(resultPart, '===Shop===\r\n'..shopTable)
-- Show icon text when it's the only entry in the table
end
local notext = (Shared.tableCount(tableData) + Shared.tableCount(purchaseArray) > 1)


local creationTable = p._getCreationTable(item)
if purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then
if creationTable ~= '' then
source = Shop._getPurchaseExpansionIcon(purchase) .. Common.getPurchaseIcon({purchase})
if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
-- Always show icon text when there's multiple items
table.insert(resultPart, '===Creation===\r\n'..creationTable)
notext = false
else
source = Icons.Icon({'Shop'}) .. ' Purchase'
end
 
table.insert(tableData, {
['reqs'] = Common.getRequirementString(purchase.purchaseRequirements, 'None'),
['isAbyssal'] = namespace == 'melvorItA',
['costs'] = Shop.getCostString(purchase.cost, false),
['qty'] = Shop._getPurchaseContents(purchase, true, notext),
['source'] = source,
['center'] = notext
})
end
end


local upgradeTable = p._getItemUpgradeTable(item)
return tableData
if upgradeTable ~= '' then
end
if not Shared.tableIsEmpty(resultPart) then table.insert(resultPart, '\r\n') end
 
if creationTable ~= '' then table.insert(resultPart, '===Creation===\r\n') end
function p._getItemSourceTables(item)
table.insert(resultPart, upgradeTable)
local resultPart = {}
local sourceData = {}
 
p._getCreationTableData(item, sourceData)
p._getItemUpgradeTableData(item, sourceData)
p._getItemShopTableData(item, sourceData)
 
local sourceTable = p.buildCreationTable(item, sourceData)
if sourceTable ~= '' then
table.insert(resultPart, sourceTable)
end
end
--[[ TODO: Uncomment this section


local townshipTable = p._getTownshipTraderTable(item)
local townshipTable = p._getTownshipTraderTable(item)
Line 1,599: Line 1,582:
table.insert(resultPart, '===Loot===\r\n'..lootTable)
table.insert(resultPart, '===Loot===\r\n'..lootTable)
end
end
--]]
return table.concat(resultPart)
return table.concat(resultPart)
end
end
Line 1,722: Line 1,706:
end
end


--[[
 
-- Uncomment this block and execute 'p.test()' within the debug console
-- Uncomment this block and execute 'p.test()' within the debug console
-- to test after making changes
-- to test after making changes
function p.test()
function p.test()
local checkItems = {
local checkItems = {
"Circlet of Rhaelyx",
-- "Circlet of Rhaelyx",
"Jewel of Rhaelyx",
-- "Jewel of Rhaelyx",
"Signet Ring Half (a)",
-- "Signet Ring Half (a)",
"Signet Ring Half (b)",
-- "Signet Ring Half (b)",
"Gold Topaz Ring",
-- "Astrology Lesser Relic",
"Astrology Lesser Relic",
-- "Mysterious Stone",
"Mysterious Stone",
-- "Gold Topaz Ring",
"Charcoal",
"Ash",
"Coal Ore",
"Rune Essence",
"Gold Bar",
"Gold Bar",
"Raw Shrimp",
-- "Rune Platebody",
"Coal Ore",
"Rune Platebody",
"Arrow Shafts",
"Arrow Shafts",
"Yew Longbow",
-- "Yew Longbow",
"Water Rune",
-- "Water Rune",
"Steam Rune",
"Steam Rune",
"Controlled Heat Potion II",
-- "Wolf",
"Wolf",
"Fox",
"Cyclops",
"Leprechaun",
"Leprechaun",
"Redwood Logs",
"Void Wisp",
-- "Redwood Logs",
-- "Shadow Raven Nest",
"Raw Shrimp",
"Shrimp",
"Carrot Cake",
"Carrot Cake",
"Carrot Cake (Perfect)",
-- "Carrot Cake (Perfect)",
"Mantalyme Herb",
-- "Mantalyme Herb",
"Carrot",
"Carrot",
"Controlled Heat Potion II",
"Topaz",
"Topaz",
"Rune Essence",
"Oricha",
"Sanguine Blade",
"Nightopal",
"Ring of Power",
-- "Sanguine Blade",
"Infernal Claw",
-- "Ring of Power",
"Chapeau Noir",
-- "Infernal Claw",
-- "Chapeau Noir",
"Stardust",
"Stardust",
"Golden Stardust",
"Abyssal Stardust",
"Rope",
"Rope",
"Ancient Ring of Mastery",
"Ancient Ring of Mastery",
"Mastery Token (Cooking)",
"Mastery Token (Cooking)",
"Gem Gloves",
"Gem Gloves",
"Magic Bones",
"Bowstring",
"Superior Max Skillcape",
"Thief's Moneysack",
"Thief's Moneysack",
"Golden Stardust",
"Golden Star",
"Golden Star",
"Slayer Deterer",
-- "Slayer Deterer",
"Paper",
"Paper",
"Lemon",
-- "Lemon",
"Aranite Brush",
"Aranite Brush",
"Barrier Dust",
"Barrier Dust",
"Shadow Raven Nest",
"Gloom Resin",
"Void Wisp"
"Gloom Amber",
"Gloom Vine",
"Gloom Vein Seed",
"Elite Chest",
"Abyssal Coin Contract II",
"Dark Summon Consumable II",
}
}
local checkFuncs = {
local checkFuncs = {
--p.getItemSourceTables,
p.getItemSourceTables,
--p.getCreationTable,
--p.getCreationTable,
--p.getItemSources,
--p.getItemSources,
Line 1,799: Line 1,799:
end
end
end
end
--]]
 


return p
return p