Anonymous

Module:Skills: Difference between revisions

From Melvor Idle
2,892 bytes added ,  27 December 2022
Use printError function
(Update for v1.0.2)
(Use printError function)
(20 intermediate revisions by 2 users not shown)
Line 1: Line 1:
--This module should avoid including skill specific functions which generate
--output for wiki pages, especially those which require() other modules. For
--these functions, consider using the appropriate module from the below list.
--Some skills have their own modules:
--Some skills have their own modules:
--Module:Magic for Magic
--Module:Magic for Magic
Line 11: Line 15:


local p = {}
local p = {}
local ItemData = mw.loadData('Module:Items/data')
local SkillData = mw.loadData('Module:Skills/data')


local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Items = require('Module:Items')
local Items = require('Module:Items')
local ItemSourceTables = require('Module:Items/SourceTables')
local Icons = require('Module:Icons')
local Icons = require('Module:Icons')


local MasteryCheckpoints = {.1, .25, .5, .95}
-- Thieving
function p.getThievingNPCByID(npcID)
return GameData.getEntityByID(SkillData.Thieving.npcs, npcID)
end
 
function p.getThievingNPC(npcName)
return GameData.getEntityByName(SkillData.Thieving.npcs, npcName)
end
 
function p.getThievingNPCArea(npc)
for i, area in ipairs(SkillData.Thieving.areas) do
for j, npcID in ipairs(area.npcIDs) do
if npcID == npc.id then
return area
end
end
end
end
 
function p._getThievingNPCStat(npc, statName)
local result = nil
 
if statName == 'level' then
result = Icons._SkillReq('Thieving', npc.level)
elseif statName == 'maxHit' then
result = npc.maxHit * 10
elseif statName == 'area' then
local area = p.getThievingNPCArea(npc)
result = area.name
else
result = npc[statName]
end
 
if result == nil then
result = ''
end
 
return result
end
 
function p.getThievingNPCStat(frame)
local npcName = frame.args ~= nil and frame.args[1] or frame[1]
local statName = frame.args ~= nil and frame.args[2] or frame[2]
local npc = p.getThievingNPC(npcName)
if npc == nil then
return Shared.printError('Invalid Thieving NPC ' .. npcName)
end
 
return p._getThievingNPCStat(npc, statName)
end
 
function p.getThievingSourcesForItem(itemID)
local resultArray = {}
local areaNPCs = {}
 
--First check area unique drops
--If an area drops the item, add all the NPC ids to the list so we can add them later
for i, area in pairs(SkillData.Thieving.areas) do
for j, drop in pairs(area.uniqueDrops) do
if drop.id == itemID then
for k, npcID in ipairs(area.npcIDs) do
areaNPCs[npcID] = drop.quantity
end
break
end
end
end
 
--Now go through and get drop chances on each NPC if needed
for i, npc in pairs(SkillData.Thieving.npcs) do
local totalWt = 0
local dropWt = 0
local dropQty = { min = 0, max = 0 }
for j, drop in ipairs(npc.lootTable) do
totalWt = totalWt + drop.weight
if drop.itemID == itemID then
dropWt = drop.weight
dropQty = { min = drop.minQuantity, max = drop.maxQuantity }
end
end
if dropWt > 0 then
table.insert(resultArray, {npc = npc.name, minQty = dropQty.min, maxQty = dropQty.max, wt = dropWt * SkillData.Thieving.itemChance, totalWt = totalWt * 100, level = npc.level, npcID = npc.id})
end
 
--Chance of -1 on unique drops is to indicate variable chance
if npc.uniqueDrop ~= nil and npc.uniqueDrop.id == itemID then
table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.quantity, maxQty = npc.uniqueDrop.quantity, wt = -1, totalWt = -1, level = npc.level, npcID = npc.id})
end
 
if areaNPCs[npc.id] ~= nil then
table.insert(resultArray, {npc = npc.name, minQty = areaNPCs[npc.id], maxQty = areaNPCs[npc.id], wt = SkillData.Thieving.baseAreaUniqueChance, totalWt = 100, level = npc.level, npcID = npc.id})
end
end
 
for i, drop in ipairs(SkillData.Thieving.generalRareItems) do
if drop.itemID == itemID then
if drop.npcs == nil then
table.insert(resultArray, {npc = 'all', minQty = 1, maxQty = 1, wt = 1, totalWt = Shared.round2(1/(drop.chance/100), 0), level = 1, npcID = itemID})
else
for j, npcID in ipairs(drop.npcs) do
local npc = p.getThievingNPCByID(npcID)
if npc ~= nil then
table.insert(resultArray, {npc = npc.name, minQty = 1, maxQty = 1, wt = 1, totalWt = Shared.round2(1/(drop.chance/100), 0), level = npc.level, npcID = npc.id})
end
end
end
end
end
 
return resultArray
end
 
-- Astrology
function p.getConstellationByID(constID)
return GameData.getEntityByID(SkillData.Astrology.recipes, constID)
end
 
function p.getConstellation(constName)
return GameData.getEntityByName(SkillData.Astrology.recipes, constName)
end
 
function p.getConstellations(checkFunc)
return GameData.getEntities(SkillData.Astrology.recipes, checkFunc)
end
 
-- For a given constellation cons and modifier value modValue, generates and returns
-- a table of modifiers, much like any other item/object elsewhere in the game.
-- includeStandard: true|false, determines whether standard modifiers are included
-- includeUnique: true|false, determines whether unique modifiers are included
-- isDistinct: true|false, if true, the returned list of modifiers is de-duplicated
-- asKeyValue: true|false, if true, returns key/value pairs like usual modifier objects
function p._buildAstrologyModifierArray(cons, modValue, includeStandard, includeUnique, isDistinct, asKeyValue)
-- Temporary function to determine if the table already contains a given modifier
local containsMod = function(modList, modNew)
for i, modItem in ipairs(modList) do
-- Check mod names & value data types both equal
if modItem[1] == modNew[1] and type(modItem[2]) == type(modNew[2]) then
if type(modItem[2]) == 'table' then
if Shared.tablesEqual(modItem[2], modNew[2]) then
return true
end
elseif modItem[2] == modNew[2] then
return true
end
end
end
return false
end
 
local addToArray = function(modArray, modNew)
if not isDistinct or (isDistinct and not containsMod(modArray, modNew)) then
table.insert(modArray, modNew)
end
end
 
local modTypes = {}
if includeStandard then
table.insert(modTypes, 'standardModifiers')
end
if includeUnique then
table.insert(modTypes, 'uniqueModifiers')
end
 
local modArray = {}
local isSkillMod = {}
--Adding a Group Number to hold together different bonuses from the same modifier [Falterfire 22/10/27]
local groupNum = 0
 
for _, modType in ipairs(modTypes) do
for i, modTypeData in ipairs(cons[modType]) do
groupNum = groupNum + 1
local modVal = nil
if modValue ~= nil then
modVal = modValue
else
modVal = modTypeData.incrementValue * modTypeData.maxCount
end
for j, modifier in ipairs(modTypeData.modifiers) do
local modEntry = (modifier.skill ~= nil and { skillID = modifier.skill, value = modVal }) or modVal
addToArray(modArray, {modifier.key, modEntry, group = groupNum})
end
end
end


if asKeyValue then
local modArrayKV = {}
for i, modDefn in ipairs(modArray) do
local modName, modVal = modDefn[1], modDefn[2]
local isSkill = type(modVal) == 'table' and modVal.skillID ~= nil
if modArrayKV[modName] == nil then
modArrayKV[modName] = (isSkill and { modVal } or modVal)
elseif isSkill then
table.insert(modArrayKV[modName], modVal)
else
modArrayKV[modName] = modArrayKV[modName] + modVal
end
end
return modArrayKV
else
return modArray
end
end
-- Mastery
function p.getMasteryUnlockTable(frame)
function p.getMasteryUnlockTable(frame)
local skillName = frame.args ~= nil and frame.args[1] or frame
local skillName = frame.args ~= nil and frame.args[1] or frame
local skillID = Constants.getSkillID(skillName)
local skillID = Constants.getSkillID(skillName)
if skillID == nil then
if skillID == nil then
return "ERROR: Failed to find a skill ID for "..skillName
return Shared.printError('Failed to find a skill ID for ' .. skillName)
end
end


local unlockTable = SkillData.MasteryUnlocks[skillID]
local _, localSkillID = GameData.getLocalID(skillID)
-- Clone so that we can sort by level
local unlockTable = Shared.clone(SkillData[localSkillID].masteryLevelUnlocks)
if unlockTable == nil then
if unlockTable == nil then
return 'ERROR: Failed to find Mastery Unlock data for '..skillName
return Shared.printError('Failed to find Mastery Unlock data for ' .. skillName)
end
end
table.sort(unlockTable, function(a, b) return (a.level == b.level and a.descriptionID < b.descriptionID) or a.level < b.level end)


local result = '{|class="wikitable"\r\n!Level!!Unlock'
local result = '{|class="wikitable"\r\n!Level!!Unlock'
for i, unlock in Shared.skpairs(unlockTable) do
for i, unlock in ipairs(unlockTable) do
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|'..unlock.level..'||'..unlock.unlock
result = result..'\r\n|'..unlock.level..'||'..unlock.description
end
end
result = result..'\r\n|}'
result = result..'\r\n|}'
Line 48: Line 255:
local skillID = Constants.getSkillID(skillName)
local skillID = Constants.getSkillID(skillName)
if skillID == nil then
if skillID == nil then
return "ERROR: Failed to find a skill ID for "..skillName
return Shared.printError('Failed to find a skill ID for ' .. skillName)
end
end


if SkillData.MasteryCheckpoints[skillID] == nil then
local _, localSkillID = GameData.getLocalID(skillID)
return 'ERROR: Failed to find Mastery Unlock data for '..skillName
local checkpoints = SkillData[localSkillID].masteryCheckpoints
if checkpoints == nil then
return Shared.printError('Failed to find Mastery Unlock data for ' .. skillName)
end
end


local bonuses = SkillData.MasteryCheckpoints[skillID].bonuses
local totalPoolXP = SkillData[localSkillID].baseMasteryPoolCap
local totalPoolXP = SkillData.MasteryPoolXP[skillID + 1]
local checkpointPct = GameData.rawData.masteryCheckpoints
 
local result = '{|class="wikitable"\r\n!Pool %!!style="width:100px"|Pool XP!!Bonus'
local result = '{|class="wikitable"\r\n!Pool %!!style="width:100px"|Pool XP!!Bonus'
for i, bonus in Shared.skpairs(bonuses) do
for i, checkpointDesc in ipairs(checkpoints) do
result = result..'\r\n|-'
result = result..'\r\n|-'
result = result..'\r\n|'..(MasteryCheckpoints[i] * 100)..'%||'
result = result..'\r\n|'..checkpointPct[i]..'%||'
result = result..Shared.formatnum(totalPoolXP * MasteryCheckpoints[i])..' xp||'..bonus
result = result..Shared.formatnum(math.floor(totalPoolXP * checkpointPct[i] / 100))..' xp||'..checkpointDesc
end
end
result = result..'\r\n|-\r\n!colspan="2"|Total Mastery Pool XP'
result = result..'\r\n|-\r\n!colspan="2"|Total Mastery Pool XP'
Line 70: Line 278:
end
end


function p._getFarmingTable(category)
function p.getMasteryTokenTable()
local seedList = {}
local baseTokenChance = 18500
if category == 'Allotment' or category == 'Herb' or category == 'Tree' then
local masterySkills = {}
seedList = Items.getItems(function(item) return item.tier == category end)
local CCI = Items.getItemByID('melvorD:Clue_Chasers_Insignia')
else
if CCI == nil then return '' end
return 'ERROR: Invalid farming category. Please choose Allotment, Herb, or Tree'
end
 
local result = '{|class="wikitable sortable stickyHeader"'
result = result..'\r\n|- class="headerRow-0"'
result = result..'\r\n!colspan=2|Seeds!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level'
result = result..'!!XP!!Growth Time!!Seed Value'
if category == 'Allotment' then
result = result..'!!colspan="2"|Crop!!Crop Healing!!Crop Value'
elseif category == 'Herb' then
result = result..'!!colspan="2"|Herb!!Herb Value'
elseif category == 'Tree' then
result = result..'!!colspan="2"|Logs!!Log Value'
end
result = result..'!!Seed Sources'


table.sort(seedList, function(a, b) return a.farmingLevel < b.farmingLevel end)
-- Build table of mastery skills
 
for skillLocalID, skill in pairs(SkillData) do
for i, seed in pairs(seedList) do
if skill.masteryTokenID ~= nil then
result = result..'\r\n|-'
table.insert(masterySkills, skill)
result = result..'\r\n|'..Icons.Icon({seed.name, type='item', size='50', notext=true})..'||[['..seed.name..']]'
result = result..'||'..seed.farmingLevel..'||'..Shared.formatnum(seed.farmingXP)
result = result..'||data-sort-value="'..seed.timeToGrow..'"|'..Shared.timeString(seed.timeToGrow, true)
result = result..'||data-sort-value="'..seed.sellsFor..'"|'..Icons.GP(seed.sellsFor)
 
local crop = Items.getItemByID(seed.grownItemID)
result = result..'||'..Icons.Icon({crop.name, type='item', size='50', notext=true})..'||[['..crop.name..']]'
if category == 'Allotment' then
result = result..'||'..Icons.Icon({'Hitpoints', type='skill', notext=true})..' '..(crop.healsFor * 10)
end
end
result = result..'||data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
result = result..'||'..ItemSourceTables._getItemSources(seed)
end
end
table.sort(masterySkills,
function(a, b)
if a.milestoneCount == b.milestoneCount then
return a.name < b.name
else
return a.milestoneCount > b.milestoneCount
end
end)


result = result..'\r\n|}'
-- Generate output table
return result
local resultPart = {}
end
local CCIIcon = Icons.Icon({CCI.name, type='item', notext=true})


function p.getFarmingTable(frame)
table.insert(resultPart, '{| class="wikitable sortable"')
local category = frame.args ~= nil and frame.args[1] or frame
table.insert(resultPart, '\r\n!rowspan="2"|Token!!rowspan="2"|Skill!!colspan="2"|Approximate Mastery Token Chance')
table.insert(resultPart, '\r\n|-\r\n!Without ' .. CCIIcon .. '!!With ' .. CCIIcon)


return p._getFarmingTable(category)
for i, skill in ipairs(masterySkills) do
end
local token = Items.getItemByID(skill.masteryTokenID)
local denom = math.floor(baseTokenChance / skill.milestoneCount)
local denomCCI = Shared.round(baseTokenChance / (skill.milestoneCount * (1 + CCI.modifiers.increasedOffItemChance / 100)), 0, 0)


function p.getFarmingFoodTable(frame)
table.insert(resultPart, '\r\n|-')
local result = '{| class="wikitable sortable stickyHeader"'
table.insert(resultPart, '\r\n|style="text-align:center"|' .. Icons.Icon({token.name, type='item', size=50, notext=true}))
result = result..'\r\n|- class="headerRow-0"'
table.insert(resultPart, '\r\n|' .. Icons.Icon({skill.name, type='skill'}))
result = result..'\r\n!colspan="2"|Crop!!'..Icons.Icon({"Farming", type="skill", notext=true})..' Level'
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. denom .. '"|1/' .. Shared.formatnum(denom))
result = result..'!!Healing!!Value'
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. denomCCI .. '"|1/' .. Shared.formatnum(denomCCI))
 
local itemArray = Items.getItems(function(item) return item.grownItemID ~= nil end)
 
table.sort(itemArray, function(a, b) return a.farmingLevel < b.farmingLevel end)
 
for i, item in Shared.skpairs(itemArray) do
local crop = Items.getItemByID(item.grownItemID)
if crop.healsFor ~= nil and crop.healsFor > 0 then
result = result..'\r\n|-'
result = result..'\r\n|'..Icons.Icon({crop.name, type='item', notext='true', size='50'})..'||[['..crop.name..']]'
result = result..'||style="text-align:right;"|'..item.farmingLevel
result = result..'||style="text-align:right" data-sort-value="'..crop.healsFor..'"|'..Icons.Icon({"Hitpoints", type="skill", notext=true})..' '..(crop.healsFor * 10)
result = result..'||style="text-align:right" data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
end
end
end
table.insert(resultPart, '\r\n|}')


result = result..'\r\n|}'
return table.concat(resultPart)
end


return result
-- Skill unlock costs for Adventure game mode
end
function p.getSkillUnlockCostTable()
local advMode = GameData.getEntityByID('gamemodes', 'melvorF:Adventure')
if advMode ~= nil then
local unlockCount = Shared.tableCount(GameData.skillData) - Shared.tableCount(advMode.startingSkills)
local costLength = Shared.tableCount(advMode.skillUnlockCost)
local returnPart = {}
table.insert(returnPart, '{| class="wikitable stickyHeader"\r\n|- class="headerRow-0"\r\n!Unlock!!Cost!!Cumulative Cost')


function p.getFarmingPlotTable(frame)
local accCost = 0
local areaName = frame.args ~= nil and frame.args[1] or frame
for i = 1, unlockCount, 1 do
local patches = nil
local cost = advMode.skillUnlockCost[math.min(i, costLength)]
for i, area in Shared.skpairs(SkillData.Farming.Patches) do
accCost = accCost + cost
if area.areaName == areaName then
table.insert(returnPart, '|-')
patches = area.patches
table.insert(returnPart, '|' .. i .. '||' .. Icons.GP(cost) .. '||' .. Icons.GP(accCost))
break
end
end
end
table.insert(returnPart, '|}')
if patches == nil then
return "ERROR: Invalid area name.[[Category:Pages with script errors]]"
end
 
local result = '{|class="wikitable"'
result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'


for i, patch in Shared.skpairs(patches) do
return table.concat(returnPart, '\r\n')
result = result..'\r\n|-\r\n|'..i
result = result..'||style="text-align:right;" data-sort-value="' .. patch.level .. '"|'..patch.level
if patch.cost == 0 then
result = result..'||Free'
else
result = result..'||style="text-align:right;" data-sort-value="'..patch.cost..'"|'..Icons.GP(patch.cost)
end
end
end
result = result..'\r\n|}'
return result
end
end


-- Accepts 1 parameter, being either:
-- Accepts 1 parameter, being either:
--  'Smelting', for which a table of all bars is generated, or
--  'Bars', for which a table of all bars is generated, or
--  A bar or tier name, which if valid generates a table of all smithing recipes using that bar/tier
--  A bar or tier name, which if valid generates a table of all smithing recipes using that bar/tier
function p.getSmithingTable(frame)
function p.getSmithingTable(frame)
local tableType = frame.args ~= nil and frame.args[1] or frame
local tableType = frame.args ~= nil and frame.args[1] or frame
tableType = Shared.splitString(tableType, ' ')[1]
 
-- Translates Smithing category names to Smithing recipe data categories
-- Has a valid category been passed (by name)?
local categoryMap = {
local category = GameData.getEntityByName(SkillData.Smithing.categories, tableType)
['Smelting'] = 0,
if category == nil then
['Bronze'] = 1,
return Shared.printError('Invalid Smithing category: "' .. tableType .. '"')
['Iron'] = 2,
['Steel'] = 3,
['Mithril'] = 4,
['Adamant'] = 5,
['Adamantite'] = 5,
['Rune'] = 6,
['Runite'] = 6,
['Dragon'] = 7,
['Dragonite'] = 7
}
local categoryID = categoryMap[tableType]
if categoryID == nil then
return 'ERROR: Invalid Smithing category: "' .. tableType .. '"[[Category:Pages with script errors]]'
end
end


Line 203: Line 360:
-- The bar list will be used later for value/bar calculations
-- The bar list will be used later for value/bar calculations
local recipeList, barIDList = {}, {}
local recipeList, barIDList = {}, {}
for i, recipe in ipairs(SkillData.Smithing.Recipes) do
for i, recipe in ipairs(SkillData.Smithing.recipes) do
if recipe.category == categoryID then
if recipe.categoryID == category.id then
local recipeItem = Items.getItemByID(recipe.itemID)
local recipeItem = Items.getItemByID(recipe.productID)
if recipeItem ~= nil then
if recipeItem ~= nil then
table.insert(recipeList, { id = i, level = recipe.level, itemName = recipeItem.name, itemValue = recipeItem.sellsFor })
table.insert(recipeList, { id = i, level = recipe.level, itemName = recipeItem.name, itemValue = recipeItem.sellsFor, expIcon = Icons.getExpansionIcon(recipeItem.id) })
end
end
elseif recipe.category == 0 then
elseif recipe.categoryID == 'melvorD:Bars' then
barIDList[recipe.itemID] = true
barIDList[recipe.productID] = true
end
end
end
end
Line 220: Line 377:
table.insert(resultPart, '\r\n!Item!!Name!!'..Icons.Icon({'Smithing', type='skill', notext=true})..' Level!!XP!!Value!!Ingredients')
table.insert(resultPart, '\r\n!Item!!Name!!'..Icons.Icon({'Smithing', type='skill', notext=true})..' Level!!XP!!Value!!Ingredients')
--Adding value/bar for things other than smelting
--Adding value/bar for things other than smelting
if categoryID > 0 then
if category.id ~= 'melvorD:Bars' then
table.insert(resultPart, '!!Value/Bar')
table.insert(resultPart, '!!Value/Bar')
end
end
Line 233: Line 390:


for i, recipeDef in ipairs(recipeList) do
for i, recipeDef in ipairs(recipeList) do
local recipe = SkillData.Smithing.Recipes[recipeDef.id]
local recipe = SkillData.Smithing.recipes[recipeDef.id]
local totalValue = recipe.baseQuantity * recipeDef.itemValue
local totalValue = recipe.baseQuantity * recipeDef.itemValue
-- Determine the bar quantity & build the recipe cost string
-- Determine the bar quantity & build the recipe cost string
Line 240: Line 397:
local costItem = Items.getItemByID(itemCost.id)
local costItem = Items.getItemByID(itemCost.id)
if costItem ~= nil then
if costItem ~= nil then
table.insert(costString, Icons.Icon({costItem.name, type='item', qty=itemCost.qty, notext=true}))
table.insert(costString, Icons.Icon({costItem.name, type='item', qty=itemCost.quantity, notext=true}))
end
end
if barIDList[itemCost.id] then
if barIDList[itemCost.id] then
barQty = barQty + itemCost.qty
barQty = barQty + itemCost.quantity
end
end
end
end
Line 250: Line 407:
table.insert(resultPart, '\r\n| ' .. Icons.Icon({recipeDef.itemName, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n| ' .. Icons.Icon({recipeDef.itemName, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n| ')
table.insert(resultPart, '\r\n| ')
table.insert(resultPart, recipeDef.expIcon)
if recipe.baseQuantity > 1 then
if recipe.baseQuantity > 1 then
table.insert(resultPart, recipe.baseQuantity .. 'x ')
table.insert(resultPart, recipe.baseQuantity .. 'x ')
Line 255: Line 413:
table.insert(resultPart, Icons.Icon({recipeDef.itemName, type='item', noicon=true}))
table.insert(resultPart, Icons.Icon({recipeDef.itemName, type='item', noicon=true}))
table.insert(resultPart, '\r\n|data-sort-value="' .. recipe.level .. '"| ' .. Icons._SkillReq('Smithing', recipe.level))
table.insert(resultPart, '\r\n|data-sort-value="' .. recipe.level .. '"| ' .. Icons._SkillReq('Smithing', recipe.level))
table.insert(resultPart, '\r\n|data-sort-value="' .. recipe.baseXP .. '"| ' .. Shared.formatnum(recipe.baseXP))
table.insert(resultPart, '\r\n|data-sort-value="' .. recipe.baseExperience .. '"| ' .. Shared.formatnum(recipe.baseExperience))
table.insert(resultPart, '\r\n|data-sort-value="' .. totalValue .. '"| ' .. Icons.GP(recipeDef.itemValue))
table.insert(resultPart, '\r\n|data-sort-value="' .. totalValue .. '"| ' .. Icons.GP(recipeDef.itemValue))
if recipe.baseQuantity > 1 then
if recipe.baseQuantity > 1 then
Line 261: Line 419:
end
end
table.insert(resultPart, '\r\n| ' .. table.concat(costString, ', '))
table.insert(resultPart, '\r\n| ' .. table.concat(costString, ', '))
if categoryID > 0 then
if category.id ~= 'melvorD:Bars' then
local barVal, barValTxt = 0, 'N/A'
local barVal, barValTxt = 0, 'N/A'
if barQty > 0 then
if barQty > 0 then
barVal = totalValue / barQty
barVal = totalValue / barQty
barTxt = Icons.GP(Shared.round(barVal, 1, 1))
barValTxt = Icons.GP(Shared.round(barVal, 1, 1))
end
end
table.insert(resultPart, '\r\n|data-sort-value="' .. barVal .. '"| ' .. barTxt)
table.insert(resultPart, '\r\n|data-sort-value="' .. barVal .. '"| ' .. barValTxt)
end
end
end
end
Line 284: Line 442:
table.insert(resultPart, '\r\n!XP!!XP/s!!XP!!XP/s')
table.insert(resultPart, '\r\n!XP!!XP/s!!XP!!XP/s')


for i, logData in Shared.skpairs(SkillData.Firemaking) do
for i, logData in ipairs(SkillData.Firemaking.logs) do
local logs = Items.getItemByID(logData.logID)
local logs = Items.getItemByID(logData.logID)
local name = logs.name
local name = logs.name
local burnTime = logData.baseInterval / 1000
local burnTime = logData.baseInterval / 1000
local bonfireTime = logData.baseBonfireInterval / 1000
local bonfireTime = logData.baseBonfireInterval / 1000
local XPS = logData.baseXP / burnTime
local XPS = logData.baseExperience / burnTime
local XP_BF = logData.baseXP * (1 + logData.bonfireXPBonus / 100)
local XP_BF = logData.baseExperience * (1 + logData.bonfireXPBonus / 100)
local XPS_BF = XP_BF / burnTime
local XPS_BF = XP_BF / burnTime


table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='item', size='50', notext=true}))
table.insert(resultPart, '\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='item', size='50', notext=true}))
table.insert(resultPart, '||[['..name..']]')
table.insert(resultPart, '||'..Icons.getExpansionIcon(logs.id)..Icons.Icon({name, type='item', noicon=true}))
table.insert(resultPart, '||style ="text-align: right;"|'..logData.level)
table.insert(resultPart, '||style ="text-align: right;"|'..logData.level)
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..burnTime..'"|'..Shared.timeString(burnTime, true))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..burnTime..'"|'..Shared.timeString(burnTime, true))
table.insert(resultPart, '||style ="text-align: right;"|'..logData.baseXP)
table.insert(resultPart, '||style ="text-align: right;"|'..logData.baseExperience)
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..XPS..'"|'..Shared.round(XPS, 2, 2))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..XPS..'"|'..Shared.round(XPS, 2, 2))
table.insert(resultPart, '||style ="text-align: right;"|'..Shared.round(XP_BF, 2, 0))
table.insert(resultPart, '||style ="text-align: right;"|'..Shared.round(XP_BF, 2, 0))
Line 308: Line 466:
table.insert(resultPart, '\r\n|}')
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
return table.concat(resultPart)
end
function p.getMasteryTokenTable()
local baseTokenChance = 18500
local masterySkills = {}
-- Find all mastery tokens
local masteryTokens = Items.getItems(function(item) return item.isToken ~= nil and item.skill ~= nil and item.isToken end)
for i, item in pairs(masteryTokens) do
local milestones = SkillData.Milestones[item.skill + 1]
if milestones ~= nil then
table.insert(masterySkills, {tokenRef = i, skillID = item.skill, milestoneCount = milestones})
end
end
table.sort(masterySkills, function(a, b)
if a['milestoneCount'] == b['milestoneCount'] then
return a['skillID'] < b['skillID']
else
return a['milestoneCount'] > b['milestoneCount']
end
end)
-- Generate output table
local resultPart = {}
local CCI = Items.getItem('Clue Chasers Insignia')
local CCIIcon = Icons.Icon({'Clue Chasers Insignia', type='item', notext=true})
if CCI == nil then return '' end
table.insert(resultPart, '{| class="wikitable sortable"')
table.insert(resultPart, '\r\n!rowspan="2"|Token!!rowspan="2"|Skill!!colspan="2"|Approximate Mastery Token Chance')
table.insert(resultPart, '\r\n|-\r\n!Without ' .. CCIIcon .. '!!With ' .. CCIIcon)
for i, m in ipairs(masterySkills) do
local token = masteryTokens[m.tokenRef]
local denom = math.floor(baseTokenChance / m['milestoneCount'])
local denomCCI = math.floor(baseTokenChance / m['milestoneCount'] * (1 - CCI.increasedItemChance / 100))
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|style="text-align:center"|' .. Icons.Icon({token.name, type='item', size=50, notext=true}))
table.insert(resultPart, '\r\n|' .. Icons.Icon({Constants.getSkillName(m['skillID']), type='skill'}))
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. denom .. '"|1/' .. Shared.formatnum(denom))
table.insert(resultPart, '\r\n|style="text-align:right" data-sort-value="' .. denomCCI .. '"|1/' .. Shared.formatnum(denomCCI))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getSkillUnlockCostTable()
local returnPart = {}
table.insert(returnPart, '{| class="wikitable stickyHeader"\r\n|- class="headerRow-0"\r\n!Unlock!!Cost!!Cumulative Cost')
local accCost = 0
for i, cost in ipairs(SkillData.SkillUnlockCosts) do
accCost = accCost + cost
table.insert(returnPart, '|-')
table.insert(returnPart, '|' .. i .. '||' .. Icons.GP(cost) .. '||' .. Icons.GP(accCost))
end
table.insert(returnPart, '|}')
return table.concat(returnPart, '\r\n')
end
end


return p
return p