Anonymous

Module:Skills: Difference between revisions

From Melvor Idle
Adjusted the appearance of the Lesser Relics table
m (Newline fun times)
(Adjusted the appearance of the Lesser Relics table)
 
(57 intermediate revisions by 4 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
--Module:Prayer for Prayer
--Module:Prayer for Prayer
--Module:Agility for Agility
--Module:Skills/Agility for Agility
--Module:Skills/Summoning for Summoning
--Module:Skills/Gathering for Mining, Fishing, Woodcutting
--Module:Skills/Gathering for Mining, Fishing, Woodcutting
--Module:Skills/Artisan for Smithing, Cooking, Herblore, etc.
--Module:Skills/Artisan for Smithing, Cooking, Herblore, etc.
--Also be aware of:
--Module:Navboxes for navigation boxes appearing near the bottom of pages


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


local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
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}
-- Given a skill ID, returns the key for that skill's recipe data.
-- If the skill has no recipes (e.g. is a combat skill) then the
-- return value is nil
function p.getSkillRecipeKey(skillID)
-- Convert skillID to local ID if not already
local ns, localSkillID = GameData.getLocalID(skillID)
local recipeIDs = {
["Woodcutting"] = 'trees',
["Fishing"] = 'fish',
["Firemaking"] = 'logs',
["Mining"] = 'rockData',
["Thieving"] = 'npcs',
["Agility"] = 'obstacles',
["Cooking"] = 'recipes',
["Smithing"] = 'recipes',
["Farming"] = 'recipes',
["Summoning"] = 'recipes',
["Fletching"] = 'recipes',
["Crafting"] = 'recipes',
["Runecrafting"] = 'recipes',
["Herblore"] = 'recipes',
["Astrology"] = 'recipes'
}
return recipeIDs[localSkillID]
end


function p.getSkillID(skillName)
-- Given a skill ID & recipe, returns the skill level requirement for
  for skName, ID in Shared.skpairs(Constants.skill) do
-- that recipe. If the level could not be determined, then the return
    if skName == skillName then
-- value is nil
      return ID
function p.getRecipeLevel(skillID, recipe)
    end
-- Convert skillID to local ID if not already
  end
local ns, localSkillID = GameData.getLocalID(skillID)
  return nil
if localSkillID == 'Agility' then
-- For Agility, level is derived from obstacle category
if recipe.category ~= nil then
-- Obstacle
return SkillData.Agility.obstacleUnlockLevels[recipe.category+1]
else
-- Pillar
local nsR, localRecipeID = GameData.getLocalID(recipe.id)
if localRecipeID ~= nil then
if string.find(localRecipeID, '^Pillar') ~= nil then
return 99
elseif string.find(localRecipeID, '^ElitePillar') ~= nil then
return 120
end
end
end
else
-- For all other skills, the recipe should have a level property
return recipe.level
end
end
end


function p.getSkillName(skillID)
-- Thieving
  for skName, ID in Shared.skpairs(Constants.skill) do
function p.getThievingNPCByID(npcID)
    if ID == skillID then
return GameData.getEntityByID(SkillData.Thieving.npcs, npcID)
      return skName
    end
  end
  return nil
end
end


function p.getThievingNPCByID(ID)
function p.getThievingNPC(npcName)
  local result = Shared.clone(SkillData.Thieving[ID + 1])
return GameData.getEntityByName(SkillData.Thieving.npcs, npcName)
  if result ~= nil then
    result.id = ID
  end
  return result
end
end


function p.getThievingNPC(name)
function p.getThievingNPCArea(npc)
  local result = nil
for i, area in ipairs(SkillData.Thieving.areas) do
  for i, npc in pairs(SkillData.Thieving) do
for j, npcID in ipairs(area.npcIDs) do
    if name == npc.name then
if npcID == npc.id then
      result = Shared.clone(npc)
return area
      result.id = i - 1
end
      break
end
    end
end
  end
end
  return result
 
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
end


function p.getThievingNPCStat(frame)
function p.getThievingNPCStat(frame)
  local args = frame.args ~= nil and frame.args or frame
local npcName = frame.args ~= nil and frame.args[1] or frame[1]
  local npcName = args[1]
local statName = frame.args ~= nil and frame.args[2] or frame[2]
  local statName = args[2]
local npc = p.getThievingNPC(npcName)
  local npc = p.getThievingNPC(npcName)
if npc == nil then
  if npc == nil then
return Shared.printError('Invalid Thieving NPC ' .. npcName)
    return 'ERROR: Failed to find Thieving NPC with name ' .. name .. '[[Category:Pages with script errors]]'
end
  end
 
 
return p._getThievingNPCStat(npc, statName)
  return p._getThievingNPCStat(npc, statName)
end
end


function p._getThievingNPCStat(npc, stat)
function p.getThievingSourcesForItem(itemID)
  local result = npc[stat]
local resultArray = {}
  -- Overrides below
local areaNPCs = {}
  if stat == 'maxHit' then
    result = result * 10
  elseif stat == 'lootTable' then
    return p._formatLootTable(npc['lootTable'])
  elseif stat == 'requirements' then
  if npc['level'] ~= nil then
    result = Icons._SkillReq('Thieving', npc['level'], true)
  else
    result = 'None'
  end
  end


  return result
--First check area unique drops
end
--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] = { qty = drop.quantity, area = area }
end
break
end
end
end


function p._formatLootTable(lootTableIn)
--Now go through and get drop chances on each NPC if needed
  -- Expects lootTableIn to be in format {{itemID_1, itemWeight_1}, ..., {itemID_n, itemWeight_n}}
for i, npc in pairs(SkillData.Thieving.npcs) do
  if Shared.tableCount(lootTableIn) == 0 then
local totalWt = 0
    return ''
local dropWt = 0
  end
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, type = 'npc'})
end


  local lootTable = Shared.clone(lootTableIn)
--Chance of -1 on unique drops is to indicate variable chance
  -- Sort table from most to least common drop
if npc.uniqueDrop ~= nil and npc.uniqueDrop.id == itemID then
  table.sort(lootTable, function(a, b)
table.insert(resultArray, {npc = npc.name, minQty = npc.uniqueDrop.quantity, maxQty = npc.uniqueDrop.quantity, wt = -1, totalWt = -1, level = npc.level, npcID = npc.id, type = 'npcUnique'})
                          return a[2] > b[2]
end
                        end)


  local totalWeight = 0
local areaNPC = areaNPCs[npc.id]
  for i, drop in pairs(lootTable) do
if areaNPC ~= nil then
    totalWeight = totalWeight + drop[2]
table.insert(resultArray, {npc = npc.name, minQty = areaNPC.qty, maxQty = areaNPC.qty, wt = SkillData.Thieving.baseAreaUniqueChance, totalWt = 100, level = npc.level, npcID = npc.id, area = areaNPC.area, type = 'areaUnique'})
  end
end
  if totalWeight == 0 then
end
    return ''
  end


  -- Get the length (in characters) of the largest drop chance so that they can be right aligned
for i, drop in ipairs(SkillData.Thieving.generalRareItems) do
  local maxDropLen = string.len(Shared.round(lootTable[1][2] / totalWeight * 100, 2, 2))
if drop.itemID == itemID then
  local returnPart = {}
if drop.npcs == nil then
  for i, drop in pairs(lootTable) do
table.insert(resultArray, {npc = 'all', minQty = 1, maxQty = 1, wt = 1, totalWt = Shared.round2(1/(drop.chance/100), 0), level = 1, npcID = itemID, type = 'generalRare'})
    local item, itemText, dropChance = Items.getItemByID(drop[1]), nil, Shared.round(drop[2] / totalWeight * 100, 2, 2)
else
    if item == nil then
for j, npcID in ipairs(drop.npcs) do
      itemText = 'Unknown'
local npc = p.getThievingNPCByID(npcID)
    else
if npc ~= nil then
      itemText = Icons.Icon({item.name, type='item'})
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, type = 'generalRare'})
    end
end
    table.insert(returnPart, '* ' .. string.rep(' ', math.max(0, (maxDropLen - string.len(dropChance)) * 2)) .. dropChance .. '% ' .. itemText)
end
  end
end
end
end


  return table.concat(returnPart, '\r\n')
return resultArray
end
end


function p.getMasteryUnlockTable(frame)
-- Astrology
  local skillName = frame.args ~= nil and frame.args[1] or frame
function p.getConstellationByID(constID)
  local skillID = p.getSkillID(skillName)
return GameData.getEntityByID(SkillData.Astrology.recipes, constID)
  if skillID == nil then
end
    return "ERROR: Failed to find a skill ID for "..skillName
  end


  local unlockTable = SkillData.MasteryUnlocks[skillID]
function p.getConstellation(constName)
  if unlockTable == nil then
return GameData.getEntityByName(SkillData.Astrology.recipes, constName)
    return 'ERROR: Failed to find Mastery Unlock data for '..skillName
end
  end


  local result = '{|class="wikitable"\r\n!Level!!Unlock'
function p.getConstellations(checkFunc)
  for i, unlock in Shared.skpairs(unlockTable) do
return GameData.getEntities(SkillData.Astrology.recipes, checkFunc)
    result = result..'\r\n|-'
    result = result..'\r\n|'..unlock.level..'||'..unlock.unlock
  end
  result = result..'\r\n|}'
  return result
end
end


function p.getMasteryCheckpointTable(frame)
-- For a given constellation cons and modifier value modValue, generates and returns
  local skillName = frame.args ~= nil and frame.args[1] or frame
-- a table of modifiers, much like any other item/object elsewhere in the game.
  local skillID = p.getSkillID(skillName)
-- includeStandard: true|false, determines whether standard modifiers are included
  if skillID == nil then
-- includeUnique: true|false, determines whether unique modifiers are included
    return "ERROR: Failed to find a skill ID for "..skillName
-- isDistinct: true|false, if true, the returned list of modifiers is de-duplicated
  end
-- 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 masteryReq = {
['standardModifiers'] = { 1, 40, 80 },
['uniqueModifiers'] = { 20, 60, 99 }
}


  if SkillData.MasteryCheckpoints[skillID] == nil then
local modArray = {}
    return 'ERROR: Failed to find Mastery Unlock data for '..skillName
local isSkillMod = {}
  end
--Adding a Group Number to hold together different bonuses from the same modifier [Falterfire 22/10/27]
local groupNum = 0


  local bonuses = SkillData.MasteryCheckpoints[skillID].bonuses
for _, modType in ipairs(modTypes) do
  local totalPoolXP = SkillData.MasteryPoolXP[skillID + 1]
for i, modTypeData in ipairs(cons[modType]) do
groupNum = masteryReq[modType][i]
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


  local result = '{|class="wikitable"\r\n!Pool %!!style="width:100px"|Pool XP!!Bonus'
if asKeyValue then
  for i, bonus in Shared.skpairs(bonuses) do
local modArrayKV = {}
    result = result..'\r\n|-'
for i, modDefn in ipairs(modArray) do
    result = result..'\r\n|'..(MasteryCheckpoints[i] * 100)..'%||'
local modName, modVal = modDefn[1], modDefn[2]
    result = result..Shared.formatnum(totalPoolXP * MasteryCheckpoints[i])..' xp||'..bonus
local isSkill = type(modVal) == 'table' and modVal.skillID ~= nil
  end
if modArrayKV[modName] == nil then
  result = result..'\r\n|-\r\n!colspan="2"|Total Mastery Pool XP'
modArrayKV[modName] = (isSkill and { modVal } or modVal)
  result = result..'\r\n|'..Shared.formatnum(totalPoolXP)
elseif isSkill then
  result = result..'\r\n|}'
table.insert(modArrayKV[modName], modVal)
  return result
else
modArrayKV[modName] = modArrayKV[modName] + modVal
end
end
return modArrayKV
else
return modArray
end
end
end


function p._getFarmingTable(category)
-- Mastery
  local seedList = {}
function p.getMasteryUnlockTable(frame)
  if category == 'Allotment' or category == 'Herb' or category == 'Tree' then
local skillName = frame.args ~= nil and frame.args[1] or frame
    seedList = Items.getItems(function(item) return item.tier == category end)
local skillID = Constants.getSkillID(skillName)
  else
if skillID == nil then
    return 'ERROR: Invalid farming category. Please choose Allotment, Herb, or Tree'
return Shared.printError('Failed to find a skill ID for ' .. skillName)
  end
end


  local result = '{|class="wikitable sortable stickyHeader"'
local _, localSkillID = GameData.getLocalID(skillID)
  result = result..'\r\n|- class="headerRow-0"'
-- Clone so that we can sort by level
  result = result..'\r\n!colspan=2|Seeds!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level'
local unlockTable = Shared.clone(SkillData[localSkillID].masteryLevelUnlocks)
  result = result..'!!XP!!Growth Time!!Seed Value'
if unlockTable == nil then
  if category == 'Allotment' then
return Shared.printError('Failed to find Mastery Unlock data for ' .. skillName)
    result = result..'!!colspan="2"|Crop!!Crop Healing!!Crop Value'
end
  elseif category == 'Herb' then
table.sort(unlockTable, function(a, b) return (a.level == b.level and a.descriptionID < b.descriptionID) or a.level < b.level end)
    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)


  for i, seed in pairs(seedList) do
local result = '{|class="wikitable"\r\n!Level!!Unlock'
    result = result..'\r\n|-'
for i, unlock in ipairs(unlockTable) do
    result = result..'\r\n|'..Icons.Icon({seed.name, type='item', size='50', notext=true})..'||[['..seed.name..']]'
result = result..'\r\n|-'
    result = result..'||'..seed.farmingLevel..'||'..Shared.formatnum(seed.farmingXP)
result = result..'\r\n|'..unlock.level..'||'..unlock.description
    result = result..'||data-sort-value="'..seed.timeToGrow..'"|'..Shared.timeString(seed.timeToGrow, true)
end
    result = result..'||data-sort-value="'..seed.sellsFor..'"|'..Icons.GP(seed.sellsFor)
result = result..'\r\n|}'
return result
end


    local crop = Items.getItemByID(seed.grownItemID)
function p.getMasteryCheckpointTable(frame)
    result = result..'||'..Icons.Icon({crop.name, type='item', size='50', notext=true})..'||[['..crop.name..']]'
local skillName = frame.args ~= nil and frame.args[1] or frame
    if category == 'Allotment' then
local skillID = Constants.getSkillID(skillName)
      result = result..'||'..Icons.Icon({'Hitpoints', type='skill', notext=true})..' '..(crop.healsFor * 10)
if skillID == nil then
    end
return Shared.printError('Failed to find a skill ID for ' .. skillName)
    result = result..'||data-sort-value="'..crop.sellsFor..'"|'..Icons.GP(crop.sellsFor)
end
    result = result..'||'..ItemSourceTables._getItemSources(seed)
  end


  result = result..'\r\n|}'
local _, localSkillID = GameData.getLocalID(skillID)
  return result
local checkpoints = SkillData[localSkillID].masteryCheckpoints
if checkpoints == nil then
return Shared.printError('Failed to find Mastery Unlock data for ' .. skillName)
end
 
local totalPoolXP = SkillData[localSkillID].baseMasteryPoolCap
local checkpointPct = GameData.rawData.masteryCheckpoints
local result = '{|class="wikitable"\r\n!Pool %!!style="width:100px"|Pool XP!!Bonus'
for i, checkpointDesc in ipairs(checkpoints) do
result = result..'\r\n|-'
result = result..'\r\n|'..checkpointPct[i]..'%||'
result = result..Shared.formatnum(math.floor(totalPoolXP * checkpointPct[i] / 100))..' xp||'..checkpointDesc
end
result = result..'\r\n|-\r\n!colspan="2"|Total Mastery Pool XP'
result = result..'\r\n|'..Shared.formatnum(totalPoolXP)
result = result..'\r\n|}'
return result
end
end


function p.getFarmingTable(frame)
function p.getMasteryTokenTable()
  local category = frame.args ~= nil and frame.args[1] or frame
-- Defines which skill levels should be included within the output
local skillLevels = {
{
["id"] = 'Base',
["level"] = 99,
["description"] = '[[Full Version|Base Game]] (Level 99)'
}, {
["id"] = 'TotH',
["level"] = 120,
["description"] = Icons.TotH() .. ' [[Throne of the Herald Expansion|Throne of the Herald]] (Level 120)'
}
}
local baseTokenChance = 18500
local masteryActionCount = {}
local CCI_ID = 'melvorD:Clue_Chasers_Insignia'
local CCI = Items.getItemByID(CCI_ID)
if CCI == nil then
return Shared.printError('Failed to find item with ID ' .. CCI_ID)
end
 
-- Iterate over each skill with mastery, determining the number of
-- mastery actions for each
for skillLocalID, skill in pairs(SkillData) do
if skill.masteryTokenID ~= nil then
local actCount = { ["skill"] = skill }
for i, levelDef in ipairs(skillLevels) do
actCount[levelDef.id] = 0
end


  return p._getFarmingTable(category)
local recipeKey = p.getSkillRecipeKey(skillLocalID)
end
if recipeKey ~= nil then
local recipeData = skill[recipeKey]
for i, recipe in ipairs(recipeData) do
if recipe.noMastery == nil or not recipe.noMastery then
local skillLevel = p.getRecipeLevel(skillLocalID, recipe)
if skillLevel ~= nil then
for j, levelDef in ipairs(skillLevels) do
if skillLevel <= levelDef.level then
actCount[levelDef.id] = actCount[levelDef.id] + 1
end
end
end
end
end
end
table.insert(masteryActionCount, actCount)
end
end


function p.getFarmingFoodTable(frame)
local firstID = skillLevels[1].id
  local result = '{| class="wikitable sortable stickyHeader"'
table.sort(masteryActionCount,
  result = result..'\r\n|- class="headerRow-0"'
function(a, b)
  result = result..'\r\n!colspan="2"|Crop!!'..Icons.Icon({"Farming", type="skill", notext=true})..' Level'
if a[firstID] == b[firstID] then
  result = result..'!!Healing!!Value'
return a.skill.name < b.skill.name
 
else
  local itemArray = Items.getItems(function(item) return item.grownItemID ~= nil end)
return a[firstID] > b[firstID]
end
end)
-- Generate output table
local resultPart = {}
local CCIIcon = Icons.Icon({CCI.name, type='item', notext=true})
local columnPairs = Shared.tableCount(skillLevels)


  table.sort(itemArray, function(a, b) return a.farmingLevel < b.farmingLevel end)
-- Generate header
table.insert(resultPart, '{| class="wikitable sortable"')
table.insert(resultPart, '\n!rowspan="3"|Token!!rowspan="3"|Skill!!colspan="' .. columnPairs * 2 .. '"|Approximate Mastery Token Chance')
table.insert(resultPart, '\n|-')
for i, levelDef in ipairs(skillLevels) do
table.insert(resultPart, '\n!colspan="2"| ' .. levelDef.description)
end
table.insert(resultPart, '\n|-' .. string.rep('\n!Without ' .. CCIIcon .. '\n!With ' .. CCIIcon, columnPairs))


  for i, item in Shared.skpairs(itemArray) do
for i, rowData in ipairs(masteryActionCount) do
    local crop = Items.getItemByID(item.grownItemID)
local token = Items.getItemByID(rowData.skill.masteryTokenID)
    if crop.healsFor ~= nil and crop.healsFor > 0 then
table.insert(resultPart, '\n|-')
      result = result..'\r\n|-'
table.insert(resultPart, '\n|style="text-align:center"|' .. Icons.Icon({token.name, type='item', size=50, notext=true}))
      result = result..'\r\n|'..Icons.Icon({crop.name, type='item', notext='true', size='50'})..'||[['..crop.name..']]'
table.insert(resultPart, '\n|' .. Icons.Icon({rowData.skill.name, type='skill'}))
      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


  result = result..'\r\n|}'
for j, levelDef in ipairs(skillLevels) do
local actCount = rowData[levelDef.id]
local denom, denomCCI = 0, 0
if actCount > 0 then
denom = math.floor(baseTokenChance / actCount)
denomCCI = Shared.round(baseTokenChance / (actCount * (1 + CCI.modifiers.increasedOffItemChance / 100)), 0, 0)
end
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. denom .. '"|1/' .. Shared.formatnum(denom))
table.insert(resultPart, '\n|style="text-align:right" data-sort-value="' .. denomCCI .. '"|1/' .. Shared.formatnum(denomCCI))
end
end
table.insert(resultPart, '\n|}')


  return result
return table.concat(resultPart)
end
end


function p.getFarmingPlotTable(frame)
-- Skill unlock costs for Adventure game mode
  local areaName = frame.args ~= nil and frame.args[1] or frame
function p.getSkillUnlockCostTable()
  local patches = nil
local advMode = GameData.getEntityByID('gamemodes', 'melvorF:Adventure')
  for i, area in Shared.skpairs(SkillData.Farming.Patches) do
if advMode ~= nil then
    if area.areaName == areaName then
local unlockCount = Shared.tableCount(GameData.skillData) - Shared.tableCount(advMode.startingSkills)
      patches = area.patches
local costLength = Shared.tableCount(advMode.skillUnlockCost)
      break
local returnPart = {}
    end
table.insert(returnPart, '{| class="wikitable stickyHeader"\r\n|- class="headerRow-0"\r\n!Unlock!!Cost!!Cumulative Cost')
  end
  if patches == nil then
    return "ERROR: Invalid area name.[[Category:Pages with script errors"
  end


  local result = '{|class="wikitable"'
local accCost = 0
  result = result..'\r\n!Plot!!'..Icons.Icon({'Farming', type='skill', notext=true})..' Level!!Cost'
for i = 1, unlockCount, 1 do
local cost = advMode.skillUnlockCost[math.min(i, costLength)]
accCost = accCost + cost
table.insert(returnPart, '|-')
table.insert(returnPart, '|' .. i .. '||' .. Icons.GP(cost) .. '||' .. Icons.GP(accCost))
end
table.insert(returnPart, '|}')


  for i, patch in Shared.skpairs(patches) do
return table.concat(returnPart, '\r\n')
    result = result..'\r\n|-\r\n|'..i
end
    result = result..'||style="text-align:right;" data-sort-value="0"|'..patch.level
end
    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


  result = result..'\r\n|}'
function p.getFiremakingTable(frame)
  return result
local resultPart = {}
end
table.insert(resultPart, '{| class="wikitable sortable stickyHeader"')
table.insert(resultPart, '\r\n|-class="headerRow-0"')
table.insert(resultPart, '\r\n!colspan="2" rowspan="2"|Logs!!rowspan="2"|'..Icons.Icon({'Firemaking', type='skill', notext=true})..' Level')
table.insert(resultPart, '!!rowspan="2"|Burn Time!!colspan="2"|Without Bonfire!!colspan="2"|With Bonfire!!rowspan="2"|Bonfire Bonus!!rowspan="2"|Bonfire Time')
table.insert(resultPart, '\r\n|-class="headerRow-1"')
table.insert(resultPart, '\r\n!XP!!XP/s!!XP!!XP/s')


function p.getPotionNavbox(frame)
for i, logData in ipairs(SkillData.Firemaking.logs) do
  --•
local logs = Items.getItemByID(logData.logID)
  local result = '{| class="wikitable" style="margin:auto; clear:both; width: 100%"'
local name = logs.name
  result = result..'\r\n!colspan=2|'..Icons.Icon({'Herblore', 'Potions', type='skill'})
local burnTime = logData.baseInterval / 1000
local bonfireTime = logData.baseBonfireInterval / 1000
local XPS = logData.baseExperience / burnTime
local XP_BF = logData.baseExperience * (1 + logData.bonfireXPBonus / 100)
local XPS_BF = Shared.round(XP_BF / burnTime, 2, 2)
XP_BF = Shared.round(XP_BF, 2, 0)


  local CombatPots = {}
table.insert(resultPart, '\r\n|-')
  local SkillPots = {}
table.insert(resultPart, '\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='item', size='50', notext=true}))
  for i, potData in Shared.skpairs(SkillData.Herblore.ItemData) do
table.insert(resultPart, '||'..Icons.getExpansionIcon(logs.id)..Icons.Icon({name, type='item', noicon=true}))
    if potData.category == 0 then
table.insert(resultPart, '||style ="text-align: right;"|'..logData.level)
      table.insert(CombatPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..burnTime..'"|'..Shared.timeString(burnTime, true))
    else
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="' .. logData.baseExperience .. '"| ' .. Shared.formatnum(logData.baseExperience))
      if potData.name == 'Bird Nests Potion' then
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..XPS..'"|'..Shared.formatnum(Shared.round(XPS, 2, 2)))
        table.insert(SkillPots, Icons.Icon({"Bird Nest Potion", type='item', img="Bird Nest Potion I"}))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="' .. XP_BF .. '"| ' .. Shared.formatnum(XP_BF))
      else
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..XPS_BF..'"|'..Shared.formatnum(XPS_BF, 2, 2))
        table.insert(SkillPots, Icons.Icon({potData.name, type='item', img=(potData.name..' I')}))
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..logData.bonfireXPBonus..'"|'..logData.bonfireXPBonus..'%')
      end
table.insert(resultPart, '||style ="text-align: right;" data-sort-value="'..bonfireTime..'"|'..Shared.timeString(bonfireTime, true))
    end
end
  end


  result = result..'\r\n|-\r\n!Combat Potions\r\n|class="center" style="vertical-align:middle;"'
table.insert(resultPart, '\r\n|}')
  result = result..'|'..table.concat(CombatPots, ' • ')
return table.concat(resultPart)
  result = result..'\r\n|-\r\n!Skill Potions\r\n|class="center" style="vertical-align:middle;"'
  result = result..'|'..table.concat(SkillPots, ' • ')
  result = result..'\r\n|}'
  return result
end
end


function p.getSmithingTable(frame)
function p.getAncientRelicsTable(frame)
  local tableType = frame.args ~= nil and frame.args[1] or frame
local skillName = frame.args ~= nil and frame.args[1] or frame
  local bar = nil
local skillID = nil
  if tableType ~= 'Smelting' then
if skillName ~= nil and skillName ~= '' then
    bar = Items.getItem(tableType)
skillID = Constants.getSkillID(skillName)
    if bar == nil then
if skillID == nil then
      return 'ERROR: Could not find an item named '..tableType..' to build a smithing table with'
return Shared.printError('Failed to find a skill ID for ' .. skillName)
    elseif bar.type ~= 'Bar' then
end
      return 'ERROR: '..tableType.." is not a bar and thus can't be used for smithing"
end
    end
  end


  local smithList = {}
local resultPart = {}
  for i, item in pairs(ItemData.Items) do
table.insert(resultPart, '{| class="wikitable sortable stickyHeader lighttable"')
    if item.smithingLevel ~= nil then
table.insert(resultPart, '\n|-class="headerRow-0"')
      if tableType == 'Smelting' then
table.insert(resultPart, '\n|-\n!colspan="2"|Skill\n!Relic\n!Modifiers')
        if item.type == 'Bar' then
          table.insert(smithList, item)
        end
      else
        for j, req in pairs(item.smithReq) do
          if req.id == bar.id then
            table.insert(smithList, item)
          end
        end
      end
    end
  end


  local result = '{|class="wikitable sortable stickyHeader"'
local relics = GameData.getEntities('ancientRelics',
  result = result..'\r\n|-class="headerRow-0"'
function(relic)
  result = result..'\r\n!Item!!Name!!'..Icons.Icon({'Smithing', type='skill', notext=true})..' Level!!XP!!Value!!Ingredients'
return skillID == nil or relic.skillID == skillID
  --Adding value/bar for things other than smelting
end)
  if bar ~= nil then result = result..'!!Value/Bar' end
table.sort(relics,
function (a, b)
local skillNameA, skillNameB = Constants.getSkillName(a.skillID), Constants.getSkillName(b.skillID)
if skillNameA == skillNameB then
-- Order by numbers at the end of relic IDs
-- Relics have a 'number' property, but this appears to contain duplicates
return string.sub(a.id, string.len(a.id)) < string.sub(b.id, string.len(b.id))
else
return skillNameA < skillNameB
end
end)


  table.sort(smithList, function(a, b)
local function appendSkillRows(resultTable, rowTable, relicCount, skillID)
                          if a.smithingLevel ~= b.smithingLevel then
local skillName = Constants.getSkillName(skillID)
                            return a.smithingLevel < b.smithingLevel
table.insert(resultTable, '\n|-\n|rowspan="' .. relicCount .. '"| ' .. Icons.Icon({skillName, type='skill', notext=true, size=50}))
                          else
table.insert(resultTable, '\n|rowspan="' .. relicCount .. '"| ' .. Icons.Icon({skillName, type='skill', noicon=true}))
                            return a.name < b.name
table.insert(resultTable, table.concat(rowTable))
                          end end)
end
  for i, item in Shared.skpairs(smithList) do
    result = result..'\r\n|-'
    result = result..'\r\n|'..Icons.Icon({item.name, type='item', size='50', notext=true})..'||'
    local qty = item.smithingQty ~= nil and item.smithingQty or 1
    if qty > 1 then
      result = result..item.smithingQty..'x '
    end
    result = result..'[['..item.name..']]'
    result = result..'||data-sort-value="'..item.smithingLevel..'"|'..Icons._SkillReq('Smithing', item.smithingLevel)
    result = result..'||'..item.smithingXP
    local totalValue = item.sellsFor * qty
    result = result..'||data-sort-value="'..totalValue..'"|'..Icons.GP(item.sellsFor)
    if qty > 1 then
      result = result..' (x'..qty..')'
    end
    result = result..'||'
    local barQty = 0
    for i, mat in Shared.skpairs(item.smithReq) do
      matItem = Items.getItemByID(mat.id)
      if i > 1 then result = result..', ' end
      result = result..Icons.Icon({matItem.name, type='item', qty=mat.qty, notext=true})
      if bar ~= nil and mat.id == bar.id then
        barQty = mat.qty
      end
    end
    --Add the data for the value per bar
    if bar ~= nil then
      if barQty == 0 then
        result = result..'||data-sort-value="0"|N/A'
      else
        local barVal = totalValue / barQty
        result = result..'||data-sort-value="'..barVal..'"|'..Icons.GP(Shared.round(barVal, 1, 1))
      end
    end
  end


  result = result..'\r\n|}'
local skillRelicCount, currentSkillID, tablePart = 0, nil, {}
  return result
for i, relic in ipairs(relics) do
if currentSkillID == nil then
currentSkillID = relic.skillID
elseif relic.skillID ~= currentSkillID then
appendSkillRows(resultPart, tablePart, skillRelicCount, currentSkillID)
tablePart = {}
currentSkillID = relic.skillID
skillRelicCount = 0
end
 
skillRelicCount = skillRelicCount + 1
if skillRelicCount > 1 then
table.insert(tablePart, '\n|-')
end
table.insert(tablePart, '\n| ' .. skillRelicCount .. '\n| ' .. Constants.getModifiersText(relic.modifiers))
end
appendSkillRows(resultPart, tablePart, skillRelicCount, currentSkillID)
table.insert(resultPart, '\n|}')
 
return table.concat(resultPart)
end
end


function p.getFiremakingTable(frame)
function p.getLesserRelicsTable(frame)
  local result = '{| class="wikitable sortable stickyHeader"'
local lesserRelics = {}
  result = result..'\r\n|-class="headerRow-0"'
-- Iterate over each skill with a global rare drop then check
  result = result..'\r\n!colspan="2"|Logs!!'..Icons.Icon({'Firemaking', type='skill', notext=true})..' Level'
-- if the skill has a Lesser Relic drop
  result = result..'!!XP!!Burn Time!!XP/s!!Bonfire Bonus!!Bonfire Time'
for skillLocalID, skill in pairs(SkillData) do
if skill.rareDrops ~= nil then
for i, drops in pairs(skill.rareDrops) do
if string.match(drops.itemID, '_Lesser_Relic') then
table.insert(lesserRelics, Items.getItemByID(drops.itemID))
end
end
end
end
table.sort(lesserRelics, function(a, b) return a.name < b.name end)


  for i, logData in Shared.skpairs(SkillData.Firemaking) do
-- Create the Table
    result = result..'\r\n|-'
local resultTable = mw.html.create('table')
    local name = Shared.titleCase(logData.type..' Logs')
resultTable:addClass('wikitable sortable')
    result = result..'\r\n|data-sort-value="'..name..'"|'..Icons.Icon({name, type='item', size='50', notext=true})
resultTable:tag('tr'):addClass('headerRow-0')
    result = result..'||[['..name..']]'
:tag('th'):wikitext('Icon')
    result = result..'||style ="text-align: right;"|'..logData.level
:tag('th'):wikitext('Lesser Relic')
    result = result..'||style ="text-align: right;"|'..logData.xp
:tag('th'):wikitext('Modifiers')
    local burnTime = logData.interval / 1000
    local XPS = logData.xp / burnTime
    result = result..'||style ="text-align: right;" data-sort-value="'..burnTime..'"|'..Shared.timeString(burnTime, true)
    result = result..'||style ="text-align: right;" data-sort-value="'..XPS..'"|'..Shared.round(XPS, 2, 2)
    result = result..'||style ="text-align: right;" data-sort-value="'..logData.bonfireBonus..'"|'..logData.bonfireBonus..'%'
    result = result..'||style ="text-align: right;" data-sort-value="'..logData.bonfireInterval..'"|'..Shared.timeString(logData.bonfireInterval / 1000, true)
  end


  result = result..'\r\n|}'
for _, relic in ipairs(lesserRelics) do
  return result
local tr = mw.html.create('tr')
tr:tag('td'):wikitext(Icons.Icon({relic.name, type='item', size='50', notext=true}))
tr:tag('td'):wikitext(Icons.Icon({relic.name, type='item', noicon=true}))
tr:tag('td'):wikitext(Constants.getModifiersText(relic.modifiers))
resultTable:node(tr)
end
return resultTable
end
end


return p
return p
463

edits