Anonymous

Module:Items/SourceTables/Sandbox: Difference between revisions

From Melvor Idle
Centered Qty and Chance in Loot Tables; Make Creation Table sortable and give it a sticky header; Pull Arch and Mining & Alt Magic Gem sources into Creation Tables; Sorted tables by output quantity
(Separate Rune costs in Creation Table; Add Township and Superheat creation data into Creation Table;)
(Centered Qty and Chance in Loot Tables; Make Creation Table sortable and give it a sticky header; Pull Arch and Mining & Alt Magic Gem sources into Creation Tables; Sorted tables by output quantity)
 
Line 18: Line 18:
['melvorAoD:EarthGolem'] = 'Earth Golem (AoD)'
['melvorAoD:EarthGolem'] = 'Earth Golem (AoD)'
}
}
--[[
TODO:
-- Move Township Task and Cartography Disovery rewards under Item Sources (?)
-- Center Quantity & Chance in Loot Tables
Creation-Tables:
-- Move anything that isn't Combat, Thieving, or Chests out of Loot Sources and into Creation Tables (Alt Magic Spells & Gems) (?)
Use-Tables:
-- Shrink icon size down (to 25px?)
-- Or change "Item Created" to a single cell? (1 (IMG) Barrier Touch Potion I)
-- Change Type header to Source
-- Change Requirements header to Requires
-- Change XP header to Exp
-- Change XP display to: Icon - Value - (' XP' or ' AXP' if abyssal)
-- Remove right alignment on XP, Requirements
-- Add 'table-na' to cells with N/A
--]]


local function doesRecipeHaveItemID(recipe, itemID)
local function doesRecipeHaveItemID(recipe, itemID)
Line 49: Line 32:
function p._getCreationTableData(item, tableData)
function p._getCreationTableData(item, tableData)
if tableData == nil then tableData = {} end
if tableData == nil then tableData = {} end
local skill = ''
local reqs = nil
local source = nil
local time = 0
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 itemID = item.id
local itemID = item.id
Line 68: Line 38:
local skillIDs = {
local skillIDs = {
['Gathering'] = {
['Gathering'] = {
['Farming'] = { recipeKey = 'recipes', recipeType = 'item' },
['Farming'] = { recipeKey = 'recipes' },
['Woodcutting'] = { recipeKey = 'trees', recipeType = 'tree' },
['Woodcutting'] = { recipeKey = 'trees' },
['Fishing'] = { recipeKey = 'fish', recipeType = 'item' },
['Fishing'] = { recipeKey = 'fish' },
['Firemaking'] = { recipeKey = 'logs', recipeType = 'item' },
['Firemaking'] = { recipeKey = 'logs' },
['Mining'] = { recipeKey = 'rockData', recipeType = 'rock' },
['Mining'] = { recipeKey = 'rockData' },
['Cartography'] = { recipeKey = 'paperRecipes', recipeType = 'item' },
['Cartography'] = { recipeKey = 'paperRecipes' },
['Harvesting'] = { recipeKey = 'veinData', recipeType = 'vein' }
['Harvesting'] = { recipeKey = 'veinData' }
},
},
['Artisan'] = {
['Artisan'] = {
Line 92: Line 62:
local skillData = SkillData[localSkillID]
local skillData = SkillData[localSkillID]
local skill = skillData.name
local skill = skillData.name
local reqs, lvl, isAbyssal, xp, qty, source, costs, time, maxTime, weight, totalWeight = nil, 0, false, 0, 1, nil, nil, 0, nil, nil, nil
local lvl, reqs, isAbyssal, xp, costs, qty, source, time, maxTime, weight, totalWeight = nil, nil, false, nil, nil, nil, nil, nil, nil, 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 163: Line 133:
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['lvl'] = lvl,
['reqs'] = reqs,
['reqs'] = reqs,
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
Line 186: Line 157:
local skillData = SkillData[localSkillID]
local skillData = SkillData[localSkillID]
local skill = skillData.name
local skill = skillData.name
local reqs, lvl, isAbyssal, xp, qty, source, costs, time, maxTime = nil, 0, false, 0, 1, Icons.Icon({ skill, type='skill' }), nil, 0, nil
local lvl, reqs, isAbyssal, xp, costs, qty, source, time, maxTime = nil, nil, false, nil, nil, nil, nil, nil, nil, 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 213: Line 184:
if levelUnlock ~= nil then
if levelUnlock ~= nil then
reqs = reqs .. '<br>' .. Icons._MasteryReq(item.name, levelUnlock.level, false)
reqs = reqs .. '<br>' .. Icons._MasteryReq(item.name, levelUnlock.level, false)
end
end
if localSkillID == 'Cooking' then
-- Cooking includes the required utility (fire, furnace, pot) as a special requirement
local cookingCatIcon = {
["melvorD:Fire"] = 'Normal Cooking Fire',
["melvorD:Furnace"] = 'Basic Furnace',
["melvorD:Pot"] = 'Basic Pot'
}
local categoryIconName, categoryName = cookingCatIcon[recipe.categoryID], nil
local recipeCategory = GameData.getEntityByID(SkillData.Cooking.categories, recipe.categoryID)
if recipeCategory ~= nil then
categoryName = recipeCategory.modifierName or recipeCategory.name
end
if categoryIconName ~= nil and categoryName ~= nil then
reqs = reqs .. '<br>' .. Icons.Icon({ 'Cooking', categoryName, section = 'Cooking Upgrades', img = categoryIconName, type = 'upgrade' })
end
end
end
end
Line 266: Line 221:
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['reqs'] = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['lvl'] = lvl,
['reqs'] = reqs,
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = xp,
['xp'] = xp,
Line 278: Line 234:
local reqPart, qtyPart = {}, {}
local reqPart, qtyPart = {}, {}
for j, altCost in ipairs(recipe.alternativeCosts) do
for j, altCost in ipairs(recipe.alternativeCosts) do
local costsStr = ''
for k, itemCost in ipairs(altCost.itemCosts) do
if k > 1 then costsStr = costsStr .. '<br>' end
local reqItem = Items.getItemByID(itemCost.id)
if reqItem == nil then
costsStr = costsStr .. itemCost.quantity .. 'x ?????'
else
costsStr = costsStr .. Icons.Icon({ reqItem.name, type='item', qty=itemCost.quantity })
end
end
costsStr = costsStr .. Common.getCostString({ ["items"] = {}, ["currencies"] = recipe.currencyCosts }, '')
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['reqs'] = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['lvl'] = lvl,
['reqs'] = reqs,
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = xp,
['xp'] = xp,
['costs'] = costsStr,
['costs'] = Common.getCostString({ ["items"] = altCost.itemCosts, ["currencies"] = recipe.currencyCosts }),
['qty'] = qty * altCost.quantityMultiplier,
['qty'] = qty * altCost.quantityMultiplier,
['source'] = Icons.Icon({ skill, type='skill' }),
['source'] = source,
['time'] = time,
['time'] = time,
['maxTime'] = maxTime
['maxTime'] = maxTime
Line 303: Line 249:
-- Finally, normal recipes with a single set of item costs
-- Finally, normal recipes with a single set of item costs
elseif type(recipe.itemCosts) == 'table' and not Shared.tableIsEmpty(recipe.itemCosts) then
elseif type(recipe.itemCosts) == 'table' and not Shared.tableIsEmpty(recipe.itemCosts) then
if localSkillID == 'Cooking' then
-- Cooking includes the required utility (fire, furnace, pot) as a special requirement
local cookingCatIcon = {
["melvorD:Fire"] = 'Normal Cooking Fire',
["melvorD:Furnace"] = 'Basic Furnace',
["melvorD:Pot"] = 'Basic Pot'
}
local categoryIconName, categoryName = cookingCatIcon[recipe.categoryID], nil
local recipeCategory = GameData.getEntityByID(SkillData.Cooking.categories, recipe.categoryID)
if recipeCategory ~= nil then
categoryName = recipeCategory.modifierName or recipeCategory.name
end
if categoryIconName ~= nil and categoryName ~= nil then
reqs = reqs .. '<br>' .. Icons.Icon({ 'Cooking', categoryName, section = 'Cooking Upgrades', img = categoryIconName, type = 'upgrade' })
end
end
table.insert(tableData, {
table.insert(tableData, {
['skill'] = skill,
['skill'] = skill,
['reqs'] = Icons._SkillReq(skill, lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['lvl'] = lvl,
['reqs'] = reqs,
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['xp'] = xp,
['xp'] = xp,
['costs'] = recipe.itemCosts,
['costs'] = Common.getCostString({ ["items"] = recipe.itemCosts, ["currencies"] = recipe.currencyCosts }),
['qty'] = qty,
['qty'] = qty,
['source'] = Icons.Icon({ skill, type='skill' }),
['source'] = source,
['time'] = time,
['time'] = time,
['maxTime'] = maxTime,
['maxTime'] = maxTime
['currencyCost'] = recipe.currencyCosts
})
})
end
end
Line 327: Line 290:
local imgType = Magic._getSpellIconType(altSpell)
local imgType = Magic._getSpellIconType(altSpell)
table.insert(tableData, {
table.insert(tableData, {
['skill'] = 'Magic',
['skill'] = 'Alt Magic',
['reqs'] = Icons._SkillReq('Magic', altSpell.level, false),
['lvl'] = altSpell.level,
['reqs'] = Icons.Icon({'Alt Magic', type='skill', notext=true}) .. ' Level ' .. altSpell.level,
['isAbyssal'] = false,
['isAbyssal'] = false,
['xp'] = altSpell.baseExperience,
['xp'] = altSpell.baseExperience,
Line 348: Line 312:
table.insert(tableData, {
table.insert(tableData, {
['skill'] = 'Astrology',
['skill'] = 'Astrology',
['lvl'] = 1,
['reqs'] = Icons._SkillReq('Astrology', 1, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['reqs'] = Icons._SkillReq('Astrology', 1, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['isAbyssal'] = isAbyssal,
['isAbyssal'] = isAbyssal,
['qty'] = qty,
['qty'] = 1,
['xp'] = (isAbyssal and 1238 or 5), -- Use the XP value for the first constellation
['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) }),
['source'] = Icons.Icon({ 'Astrology', type='skill', class=(isAbyssal and 'abyss-icon' or nil) }),
Line 358: Line 323:
end
end


return tableData
-- Can we find this in an Archaeology digsite?
end
for i, drop in ipairs(p._getItemArchSources(item)) do
 
if drop.name ~= nil then
function p.buildCreationTable(item, tableData)
table.insert(tableData, {
if Shared.tableIsEmpty(tableData) then return '' end
['skill'] = 'Archaeology',
['lvl'] = drop.level,
['reqs'] = Icons._SkillReq('Archaeology', drop.level) .. ' ('..drop.size..')',
['isAbyssal'] = false,
['minqty'] = drop.minQty,
['qty'] = drop.maxQty,
['source'] = Icons.Icon({ drop.name, type='poi' }),
['time'] = 4,
['weight'] = drop.dropWt,
['totalWeight'] = drop.totalWt
--['expIcon'] = Icons.getExpansionIcon(drop.id)}),
})
end
end


local showSource = false
-- Mining: Gems, and also Alt. Magic spells producing random gems
local showRequirements = false
if Shared.contains({'Gem', 'Superior Gem', 'Abyssal Gem'}, item.type) then
local showInputs = false
local gemKeys = { 'randomGems', 'randomSuperiorGems', 'randomAbyssalGems' }
local showRunes = false
for i, gemKey in ipairs(gemKeys) do
local showOutputs = false
local thisGem, totalGemWeight = nil, 0
local showXP = false
for j, gem in ipairs(GameData.rawData[gemKey]) do
local showTime = false
totalGemWeight = totalGemWeight + gem.weight
local showChance = false
if gem.itemID == item.id then
local colspan = -1 -- colspan only needs to be set when there are 3+ columns in the table
thisGem = gem
end
end
if thisGem ~= nil then
--local expIcon = ''
local sourceTxt, lvl, isAbyssal = nil, nil, false


for i, data in ipairs(tableData) do
if item.type == 'Abyssal Gem' then
if not showSource and tableData[1].source ~= tableData[i].source then
sourceTxt = '[[Mining#Abyssal Gems|Abyssal Gem]]'
showSource = true
lvl = 1
colspan = colspan + 1
isAbyssal = true
elseif item.type == 'Superior Gem' then
--expIcon = Icons.TotH()
sourceTxt = '[[Mining#Superior Gems|Superior Gem]]'
-- Superior gems can only be found with Mining 100 or above
lvl = 100
else
sourceTxt = '[[Mining#Gems|Gem]]'
-- Gems can only be found with any Mining level
lvl = 1
end
table.insert(tableData, {
['skill'] = 'Mining',
['lvl'] = lvl,
['reqs'] = Icons._SkillReq('Mining', lvl, false, (isAbyssal and 'melvorItA:Abyssal' or nil)),
['isAbyssal'] = isAbyssal,
['minqty'] = thisGem.minQuantity,
['qty'] = thisGem.maxQuantity,
['source'] = sourceTxt,
['time'] = 3,
['weight'] = thisGem.weight,
['totalWeight'] = totalGemWeight,
--expIcon = expIcon
})
 
-- Check for Alt. Magic spells also
local producesKey = (gemKey == 'randomGems' and 'RandomGem') or (gemKey == 'randomSuperiorGems' and 'RandomSuperiorGem') or nil
if producesKey ~= nil then
for j, spell in ipairs(Magic.getSpellsBySpellBook('altMagic')) do
if spell.produces ~= nil and spell.produces == producesKey then
table.insert(tableData, {
['skill'] = 'Alt Magic',
['lvl'] = spell.level,
['reqs'] = Icons.Icon({'Alt Magic', type='skill', notext=true}) .. ' Level ' .. spell.level,
['minqty'] = thisGem.minQuantity,
['qty'] = thisGem.maxQuantity,
['source'] = Icons.Icon({ spell.name, type=Magic._getSpellIconType(spell) }),
['time'] = 2,
['weight'] = thisGem.weight,
['totalWeight'] = totalGemWeight,
--expIcon = Icons.getExpansionIcon(spell.id)
})
end
end
end
end
end
end
if not showRequirements and tableData[1].reqs ~= tableData[i].reqs then
end
showRequirements = true
 
colspan = colspan + 1
return tableData
end
 
function p.buildCreationTable(item, tableData)
if Shared.tableIsEmpty(tableData) then return '' end
 
table.sort(tableData, function(a, b) return (a.qty or 1) < (b.qty or 1) end)
 
local showSource = false
local showRequirements = false
local showInputs = false
local showRunes = false
local showOutputs = false
local showXP = false
local showTime = false
local showChance = false
local colspan = -1 -- colspan only needs to be set when there are 3+ columns in the table
 
for i, data in ipairs(tableData) do
if not showSource and tableData[1].source ~= tableData[i].source then
showSource = true
colspan = colspan + 1
end
if not showRequirements and tableData[1].reqs ~= tableData[i].reqs then
showRequirements = true
colspan = colspan + 1
end
end
if not showInputs and tableData[1].costs ~= tableData[i].costs then
if not showInputs and tableData[1].costs ~= tableData[i].costs then
Line 391: Line 444:
colspan = colspan + 1
colspan = colspan + 1
end
end
if not showOutputs and tableData[1].qty ~= tableData[i].qty then
if not showOutputs and (tableData[1].qty ~= tableData[i].qty or tableData[1].contents ~= tableData[i].contents) then
showOutputs = true
showOutputs = true
colspan = colspan + 1
colspan = colspan + 1
Line 422: Line 475:
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
if data.currencyCost ~= nil then
costsRow:wikitext('<br>' .. Common.getCostString({ ["items"] = {}, ["currencies"] = data.currencyCost }, ''))
end
end
else
else
Line 434: Line 483:


local resultTable = mw.html.create('table')
local resultTable = mw.html.create('table')
resultTable:addClass('wikitable')
resultTable:addClass('wikitable stickyHeader')
local tableHeader = resultTable:tag('tr')
local tableHeader = resultTable:tag('tr'):addClass('headerRow-0')
 
local makeSortable = Shared.contains({ showSource, showRequirements, showInputs, showRunes, showOutputs, showXP, showTime, showChance }, true)
if makeSortable then
resultTable:addClass('sortable')
end


if showSource then tableHeader:tag('th'):wikitext('Source') end
if showSource then tableHeader:tag('th'):wikitext('Source') end
Line 446: Line 500:
if showChance then tableHeader:tag('th'):wikitext('Chance'):attr('colspan', 2) 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)
if makeSortable then
for i, data in ipairs(tableData) do
-- Populate table data with any unique entries (Ex: Ash's Inputs, Outputs, Exp, Time)
local recipeRow = resultTable:tag('tr')
for i, data in ipairs(tableData) do
local recipeRow = resultTable:tag('tr')
 
if showSource then recipeRow:tag('td'):wikitext(data.source):attr('data-sort-value', data.skill) end


if showSource then recipeRow:tag('td'):wikitext(data.source) end
if showRequirements then
if showRequirements then
if data.reqs ~= nil then
if data.reqs ~= nil then
recipeRow:tag('td'):wikitext(data.reqs):attr('data-sort-value', (data.lvl or 0))
recipeRow:tag('td'):wikitext(data.reqs)
else
else
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na'):attr('data-sort-value', 0)
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
end
end
end
end


if showInputs then
if showInputs then
if data.costs ~= nil then
if data.costs ~= nil then
addCostsRow(recipeRow, data, 1)
addCostsRow(recipeRow, data, 1)
else
else
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
end
end
end
end


if showRunes then
if showRunes then
if data.runeCost ~= nil then
if data.runeCost ~= nil then
recipeRow:tag('td'):wikitext(data.runeCost):addClass('center')
recipeRow:tag('td'):wikitext(data.runeCost):addClass('center')
else
else
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
end
end
end
end


if showOutputs then
if showOutputs then
if type(data.qty) == 'string' then
local outputData = recipeRow:tag('td'):attr('data-sort-value', (data.qty or 1))
local outputData = recipeRow:tag('td'):wikitext(data.qty)
if data.contents ~= nil then
if data.center then outputData:addClass('center') end
outputData:wikitext(data.contents)
elseif data.qty ~= nil then
if data.center then outputData:addClass('center') end
recipeRow:tag('td'):wikitext(Icons.Icon({ item.name, type='item', notext=true, qty=(data.qty or 1) })):addClass('center')
elseif data.qty ~= nil then
else
if data.minqty ~= nil and data.minqty ~= data.qty then
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
outputRow:wikitext((data.minqty ~= nil and (Num.formatnum(data.minqty) .. ' - ') or ''))
end
outputData:wikitext(Icons.Icon({ item.name, type='item', notext=true, qty=(data.qty or 1) })):addClass('center'):attr('data-sort-value', (data.qty or 1))
else
outputData:wikitext('N/A'):addClass('table-na')
end
end
end
end


if showXP then
if showXP then
if data.skill ~= nil and data.xp ~= nil then
if data.skill ~= nil and data.xp ~= nil then
local iconClass = (data.isAbyssal and 'abyss-icon' or nil)
local iconClass = (data.isAbyssal and 'abyss-icon' or nil)
local xpText = (data.isAbyssal and ' AXP' or ' XP')
local xpText = (data.isAbyssal and ' AXP' or ' XP')
recipeRow:tag('td')
recipeRow:tag('td'):attr('data-sort-value', data.xp)
:wikitext(Icons.Icon({ data.skill, notext=true, type='skill', class=iconClass }))
:wikitext(Icons.Icon({ data.skill, notext=true, type='skill', class=iconClass }))
:wikitext(' ' .. Num.formatnum(data.xp) .. xpText)
:wikitext(' ' .. Num.formatnum(data.xp) .. xpText)
else
else
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na'):attr('data-sort-value', 0)
end
end
end
end
 
if showTime then
if showTime then
if data.time ~= nil then
if data.time ~= nil then
recipeRow:tag('td'):wikitext(Shared.timeString(data.time, true)):addClass('center')
recipeRow:tag('td'):wikitext(Shared.timeString(data.time, true)):addClass('center'):attr('data-sort-value', data.time)
else
else
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na')
end
end
end
end
 
if showChance then
if showChance then
if data.weight ~= nil then
if data.weight ~= nil then
recipeRow:tag('td'):wikitext(Num.fraction(data.weight, (data.totalWeight or 100))):addClass('center')
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
recipeRow:tag('td'):wikitext(Num.round2(data.weight / (data.totalWeight or 100) * 100, 2) .. '%'):addClass('center')
local chance = data.weight / (data.totalWeight or 100) * 100
else
local fmt = (chance < 0.10 and '%.2g') or '%.2f'
recipeRow:tag('td'):wikitext('100%'):addClass('center'):attr('colspan', 2)
local percent = string.format(fmt, chance)
 
recipeRow:tag('td'):wikitext(Num.fraction(data.weight, (data.totalWeight or 100))):addClass('center'):attr('data-sort-value', percent)
recipeRow:tag('td'):wikitext(percent .. '%'):addClass('center')
else
recipeRow:tag('td'):wikitext('100%'):addClass('center'):attr('colspan', 2):attr('data-sort-value', 100)
end
end
end
end
end
Line 518: Line 586:
resultTable:tag('tr')
resultTable:tag('tr')
:tag('th'):wikitext('Source'):css('text-align', 'right')
:tag('th'):wikitext('Source'):css('text-align', 'right')
:tag('td'):attr('colspan', colspan):wikitext(tableData[1].source)
:tag('td'):attr('colspan', colspan):wikitext(tableData[1].source)
end
end


Line 534: Line 602:
end
end


if showRunes and type(tableData[1].runeCost) == 'string' then
if not showRunes and type(tableData[1].runeCost) == 'string' then
local costStr = tableData[1].runeCost:gsub(', ', '<br>')
resultTable:tag('tr')
recipeRow:tag('td'):wikitext('<br>' .. costStr):addClass('center')
:tag('th'):wikitext('Runes'):css('text-align', 'right')
:tag('td'):wikitext(tableData[1].runeCost):addClass('center')
end
end


if not showOutputs and tableData[1].qty ~= nil then
if not showOutputs and (tableData[1].qty ~= nil or tableData[1].contents ~= nil) then
local outputRow = resultTable:tag('tr')
local outputRow = resultTable:tag('tr')
:tag('th'):wikitext('Outputs'):css('text-align', 'right')
:tag('th'):wikitext('Outputs'):css('text-align', 'right')


if type(tableData[1].qty) == 'string' then
if tableData[1].contents ~= nil then
outputRow:tag('td'):wikitext(tableData[1].qty)
outputRow:tag('td'):wikitext(tableData[1].contents)
else
else
outputRow:tag('td'):attr('colspan', colspan):wikitext(Icons.Icon({ item.name, type='item', qty=(tableData[1].qty or 1) }))
local outputData = outputRow:tag('td'):attr('colspan', colspan)
if tableData[1].minqty ~= nil and tableData[1].minqty ~= tableData[1].qty then
outputData:wikitext((tableData[1].minqty ~= nil and (Num.formatnum(tableData[1].minqty) .. ' - ') or ''))
end
outputData:wikitext(Icons.Icon({ item.name, type='item', qty=(tableData[1].qty or 1) }))
end
end
end
end
Line 554: Line 627:
resultTable:tag('tr')
resultTable:tag('tr')
:tag('th'):wikitext('Base Exp'):css('text-align', 'right')
:tag('th'):wikitext('Base Exp'):css('text-align', 'right')
:tag('td'):attr('colspan', colspan):wikitext(Num.formatnum(tableData[1].xp) .. xpText)
:tag('td'):attr('colspan', colspan):wikitext(Num.formatnum(tableData[1].xp) .. xpText)
end
end


Line 562: Line 635:


local timeData = timeHeader:tag('td'):attr('colspan', colspan)
local timeData = timeHeader:tag('td'):attr('colspan', colspan)
timeData:wikitext(Shared.timeString(tableData[1].time, true))
:wikitext(Shared.timeString(tableData[1].time, true))


if tableData[1].maxTime ~= nil and tableData[1].maxTime > tableData[1].time then
if tableData[1].maxTime ~= nil and tableData[1].maxTime > tableData[1].time then
Line 570: Line 643:


if not showChance and tableData[1].weight ~= nil then
if not showChance and tableData[1].weight ~= nil then
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places
local chance = tableData[1].weight / (tableData[1].totalWeight or 100) * 100
local fmt = (chance < 0.10 and '%.2g') or '%.2f'
local percent = string.format(fmt, chance)
local chanceData = resultTable:tag('tr')
local chanceData = resultTable:tag('tr')
:tag('th'):wikitext('Base Chance'):css('text-align', 'right')
:tag('th'):wikitext('Base Chance'):css('text-align', 'right')
:tag('td'):attr('colspan', colspan)
:tag('td'):attr('colspan', colspan)
:wikitext(Num.fraction(tableData[1].weight, (tableData[1].totalWeight or 100)))
:wikitext(Num.fraction(tableData[1].weight, (tableData[1].totalWeight or 100)) .. ' (' .. percent .. '%)')
:wikitext(' (' .. Num.round2(tableData[1].weight / (tableData[1].totalWeight or 100) * 100, 2) .. '%)')
end
end


Line 1,115: Line 1,191:
function p._getItemLootSourceTable(item)
function p._getItemLootSourceTable(item)
local resultPart = {}
local resultPart = {}
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '{| class="wikitable sortable stickyHeader col-3-center col-4-center"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!Source!!Level!!Quantity!!colspan="2"|Chance')
table.insert(resultPart, '\r\n!Source!!Level!!Qty!!colspan="2"|Chance')


--Set up function for adding rows
--Set up function for adding rows
Line 1,135: Line 1,211:
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|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
local chance = weight / totalWeight * 100
local chance = weight / totalWeight * 100
Line 1,152: Line 1,228:
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="' .. chanceStr .. '"| ' .. Num.fraction(weight, totalWeight) .. '\r\n|')
table.insert(rowPart, '\r\n|data-sort-value="' .. chanceStr .. '"| ' .. Num.fraction(weight, totalWeight) .. '\r\n|')
end
end
end
end
if weight == -1 then
if weight == -1 then
--Weight of -1 means this is a weird row that has a variable percentage
--Weight of -1 means this is a weird row that has a variable percentage
table.insert(rowPart, 'style="text-align: right;" data-sort-value="0"|Varies (see Thieving page)')
table.insert(rowPart, 'data-sort-value="0"|Varies (see Thieving page)')
else
else
table.insert(rowPart, 'style="text-align: right;" data-sort-value="'.. chanceStr .. '"|'..chanceStr..'%')
table.insert(rowPart, 'data-sort-value="'.. chanceStr .. '"|'..chanceStr..'%')
end
end
return table.concat(rowPart)
return table.concat(rowPart)
Line 1,180: Line 1,256:
level = Icons.Icon({'Combat', 'Monsters', notext=true}) .. ' Level ' .. Num.formatnum(monsterLevel),
level = Icons.Icon({'Combat', 'Monsters', notext=true}) .. ' Level ' .. Num.formatnum(monsterLevel),
levelNum = monsterLevel,
levelNum = monsterLevel,
minqty = drop.minQty,
qty = drop.maxQty,
weight = drop.dropWt,
totalWeight = drop.totalWt,
expIcon = Icons.getExpansionIcon(drop.id)})
end
end
--Patching in here because it uses the same format
--Can we find this in an Archaeology digsite?
for i, drop in ipairs(p._getItemArchSources(item)) do
if drop.name ~= nil then
table.insert(dropRows, {
source = Icons.Icon({drop.name, type='poi'}),
level = Icons._SkillReq('Archaeology', drop.level) .. ' ('..drop.size..')',
levelNum = drop.level,
minqty = drop.minQty,  
minqty = drop.minQty,  
qty = drop.maxQty,  
qty = drop.maxQty,  
Line 1,349: Line 1,409:
end
end


-- Mining: Gems, and also Alt. Magic spells producing random gems
--Make sure to return nothing if there are no drop sources
if Shared.contains({'Gem', 'Superior Gem', 'Abyssal Gem'}, item.type) then
if Shared.tableIsEmpty(dropRows) then return '' end
local gemKeys = { 'randomGems', 'randomSuperiorGems', 'randomAbyssalGems' }
for i, gemKey in ipairs(gemKeys) do
table.sort(dropRows, function(a, b)
local thisGem, totalGemWeight = nil, 0
for j, gem in ipairs(GameData.rawData[gemKey]) do
totalGemWeight = totalGemWeight + gem.weight
if gem.itemID == item.id then
thisGem = gem
end
end
if thisGem ~= nil then
local expIcon = ''
local sourceTxt
local lv = nil
if item.type == 'Superior Gem' then
expIcon = Icons.TotH()
sourceTxt = '[[Mining#Superior Gems|Superior Gem]]'
-- Superior gems can only be found with Mining 100 or above
lv = 100
else
sourceTxt = '[[Mining#Gems|Gem]]'
-- Gems can only be found with any Mining level
lv = 1
end
table.insert(dropRows, {
source = sourceTxt,
level = Icons._SkillReq('Mining', lv),
levelNum = lv,
minqty = thisGem.minQuantity,
qty = thisGem.maxQuantity,
weight = thisGem.weight,
totalWeight = totalGemWeight,
expIcon = expIcon})
-- Check for Alt. Magic spells also
local producesKey = (gemKey == 'randomGems' and 'RandomGem') or (gemKey == 'randomSuperiorGems' and 'RandomSuperiorGem') or nil
if producesKey ~= nil then
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=Magic._getSpellIconType(spell)}),
level = Icons.Icon({'Alternative Magic', type='skill', img='Magic', notext=true}) .. ' Level ' .. spell.level,
levelNum = spell.level,
minqty = thisGem.minQuantity,
qty = thisGem.maxQuantity,
weight = thisGem.weight,
totalWeight = totalGemWeight,
expIcon = Icons.getExpansionIcon(spell.id)})
end
end
end
end
end
end
 
--Make sure to return nothing if there are no drop sources
if Shared.tableIsEmpty(dropRows) then return '' end
table.sort(dropRows, function(a, b)
if a.weight / a.totalWeight == b.weight / b.totalWeight then
if a.weight / a.totalWeight == b.weight / b.totalWeight then
if a.minqty + a.qty == b.minqty + b.qty then
if a.minqty + a.qty == b.minqty + b.qty then
Line 1,442: Line 1,446:
local upgrade = GameData.getEntityByProperty('itemUpgrades', 'upgradedItemID', item.id)
local upgrade = GameData.getEntityByProperty('itemUpgrades', 'upgradedItemID', item.id)
if upgrade ~= nil then
if upgrade ~= nil then
local upgradeCost = Common.getCostString({
local reqs = nil
["items"] = upgrade.itemCosts,
if item.charges ~= nil and item.tier ~= nil then
["currencies"] = upgrade.currencyCosts
local levelUnlock = GameData.getEntityByProperty(SkillData.Herblore.masteryLevelUnlocks, 'descriptionID', item.tier + 1)
})
if levelUnlock ~= nil then
reqs = Icons._MasteryReq(item.name, levelUnlock.level, false)
end
end


table.insert(tableData, {
table.insert(tableData, {
['costs'] = upgradeCost,
['reqs'] = reqs,
['qty'] = 1,
['costs'] = Common.getCostString({ ["items"] = upgrade.itemCosts, ["currencies"] = upgrade.currencyCosts }),
['source'] = '[[Upgrading Items|Item Upgrade]]'
['qty'] = (upgrade.quantity or 1),
['source'] = '[[Upgrading Items|Item ' .. (upgrade.isDowngrade and 'Downgrade' or 'Upgrade') ..']]'
})
})
end
end
Line 1,505: Line 1,513:


table.insert(tableData, {
table.insert(tableData, {
['skill'] = 'Magic',
['skill'] = 'Alt Magic',
['reqs'] = smithingReq .. '<br>' .. Icons._SkillReq('Magic', spell.level, false),
['lvl'] = spell.level,
['reqs'] = smithingReq .. '<br>' .. Icons.Icon({'Alt Magic', type='skill', notext=true}) .. ' Level ' .. spell.level,
['isAbyssal'] = false,
['isAbyssal'] = false,
['xp'] = spell.baseExperience,
['xp'] = spell.baseExperience,
Line 1,541: Line 1,550:
local resName = (res ~= nil and res.name) or 'Unknown'
local resName = (res ~= nil and res.name) or 'Unknown'
local resQty = math.max(item.sellsFor, 2)
local resQty = math.max(item.sellsFor, 2)
local townshipReq = GameData.getEntityByProperty(tradeDef.unlockRequirements, 'skillID', 'melvorD:Township')
local lvl = townshipReq.level + (townshipReq.type == 'AbyssalLevel' and 120 or 0)


table.insert(tableData, {
table.insert(tableData, {
['reqs'] = Shop.getRequirementString(tradeDef.unlockRequirements),
['lvl'] = lvl,
['reqs'] = Common.getRequirementString(tradeDef.unlockRequirements),
['isAbyssal'] = namespace == 'melvorItA',
['isAbyssal'] = namespace == 'melvorItA',
['costs'] = Icons.Icon({ resName, qty=resQty, type='resource' }),
['costs'] = Icons.Icon({ resName, qty=resQty, type='resource' }),
['qty'] = 1,
['qty'] = 1,
['source'] = Icons.Icon({'Township', 'Trader', type='skill'}),
['source'] = Icons.Icon({ 'Township', 'Trader', type='skill' }),
})
})
break
break
Line 1,557: Line 1,569:
end
end


-- TODO: Remove this maybe?
function p._getItemShopTableData(item, tableData)
function p._getItemShopTableData(item, tableData)
if tableData == nil then tableData = {} end
if tableData == nil then tableData = {} end
Line 1,567: Line 1,578:
local namespace, localID = Shared.getLocalID(purchase.id)
local namespace, localID = Shared.getLocalID(purchase.id)
local source = nil
local source = nil
-- Show icon text when it's the only entry in the table
-- Show icon text when it's the only source of this item
local notext = (Shared.tableCount(tableData) + Shared.tableCount(purchaseArray) > 1)
local notext = (Shared.tableCount(tableData) + Shared.tableCount(purchaseArray) > 1)


Line 1,581: Line 1,592:
['reqs'] = Common.getRequirementString(purchase.purchaseRequirements),
['reqs'] = Common.getRequirementString(purchase.purchaseRequirements),
['isAbyssal'] = namespace == 'melvorItA',
['isAbyssal'] = namespace == 'melvorItA',
['costs'] = Shop.getCostString(purchase.cost, false),
['costs'] = Common.getCostString(purchase.cost),
['qty'] = Shop._getPurchaseContents(purchase, true, notext),
['qty'] = purchaseData.qty,
['contents'] = Shop._getPurchaseContents(purchase, true, notext),
['source'] = source,
['source'] = source,
['center'] = notext
['center'] = notext
Line 1,611: Line 1,623:
table.insert(resultPart, sourceTable)
table.insert(resultPart, sourceTable)
end
end
--[[ TODO: Uncomment this section
 
local lootTable = p._getItemLootSourceTable(item)
local lootTable = p._getItemLootSourceTable(item)
if lootTable ~= '' then
if lootTable ~= '' then
Line 1,617: Line 1,629:
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,718: Line 1,730:
if found ~= nil then
if found ~= nil then
local min = found.minQuantity
local max = found.maxQuantity
table.insert(resultArray, {
table.insert(resultArray, {
id = digSite.id,  
id = digSite.id,  
Line 1,725: Line 1,735:
level = digSite.level,
level = digSite.level,
size = sizeName,  
size = sizeName,  
minQty = min,  
minQty = found.minQuantity,  
maxQty = max,  
maxQty = found.maxQuantity,  
dropWt = found.weight,  
dropWt = found.weight,  
totalWt = sizeWeight})
totalWt = sizeWeight
})
end
end
end
end
Line 1,741: Line 1,752:
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
Line 1,751: Line 1,762:
-- "Signet Ring Half (b)",
-- "Signet Ring Half (b)",
-- "Astrology Lesser Relic",
-- "Astrology Lesser Relic",
-- "Mysterious Stone",
"Mysterious Stone",
-- "Gold Topaz Ring",
"Charge Stone of Rhaelyx",
"Gold Topaz Ring",
"Charcoal",
"Charcoal",
"Ash",
"Ash",
"Coal Ore",
"Coal Ore",
"Golden Star",
"Potion Box III",
"Rune Essence",
"Rune Essence",
"Dragonite Bar",
"Dragonite Bar",
"Holy Dust",
-- "Rune Platebody",
-- "Rune Platebody",
"Arrow Shafts",
"Arrow Shafts",
Line 1,790: Line 1,805:
"Mastery Token (Cooking)",
"Mastery Token (Cooking)",
"Thief's Moneysack",
"Thief's Moneysack",
"Golden Star",
-- "Slayer Deterer",
-- "Slayer Deterer",
"Paper",
"Paper",
-- "Lemon",
-- "Lemon",
"Aranite Brush",
"Aranite Brush",
"Charged Diamond Shard",
"Barrier Dust",
"Barrier Dust",
"Gloom Resin",
"Gloom Resin",
Line 1,812: Line 1,827:
"Dragon Javelin",
"Dragon Javelin",
"Skillers Body",
"Skillers Body",
"Abyssal Compost",
}
}
local checkFuncs = {
local checkFuncs = {
Line 1,839: Line 1,855:
end
end
end
end
 
--]]


return p
return p