4,978
edits
m (Clean up chance data) |
(Add Harvesting item chance data; Change common FM products' input to 'Any Melvor Logs' instead of '1 Normal Logs'; Cleanup some stuff) |
||
Line 29: | Line 29: | ||
'Rune Essence', -- Multi source (Category, Time, XP, Qty, Runes) | 'Rune Essence', -- Multi source (Category, Time, XP, Qty, Runes) | ||
'Coal Ore', -- Multi source (Same Qty, everything else is different) | 'Coal Ore', -- Multi source (Same Qty, everything else is different) | ||
'Charcoal', | |||
'Ash', -- Requires a fix in _getCreationTable | 'Ash', -- Requires a fix in _getCreationTable | ||
'Withered Ash', -- Requires a fix in _getCreationTable | 'Withered Ash', -- Requires a fix in _getCreationTable | ||
'Eternal Ash', | |||
'Generous Fire Spirit', | 'Generous Fire Spirit', | ||
-- "Circlet of Rhaelyx", -- The following commented out items have no creation source data; This may appear later | -- "Circlet of Rhaelyx", -- The following commented out items have no creation source data; This may appear later | ||
Line 76: | Line 78: | ||
"Barrier Dust", | "Barrier Dust", | ||
-- "Shadow Raven Nest", | -- "Shadow Raven Nest", | ||
'Obsidian Vein Seed' | 'Obsidian Tendril', | ||
'Obsidian Bark', | |||
'Obsidian Thorn', | |||
'Obsidian Vein Seed', | |||
} | } | ||
Line 87: | Line 92: | ||
local function doesRecipeHaveItemID(recipe, itemID) | local function doesRecipeHaveItemID(recipe, itemID) | ||
if recipe.productId == itemID then | if recipe.productId == itemID then | ||
return true | return true | ||
elseif Shared.contains(recipe.primaryProducts, itemID) or Shared.contains(recipe.secondaryProducts, itemID) then | elseif Shared.contains(recipe.primaryProducts, itemID) or Shared.contains(recipe.secondaryProducts, itemID) then | ||
return true | return true | ||
elseif type(recipe.products) == 'table' then | elseif type(recipe.products) == 'table' then | ||
return GameData.getEntityByProperty(recipe.products, 'itemID', itemID) ~= nil | |||
end | end | ||
return false | return false | ||
end | end | ||
Line 149: | Line 146: | ||
local lvl, isAbyssal, xp, qty, source, costs, time, maxTime, chance = 0, false, 0, 0, nil, nil, 0, nil, nil | local lvl, isAbyssal, xp, qty, source, costs, time, maxTime, chance = 0, false, 0, 0, nil, nil, 0, nil, nil | ||
for i, recipe in ipairs(skillData[dataProp.recipeKey]) do | for i, recipe in ipairs(skillData[dataProp.recipeKey]) do | ||
local hasProduct | local hasProduct = doesRecipeHaveItemID(recipe, itemID) | ||
if hasProduct then | if hasProduct then | ||
lvl, isAbyssal = Skills.getRecipeLevelRealm(localSkillID, recipe) | lvl, isAbyssal = Skills.getRecipeLevelRealm(localSkillID, recipe) | ||
Line 182: | Line 179: | ||
end | end | ||
local costItem = Items.getItemByID(recipe.logID) | if Shared.contains({ 'melvorD:Generous_Fire_Spirit', 'melvorD:Coal_Ore', 'melvorTotH:Charcoal' }, itemID) then | ||
costs = 'Any {{Icon|Woodcutting|Melvor Logs|img=Melvor Realm|ext=svg|section=Logs}}' | |||
else | |||
local costItem = Items.getItemByID(recipe.logID) | |||
costs = Icons.Icon({ costItem.name, type='item', qty=1 }) | |||
end | |||
if itemID == 'melvorF:Ash' then | if itemID == 'melvorF:Ash' then | ||
qty = time | qty = time | ||
elseif itemID == 'melvorItA:Withered_Ash' then | elseif itemID == 'melvorItA:Withered_Ash' or itemID == 'melvorItA:Eternal_Ash' then | ||
qty = math.max(math.floor(recipe.abyssalLevel / 10), 1) | qty = math.max(math.floor(recipe.abyssalLevel / 10), 1) | ||
end | end | ||
elseif localSkillID == 'Cartography' then | elseif localSkillID == 'Cartography' then | ||
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 | elseif localSkillID == 'Harvesting' then | ||
chance = ' | local totalWeight = 0 | ||
local itemChanceData = nil | |||
for i, product in ipairs(recipe.products) do | |||
totalWeight = totalWeight + (product.weight or 0) | |||
if product.itemID == itemID then itemChanceData = product end | |||
end | |||
if itemChanceData ~= nil then | |||
chance = Num.round2((itemChanceData.weight / totalWeight * 100), 2) .. '%' | |||
specialReq = itemChanceData.minIntensityPercent .. '% ' .. Icons.Icon({ recipe.name, type='vein', notext=true }) .. ' Intensity' | |||
end | |||
end | end | ||
-- Special requirements | -- Special requirements | ||
if recipe.totalMasteryRequired ~= nil then | if recipe.totalMasteryRequired ~= nil then | ||
specialReq = Icons.Icon({'Mastery', notext=true}) .. Num.formatnum(recipe.totalMasteryRequired) .. ' total [[' .. skill .. ']] [[Mastery]]' | specialReq = Icons.Icon({ 'Mastery', notext=true }) .. ' ' .. Num.formatnum(recipe.totalMasteryRequired) .. ' total [[' .. skill .. ']] [[Mastery]]' | ||
end | end | ||
table.insert(tableData, { | table.insert(tableData, { | ||
Line 215: | Line 228: | ||
-- Most recipes have a single item source or the item source data | -- Most recipes have a single item source or the item source data | ||
-- is nearly all the same. The following items have some uniqueness | -- is nearly all the same. The following items have some uniqueness | ||
if not Shared.contains({'melvorF:Ash', 'melvorItA:Withered_Ash', 'melvorAoD:Paper'}, itemID) then break end | if not Shared.contains({ 'melvorF:Ash', 'melvorItA:Withered_Ash', 'melvorAoD:Paper' }, itemID) then break end | ||
end | end | ||
end | end | ||
Line 283: | Line 296: | ||
local shard = Items.getItemByID(itemCost.id) | local shard = Items.getItemByID(itemCost.id) | ||
if shard ~= nil then | if shard ~= nil then | ||
table.insert(shardCostArray, Icons.Icon({shard.name, type='item', qty=itemCost.quantity})) | table.insert(shardCostArray, Icons.Icon({ shard.name, type='item', qty=itemCost.quantity })) | ||
end | end | ||
end | end | ||
-- Other costs | -- Other costs | ||
table.insert(otherCostArray, Common.getCostString({ ["items"] = {}, ["currencies"] = recipe.currencyCosts})) | table.insert(otherCostArray, Common.getCostString({ ["items"] = {}, ["currencies"] = recipe.currencyCosts })) | ||
for j, nonShardID in ipairs(recipe.nonShardItemCosts) do | for j, nonShardID in ipairs(recipe.nonShardItemCosts) do | ||
local nonShard = Items.getItemByID(nonShardID) | local nonShard = Items.getItemByID(nonShardID) | ||
Line 293: | Line 306: | ||
local itemValue = math.max(nonShard.sellsFor, 20) | local itemValue = math.max(nonShard.sellsFor, 20) | ||
local nonShardQty = math.max(1, math.ceil(recipeCost / itemValue)) | local nonShardQty = math.max(1, math.ceil(recipeCost / itemValue)) | ||
table.insert(otherCostArray, Icons.Icon({nonShard.name, type='item', qty=nonShardQty})) | table.insert(otherCostArray, Icons.Icon({ nonShard.name, type='item', qty=nonShardQty })) | ||
end | end | ||
end | end | ||
Line 301: | Line 314: | ||
costs = costs .. '<br>' .. (costLen == 1 and '' or 'and one of the following:<br>') .. table.concat(otherCostArray, "<br>'''OR''' ") | costs = costs .. '<br>' .. (costLen == 1 and '' or 'and one of the following:<br>') .. table.concat(otherCostArray, "<br>'''OR''' ") | ||
end | end | ||
specialReq = 'At least 1 ' .. Icons.Icon({'Summoning%23Summoning Marks', item.name, img=item.name, type='mark'}) .. ' mark discovered' | specialReq = 'At least 1 ' .. Icons.Icon({ 'Summoning%23Summoning Marks', item.name, img=item.name, type='mark' }) .. ' mark discovered' | ||
table.insert(tableData, { | table.insert(tableData, { | ||
['skill'] = skill, | ['skill'] = skill, | ||
Line 311: | Line 324: | ||
['source'] = source, | ['source'] = source, | ||
['time'] = time, | ['time'] = time, | ||
['specialReq'] = specialReq | ['specialReq'] = specialReq | ||
}) | }) | ||
Line 325: | Line 337: | ||
costsStr = costsStr .. itemCost.quantity .. 'x ?????' | costsStr = costsStr .. itemCost.quantity .. 'x ?????' | ||
else | else | ||
costsStr = costsStr .. Icons.Icon({reqItem.name, type='item', qty=itemCost.quantity}) | costsStr = costsStr .. Icons.Icon({ reqItem.name, type='item', qty=itemCost.quantity }) | ||
end | end | ||
end | end | ||
costsStr = costsStr .. (Common.getCostString({ ["items"] = {}, ["currencies"] = recipe.currencyCosts}) or '') | costsStr = costsStr .. (Common.getCostString({ ["items"] = {}, ["currencies"] = recipe.currencyCosts }) or '') | ||
table.insert(tableData, { | table.insert(tableData, { | ||
['skill'] = skill, | ['skill'] = skill, | ||
Line 376: | Line 388: | ||
['source'] = Icons.Icon({ altSpell.name, type='spell' }), | ['source'] = Icons.Icon({ altSpell.name, type='spell' }), | ||
['time'] = 2, | ['time'] = 2, | ||
['altCosts'] = Magic._getAltSpellCostText(altSpell) | ['altCosts'] = Magic._getAltSpellCostText(altSpell) | ||
}) | }) | ||
Line 388: | Line 399: | ||
local namespace, localID = Shared.getLocalID(stardustChanceData.itemID) | local namespace, localID = Shared.getLocalID(stardustChanceData.itemID) | ||
local isAbyssal = namespace == 'melvorItA' | local isAbyssal = namespace == 'melvorItA' | ||
table.insert(tableData, { | table.insert(tableData, { | ||
['skill'] = 'Astrology', | ['skill'] = 'Astrology', | ||
['lvl'] = 1, | ['lvl'] = 1, | ||
['isAbyssal'] = isAbyssal, | ['isAbyssal'] = isAbyssal, | ||
['xp'] = (isAbyssal and 1238 or 5), | ['xp'] = (isAbyssal and 1238 or 5), -- Use the (A)XP value for the first (abyssal) 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 '')}), | ||
['time'] = 3, | ['time'] = 3, | ||
Line 403: | Line 413: | ||
return '' | return '' | ||
else | else | ||
return p. | return p.buildCreationTable(tableData, item) | ||
end | end | ||
end | end | ||
function p. | function p.buildCreationTable(tableData, item) | ||
local showSource = false | local showSource = false | ||
local showRequirements = false | local showRequirements = false | ||
Line 450: | Line 425: | ||
local showTime = false | local showTime = false | ||
local showChance = false | local showChance = false | ||
local colspan = -1 | local colspan = -1 -- colspan only needs to be set when there are 3+ columns in the table | ||
for i, data in ipairs(tableData) do | for i, data in ipairs(tableData) do | ||
Line 483: | Line 458: | ||
end | end | ||
local function addCostsRow(row, data, | colspan = math.max(colspan, 1) | ||
local costsRow = row:tag('td'):attr('colspan', | |||
local function addCostsRow(row, data, span) | |||
local costsRow = row:tag('td'):attr('colspan', span) | |||
if type(data.costs) == 'table' then | if type(data.costs) == 'table' then | ||
for i, mat in ipairs(data.costs) do | for i, mat in ipairs(data.costs) do | ||
Line 544: | Line 521: | ||
if showXP then recipeRow:tag('td'):wikitext(Icons.Icon({data.skill, (Num.formatnum(data.xp) .. (data.isAbyssal and ' AXP' or ' XP')), nolink=true, type='skill'})) end | if showXP then recipeRow:tag('td'):wikitext(Icons.Icon({data.skill, (Num.formatnum(data.xp) .. (data.isAbyssal and ' AXP' or ' XP')), nolink=true, type='skill'})) end | ||
if showTime then recipeRow:tag('td'):wikitext(Shared.timeString(data.time, true)):addClass('center') end | if showTime then recipeRow:tag('td'):wikitext(Shared.timeString(data.time, true)):addClass('center') end | ||
if showChance then recipeRow:tag('td'):wikitext(data.chance):addClass('center') end | if showChance then recipeRow:tag('td'):wikitext((data.chance or '100%')):addClass('center') end | ||
end | end | ||
Line 552: | Line 529: | ||
:wikitext('Source') | :wikitext('Source') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
:tag('td'):attr('colspan', | :tag('td'):attr('colspan', colspan):wikitext(tableData[1].source) | ||
end | end | ||
if not showRequirements and tableData[1].skill ~= nil and tableData[1].lvl ~= nil then | if not showRequirements and tableData[1].skill ~= nil and tableData[1].lvl ~= nil then | ||
Line 559: | Line 536: | ||
:wikitext('Requires') | :wikitext('Requires') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
local reqData = reqRow:tag('td'):attr('colspan', | local reqData = reqRow:tag('td'):attr('colspan', colspan) | ||
reqData:wikitext(Icons._SkillReq(tableData[1].skill, tableData[1].lvl, false, (tableData[1].isAbyssal and "melvorItA:Abyssal" or nil))) | reqData:wikitext(Icons._SkillReq(tableData[1].skill, tableData[1].lvl, false, (tableData[1].isAbyssal and "melvorItA:Abyssal" or nil))) | ||
Line 579: | Line 556: | ||
:wikitext('Outputs') | :wikitext('Outputs') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
:tag('td'):attr('colspan', | :tag('td'):attr('colspan', colspan):wikitext(Icons.Icon({ item.name, type='item', qty=tableData[1].qty })) | ||
end | end | ||
if not showXP and tableData[1].xp ~= nil then | if not showXP and tableData[1].xp ~= nil then | ||
Line 586: | Line 563: | ||
:wikitext('Base Exp') | :wikitext('Base Exp') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
:tag('td'):attr('colspan', | :tag('td'):attr('colspan', colspan):wikitext(Num.formatnum(tableData[1].xp) .. (tableData[1].isAbyssal and ' AXP' or ' XP')) | ||
end | end | ||
if not showTime and tableData[1].time ~= nil then | if not showTime and tableData[1].time ~= nil then | ||
Line 595: | Line 572: | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
local timeData = timeHeader:tag('td'):attr('colspan', | local timeData = timeHeader:tag('td'):attr('colspan', colspan) | ||
timeData:wikitext(Shared.timeString(tableData[1].time, true)) | timeData:wikitext(Shared.timeString(tableData[1].time, true)) | ||
Line 607: | Line 584: | ||
:wikitext('Base Chance') | :wikitext('Base Chance') | ||
:css('text-align', 'right') | :css('text-align', 'right') | ||
:tag('td'):attr('colspan', | :tag('td'):attr('colspan', colspan):wikitext(tableData[1].chance) | ||
end | end | ||
return '==' .. item.name .. '==\n' .. tostring(resultTable) -- TODO: Remove item.name header | return '==' .. item.name .. '==\n' .. tostring(resultTable) -- TODO: Remove item.name header | ||
end | end | ||