4,980
edits
m (Fix specialReq not being set per skill causing it to carry over to other entries) |
(Merge Shop, Upgrade, Township and Superheat creation data into Creation Table (CT); Pull Arch & Gem data out of Loot Tables (LT) and into CT; Centered Qty and Chance in LT; Added 1/x values to Chances; Add output quantity for upgraded items in CT; Fix Ash reqs only displaying the first req; Make CT sortable & stickyHeader; Sorted CT by output quantity; Separate Rune costs in CT; Many other misc minor changes) |
||
Line 30: | Line 30: | ||
end | end | ||
function p. | function p._getCreationTableData(item, tableData) | ||
if tableData == nil then tableData = {} end | |||
local itemID = item.id | local itemID = item.id | ||
--First figure out what skill is used to make this... | --First figure out what skill is used to make this... | ||
Line 73: | Line 62: | ||
local skillData = SkillData[localSkillID] | local skillData = SkillData[localSkillID] | ||
local skill = skillData.name | local skill = skillData.name | ||
local lvl, isAbyssal, xp, qty, source | 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 80: | Line 69: | ||
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 96: | Line 81: | ||
elseif skillData.baseInterval ~= nil then | elseif skillData.baseInterval ~= nil then | ||
time = skillData.baseInterval / 1000 | time = skillData.baseInterval / 1000 | ||
end | end | ||
-- | -- Custom Chance, Qty, and Costs data | ||
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 | ||
weight = itemChanceData.chance | |||
elseif itemID == 'melvorD:Generous_Fire_Spirit' then | elseif itemID == 'melvorD:Generous_Fire_Spirit' then | ||
weight = 0.1 | |||
end | end | ||
Line 121: | Line 108: | ||
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 itemChanceData = nil | local itemChanceData = nil | ||
totalWeight = 0 | |||
for i, product in ipairs(recipe.products) do | for i, product in ipairs(recipe.products) do | ||
Line 134: | Line 123: | ||
if itemChanceData ~= nil then | if itemChanceData ~= nil then | ||
weight = itemChanceData.weight | |||
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 | ||
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, | ['lvl'] = lvl, | ||
['reqs'] = reqs, | |||
['isAbyssal'] = isAbyssal, | ['isAbyssal'] = isAbyssal, | ||
['xp'] = xp, | ['xp'] = xp, | ||
Line 152: | Line 142: | ||
['time'] = time, | ['time'] = time, | ||
['maxTime'] = maxTime, | ['maxTime'] = maxTime, | ||
[' | ['weight'] = weight, | ||
[' | ['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 167: | Line 157: | ||
local skillData = SkillData[localSkillID] | local skillData = SkillData[localSkillID] | ||
local skill = skillData.name | local skill = skillData.name | ||
local lvl, isAbyssal, xp, qty, source | 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 175: | Line 165: | ||
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 192: | Line 183: | ||
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 | ||
reqs = reqs .. '<br>' .. Icons._MasteryReq(item.name, levelUnlock.level, false) | |||
end | end | ||
end | end | ||
Line 243: | Line 218: | ||
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 | ||
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, | ['lvl'] = lvl, | ||
['reqs'] = reqs, | |||
['isAbyssal'] = isAbyssal, | ['isAbyssal'] = isAbyssal, | ||
['xp'] = xp, | ['xp'] = xp, | ||
Line 252: | Line 228: | ||
['qty'] = qty, | ['qty'] = qty, | ||
['source'] = source, | ['source'] = source, | ||
['time'] = time | ['time'] = time | ||
}) | }) | ||
-- Some items (such as Arrow shafts) have multiple recipes | -- Some items (such as Arrow shafts) have multiple recipes | ||
Line 259: | Line 234: | ||
local reqPart, qtyPart = {}, {} | local reqPart, qtyPart = {}, {} | ||
for j, altCost in ipairs(recipe.alternativeCosts) do | for j, altCost in ipairs(recipe.alternativeCosts) do | ||
table.insert(tableData, { | table.insert(tableData, { | ||
['skill'] = skill, | ['skill'] = skill, | ||
['lvl'] = lvl, | ['lvl'] = lvl, | ||
['reqs'] = reqs, | |||
['isAbyssal'] = isAbyssal, | ['isAbyssal'] = isAbyssal, | ||
['xp'] = xp, | ['xp'] = xp, | ||
['costs'] = | ['costs'] = Common.getCostString({ ["items"] = altCost.itemCosts, ["currencies"] = recipe.currencyCosts }), | ||
['qty'] = | ['qty'] = qty * altCost.quantityMultiplier, | ||
['source'] = | ['source'] = source, | ||
['time'] = time, | ['time'] = time, | ||
['maxTime'] = maxTime | ['maxTime'] = maxTime | ||
}) | }) | ||
end | end | ||
-- 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, | ||
['lvl'] = lvl, | ['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'] = | ['source'] = source, | ||
['time'] = time, | ['time'] = time, | ||
['maxTime'] = maxTime | ['maxTime'] = maxTime | ||
}) | }) | ||
end | end | ||
Line 309: | Line 289: | ||
if altSpell.produces == itemID then | if altSpell.produces == itemID then | ||
table.insert(tableData, { | table.insert(tableData, { | ||
['skill'] = 'Magic', | ['skill'] = 'Alt Magic', | ||
['lvl'] = altSpell.level, | ['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, | ||
['costs'] = altSpell | ['costs'] = Magic._getAltSpellCostText(altSpell), | ||
['qty'] = altSpell.productionRatio, | ['qty'] = altSpell.productionRatio, | ||
['source'] = Icons.Icon({ altSpell.name, type= | ['source'] = Icons.Icon({ altSpell.name, type=Magic._getSpellIconType(altSpell) }), | ||
['time'] = 2, | ['time'] = 2, | ||
[' | ['runeCost'] = Magic._getSpellRunes(altSpell) | ||
}) | }) | ||
end | end | ||
Line 331: | Line 312: | ||
['skill'] = 'Astrology', | ['skill'] = 'Astrology', | ||
['lvl'] = 1, | ['lvl'] = 1, | ||
['reqs'] = Icons._SkillReq('Astrology', 1, false, (isAbyssal and 'melvorItA:Abyssal' or nil)), | |||
['isAbyssal'] = isAbyssal, | ['isAbyssal'] = isAbyssal, | ||
['qty'] = | ['qty'] = 1, | ||
['xp'] = (isAbyssal and 1238 or 5), -- Use the | ['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 | ['source'] = Icons.Icon({ 'Astrology', type='skill', class=(isAbyssal and 'abyss-icon' or nil) }), | ||
['time'] = 3, | ['time'] = 3, | ||
[' | ['weight'] = stardustChanceData.chance | ||
}) | }) | ||
end | end | ||
if | -- Can we find this in an Archaeology digsite? | ||
for i, drop in ipairs(p._getItemArchSources(item)) do | |||
if drop.name ~= nil then | |||
table.insert(tableData, { | |||
['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 | end | ||
-- Mining: Gems, and also Alt. Magic spells producing random gems | |||
if Shared.contains({'Gem', 'Superior Gem', 'Abyssal Gem'}, item.type) then | |||
local gemKeys = { 'randomGems', 'randomSuperiorGems', 'randomAbyssalGems' } | |||
for i, gemKey in ipairs(gemKeys) do | |||
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, lvl, isAbyssal = nil, nil, false | |||
if item.type == 'Abyssal Gem' then | |||
sourceTxt = '[[Mining#Abyssal Gems|Abyssal Gem]]' | |||
lvl = 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 | ||
end | end | ||
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 | |||
for i, data in ipairs(tableData) do | |||
if showSource | |||
if showRequirements | |||
end | end | ||
if not showInputs and tableData[1].costs ~= tableData[i].costs then | |||
if showInputs and | showInputs = true | ||
colspan = colspan + 1 | |||
end | end | ||
if not showRunes and tableData[1].runeCost ~= tableData[i].runeCost then | |||
if showOutputs | showRunes = true | ||
if showXP then | colspan = colspan + 1 | ||
end | |||
if not showOutputs and (tableData[1].qty ~= tableData[i].qty or tableData[1].contents ~= tableData[i].contents) then | |||
showOutputs = true | |||
colspan = colspan + 1 | |||
end | |||
if not showXP and tableData[1].xp ~= tableData[i].xp then | |||
showXP = true | |||
colspan = colspan + 1 | |||
end | |||
if not showTime and tableData[1].time ~= tableData[i].time then | |||
showTime = true | |||
colspan = colspan + 1 | |||
end | |||
if not showChance and tableData[1].weight ~= tableData[i].weight then | |||
showChance = true | |||
colspan = colspan + 2 | |||
end | end | ||
end | end | ||
colspan = math.max(colspan, 1) | |||
if | local function addCostsRow(row, data, span) | ||
local costsRow = row:tag('td'):attr('colspan', span) | |||
if type(data.costs) == 'table' then | |||
for i, mat in ipairs(data.costs) do | |||
if i > 1 then costsRow:tag('br') end | |||
local matItem = Items.getItemByID(mat.id) | |||
if matItem == nil then | |||
costsRow:wikitext(mat.quantity .. 'x ?????') | |||
else | |||
costsRow:wikitext(Icons.Icon({ matItem.name, type='item', qty=mat.quantity })) | |||
end | |||
end | |||
else | |||
local costStr = data.costs:gsub(', ', '<br>') | |||
costsRow:wikitext(costStr) | |||
end | |||
end | end | ||
local resultTable = mw.html.create('table') | |||
resultTable:addClass('wikitable stickyHeader') | |||
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 | end | ||
if showSource then tableHeader:tag('th'):wikitext('Source') end | |||
if showRequirements then tableHeader:tag('th'):wikitext('Requires') end | |||
if showInputs then tableHeader:tag('th'):wikitext('Costs') end | |||
if showRunes then tableHeader:tag('th'):wikitext('Runes') end | |||
if showOutputs then tableHeader:tag('th'):wikitext('Outputs') end | |||
if showXP then tableHeader:tag('th'):wikitext('Exp') end | |||
if showTime then tableHeader:tag('th'):wikitext('Time') end | |||
if showChance then tableHeader:tag('th'):wikitext('Chance'):attr('colspan', 2) end | |||
if makeSortable then | |||
-- Populate table data with any unique entries (Ex: Ash's Inputs, Outputs, Exp, Time) | |||
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 | |||
end | |||
if showRequirements then | |||
if data.reqs ~= nil then | |||
recipeRow:tag('td'):wikitext(data.reqs):attr('data-sort-value', (data.lvl or 0)) | |||
else | |||
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na'):attr('data-sort-value', 0) | |||
end | |||
end | |||
if showInputs then | |||
end | if data.costs ~= nil then | ||
addCostsRow(recipeRow, data, 1) | |||
else | |||
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na') | |||
end | |||
end | |||
if showRunes then | |||
if data.runeCost ~= nil then | |||
recipeRow:tag('td'):wikitext(data.runeCost):addClass('center') | |||
else | |||
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na') | |||
end | |||
end | |||
if showOutputs then | |||
local outputData = recipeRow:tag('td'):attr('data-sort-value', (data.qty or 1)) | |||
if data.contents ~= nil then | |||
outputData:wikitext(data.contents) | |||
if data.center then outputData:addClass('center') end | |||
elseif data.qty ~= nil then | |||
if data.minqty ~= nil and data.minqty ~= data.qty then | |||
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 | |||
if showXP then | |||
if 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'):attr('data-sort-value', data.xp) | |||
:wikitext(Icons.Icon({ data.skill, notext=true, type='skill', class=iconClass })) | |||
:wikitext(' ' .. Num.formatnum(data.xp) .. xpText) | |||
else | |||
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na'):attr('data-sort-value', 0) | |||
end | |||
end | |||
if showTime then | |||
if data.time ~= nil then | |||
recipeRow:tag('td'):wikitext(Shared.timeString(data.time, true)):addClass('center'):attr('data-sort-value', data.time) | |||
else | |||
recipeRow:tag('td'):wikitext('N/A'):addClass('table-na') | |||
end | end | ||
end | end | ||
if showChance then | |||
if data.weight ~= nil then | |||
-- If chance is less than 0.10% then show 2 significant figures, otherwise 2 decimal places | |||
local chance = data.weight / (data.totalWeight or 100) * 100 | |||
local fmt = (chance < 0.10 and '%.2g') or '%.2f' | |||
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 | ||
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 | |||
resultTable:tag('tr') | |||
:tag('th'):wikitext('Source'):css('text-align', 'right') | |||
:tag('td'):attr('colspan', colspan):wikitext(tableData[1].source) | |||
end | |||
if not showRequirements and tableData[1].reqs ~= nil then | |||
local reqRow = resultTable:tag('tr') | |||
:tag('th'):wikitext('Requires'):css('text-align', 'right') | |||
:tag('td'):wikitext(tableData[1].reqs):attr('colspan', colspan) | |||
end | |||
if not showInputs and tableData[1].costs ~= nil then | |||
local costRow = resultTable:tag('tr') | |||
:tag('th'):wikitext('Costs'):css('text-align', 'right') | |||
addCostsRow(costRow, tableData[1], colspan) | |||
end | |||
if not showRunes and type(tableData[1].runeCost) == 'string' then | |||
resultTable:tag('tr') | |||
:tag('th'):wikitext('Runes'):css('text-align', 'right') | |||
:tag('td'):wikitext(tableData[1].runeCost):addClass('center') | |||
end | |||
if not showOutputs and (tableData[1].qty ~= nil or tableData[1].contents ~= nil) then | |||
local outputRow = resultTable:tag('tr') | |||
:tag('th'):wikitext('Outputs'):css('text-align', 'right') | |||
if tableData[1].contents ~= nil then | |||
outputRow:tag('td'):wikitext(tableData[1].contents) | |||
else | |||
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 | end | ||
outputData:wikitext(Icons.Icon({ item.name, type='item', qty=(tableData[1].qty or 1) })) | |||
end | end | ||
end | end | ||
if not showXP and tableData[1].xp ~= nil then | |||
local xpText = (tableData[1].isAbyssal and ' AXP' or ' XP') | |||
resultTable:tag('tr') | |||
:tag('th'):wikitext('Base Exp'):css('text-align', 'right') | |||
:tag('td'):attr('colspan', colspan):wikitext(Num.formatnum(tableData[1].xp) .. xpText) | |||
end | |||
if not showTime and tableData[1].time ~= nil then | |||
resultTable:tag('tr') | |||
local timeHeader = resultTable:tag('th'):wikitext('Base Time'):css('text-align', 'right') | |||
local timeData = timeHeader:tag('td'):attr('colspan', colspan) | |||
:wikitext(Shared.timeString(tableData[1].time, true)) | |||
if tableData[1].maxTime ~= nil and tableData[1].maxTime > tableData[1].time then | |||
timeData:wikitext(' - ' .. Shared.timeString(tableData[1].maxTime, true)) | |||
end | end | ||
end | end | ||
if not | 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') | |||
:tag('th'):wikitext('Base Chance'):css('text-align', 'right') | |||
:tag('td'):attr('colspan', colspan) | |||
:wikitext(Num.fraction(tableData[1].weight, (tableData[1].totalWeight or 100)) .. ' (' .. percent .. '%)') | |||
end | end | ||
return tostring(resultTable) | |||
end | |||
function p.getCreationTable(frame) | |||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = Items.getItem(itemName) | |||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | end | ||
return p.buildCreationTable(p._getCreationTableData(item), item) | |||
end | |||
function p._getItemSources(item, asList, addCategories, separator) | |||
local | local lineArray = {} | ||
local categoryArray = {} | |||
local sep = separator or ',' | |||
local | --Alright, time to go through all the ways you can get an item... | ||
for | --First up: Can we kill somebody and take theirs? | ||
if not | local killStrPart = {} | ||
for i, monster in ipairs(GameData.rawData.monsters) do | |||
local isDrop = false | |||
if monster.bones ~= nil and monster.bones.itemID == item.id and Monsters._getMonsterBones(monster) ~= nil then | |||
end | -- Item is a bone, and is either a shard from God dungeons or dropped by a non-boss monster with a loot table | ||
local | isDrop = true | ||
table.insert( | elseif monster.barrierPercent ~= nil and 'melvorAoD:Barrier_Dust' == item.id and not Monsters._isDungeonOnlyMonster(monster) then | ||
-- Item is Barrier Dust and is not a dungeon exclusive monster | |||
isDrop = true | |||
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 | |||
if loot.itemID == item.id and not Monsters._isDungeonOnlyMonster(monster) then | |||
isDrop = true | |||
break | |||
end | |||
end | |||
end | |||
if isDrop then | |||
-- Item drops when the monster is killed | |||
local iconName = monster.name | |||
if SourceOverrides[monster.id] ~= nil then | |||
iconName = SourceOverrides[monster.id] | |||
end | |||
table.insert(killStrPart, Icons.Icon({iconName, type='monster', notext=true})) | |||
end | end | ||
end | end | ||
-- Is the item dropped from any dungeon? | |||
-- | local dungeonStrPart = {} | ||
local | local dungeonEntities = { | ||
['Dungeon'] = GameData.rawData.dungeons, | |||
['The Abyss'] = GameData.rawData.abyssDepths | |||
} | |||
for i, | for entity, dungeons in pairs(dungeonEntities) do | ||
if | for i, dungeon in ipairs(dungeons) do | ||
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or | |||
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then | |||
table.insert(dungeonStrPart, Icons.Icon({dungeon.name, type='combatArea', notext=true})) | |||
table.insert( | 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 eventCycle, itemRewardID in ipairs(event.itemRewardIDs) do | |||
if item.id == itemRewardID then | |||
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='combatArea', notext=true})) | |||
break | |||
end | |||
end | |||
end | |||
end | |||
end | end | ||
end | end | ||
for i, stronghold in ipairs(GameData.rawData.strongholds) do | |||
for tier, tierData in pairs(stronghold.tiers) do | |||
if type(tierData.rewards) == 'table' and type(tierData.rewards.items) == 'table' then | |||
for i, reward in ipairs(tierData.rewards.items) do | |||
end | if reward.id == item.id then | ||
if not Shared.tableIsEmpty( | table.insert(dungeonStrPart, Icons.Icon({stronghold.name, type='combatArea', notext=true})) | ||
table.insert(lineArray, ' | end | ||
end | |||
end | |||
end | |||
end | |||
if not Shared.tableIsEmpty(dungeonStrPart) then | |||
table.insert(lineArray, 'Completing: ' .. table.concat(dungeonStrPart, sep)) | |||
end | |||
if not Shared.tableIsEmpty(killStrPart) then | |||
table.insert(lineArray, 'Killing: ' .. table.concat(killStrPart, sep)) | |||
end | end | ||
-- | -- Can we find it in an openable item? | ||
local | local lootPart = {} | ||
for i, item2 in ipairs(GameData.rawData.items) do | |||
if item2.dropTable ~= nil then | |||
for j, loot in ipairs(item2.dropTable) do | |||
if loot.itemID == item.id then | |||
table.insert(lootPart, Icons.Icon({item2.name, type='item', notext=true})) | |||
break | |||
end | |||
end | |||
end | |||
end | |||
if not Shared.tableIsEmpty(lootPart) then | |||
table.insert(lineArray, 'Opening: ' .. table.concat(lootPart, sep)) | |||
end | |||
-- | -- Is the item a result of upgrading/downgrading another item? | ||
for | 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 | end | ||
end | end | ||
local upgradeCat = false | |||
for | for catName, parts in pairs(upgradePart) do | ||
if not Shared.tableIsEmpty(parts) then | |||
if not upgradeCat then | |||
table.insert(categoryArray, '[[Category:Upgraded Items]]') | |||
if | upgradeCat = true | ||
table.insert( | |||
end | end | ||
local typeText = (catName == 'up' and 'Upgrading') or 'Downgrading' | |||
table.insert(lineArray, typeText .. ': ' .. table.concat(parts, sep)) | |||
end | end | ||
end | end | ||
-- | --Next: Can we take it from somebody else -without- killing them? | ||
local thiefItems = Skills.getThievingSourcesForItem(item.id) | |||
if type(thiefItems) == 'table' then | |||
local includedNPCs = {} | |||
local thiefPart = {} | |||
for i, thiefRow in ipairs(thiefItems) do | |||
if thiefRow.npc == 'all' then | |||
--if 'all' is the npc, this is a rare item so just say 'Thieving level 1' | |||
table.insert(lineArray, Icons._SkillReq('Thieving', 1)) | |||
elseif not Shared.contains(includedNPCs, thiefRow.npc) then | |||
table.insert(thiefPart, Icons.Icon({thiefRow.npc, type='thieving', notext=true})) | |||
table.insert(includedNPCs, thiefRow.npc) | |||
end | end | ||
end | end | ||
if | if not Shared.tableIsEmpty(thiefPart) then | ||
table.insert(lineArray, 'Pickpocketing: ' .. table.concat(thiefPart, sep)) | |||
end | end | ||
end | |||
-- Can we get this item by casting an Alt. Magic spell? | |||
local castPart = {} | |||
for i, spell in ipairs(Magic.getSpellsProducingItem(item.id)) do | |||
table.insert(castPart, Icons.Icon({spell.name, type=Magic._getSpellIconType(spell), notext=true})) | |||
end | |||
if not Shared.tableIsEmpty(castPart) then | |||
table.insert(lineArray, 'Casting: ' .. table.concat(castPart, sep)) | |||
end | end | ||
-- | --Check if we can make it ourselves | ||
local skillIDs = { | |||
['Gathering'] = { | |||
['Woodcutting'] = { recipeKey = 'trees' }, | |||
['Fishing'] = { recipeKey = 'fish' }, | |||
['Firemaking'] = { recipeKey = 'logs' }, | |||
['Mining'] = { recipeKey = 'rockData' }, | |||
['Farming'] = { recipeKey = 'recipes' }, | |||
['Harvesting'] = { recipeKey = 'veinData' } | |||
}, | |||
['Artisan'] = { | |||
['Cooking'] = { }, | |||
['Smithing'] = { }, | |||
['Fletching'] = { }, | |||
['Crafting'] = { }, | |||
['Runecrafting'] = { }, | |||
['Herblore'] = { }, | |||
['Summoning'] = { } | |||
} | |||
} | |||
-- | |||
for i, | -- Gathering skills | ||
for localSkillID, dataProp in pairs(skillIDs.Gathering) do | |||
local skillData = SkillData[localSkillID] | |||
local skill = skillData.name | |||
for i, recipe in ipairs(skillData[dataProp.recipeKey]) do | |||
local hasProduct = doesRecipeHaveItemID(recipe, item.id) | |||
if hasProduct then | |||
if localSkillID == 'Farming' and recipe.seedCost ~= nil then | |||
local seedItem = Items.getItemByID(recipe.seedCost.id) | |||
if seedItem ~= nil then | |||
table.insert(lineArray, 'Growing: ' .. Icons.Icon({seedItem.name, type='item', notext='true'})) | |||
end | |||
else | |||
local level, isAbyssal = Skills.getRecipeLevelRealm(localSkillID, recipe) | |||
table.insert(lineArray, Icons._SkillReq(skill, level, false, (isAbyssal and "melvorItA:Abyssal" or nil))) | |||
end | |||
break | |||
end | |||
end | end | ||
end | end | ||
-- | -- Artisan skills | ||
for localSkillID, dataProp in pairs(skillIDs.Artisan) do | |||
local skillData = SkillData[localSkillID] | |||
local skill = skillData.name | |||
for i, recipe in ipairs(skillData.recipes) do | |||
if recipe.productID == item.id or | |||
(localSkillID == 'Cooking' and recipe.perfectCookID == item.id) or | |||
(localSkillID == 'Herblore' and Shared.contains(recipe.potionIDs, item.id)) then | |||
local level, isAbyssal = Skills.getRecipeLevelRealm(localSkillID, recipe) | |||
table.insert(lineArray, Icons._SkillReq(skill, level, false, (isAbyssal and "melvorItA:Abyssal" or nil))) | |||
break | |||
end | |||
end | end | ||
end | end | ||
-- | |||
for i, | -- Township trading | ||
for i, tsResource in ipairs(SkillData.Township.itemConversions.fromTownship) do | |||
local found = false | local found = false | ||
for j, | for j, tradeDef in ipairs(tsResource.items) do | ||
if | if tradeDef.itemID == item.id then | ||
found = true | |||
local levelReq = nil | |||
if tradeDef.unlockRequirements ~= nil then | |||
for k, req in ipairs(tradeDef.unlockRequirements) do | |||
if req.type == 'SkillLevel' and req.skillID == 'melvorD:Township' then | |||
levelReq = req.level | |||
break | |||
end | end | ||
end | end | ||
if levelReq == nil then | |||
table.insert(lineArray, Icons.Icon({SkillData.Township.name, type='skill'})) | |||
else | |||
table.insert(lineArray, Icons._SkillReq(SkillData.Township.name, levelReq)) | |||
end | |||
end | end | ||
end | |||
if found then | |||
break | |||
end | end | ||
end | end | ||
Line 831: | Line 905: | ||
end | end | ||
end | end | ||
-- | |||
for i, | -- Archaeology sources | ||
-- Digsites | |||
for i, digsite in ipairs(SkillData.Archaeology.digSites) do | |||
local found = false | local found = false | ||
for artefactType, artefactItems in pairs(digsite.artefacts) do | |||
for j, itemDef in ipairs( | for j, itemDef in ipairs(artefactItems) do | ||
if itemDef. | if itemDef.itemID == item.id then | ||
table.insert(lineArray, Icons. | table.insert(lineArray, Icons._SkillReq(SkillData.Archaeology.name, digsite.level)) | ||
found = true | found = true | ||
break | break | ||
Line 845: | Line 921: | ||
break | break | ||
end | end | ||
end | |||
if found then | |||
break | |||
end | end | ||
end | end | ||
-- Museum rewards | |||
-- | for i, museumReward in ipairs(SkillData.Archaeology.museumRewards) do | ||
for i, | if type(museumReward.items) == 'table' and Shared.contains(museumReward.items, item.id) then | ||
if | table.insert(lineArray, Icons.Icon('Museum')) | ||
table.insert(lineArray, Icons.Icon( | break | ||
end | end | ||
end | end | ||
-- | -- Cartography | ||
-- | -- Paper | ||
for i, recipe in ipairs(SkillData.Cartography.paperRecipes) do | |||
if recipe.productId == item.id then | |||
table.insert(lineArray, Icons.Icon({SkillData.Cartography.name, type='skill'})) | |||
break | |||
end | end | ||
end | |||
-- | -- POI discovery rewards | ||
for i, worldMap in ipairs(SkillData.Cartography.worldMaps) do | |||
local found = false | |||
for j, poi in ipairs(worldMap.pointsOfInterest) do | |||
if type(poi.discoveryRewards) == 'table' and type(poi.discoveryRewards.items) == 'table' then | |||
for k, itemDef in ipairs(poi.discoveryRewards.items) do | |||
if itemDef.id == item.id then | |||
-- Find level for POI hex | |||
local level = 1 | |||
local poiHex = nil | |||
local skillID = SkillData.Cartography.skillID | |||
for m, hex in ipairs(worldMap.hexes) do | |||
if hex.coordinates.q == poi.coords.q and hex.coordinates.r == poi.coords.r then | |||
for n, req in ipairs(hex.requirements) do | |||
if req.type == 'SkillLevel' and req.skillID == skillID then | |||
level = req.level | |||
break | |||
end | |||
end | |||
break | |||
end | |||
end | |||
table.insert(lineArray, Icons._SkillReq(SkillData.Cartography.name, level)) | |||
found = true | |||
break | |||
end | |||
end | |||
if found then | |||
break | |||
end | |||
end | |||
end | |||
if found then | |||
break | |||
end | end | ||
end | end | ||
-- Travel events | |||
-- | for i, event in ipairs(SkillData.Cartography.travelEvents) do | ||
local found = false | |||
if type(event.rewards) == 'table' and type(event.rewards.items) == 'table' then | |||
for j, itemDef in ipairs(event.rewards.items) do | |||
if itemDef.id == item.id and itemDef.quantity > 0 then | |||
table.insert(lineArray, Icons.Icon({SkillData.Cartography.name, type='skill'})) | |||
found = true | |||
break | |||
end | |||
end | |||
if found then | |||
break | |||
end | |||
end | |||
end | end | ||
-- | --AstrologyCheck | ||
for i, dustDrop in ipairs(SkillData.Astrology.baseRandomItemChances) do | |||
if dustDrop.itemID == item.id then | |||
table.insert(lineArray, Icons.Icon({SkillData.Astrology.name, type='skill'})) | |||
end | end | ||
end | end | ||
-- | -- Woodcutting | ||
-- | -- Raven Nest | ||
if item.id == SkillData.Woodcutting.ravenNestItemID then | |||
local levelReq = nil | |||
for i, tree in ipairs(SkillData.Woodcutting.trees) do | |||
if tree.canDropRavenNest and (levelReq == nil or tree.level < levelReq) then | |||
levelReq = tree.level | |||
end | |||
end | end | ||
table.insert(lineArray, Icons._SkillReq(SkillData.Woodcutting.name, levelReq)) | |||
-- Bird Nest, Ash, and Mushroom | |||
elseif Shared.contains({ | |||
SkillData.Woodcutting.nestItemID, | |||
SkillData.Woodcutting.ashItemID, | |||
SkillData.Woodcutting.mushroomItemID | |||
}, item.id) then | |||
table.insert(lineArray, Icons._SkillReq(SkillData.Woodcutting.name, 1)) | |||
end | |||
-- Fishing | |||
-- Junk | |||
if Shared.contains(SkillData.Fishing.junkItemIDs, item.id) then | |||
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Junk|Junk]]') | |||
elseif item.id == SkillData.Fishing.lostChestItem then | |||
table.insert(lineArray, Icons._SkillReq(SkillData.Fishing.name, 100)) | |||
end | |||
-- Specials | |||
for i, specialItem in ipairs(SkillData.Fishing.specialItems) do | |||
if GameData.getEntityByProperty(specialItem.drops, 'itemID', item.id) ~= nil then | |||
table.insert(lineArray, Icons.Icon({'Fishing', type='skill', notext=true}) .. ' [[Fishing#Special|Special]]') | |||
end | |||
end | |||
-- Firemaking: Coal | |||
if Shared.contains({SkillData.Firemaking.coalItemID, | |||
SkillData.Firemaking.ashItemID, | |||
SkillData.Firemaking.charcoalItemID, | |||
SkillData.Firemaking.fireSpiritItemID, | |||
SkillData.Firemaking.diamondItemID | |||
}, item.id) then | |||
table.insert(lineArray, Icons._SkillReq(SkillData.Firemaking.name, 1)) | |||
if | |||
table.insert(lineArray, | |||
end | end | ||
-- | -- Mining: Gems | ||
if item.id = | if (GameData.getEntityByProperty('randomGems', 'itemID', item.id) ~= nil or | ||
GameData.getEntityByProperty('randomSuperiorGems', 'itemID', item.id) ~= nil or | |||
GameData.getEntityByProperty('randomAbyssalGems', 'itemID', item.id) ~= nil) then | |||
table.insert(lineArray, | table.insert(lineArray, Icons.Icon({"Mining", type='skill', notext=true})..' [[Mining#Gems|Gem]]') | ||
elseif item.id == 'melvorTotH: | elseif item.id == SkillData.Mining.runestoneItemID then | ||
-- From pure essence mining | |||
local recipe = GameData.getEntityByID(SkillData.Mining.rockData, 'melvorTotH:Pure_Essence') | |||
if recipe ~= nil then | |||
table.insert(lineArray, Icons._SkillReq(SkillData.Mining.name, recipe.level)) | |||
end | |||
end | end | ||
-- | -- General rare drops for non-combat skills | ||
-- Includes items like Circlet/Jewel of Rhaelyx, Mysterious stones, Signet ring half (a), | |||
-- relics (for Ancient Relics mode) | |||
local skillIconList, subText = {}, '' | |||
for i, skillDataAll in ipairs(GameData.rawData.skillData) do | |||
local skillData = skillDataAll.data | |||
local skillName, displaySkillName = skillData.name, false | |||
-- All general rare drops within the Magic are for Alt. Magic | |||
if skillDataAll.skillID == 'melvorD:Magic' then | |||
skillName, displaySkillName = 'Alt. Magic', true | |||
end | end | ||
if type(skillData.rareDrops) == 'table' then | |||
for j, rareDrop in ipairs(skillData.rareDrops) do | |||
local isAltItem = (rareDrop.altItemID ~= nil and rareDrop.altItemID == item.id) | |||
if isAltItem or rareDrop.itemID == item.id then | |||
if Shared.tableIsEmpty(skillIconList) then | |||
-- Initialize subText | |||
if isAltItem then | |||
local wornItem = Items.getItemByID(rareDrop.itemID) | |||
subText = ' while wearing ' .. Icons.Icon({wornItem.name, type='item'}) | |||
elseif rareDrop.altItemID ~= nil then | |||
-- There exists an alt item, but we are not searching for it | |||
local altItem = Items.getItemByID(rareDrop.altItemID) | |||
subText = ' if not worn (Instead of ' .. Icons.Icon({altItem.name, type='item'}) .. ')' | |||
elseif rareDrop.itemID == 'melvorD:Mysterious_Stone' then | |||
local foundItem = Items.getItemByID('melvorD:Crown_of_Rhaelyx') | |||
subText = '<br>after finding ' .. Icons.Icon({foundItem.name, type='item'}) | |||
end | |||
if type(rareDrop.gamemodes) == 'table' then | |||
local gamemodeText = {} | |||
for k, gamemodeID in ipairs(rareDrop.gamemodes) do | |||
local gamemode = GameData.getEntityByID('gamemodes', gamemodeID) | |||
if gamemode ~= nil then | |||
table.insert(gamemodeText, gamemode.name) | |||
end | |||
end | |||
if not Shared.tableIsEmpty(gamemodeText) then | |||
subText = subText .. ' (' .. table.concat(gamemodeText, ', ') .. ' only)' | |||
end | |||
end | |||
end | |||
local skillText = Icons.Icon({skillName, type='skill', notext=true}) | |||
if displaySkillName then | |||
skillText = skillText .. ' (' .. Icons.Icon({skillName, type='skill', noicon=true}) .. ')' | |||
end | |||
table.insert(skillIconList, skillText) | |||
end | |||
end | end | ||
end | end | ||
end | |||
if not Shared.tableIsEmpty(skillIconList) then | |||
table.insert(lineArray, 'Any action in: ' .. table.concat(skillIconList, ', ') .. subText) | |||
skillIconList, subText = {}, '' | |||
end | end | ||
-- Supplementary stuff on top of general rare drops | |||
if | if item.id == 'melvorD:Gold_Topaz_Ring' then | ||
table.insert( | table.insert(lineArray, 'Killing any monster if not worn (Instead of '..Icons.Icon({"Signet Ring Half (b)", type="item"})..')') | ||
elseif item.id == 'melvorD:Signet_Ring_Half_B' then | |||
table.insert( | table.insert(lineArray, 'Killing any monster while wearing '..Icons.Icon({'Gold Topaz Ring', type='item'})) | ||
elseif item.id == 'melvorTotH:Deadly_Toxins_Potion' then | |||
--Adding a special override for Deadly Toxins potions | |||
table.insert(lineArray, 'Brewing [[Lethal Toxins Potion]]s while wearing '..Icons.Icon({'Toxic Maker Gloves', type='item'})) | |||
end | end | ||
--Tokens are from the appropriate skill | |||
if item.modifiers ~= nil and item.modifiers.masteryToken ~= nil then | |||
for localSkillID, skillData in pairs(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 | ||
if item | |||
-- Golbin Raid exclusive items | |||
if item.golbinRaidExclusive then | |||
table.insert(lineArray, Icons.Icon({'Golbin Raid', type='pet', img='Golden Golbin'})) | |||
end | end | ||
--Shop items (including special items like gloves that aren't otherwise listed) | |||
end | if not Shared.tableIsEmpty(Shop.getItemSourceArray(item.id)) then | ||
table.insert(lineArray, Icons.Icon({'Shop'})) | |||
end | |||
--Easter Eggs (manual list 'cause don't have a better way to do that) | |||
if Shared.contains(Items.EasterEggs, item.name) then | |||
table.insert(lineArray, '[[Easter Eggs]]') | |||
end | |||
-- Event exclusive items (also a manual list) | |||
if Shared.contains(Items.EventItems, item.name) then | |||
-- | table.insert(lineArray, '[[Events]]') | ||
end | |||
if | |||
-- Township Task reward | |||
for _, task in ipairs(SkillData.Township.tasks) do | |||
if task.rewards.items[1] ~= nil then -- Skip tasks with no items | |||
if GameData.getEntityByID(task.rewards.items, item.id) then | |||
table.insert(lineArray, Icons.Icon({'Tasks', type='township'})) | |||
break | |||
end | |||
end | end | ||
end | |||
end | |||
local resultPart = {} | |||
if asList then | |||
table.insert(resultPart, '* '..table.concat(lineArray, "\r\n* ")) | |||
else | |||
table.insert(resultPart, '<div style="max-width:180px;text-align:right">' .. table.concat(lineArray, "<br>") .. '</div>') | |||
end | |||
if addCategories then table.insert(resultPart, table.concat(categoryArray, '')) end | |||
return table.concat(resultPart) | |||
end | |||
function p.getItemSources(frame) | |||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = Items.getItem(itemName) | |||
local asList = false | |||
local addCategories = false | |||
if frame.args ~= nil then | |||
asList = frame.args.asList ~= nil and frame.args.asList ~= '' and frame.args.asList ~= 'false' | |||
addCategories = frame.args.addCategories ~= nil and frame.args.addCategories ~= '' and frame.args.addCategories ~= 'false' | |||
end | end | ||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | end | ||
return p._getItemSources(item, asList, addCategories) | |||
local | end | ||
function p._getItemLootSourceTable(item) | |||
local resultPart = {} | |||
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!Source!!Level!!Qty!!colspan="2"|Chance') | |||
--Set up function for adding rows | |||
local buildRow = function(source, level, levelNum, minqty, qty, weight, totalWeight, expIcon) | |||
if minqty == nil then minqty = 1 end | |||
if expIcon == nil then expIcon = '' end | |||
if level == nil then level = 'N/A' end | |||
local rowPart = {} | |||
table.insert(rowPart, '\r\n|-') | |||
table.insert(rowPart, '\r\n|style="text-align: left;"|'..source) | |||
-- Retrieve numeric level value for sorting, or remove anything between [[]] | |||
local levelValue = '' | |||
if levelNum ~= nil then | |||
levelValue = tostring(levelNum) | |||
else | |||
levelValue = level:match('%[%[.-%]%]%s*(%w+)$') or '' | |||
end | |||
table.insert(rowPart, '\r\n|style="text-align: left;" data-sort-value="'..levelValue..'"|'..expIcon..' '..level) | |||
table.insert(rowPart, '\r\n|data-sort-value="'..qty..'"|'..Num.formatnum(minqty)) | |||
if qty ~= minqty then table.insert(rowPart, ' - '..Num.formatnum(qty)) end | |||
local chance = weight / totalWeight * 100 | |||
-- 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 chanceStr = string.format(fmt, chance) | |||
if weight >= totalWeight then | |||
-- Fraction would be 1/1, so only show the percentage | |||
chanceStr = '100' | |||
table.insert(rowPart, '\r\n|colspan="2" ') | |||
else | |||
local fraction = Num.fraction(weight, totalWeight) | |||
if Shared.contains(fraction, '%.') then | |||
--If fraction contains decimals, something screwy happened so just show only percentage | |||
--(happens sometimes with the rare thieving items) | |||
table.insert(rowPart, '\r\n|colspan="2" ') | |||
else | |||
table.insert(rowPart, '\r\n|data-sort-value="' .. chanceStr .. '"| ' .. Num.fraction(weight, totalWeight) .. '\r\n|') | |||
end | end | ||
end | end | ||
if weight == -1 then | |||
--Weight of -1 means this is a weird row that has a variable percentage | |||
table.insert(rowPart, 'data-sort-value="0"|Varies (see Thieving page)') | |||
else | |||
table.insert(rowPart, 'data-sort-value="'.. chanceStr .. '"|'..chanceStr..'%') | |||
end | |||
return table.concat(rowPart) | |||
end | end | ||
local dropRows = {} | |||
for i, | --Alright, time to go through a few ways to get the item | ||
--First up: Can we kill somebody and take theirs? | |||
for i, drop in ipairs(p._getItemMonsterSources(item)) do | |||
local monster = GameData.getEntityByID('monsters', drop.id) | |||
local iconName = monster.name | |||
if SourceOverrides[drop.id] ~= nil then | |||
iconName = SourceOverrides[drop.id] | |||
end | end | ||
if monster ~= nil then | |||
local monsterLevel = Monsters._getMonsterCombatLevel(monster) | |||
if | table.insert(dropRows, { | ||
local | source = Icons.Icon({iconName, type='monster'}), | ||
level = Icons.Icon({'Combat', 'Monsters', notext=true}) .. ' Level ' .. Num.formatnum(monsterLevel), | |||
levelNum = monsterLevel, | |||
minqty = drop.minQty, | |||
qty = drop.maxQty, | |||
weight = drop.dropWt, | |||
totalWeight = drop.totalWt, | |||
expIcon = Icons.getExpansionIcon(drop.id)}) | |||
end | |||
end | |||
if | -- Is the item dropped from any dungeon? | ||
local dungeonEntities = { | |||
['Dungeon'] = GameData.rawData.dungeons, | |||
['The Abyss'] = GameData.rawData.abyssDepths | |||
} | |||
for entity, dungeons in pairs(dungeonEntities) do | |||
for i, dungeon in ipairs(dungeons) do | |||
if (dungeon.oneTimeRewardID ~= nil and item.id == dungeon.oneTimeRewardID) or | |||
(type(dungeon.rewardItemIDs) == 'table' and Shared.contains(dungeon.rewardItemIDs, item.id)) then | |||
table.insert(dropRows, { | |||
source = Icons.Icon({dungeon.name, type='combatArea'}), | |||
level = '[['..entity..']]', | |||
minqty = 1, | |||
qty = 1, | |||
weight = 1, | |||
totalWeight = 1, | |||
expIcon = Icons.getExpansionIcon(dungeon.id)}) | |||
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 eventCycle, itemRewardID in ipairs(event.itemRewardIDs) do | |||
if item.id == itemRewardID then | |||
local sourceTxt = Icons.Icon({dungeon.name, type='combatArea'}) .. (eventCycle == Shared.tableCount(event.itemRewardIDs) and '' or ', Cycle ' .. eventCycle) | |||
table.insert(dropRows, { | |||
source = sourceTxt, | |||
level = '[['..entity..']]', | |||
minqty = 1, | |||
qty = 1, | |||
weight = 1, | |||
totalWeight = 1}) | |||
break | |||
end | |||
end | |||
end | |||
end | |||
end | end | ||
end | end | ||
for i, stronghold in ipairs(GameData.rawData.strongholds) do | |||
for tier, tierData in pairs(stronghold.tiers) do | |||
if type(tierData.rewards) == 'table' and type(tierData.rewards.items) == 'table' then | |||
for i, reward in ipairs(tierData.rewards.items) do | |||
if reward.id == item.id then | |||
table.insert(dropRows, { | |||
source = Icons.Icon({stronghold.name, type='combatArea'}), | |||
level = '[[Strongholds|'..tier..']]', | |||
minqty = 1, | |||
qty = 1, | |||
weight = tierData.rewards.chance, | |||
totalWeight = 100, | |||
expIcon = Icons.getExpansionIcon(stronghold.id)}) | |||
end | |||
end | end | ||
end | end | ||
end | end | ||
if | end | ||
local | |||
-- Can we find it in an openable item? | |||
for i, item2 in ipairs(GameData.rawData.items) do | |||
if item2.dropTable ~= nil then | |||
local minQty, maxQty, wt, totalWt = 1, 1, 0, 0 | |||
for j, loot in ipairs(item2.dropTable) do | |||
totalWt = totalWt + loot.weight | |||
if loot.itemID == item.id then | |||
wt = loot.weight | |||
minQty = loot.minQuantity | |||
maxQty = loot.maxQuantity | |||
end | |||
end | |||
if wt > 0 then | |||
local sourceTxt = Icons.Icon({item2.name, type='item'}) | |||
table.insert(dropRows, { | |||
source = sourceTxt, | |||
level = '[[Chest]]', | |||
minqty = minQty, | |||
qty = maxQty, | |||
weight = wt, | |||
totalWeight = totalWt, | |||
expIcon = Icons.getExpansionIcon(item2.id)}) | |||
end | end | ||
end | |||
end | |||
end | |||
end | |||
-- | -- Can it be obtained from Thieving? | ||
local thiefItems = Skills.getThievingSourcesForItem(item.id) | |||
for i, thiefRow in ipairs(thiefItems) do | |||
local sourceTxt = '' | |||
if thiefRow.npc == 'all' then | |||
sourceTxt = 'Thieving Rare Drop' | |||
else | |||
sourceTxt = Icons.Icon({thiefRow.npc, type='thieving'}) | |||
end | |||
local levelNum = thiefRow.abyssalLevel or thiefRow.level | |||
local isAbyssal = thiefRow.abyssalLevel ~= nil | |||
table.insert(dropRows, { | |||
source = sourceTxt, | |||
level = Icons._SkillReq("Thieving", levelNum, false, (isAbyssal and "melvorItA:Abyssal" or nil)), | |||
levelNum = levelNum, | |||
table.insert( | minqty = thiefRow.minQty, | ||
qty = thiefRow.maxQty, | |||
weight = thiefRow.wt, | |||
totalWeight = thiefRow.totalWt, | |||
expIcon = Icons.getExpansionIcon(thiefRow.npcID)}) | |||
end | end | ||
-- Fishing: Junk & Specials | |||
if Shared.contains(SkillData.Fishing.junkItemIDs, item.id) then | |||
local fishSource = '[[Fishing#Junk|Junk]]' | |||
local fishType = Icons.Icon({'Fishing', type='skill'}) | |||
local fishTotWeight = Shared.tableCount(SkillData.Fishing.junkItemIDs) | |||
table.insert(dropRows, { | |||
source = fishSource, | |||
level = Icons._SkillReq("Fishing", 1), | |||
levelNum = 1, | |||
minqty = 1, | |||
qty = 1, | |||
weight = 1, | |||
totalWeight = fishTotWeight}) | |||
else | |||
local fishTotWeight, fishItem, realmID = {['melvorD:Melvor'] = 0, ['melvorItA:Abyssal'] = 0}, nil, nil | |||
for i, specialItem in ipairs(SkillData.Fishing.specialItems) do | |||
local | for f, drop in ipairs(specialItem.drops) do | ||
if drop.itemID == item.id then | |||
fishItem = drop | |||
realmID = specialItem.realmID | |||
end | |||
fishTotWeight[specialItem.realmID] = fishTotWeight[specialItem.realmID] + drop.weight | |||
end | |||
end | |||
if fishItem ~= nil then | |||
local fishSource = '[[Fishing#Special|Special]]' | |||
local fishType = Icons.Icon({SkillData.Fishing.name, type='skill'}) | |||
table.insert(dropRows, { | |||
source = fishSource, | |||
level = Icons._SkillReq("Fishing", 1, false, realmID), | |||
levelNum = 1, | |||
minqty = fishItem.minQuantity, | |||
qty = fishItem.maxQuantity, | |||
weight = fishItem.weight, | |||
totalWeight = fishTotWeight[realmID]}) | |||
end | |||
end | |||
--Make sure to return nothing if there are no drop sources | |||
if Shared.tableIsEmpty(dropRows) then return '' end | |||
table.insert(resultPart, | |||
table.sort(dropRows, function(a, b) | |||
if a.weight / a.totalWeight == b.weight / b.totalWeight then | |||
if a.minqty + a.qty == b.minqty + b.qty then | |||
return (a.level == b.level and a.source < b.source) or a.level < b.level | |||
else | |||
return a.minqty + a.qty > b.minqty + b.qty | |||
end | |||
else | |||
return a.weight / a.totalWeight > b.weight / b.totalWeight | |||
end | |||
end) | |||
for i, data in ipairs(dropRows) do | |||
table.insert(resultPart, buildRow(data.source, data.level, data.levelNum, data.minqty, data.qty, data.weight, data.totalWeight, data.expIcon)) | |||
end | |||
table.insert(resultPart, '\r\n|}') | |||
return table.concat(resultPart) | return table.concat(resultPart) | ||
end | end | ||
function p. | function p.getItemLootSourceTable(frame) | ||
local itemName = frame.args ~= nil and frame.args[1] or frame | local itemName = frame.args ~= nil and frame.args[1] or frame | ||
local item = Items.getItem(itemName) | local item = Items.getItem(itemName) | ||
Line 1,389: | Line 1,437: | ||
end | end | ||
return p. | return p._getItemLootSourceTable(item) | ||
end | end | ||
function p._getSuperheatSmithRecipe(item) | function p._getItemUpgradeTableData(item, tableData) | ||
local smithRecipe = GameData.getEntityByProperty(SkillData.Smithing.recipes, 'productID', item.id) | if tableData == nil then tableData = {} end | ||
local upgrade = GameData.getEntityByProperty('itemUpgrades', 'upgradedItemID', item.id) | |||
if upgrade ~= nil then | |||
local reqs = nil | |||
if item.charges ~= nil and item.tier ~= nil then | |||
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, { | |||
['reqs'] = reqs, | |||
['costs'] = Common.getCostString({ ["items"] = upgrade.itemCosts, ["currencies"] = upgrade.currencyCosts }), | |||
['qty'] = (upgrade.quantity or 1), | |||
['source'] = '[[Upgrading Items|Item ' .. (upgrade.isDowngrade and 'Downgrade' or 'Upgrade') ..']]' | |||
}) | |||
end | |||
return tableData | |||
end | |||
function p.getItemUpgradeTable(frame) | |||
local itemName = frame.args ~= nil and frame.args[1] or frame | |||
local item = Items.getItem(itemName) | |||
if item == nil then | |||
return Shared.printError('No item named "' .. itemName .. '" exists in the data module') | |||
end | |||
return p.buildCreationTable(p._getItemUpgradeTableData(item), item) | |||
end | |||
function p._getSuperheatSmithRecipe(item) | |||
local smithRecipe = GameData.getEntityByProperty(SkillData.Smithing.recipes, 'productID', item.id) | |||
if smithRecipe ~= nil and smithRecipe.categoryID == 'melvorD:Bars' then | if smithRecipe ~= nil and smithRecipe.categoryID == 'melvorD:Bars' then | ||
return smithRecipe | return smithRecipe | ||
Line 1,399: | Line 1,481: | ||
end | end | ||
function p. | function p._getItemSuperheatTableData(item, tableData) | ||
if tableData == nil then tableData = {} end | |||
-- Validate that the item can be superheated | -- Validate that the item can be superheated | ||
local smithRecipe = p._getSuperheatSmithRecipe(item) | local smithRecipe = p._getSuperheatSmithRecipe(item) | ||
Line 1,407: | Line 1,490: | ||
end | end | ||
local oreStringPart, coalString = {}, '' | local oreStringPart, coalString, smithingReq = {}, '', Icons._SkillReq('Smithing', smithRecipe.level, false) | ||
for i, mat in ipairs(smithRecipe.itemCosts) do | for i, mat in ipairs(smithRecipe.itemCosts) do | ||
local matItem = Items.getItemByID(mat.id) | local matItem = Items.getItemByID(mat.id) | ||
if mat.id == 'melvorD:Coal_Ore' then | if mat.id == 'melvorD:Coal_Ore' then | ||
coalString = Icons.Icon({matItem.name, type='item | coalString = Icons.Icon({ matItem.name, type='item', qty=mat.quantity }) | ||
else | else | ||
table.insert(oreStringPart, Icons.Icon({matItem.name, type='item | table.insert(oreStringPart, Icons.Icon({ matItem.name, type='item', qty=mat.quantity })) | ||
end | end | ||
end | end | ||
--Loop through all the variants | --Loop through all the variants | ||
Line 1,431: | Line 1,505: | ||
if spell.specialCost ~= nil and Shared.contains({ 'BarIngredientsWithCoal', 'BarIngredientsWithoutCoal' }, spell.specialCost.type) then | if spell.specialCost ~= nil and Shared.contains({ 'BarIngredientsWithCoal', 'BarIngredientsWithoutCoal' }, spell.specialCost.type) then | ||
local imgType = Magic._getSpellIconType(spell) | local imgType = Magic._getSpellIconType(spell) | ||
local costs = table.concat(oreStringPart, '<br>') | |||
if spell.specialCost.type == 'BarIngredientsWithCoal' and coalString ~= '' then | if spell.specialCost.type == 'BarIngredientsWithCoal' and coalString ~= '' then | ||
costs = costs .. '<br>' .. coalString | |||
end | end | ||
table.insert(tableData, { | |||
['skill'] = 'Alt Magic', | |||
['lvl'] = spell.level, | |||
['reqs'] = smithingReq .. '<br>' .. Icons.Icon({'Alt Magic', type='skill', notext=true}) .. ' Level ' .. spell.level, | |||
['isAbyssal'] = false, | |||
['xp'] = spell.baseExperience, | |||
['costs'] = costs, | |||
['qty'] = spell.productionRatio, | |||
['source'] = Icons.Icon({ spell.name, type=imgType }), | |||
['time'] = 2, | |||
['runeCost'] = Magic._getSpellRunes(spell) | |||
}) | |||
end | |||
end | |||
return tableData | |||
end | end | ||
Line 1,455: | Line 1,536: | ||
end | end | ||
return p. | return p.buildCreationTable(p._getItemSuperheatTableData(item), item) | ||
end | end | ||
function p. | function p._getTownshipTraderTableData(item, tableData) | ||
if tableData == nil then tableData = {} end | |||
for i, tsResource in ipairs(SkillData.Township.itemConversions.fromTownship) do | for i, tsResource in ipairs(SkillData.Township.itemConversions.fromTownship) do | ||
for j, tradeDef in ipairs(tsResource.items) do | for j, tradeDef in ipairs(tsResource.items) do | ||
if tradeDef.itemID == item.id then | if tradeDef.itemID == item.id then | ||
-- Item found, | -- Item found, insert the data | ||
local res = GameData.getEntityByID(SkillData.Township.resources, tsResource.resourceID) | local res = GameData.getEntityByID(SkillData.Township.resources, tsResource.resourceID) | ||
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( | ['lvl'] = lvl, | ||
['reqs'] = Common.getRequirementString(tradeDef.unlockRequirements), | |||
['isAbyssal'] = namespace == 'melvorItA', | |||
['costs'] = Icons.Icon({ resName, qty=resQty, type='resource' }), | |||
['qty'] = 1, | |||
['source'] = Icons.Icon({ 'Township', 'Trader', type='skill' }), | |||
}) | |||
break | |||
end | end | ||
end | end | ||
end | end | ||
return | |||
return tableData | |||
end | end | ||
function p. | function p._getItemShopTableData(item, tableData) | ||
if tableData == nil then tableData = {} end | |||
local | |||
local purchaseArray = Shop.getItemSourceArray(item.id) | |||
local | for i, purchaseData in ipairs(purchaseArray) do | ||
local purchase = purchaseData.purchase | |||
local namespace, localID = Shared.getLocalID(purchase.id) | |||
local source = nil | |||
-- Show icon text when it's the only source of this item | |||
local notext = (Shared.tableCount(tableData) + Shared.tableCount(purchaseArray) > 1) | |||
if purchase.contains.items ~= nil and Shared.tableCount(purchase.contains.items) > 1 then | |||
source = Shop._getPurchaseExpansionIcon(purchase) .. Common.getPurchaseIcon({purchase}) | |||
-- Always show icon text when there's multiple items | |||
-- notext = false | |||
else | |||
source = Icons.Icon({'Shop'}) .. ' Purchase' | |||
end | |||
table.insert(tableData, { | |||
['reqs'] = Common.getRequirementString(purchase.purchaseRequirements), | |||
['isAbyssal'] = namespace == 'melvorItA', | |||
['costs'] = Common.getCostString(purchase.cost), | |||
['qty'] = purchaseData.qty, | |||
['contents'] = Shop._getPurchaseContents(purchase, true, notext), | |||
['source'] = source, | |||
['center'] = notext | |||
}) | |||
end | end | ||
return tableData | |||
end | |||
function p._getItemSourceTables(item) | |||
local resultPart = {} | |||
local sourceData = {} | |||
-- Is the item Created or Produced by anything? | |||
p._getCreationTableData(item, sourceData) | |||
-- Can the item be obtained by upgrading? | |||
p._getItemUpgradeTableData(item, sourceData) | |||
-- Can the item be obtained by Superheating? | |||
if p._getSuperheatSmithRecipe(item) ~= nil then | if p._getSuperheatSmithRecipe(item) ~= nil then | ||
p._getItemSuperheatTableData(item, sourceData) | |||
end | |||
-- Can the item be traded for in Township? | |||
p._getTownshipTraderTableData(item, sourceData) | |||
-- Can the item be purchased? | |||
p._getItemShopTableData(item, sourceData) | |||
local sourceTable = p.buildCreationTable(item, sourceData) | |||
if sourceTable ~= '' then | |||
table.insert(resultPart, '===Creation===\r\n' .. sourceTable) | |||
end | end | ||
Line 1,516: | Line 1,626: | ||
if lootTable ~= '' then | if lootTable ~= '' then | ||
if not Shared.tableIsEmpty(resultPart) 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 | ||
return table.concat(resultPart) | return table.concat(resultPart) | ||
end | end | ||
Line 1,618: | Line 1,729: | ||
if found ~= nil then | if found ~= nil then | ||
table.insert(resultArray, { | table.insert(resultArray, { | ||
id = digSite.id, | id = digSite.id, | ||
Line 1,625: | Line 1,734: | ||
level = digSite.level, | level = digSite.level, | ||
size = sizeName, | size = sizeName, | ||
minQty = | minQty = found.minQuantity, | ||
maxQty = | maxQty = found.maxQuantity, | ||
dropWt = found.weight, | dropWt = found.weight, | ||
totalWt = sizeWeight}) | totalWt = sizeWeight | ||
}) | |||
end | end | ||
end | end | ||
Line 1,646: | Line 1,756: | ||
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)", | ||
"Astrology Lesser Relic", | -- "Astrology Lesser Relic", | ||
"Mysterious Stone", | "Mysterious Stone", | ||
"Charge Stone of Rhaelyx", | |||
"Gold Topaz Ring", | "Gold Topaz Ring", | ||
"Charcoal", | "Charcoal", | ||
"Ash", | "Ash", | ||
"Coal Ore", | "Coal Ore", | ||
"Golden Star", | |||
"Potion Box III", | |||
"Rune Essence", | "Rune Essence", | ||
" | "Dragonite Bar", | ||
"Rune Platebody", | "Holy Dust", | ||
-- "Rune Platebody", | |||
"Arrow Shafts", | "Arrow Shafts", | ||
"Yew Longbow", | -- "Yew Longbow", | ||
" | "Blood Rune", | ||
"Steam Rune", | "Steam Rune", | ||
"Wolf", | -- "Wolf", | ||
"Fox", | "Fox", | ||
"Leprechaun", | "Leprechaun", | ||
"Void Wisp", | "Void Wisp", | ||
"Redwood Logs", | -- "Redwood Logs", | ||
"Shadow Raven Nest", | -- "Shadow Raven Nest", | ||
"Raw Shrimp", | "Raw Shrimp", | ||
"Shrimp", | "Shrimp", | ||
"Carrot Cake", | "Carrot Cake", | ||
"Carrot Cake (Perfect)", | -- "Carrot Cake (Perfect)", | ||
"Mantalyme Herb", | -- "Mantalyme Herb", | ||
"Carrot", | "Carrot", | ||
"Controlled Heat Potion II", | "Controlled Heat Potion II", | ||
"Topaz", | "Topaz", | ||
"Oricha", | "Oricha", | ||
"Nightopal", | "Nightopal", | ||
"Sanguine Blade", | -- "Sanguine Blade", | ||
"Ring of Power", | -- "Ring of Power", | ||
"Infernal Claw", | -- "Infernal Claw", | ||
"Chapeau Noir", | -- "Chapeau Noir", | ||
"Stardust", | "Stardust", | ||
"Golden Stardust", | "Golden Stardust", | ||
"Abyssal Stardust", | "Abyssal Stardust", | ||
"Rope", | "Rope", | ||
"Ancient Ring of Mastery", | "Ancient Ring of Mastery", | ||
"Mastery Token (Cooking)", | "Mastery Token (Cooking)", | ||
"Thief's Moneysack", | |||
-- "Slayer Deterer", | |||
"Paper", | |||
-- "Lemon", | |||
"Aranite Brush", | |||
"Charged Diamond Shard", | |||
"Barrier Dust", | |||
"Gloom Resin", | |||
"Gloom Amber", | |||
"Gloom Vine", | |||
"Gloom Vein Seed", | |||
"Elite Chest", | |||
"Gem Gloves", | "Gem Gloves", | ||
" | "Magic Bones", | ||
" | "Bowstring", | ||
" | "Superior Max Skillcape", | ||
" | "Abyssal Coin Contract II", | ||
" | "Dark Summon Consumable II", | ||
" | "Abyssal Slayer Gear Upgrade Kit", | ||
"Topaz Bolts (Enchanted)", | |||
" | "Summoning Shard (Black)", | ||
" | "Dragon Javelin", | ||
" | "Skillers Body", | ||
" | "Abyssal Compost", | ||
" | |||
} | } | ||
local checkFuncs = { | local checkFuncs = { | ||
p.getItemSourceTables, | |||
p.getCreationTable, | --p.getCreationTable, | ||
--p.getItemSources, | --p.getItemSources, | ||
--p.getItemLootSourceTable, | --p.getItemLootSourceTable, |