Anonymous

Module:SkillUnlocks: Difference between revisions

From Melvor Idle
no edit summary
No edit summary
No edit summary
(20 intermediate revisions by 2 users not shown)
Line 1: Line 1:
local p = {}
local p = {}


--This module polls various game data sources to produce a full list of skill
-- This module polls various game data sources to produce a full list of skill
--level unlocks for each skill. The game has a hard-coded set of "milestones"
-- level unlocks for each skill. The game has a hard-coded set of "milestones"
--for each skill that does the same thing, but it is not exhaustive and not
-- for each skill that does the same thing, but it is not exhaustive and not
--permanently visible for most combat skills.
-- permanently visible for most combat skills.
 
-- TODO: Args to filter by level range and unlock type


local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
Line 11: Line 13:
local Items = require('Module:Items')
local Items = require('Module:Items')
local CombatAreas = require('Module:CombatAreas')
local CombatAreas = require('Module:CombatAreas')
local Shop = require('Module:Shop')
local Township = require('Module:Township')
local GameData = require('Module:GameData')
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local SkillData = GameData.skillData
local Num = require('Module:Number')


-- This excludes certain checks pertinent to all skills, like equipment
local SKILL_CHECK_MAP = {
local SKILL_CHECK_MAP = {
['Attack'] = {},
['Attack'] = {},
Line 20: Line 26:
['Hitpoints'] = {},
['Hitpoints'] = {},
['Ranged'] = {},
['Ranged'] = {},
['Magic'] = {'spells'},
['Magic'] = {'spells', 'altmagic'},
['Prayer'] = {'prayers'},
['Prayer'] = {'prayers'},
['Slayer'] = {'areas'},
['Slayer'] = {'areas'},
['Farming'] = {},
['Farming'] = {'mastery', 'gatheringitems', 'farmingplots'},
['Township'] = {},
['Township'] = {'townshipunlocks'},
['Woodcutting'] = {'gathering'},
['Woodcutting'] = {'mastery', 'gatheringitems'},
['Fishing'] = {'gathering'},
['Fishing'] = {'mastery', 'gatheringitems'},
['Firemaking'] = {},
['Firemaking'] = {'mastery', 'firemaking'},
['Cooking'] = {},
['Cooking'] = {'mastery', 'artisanitems'},
['Mining'] = {'gathering'},
['Mining'] = {'mastery', 'gatheringitems'},
['Smithing'] = {},
['Smithing'] = {'mastery', 'artisanitems'},
['Thieving'] = {},
['Thieving'] = {'mastery', 'thieving'},
['Fletching'] = {},
['Fletching'] = {'mastery', 'artisanitems'},
['Crafting'] = {},
['Crafting'] = {'mastery', 'artisanitems'},
['Runecrafting'] = {},
['Runecrafting'] = {'mastery', 'artisanitems'},
['Herblore'] = {},
['Herblore'] = {'mastery', 'artisanitems'},
['Agility'] = {},
['Agility'] = {'mastery', 'agilityslots'},
['Summoning'] = {},
['Summoning'] = {'mastery', 'artisanitems'},
['Astrology'] = {},
['Astrology'] = {'mastery', 'constellations'},
['Alt. Magic'] = {},
['Alt. Magic'] = {'altmagic'},
}
}
local TYPE_SORT_ORDER = {
local TYPE_SORT_ORDER = {
['spell'] = 1,
['special'] = 1,
['prayer'] = 2,
['biome'] = 2,
['gathering'] = 3,
['building'] = 3,
['item'] = 4,  
['plot'] = 4,
['combatArea'] = 5,
['obstacleslot'] = 5,
['slayerArea'] = 6,
['pillar'] = 6,
['dungeon'] = 7
['spell'] = 7,
['prayer'] = 8,
['thieving'] = 9,
['constellation'] = 10,
['gathering'] = 11,
['artisan'] = 12,
['item'] = 13,  
['combatArea'] = 14,
['slayerArea'] = 15,
['dungeon'] = 16,
['shop'] = 17,
['trader'] = 18,
['obstacle'] = 19
}
}
local VERBS_PER_SUBTYPE = {
local VERBS_PER_SUBTYPE = {
Line 56: Line 74:
['Magic Book'] = 'Wield',
['Magic Book'] = 'Wield',
['Ranged Weapon'] = 'Wield',
['Ranged Weapon'] = 'Wield',
['Ammo'] = 'Use',
['Ammo'] = 'Wield',
['Equipment'] = 'Wear',
['Equipment'] = 'Wear',
['Armour'] = 'Wear',
['Armour'] = 'Wear',
Line 73: Line 91:
['ancient'] = 'Cast',
['ancient'] = 'Cast',
['archaic'] = 'Cast',
['archaic'] = 'Cast',
['prayer'] = 'Cast',
['prayer'] = 'Use',
['altMagic'] = 'Cast',
['tree'] = 'Cut',
['tree'] = 'Cut',
['fish'] = 'Catch',
['fish'] = 'Catch',
['Essence'] = 'Mine',
['Essence'] = 'Mine',
['Ore'] = 'Mine',
['Ore'] = 'Mine',
['Gem'] = 'Mine'
['Gem'] = 'Mine',
['artisan'] = 'Create',
['melvorD:Fire'] = 'Cook',
['melvorD:Furnace'] = 'Bake',
['melvorD:Pot'] = 'Boil',
['melvorD:Bars'] = 'Smelt',
['melvorF:SkillPotions'] = 'Brew',
['melvorF:CombatPotions'] = 'Brew',
['log'] = 'Burn',
['npc'] = 'Pickpocket',
['constellation'] = 'Study',
['shop'] = 'Purchase',
['plot'] = 'New',
['biome'] = 'Access',
['basicBuilding'] = 'Build',
['trader'] = 'Trade for',
['obstacle'] = 'Build',
['obstacleslot'] = 'Build',
['pillar'] = 'Build'
}
}
local SUBTYPE_OVERRIDES = {
local SUBTYPE_OVERRIDES = {
Line 95: Line 132:
['Spectral Ice Sword'] = 'Weapon',
['Spectral Ice Sword'] = 'Weapon',
['Torrential Blast Crossbow'] = 'Ranged Weapon'
['Torrential Blast Crossbow'] = 'Ranged Weapon'
}
local SUBTYPE_SORT_OVERRIDES = {
['melvorD:Bars'] = '1',
['melvorF:StandardRunes'] = '1',
['log'] = '1'
}
}
local GEAR_SETS = {
local GEAR_SETS = {
Line 612: Line 654:
['level'] = 40,
['level'] = 40,
['entities'] = {'Rune 2H Sword', 'Rune Battleaxe', 'Rune Dagger', 'Rune Scimitar', 'Rune Sword'}
['entities'] = {'Rune 2H Sword', 'Rune Battleaxe', 'Rune Dagger', 'Rune Scimitar', 'Rune Sword'}
},
{
['verb'] = 'Wear',
['name'] = 'Melee Slayer Gear (Basic)',
['level'] = 1,
['entities'] = {'Slayer Helmet (Basic)', 'Slayer Platebody (Basic)'}
},
},
{
{
Line 780: Line 828:
['level'] = 40,
['level'] = 40,
['entities'] = {'Yew Longbow', 'Yew Shortbow'}
['entities'] = {'Yew Longbow', 'Yew Shortbow'}
},
{
['verb'] = 'Purchase',
['name'] = 'Skill Hats',
['level'] = 10,
['entities'] = {'Blacksmiths Hat', 'Burning Mans Hat', 'Crafters Hat', 'Fishermans Hat', 'Fletchers Hat', 'Miners Hat', 'Performance Enhancing Hat', 'Potion Makers Hat', 'Runecrafters Hat', 'Star Gazing Hat', 'Woodcutters Hat'}
},
{
['verb'] = 'Purchase',
['name'] = 'Skill Bodies',
['level'] = 20,
['entities'] = {'Blacksmiths Body', 'Burning Mans Body', 'Crafters Body', 'Fishermans Body', 'Fletchers Body', 'Miners Body', 'Performance Enhancing Body', 'Potion Makers Body', 'Runecrafters Body', 'Star Gazing Body', 'Woodcutters Body'}
},
{
['verb'] = 'Purchase',
['name'] = 'Skill Leggings',
['level'] = 40,
['entities'] = {'Blacksmiths Leggings', 'Burning Mans Leggings', 'Crafters Leggings', 'Fishermans Leggings', 'Fletchers Leggings', 'Miners Leggings', 'Performance Enhancing Leggings', 'Potion Makers Leggings', 'Runecrafters Leggings', 'Star Gazing Leggings', 'Woodcutters Leggings'}
},
{
['verb'] = 'Purchase',
['name'] = 'Skill Boots',
['level'] = 60,
['entities'] = {'Blacksmiths Boots', 'Burning Mans Boots', 'Crafters Boots', 'Fishermans Boots', 'Fletchers Boots', 'Miners Boots', 'Performance Enhancing Boots', 'Potion Makers Boots', 'Runecrafters Boots', 'Star Gazing Boots', 'Woodcutters Boots'}
}
}
}
}
Line 789: Line 861:
end
end
return entityType
return entityType
end
function p._getOtherSkillReqs(reqList, skillName)
-- Remove the current skill's requirement, leaving us with all others
local otherReqs = {}
for i, req in ipairs(reqList) do
if req.type ~= 'SkillLevel' or (req.skillID ~= Constants.getSkillID(skillName) and req.level ~= 1) then
table.insert(otherReqs, req)
end
end
return otherReqs
end
function p._getSpellReqs(spell)
-- Get spell requirements that aren't SkillLevel (ex. equipped item)
local reqs = {}
if spell.requiredItemID ~= nil then
        local item = Items.getItemByID(spell.requiredItemID)
        if item ~= nil then
            table.insert(reqs, {['type'] = 'item', ['item'] = item})
        end
end
if spell.requirements ~= nil then
for i, req in ipairs(spell.requirements) do
table.insert(reqs, req)
end
end
return reqs
end
function p._getGatherableReqs(node, skillName)
-- Get gatherable requirements that aren't SkillLevel (ex. shop item)
local reqs = {}
if node.shopItemPurchased ~= nil then
local purchase = GameData.getEntityByID('shopPurchases', node.shopItemPurchased)
if purchase ~= nil then
table.insert(reqs, {['type'] = 'shop', ['purchase'] = purchase})
end
end
if node.totalMasteryRequired ~= nil then
table.insert(reqs, {['type'] = 'totalMastery', ['mastery'] = node.totalMasteryRequired, ['skill'] = skillName})
end
return reqs
end
function p._addEntities(frame)
local args = frame.args ~= nil and frame.args or frame
local entityList = args[1]
local data = args[2]
local entityType = args.type
local typeParam = args.typeParam
local subType = args.subType
local subTypeParam = args.subTypeParam
local otherReqsFunc = args.otherReqsFunc
for i, entity in ipairs(data) do
local processed = {}
processed.entityName = entity.name
processed.entityType = p._getEntityTrueSubtype(typeParam ~= nil and entity[typeParam] or entityType)
processed.subType = subTypeParam ~= nil and entity[subTypeParam] or subType
processed.skillLevel = entity.level
processed.otherReqs = otherReqsFunc ~= nil and otherReqsFunc(entity) or {}
table.insert(entityList, processed)
end
return entityList
end
end


Line 802: Line 947:
end)
end)
-- TODO: This can probably be made into a generic function for each entity
for i, item in ipairs(itemList) do
for i, item in ipairs(itemList) do
local processed = {}
local processed = {}
Line 810: Line 954:
processed.subType = p._getEntityTrueSubtype(item.type, item.name)
processed.subType = p._getEntityTrueSubtype(item.type, item.name)
processed.skillLevel = Items._getItemStat(item, skillReqLabel)
processed.skillLevel = Items._getItemStat(item, skillReqLabel)
processed.otherReqs = p._getOtherSkillReqs(item.equipRequirements, skillName)
table.insert(entityList, processed)
table.insert(entityList, processed)
end
end
Line 830: Line 975:
for i, area in ipairs(areaList) do
for i, area in ipairs(areaList) do
local processed = {}
local processed = {}
processed.entity = area
processed.entityName = area.name
processed.entityName = area.name
processed.entityType = area.type
processed.entityType = area.type
Line 839: Line 983:
end
end
end
end
processed.otherReqs = p._getOtherSkillReqs(area.entryRequirements, skillName)
table.insert(entityList, processed)
table.insert(entityList, processed)
end
end
Line 845: Line 990:
end
end


function p._addSpellsWithSkillRequirement(entityList, skillName)
function p._addShopPurchasesWithSkillRequirements(entityList, skillName)
local purchaseList = Shop.getPurchases(function(purchase)
local hasSkillReq = false
for i, req in ipairs(purchase.purchaseRequirements) do
if req.type == 'SkillLevel' and req.skillID == Constants.getSkillID(skillName) then
hasSkillReq = true
end
end
return hasSkillReq
end)
for i, purchase in ipairs(purchaseList) do
-- Skip skillcapes here, as we handle them with items
if purchase.category ~= 'melvorD:Skillcapes' and purchase.category ~= 'melvorTotH:SuperiorSkillcapes' then
local processed = {}
processed.entity = purchase
processed.entityName = Shop._getPurchaseName(purchase)
processed.entityType = 'shop'
processed.subType = Shop._getPurchaseType(purchase)
for a, req in ipairs(purchase.purchaseRequirements) do
if req.type == 'SkillLevel' and req.skillID == Constants.getSkillID(skillName) then
processed.skillLevel = req.level
end
end
processed.otherReqs = p._getOtherSkillReqs(purchase.purchaseRequirements, skillName)
table.insert(entityList, processed)
end
end
return entityList
end
 
function p._addTraderItemsWithSkillRequirements(entityList, skillName)
-- Iterate over each tradable resource, then each item you can get with it
for i, tsResource in ipairs(Township.Township.itemConversions.fromTownship) do
for j, tsPurchase in ipairs(tsResource.items) do
-- Does this purchase require the current skill?
local hasCurrentSkill = false
local currentSkillLevel = 0
for k, req in ipairs(tsPurchase.unlockRequirements) do
if req.type == 'SkillLevel' and req.skillID == Constants.getSkillID(skillName) then
hasCurrentSkill = true
currentSkillLevel = req.level
end
end
if hasCurrentSkill then
local item = Items.getItemByID(tsPurchase.itemID)
local processed = {}
processed.entity = item
processed.entityName = item.name
processed.entityType = 'trader'
processed.subType = 'trader'
processed.skillLevel = currentSkillLevel
processed.otherReqs = p._getOtherSkillReqs(tsPurchase.unlockRequirements, skillName)
table.insert(entityList, processed)
end
end
end
return entityList
end
 
function p._addAgilityObstaclesWithSkillRequirements(entityList, skillName)
for i, obstacle in ipairs(SkillData.Agility.obstacles) do
for j, req in ipairs(obstacle.skillRequirements) do
-- Does this obstacle require the current skill?
local hasCurrentSkill = false
local currentSkillLevel = 0
if req.type == 'SkillLevel' and req.skillID == Constants.getSkillID(skillName) then
hasCurrentSkill = true
currentSkillLevel = req.level
end
if hasCurrentSkill then
local processed = {}
processed.entity = obstacle
processed.entityName = obstacle.name
processed.entityType = 'obstacle'
-- Category is zero-indexed
processed.subType = 'Obstacle ' .. tostring(obstacle.category + 1)
processed.skillLevel = currentSkillLevel
processed.otherReqs = p._getOtherSkillReqs(obstacle.skillRequirements, skillName)
table.insert(entityList, processed)
end
end
end
return entityList
end
 
function p._addSpells(entityList, skillName)
-- Alt. Magic is in a separate function
local SPELL_TYPES = {'standardSpells', 'auroraSpells', 'curseSpells', 'ancientSpells', 'archaicSpells'}
local SPELL_TYPES = {'standardSpells', 'auroraSpells', 'curseSpells', 'ancientSpells', 'archaicSpells'}
-- Iterate through each spell type and each spell within that type
-- Iterate through each spell type and each spell within that type
for i, spellType in ipairs(SPELL_TYPES) do
for i, spellType in ipairs(SPELL_TYPES) do
for j, spell in ipairs(GameData.rawData[spellType]) do
entityList = p._addEntities({entityList, GameData.rawData[spellType], type='spell', subTypeParam='spellBook', otherReqsFunc=p._getSpellReqs})
local processed = {}
end
processed.entity = spell
processed.entityName = spell.name
return entityList
processed.entityType = 'spell'
end
processed.subType = spell.spellBook
 
processed.skillLevel = spell.level
function p._addAltMagic(entityList, skillName)
table.insert(entityList, processed)
entityList = p._addEntities({entityList, GameData.getSkillData('melvorD:Magic')['altSpells'], type='spell', subType='altMagic', otherReqsFunc=p._getSpellReqs})
return entityList
end
 
function p._addPrayers(entityList, skillName)
entityList = p._addEntities({entityList, GameData.rawData.prayers, type='prayer', subType='prayer'})
return entityList
end
 
function p._addFiremakingActions(entityList, skillName)
for i, fireLog in ipairs(SkillData.Firemaking.logs) do
local processed = {}
local logItem = Items.getItemByID(fireLog.logID)
processed.entityName = logItem.name
processed.entityType = 'item'
processed.subType = 'log'
processed.skillLevel = fireLog.level
processed.otherReqs = {}
table.insert(entityList, processed)
end
return entityList
end
 
function p._addThievingTargets(entityList, skillName)
entityList = p._addEntities({entityList, SkillData.Thieving.npcs, type='thieving', subType='npc'})
return entityList
end
 
function p._addConstellations(entityList, skillName)
entityList = p._addEntities({entityList, SkillData.Astrology.recipes, type='constellation', subType='constellation'})
return entityList
end
 
function p._addFarmingPlots(entityList, skillName)
for i, plot in ipairs(SkillData.Farming.plots) do
local plotName = 'Unknown Plot'
if plot.categoryID == 'melvorD:Allotment' then
plotName = 'Allotment Plot'
elseif plot.categoryID == 'melvorD:Herb' then
plotName = 'Herb Plot'
elseif plot.categoryID == 'melvorD:Tree' then
plotName = 'Tree Plot'
end
end
local processed = {}
processed.entityName = plotName
processed.entityType = 'plot'
processed.subType = plot.categoryID
processed.skillLevel = plot.level
processed.otherReqs = {}
table.insert(entityList, processed)
end
end
Line 864: Line 1,152:
end
end


function p._addPrayersWithSkillRequirement(entityList, skillName)
function p._addTownshipUnlocks(entityList, skillName)
for i, prayer in ipairs(GameData.rawData.prayers) do
-- This one is weird, because some of the data uses tiers and not levels
-- Add all of the biomes
for i, biome in ipairs(Township.Township.biomes) do
local tierReqs = Township._getTierRequirements(biome.tier)
local processed = {}
local processed = {}
processed.entity = prayer
processed.entityName = biome.name
processed.entityName = prayer.name
processed.entityType = 'biome'
processed.entityType = 'prayer'
processed.subType = 'biome'
processed.subType = 'prayer'
processed.skillLevel = tierReqs.level
processed.skillLevel = prayer.level
processed.otherReqs = {['population'] = tierReqs.population}
table.insert(entityList, processed)
table.insert(entityList, processed)
end
end
-- Add all of the buildings
for i, building in ipairs(Township.Township.buildings) do
local tierReqs = Township._getTierRequirements(building.tier)
local processed = {}
processed.entityName = building.name == 'Statues' and 'Statue of Worship' or building.name
processed.entityType = 'building'
processed.subType = building.upgradesFrom ~= nil and 'upgradedBuilding' or 'basicBuilding'
processed.upgradesFrom = Township._getBuildingDowngrade(building)
processed.skillLevel = tierReqs.level
processed.otherReqs = {['population'] = tierReqs.population}
table.insert(entityList, processed)
end
-- Add the fact that health starts decreasing at level 15
local specialHealth = {
['entityType'] = 'special',
['subType'] = 'special',
['skillLevel'] = 15,
['overrideText'] = 'Township health has a 25% chance to decrease by 1% per update'
}
table.insert(entityList, specialHealth)
return entityList
return entityList
end
end


function p._addGatherablesWithSkillRequirement(entityList, skillName)
function p._addAgilityObstacleSlotsAndPillars(entityList, skillName)
for i, level in ipairs(SkillData.Agility.obstacleUnlockLevels) do
local processed = {}
processed.entityName = 'Obstacle ' .. i
processed.entityType = 'obstacleslot'
processed.subType = 'obstacleslot'
processed.skillLevel = level
processed.otherReqs = {}
table.insert(entityList, processed)
end
-- Manually add pillars
table.insert(entityList, {['entityName'] = 'Passive Pillars', ['entityType'] = 'pillar', ['subType'] = 'pillar', ['skillLevel'] = 99, ['otherReqs'] = {}})
table.insert(entityList, {['entityName'] = 'Elite Passive Pillars', ['entityType'] = 'pillar', ['subType'] = 'pillar', ['skillLevel'] = 120, ['otherReqs'] = {}})
return entityList
end
 
function p._addGatherables(entityList, skillName)
-- Figure out what to look up based on the skill
-- Figure out what to look up based on the skill
local sourceData = {}
local sourceData = {}
local processedData = {}
local subType = ''
local subType = ''
if skillName == 'Woodcutting' then
if skillName == 'Woodcutting' then
Line 891: Line 1,221:
elseif skillName == 'Mining' then
elseif skillName == 'Mining' then
sourceData = SkillData.Mining.rockData
sourceData = SkillData.Mining.rockData
elseif skillName == 'Farming' then
sourceData = SkillData.Farming.recipes
end
end
for i, node in ipairs(sourceData) do
for i, node in ipairs(sourceData) do
local gatherable = Items.getItemByID(node.productId)
local gatherable = Items.getItemByID(node.productId)
local processedItem = {
local processed = {
['level'] = node.level,
['entityName'] = node.name,
['entityType'] = 'gathering',
['subType'] = subType,
['skillLevel'] = node.level,
['item'] = gatherable
['item'] = gatherable
}
}
-- Skill-specific overrides
if skillName == 'Fishing' then
if skillName == 'Fishing' then
processedItem['name'] = gatherable.name
processed['entityName'] = gatherable.name
else
elseif skillName == 'Mining' then
processedItem['name'] = node.name
processed['subType'] = node.category
elseif skillName == 'Farming' then
processed['entityName'] = gatherable.name
processed['subType'] = node.categoryID
processed['seed'] = Items.getItemByID(node.seedCost.id)
end
end
table.insert(processedData, processedItem)
processed['otherReqs'] = p._getGatherableReqs(node, skillName)
table.insert(entityList, processed)
end
end
return entityList
end


for i, entity in ipairs(processedData) do
function p._addRecipes(entityList, skillName)
local processed = {}
-- Figure out what to look up based on the skill
processed.entity = entity
local sourceData = SkillData[skillName].recipes
processed.item = entity.item
local sameRecipeAndProduct = false
processed.entityName = entity.name
if skillName == 'Herblore' then
processed.entityType = 'gathering'
sameRecipeAndProduct = true
if skillName == 'Mining' then
end
processed.subType = entity.type
else
for i, recipe in ipairs(sourceData) do
processed.subType = subType
local product = recipe
if not sameRecipeAndProduct then
product = Items.getItemByID(recipe.productID)
end
end
processed.skillLevel = entity.level
local processed = {
['entityName'] = product.name,
['entityType'] = 'artisan',
['subType'] = recipe.categoryID,
['skillLevel'] = recipe.level,
['item'] = product
}
table.insert(entityList, processed)
table.insert(entityList, processed)
end
end
return entityList
end
function p._addSkillMastery(entityList, skillName)
-- Add the "Skill Mastery" perk when relevant
table.insert(entityList, {['entityName'] = 'Skill Mastery', ['entityType'] = 'special', ['subType'] = 'skillmastery', ['skillLevel'] = 99, ['otherReqs'] = {}})
return entityList
return entityList
Line 927: Line 1,289:
local SOURCE_FUNCS = {
local SOURCE_FUNCS = {
['areas'] = p._addAreasWithSkillRequirement,
['areas'] = p._addAreasWithSkillRequirement,
['spells'] = p._addSpellsWithSkillRequirement,
['spells'] = p._addSpells,
['prayers'] = p._addPrayersWithSkillRequirement,
['altmagic'] = p._addAltMagic,
['gathering'] = p._addGatherablesWithSkillRequirement
['prayers'] = p._addPrayers,
['gatheringitems'] = p._addGatherables,
['artisanitems'] = p._addRecipes,
['firemaking'] = p._addFiremakingActions,
['thieving'] = p._addThievingTargets,
['constellations'] = p._addConstellations,
['shop'] = p._addShopPurchasesWithSkillRequirements,
['farmingplots'] = p._addFarmingPlots,
['townshipunlocks'] = p._addTownshipUnlocks,
['agilityslots'] = p._addAgilityObstacleSlotsAndPillars,
['mastery'] = p._addSkillMastery
}
}


function p._prepareSingleEntity(entity)
function p._prepareOtherReqs(entity)
local extraReqs = {}
if entity.otherReqs ~= nil and entity.otherReqs ~= {} then
-- Don't list a bazillion skills for the max skillcapes
if entity.entityName == 'Maximum Skillcape' then
return ' (requires level 99 in all skills)'
elseif entity.entityName == 'Superior Max Skillcape' then
return ' (requires level 120 in all skills)'
else
for i, req in ipairs(entity.otherReqs) do
-- TODO: "Completion" requirement? Might not be needed
if req.type == 'SkillLevel' then
local skillInfo = Icons.Icon({Constants.getSkillName(req.skillID), type='skill', notext='true'}) .. ' ' .. req.level
table.insert(extraReqs, skillInfo)
elseif req.type == 'DungeonCompletion' then
local dungeonName = GameData.getEntityByID('dungeons', req.dungeonID).name
-- If you only need to clear it once (Impending Darkness),
-- don't bother listing the count
if req.count > 1 then
table.insert(extraReqs, Icons.Icon({dungeonName, type='dungeon', qty=req.count, notext=true}) .. ' clears')
else
table.insert(extraReqs, Icons.Icon({dungeonName, type='dungeon', notext=true}) .. ' cleared')
end
elseif req.type == 'MonsterKilled' then
local monsterName = GameData.getEntityByID('monsters', req.monsterID).name
local monsterInfo = Icons.Icon({monsterName, type='monster', qty=req.count}) .. ' kills'
table.insert(extraReqs, monsterInfo)
elseif req.type == 'item' then
local itemInfo = Icons.Icon({req.item.name, type='item'})
table.insert(extraReqs, itemInfo)
elseif req.type == 'shop' then
table.insert(extraReqs, Shop._getPurchaseIcon({req.purchase}))
elseif req.type == 'totalMastery' then
table.insert(extraReqs, Num.formatnum(req.mastery) .. ' ' .. Icons.Icon({req.skill, type='skill', notext=true}) .. ' ' .. Icons.Icon({'Mastery'}))
elseif req.type == 'TownshipBuilding' then
local building = Township._getBuildingByID(req.buildingID)
table.insert(extraReqs, Icons.Icon({building.name, type='building', qty=req.count}))
end
end
end
end
if not next(extraReqs) then
return ''
else
return ' (requires ' .. table.concat(extraReqs, ', ') .. ')'
end
end
 
function p._prepareSingleEntity(entity, skillName)
-- Special children that need extra attention
if entity.overrideText then
return entity.overrideText
end
if entity.subType == 'skillmastery' then
return 'Gain ' .. Icons.Icon({skillName, type='skill', notext='true'}) .. ' [[Mastery#The_Mastery_Pool|Skill Mastery]]'
end
if skillName == 'Farming' and entity.seed then
return 'Plant ' .. Icons.Icon({entity.seed.name, type='item'}) .. ' to grow ' .. Icons.Icon({entity.entityName, type='item'})
end
if skillName == 'Township' and entity.upgradesFrom then
return 'Upgrade ' .. Icons.Icon({entity.upgradesFrom.name, type='building'}) .. ' to ' .. Icons.Icon({entity.entityName, type='building'})
end
-- What are you doing with the thing you unlock? ("verbs")
-- What are you doing with the thing you unlock? ("verbs")
local verb = ''
local verb = ''
if VERBS_PER_SUBTYPE[entity.subType] then
if VERBS_PER_SUBTYPE[entity.subType] then
verb = VERBS_PER_SUBTYPE[entity.subType] .. ' '
verb = VERBS_PER_SUBTYPE[entity.subType] .. ' '
elseif VERBS_PER_SUBTYPE[entity.entityType] then
verb = VERBS_PER_SUBTYPE[entity.entityType] .. ' '
end
end
-- Icon overrides
-- Icon overrides
local iconType = entity.entityType
local iconType = entity.entityType
local name = entity.entityName
local iconLink = entity.entityName
local iconText = entity.entityName
local iconImg = entity.entityName
local noLink = ''
if entity.entityType == 'slayerArea' then
if entity.entityType == 'slayerArea' then
iconType = 'combatArea'
iconType = 'combatArea'
Line 951: Line 1,392:
if entity.subType == 'fish' then
if entity.subType == 'fish' then
iconType = 'item'
iconType = 'item'
elseif Shared.contains({
'melvorD:Ore',
'melvorD:Essence',
'melvorD:Gem',
'melvorItA:AbyssalOre',
'melvorItA:AbyssalGem',
'melvorItA:Outcrop',
'melvorItA:AbyssalEssence'
}, entity.subType) then
iconType = 'rock'
else
else
iconType = entity.subType
iconType = entity.subType
end
end
name = entity.item.name
iconLink = entity.item.name
end
if entity.entityType == 'artisan' or entity.entityType == 'trader' then
iconType = 'item'
end
if entity.entityType == 'shop' then
iconType = string.lower(entity.subType)
end
if entity.entityType == 'plot' then
iconType = 'skill'
iconLink = 'Farming'
iconImg = 'Farming'
noLink = 'true'
end
if entity.entityType == 'obstacle' then
iconType = 'agility'
end
if entity.entityType == 'obstacleslot' then
iconType = 'agility'
iconLink = 'Obstacles'
end
if entity.entityType == 'pillar' then
iconType = 'agility'
iconLink = 'Passive_Pillars'
end
-- Any other requirements or post-scripts?
local extraReqs = p._prepareOtherReqs(entity)
local extraPost = ''
if entity.entityType == 'obstacle' then
extraPost = ' in ' .. entity.subType
end
end
return verb .. Icons.Icon({name, entity.entityName, img=entity.entityName, type=iconType})
return verb .. Icons.Icon({iconLink, iconText, img=iconImg, type=iconType, nolink=noLink}) .. extraPost .. extraReqs
end
end


function p._prepareGearSet(gearSet)
function p._prepareGearSet(gearSet, entity)
local icons = ''
local icons = ''
for i, entity in ipairs(gearSet.entities) do
for i, itemName in ipairs(gearSet.entities) do
-- Skip trimmed armor, which always starts with an open parnthesis
-- Skip trimmed armor, which always starts with an open parnthesis
if string.sub(entity, 1, 1) ~= '(' then
if string.sub(itemName, 1, 1) ~= '(' then
icons = icons .. Icons.Icon({entity, type='item', notext=true})
icons = icons .. Icons.Icon({itemName, type='item', notext=true})
end
end
end
end
return gearSet.verb .. ' ' .. icons .. ' ' .. gearSet.name
-- Get extra requirements for whicheve piece we processed first, as they
-- should all be the same
local extraReqs = p._prepareOtherReqs(entity)
return gearSet.verb .. ' ' .. icons .. ' ' .. gearSet.name .. extraReqs
end
end


function p._getSkillUnlockTable(skillName)
function p._getSkillUnlockTable(skillName, args)
local itemsOnly = args.itemsOnly ~= nil and args.itemsOnly or false
-- TODO: Pass these min/max level params along to filter by them
local minLevel = args.minLevel ~= nil and args.minLevel or 0
local maxLevel = args.maxLevel ~= nil and args.maxLevel or 999
-- What do we need to check for this skill? Avoid checking everything for
-- What do we need to check for this skill? Avoid checking everything for
-- every skill to save time...except for items, because every skill at least
-- every skill to save time, except for a few broad things
-- has skillcapes with a level requirement
local entityList = {}
local entityList = {}
entityList = p._addItemsWithSkillRequirement(entityList, skillName)
entityList = p._addItemsWithSkillRequirement(entityList, skillName)
for i, dataSource in ipairs(SKILL_CHECK_MAP[skillName]) do
entityList = SOURCE_FUNCS[dataSource](entityList, skillName)
if not itemsOnly then
entityList = p._addShopPurchasesWithSkillRequirements(entityList, skillName)
entityList = p._addTraderItemsWithSkillRequirements(entityList, skillName)
entityList = p._addAgilityObstaclesWithSkillRequirements(entityList, skillName)
-- Now loop through the stuff relevant to this skill
for i, dataSource in ipairs(SKILL_CHECK_MAP[skillName]) do
entityList = SOURCE_FUNCS[dataSource](entityList, skillName)
end
end
end
Line 988: Line 1,486:
-- Sort the big list of everything
-- Sort the big list of everything
table.sort(entityList, function(a, b)  
table.sort(entityList, function(a, b)
local aSubTypeSort = a.subType
if SUBTYPE_SORT_OVERRIDES[a.subType] then
aSubTypeSort = SUBTYPE_SORT_OVERRIDES[a.subType]
end
local bSubTypeSort = b.subType
if SUBTYPE_SORT_OVERRIDES[b.subType] then
bSubTypeSort = SUBTYPE_SORT_OVERRIDES[b.subType]
end
-- Sort by level first
-- Sort by level first
if a.skillLevel ~= b.skillLevel then
if a.skillLevel ~= b.skillLevel then
Line 996: Line 1,502:
return TYPE_SORT_ORDER[a.entityType] < TYPE_SORT_ORDER[b.entityType]
return TYPE_SORT_ORDER[a.entityType] < TYPE_SORT_ORDER[b.entityType]
    -- Then by subtype
    -- Then by subtype
elseif a.subType ~= b.subType then
elseif aSubTypeSort ~= bSubTypeSort then
     return a.subType < b.subType
     return aSubTypeSort < bSubTypeSort
-- And finally by name
-- And finally by name
else
else
Line 1,016: Line 1,522:
-- Skip this one if it's in a gear set we've already listed
-- Skip this one if it's in a gear set we've already listed
local entityGearSet = nil
local entityGearSet = nil
if entity.entityType == 'item' then
if entity.entityType == 'item' or entity.entityType == 'shop' then
for i, gearSet in ipairs(GEAR_SETS) do
for i, gearSet in ipairs(GEAR_SETS) do
if Shared.contains(gearSet.entities, entity.entityName) then
if Shared.contains(gearSet.entities, entity.entityName) then
Line 1,042: Line 1,548:
local toInsert = ''
local toInsert = ''
if entityGearSet ~= nil then
if entityGearSet ~= nil then
toInsert = p._prepareGearSet(entityGearSet)
toInsert = p._prepareGearSet(entityGearSet, entity)
table.insert(processedSets, entityGearSet.name)
table.insert(processedSets, entityGearSet.name)
else  
else  
toInsert = p._prepareSingleEntity(entity)
toInsert = p._prepareSingleEntity(entity, skillName)
end
end
Line 1,059: Line 1,565:


function p.getSkillUnlockTable(frame)
function p.getSkillUnlockTable(frame)
local skillName = frame.args ~= nil and frame.args[1]
local args = frame.args ~= nil and frame.args or frame
return p._getSkillUnlockTable(skillName)
local skillName = args[1]
end
return p._getSkillUnlockTable(skillName, args)
 
function p.test()
local ret = {}
for i, spell in ipairs(GameData.rawData.standardSpells) do
mw.logObject(spell)
end
return ret
end
end


return p
return p
2,875

edits