Module:SkillUnlocks: Difference between revisions
From Melvor Idle
No edit summary |
No edit summary |
||
Line 26: | Line 26: | ||
['Magic Staff'] = 'Wield', | ['Magic Staff'] = 'Wield', | ||
['Magic Book'] = 'Wield', | ['Magic Book'] = 'Wield', | ||
['Armour'] = ' | ['Ranged Weapon'] = 'Wield', | ||
['Magic Armour'] = ' | ['Ammo'] = 'Use', | ||
['Ranged Armour'] = ' | ['Armour'] = 'Equip', | ||
['Slayer Armour'] = ' | ['Magic Armour'] = 'Equip', | ||
['Ring'] = ' | ['Ranged Armour'] = 'Equip', | ||
['Amulet'] = ' | ['Slayer Armour'] = 'Equip', | ||
['Ring'] = 'Equip', | |||
['Amulet'] = 'Equip', | |||
['combatArea'] = 'Access', | ['combatArea'] = 'Access', | ||
['slayerArea'] = 'Access', | ['slayerArea'] = 'Access', | ||
Line 37: | Line 39: | ||
} | } | ||
local SUBTYPE_OVERRIDES = { | local SUBTYPE_OVERRIDES = { | ||
['Agile Wings Rapier'] = 'Weapon', | |||
['Ethereal Greataxe'] = 'Weapon', | |||
['Ethereal Longbow'] = 'Ranged Weapon', | |||
['Ethereal Staff'] = 'Magic Staff', | |||
['Feather Storm Crossbow'] = 'Ranged Weapon', | |||
['FrostSpark 1H Sword'] = 'Weapon', | ['FrostSpark 1H Sword'] = 'Weapon', | ||
['Lightning Coil 2H Staff'] = 'Magic Staff', | |||
['Lightning Strike 1H Sword'] = 'Weapon', | ['Lightning Strike 1H Sword'] = 'Weapon', | ||
['Royal Toxins Spear'] = 'Weapon', | ['Royal Toxins Spear'] = 'Weapon', | ||
['Slicing Maelstrom Wand'] = 'Magic Wand', | |||
['Spectral Ice Sword'] = 'Weapon', | ['Spectral Ice Sword'] = 'Weapon', | ||
[' | ['Torrential Blast Crossbow'] = 'Ranged Weapon' | ||
} | } | ||
Revision as of 00:17, 24 April 2023
Documentation for this module may be created at Module:SkillUnlocks/doc
local p = {}
--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"
--for each skill that does the same thing, but it is not exhaustive and not
--permanently visible for most combat skills.
local Shared = require('Module:Shared')
local Constants = require('Module:Constants')
local Icons = require('Module:Icons')
local Items = require('Module:Items')
local CombatAreas = require('Module:CombatAreas')
local CHECK_ITEMS = {'Attack', 'Strength', 'Defence', 'Ranged', 'Magic',
'Slayer'}
local CHECK_AREAS = {'Slayer'}
local TYPE_SORT_ORDER = {
['item'] = 1,
['combatArea'] = 2,
['slayerArea'] = 3,
['dungeon'] = 4
}
local VERBS_PER_SUBTYPE = {
['Weapon'] = 'Wield',
['Magic Wand'] = 'Wield',
['Magic Staff'] = 'Wield',
['Magic Book'] = 'Wield',
['Ranged Weapon'] = 'Wield',
['Ammo'] = 'Use',
['Armour'] = 'Equip',
['Magic Armour'] = 'Equip',
['Ranged Armour'] = 'Equip',
['Slayer Armour'] = 'Equip',
['Ring'] = 'Equip',
['Amulet'] = 'Equip',
['combatArea'] = 'Access',
['slayerArea'] = 'Access',
['dungeon'] = 'Access'
}
local SUBTYPE_OVERRIDES = {
['Agile Wings Rapier'] = 'Weapon',
['Ethereal Greataxe'] = 'Weapon',
['Ethereal Longbow'] = 'Ranged Weapon',
['Ethereal Staff'] = 'Magic Staff',
['Feather Storm Crossbow'] = 'Ranged Weapon',
['FrostSpark 1H Sword'] = 'Weapon',
['Lightning Coil 2H Staff'] = 'Magic Staff',
['Lightning Strike 1H Sword'] = 'Weapon',
['Royal Toxins Spear'] = 'Weapon',
['Slicing Maelstrom Wand'] = 'Magic Wand',
['Spectral Ice Sword'] = 'Weapon',
['Torrential Blast Crossbow'] = 'Ranged Weapon'
}
function p._getEntityTrueSubtype(entityType, entityName)
-- Corrects types of a few entities with misleading/wrong data
if SUBTYPE_OVERRIDES[entityName] then
entityType = SUBTYPE_OVERRIDES[entityName]
end
return entityType
end
function p._addItemsWithSkillRequirement(entityList, skillName)
local skillReqLabel = skillName:lower() .. 'LevelRequired'
local itemList = Items.getItems(function(item)
-- Exclude Golbin Raid exclusives
if item.golbinRaidExclusive ~= nil and item.golbinRaidExclusive then
return false
end
return Items._getItemStat(item, skillReqLabel) ~= nil
end)
-- TODO: This can probably be made into a generic function for each entity
for i, item in ipairs(itemList) do
local processed = {}
processed.entity = item
processed.entityName = item.name
processed.entityType = 'item'
processed.subType = p._getEntityTrueSubtype(item.type, item.name)
processed.skillLevel = Items._getItemStat(item, skillReqLabel)
table.insert(entityList, processed)
end
return entityList
end
-- This covers combat areas, Slayer areas, and dungeons
function p._addAreasWithSkillRequirement(entityList, skillName)
local areaList = CombatAreas.getAreas(function(area)
local hasSkillReq = false
for i, req in ipairs(area.entryRequirements) do
if req.type == 'SkillLevel' and req.skillID == Constants.getSkillID(skillName) then
hasSkillReq = true
end
end
return hasSkillReq
end)
for i, area in ipairs(areaList) do
local processed = {}
processed.entity = area
processed.entityName = area.name
processed.entityType = area.type
processed.subType = p._getEntityTrueSubtype(area.type, area.name)
for a, req in ipairs(area.entryRequirements) do
if req.type == 'SkillLevel' and req.skillID == Constants.getSkillID(skillName) then
processed.skillLevel = req.level
end
end
table.insert(entityList, processed)
end
return entityList
end
function p._getSkillUnlockTable(skillName)
-- local skillReqLabel = skillName:lower() .. 'LevelRequired'
-- What do we need to check for this skill? (avoid checking everything for
-- every skill to save time)
local entityList = {}
if Shared.contains(CHECK_ITEMS, skillName) then
entityList = p._addItemsWithSkillRequirement(entityList, skillName)
end
if Shared.contains(CHECK_AREAS, skillName) then
entityList = p._addAreasWithSkillRequirement(entityList, skillName)
end
if Shared.tableIsEmpty(entityList) then
-- TODO: More specific empty handling
return nil
end
-- Sort the big list of everything
table.sort(entityList, function(a, b)
-- Sort by level first
if a.skillLevel ~= b.skillLevel then
return a.skillLevel < b.skillLevel
-- Then by type
elseif TYPE_SORT_ORDER[a.entityType] ~= TYPE_SORT_ORDER[b.entityType] then
return TYPE_SORT_ORDER[a.entityType] < TYPE_SORT_ORDER[b.entityType]
-- Then by subtype
elseif a.subType ~= b.subType then
return a.subType < b.subType
-- And finally by name
else
return a.entityName < b.entityName
end
end)
-- Header and columns
local resultPart = {}
table.insert(resultPart, '{| class="wikitable"\r\n|- class="headerRow-0"')
table.insert(resultPart, '\r\n!' .. Icons.Icon({skillName, type='skill', notext='true'}) .. ' Level')
table.insert(resultPart, '\r\n!Unlocks')
-- Time to iterate!
local currentLevel = 0
for i, entity in ipairs(entityList) do
local foundLevel = entity.skillLevel
-- Start a new row if the current entity's level is higher than the
-- current row
if foundLevel ~= currentLevel then
currentLevel = foundLevel
table.insert(resultPart, '\r\n|-')
table.insert(resultPart, '\r\n|' .. foundLevel)
table.insert(resultPart, '\r\n|')
else
table.insert(resultPart, '<br/>')
end
-- What are you doing with the thing you unlock? ("verbs")
local verb = ''
if VERBS_PER_SUBTYPE[entity.subType] then
verb = VERBS_PER_SUBTYPE[entity.subType] .. ' '
end
-- Icon overrides
local iconType = entity.entityType
if entity.entityType == 'slayerArea' then
iconType = 'combatArea'
end
-- Append entity to the column
table.insert(resultPart, verb .. Icons.Icon({entity.entityName, type=iconType}))
end
table.insert(resultPart, '\r\n|}')
return table.concat(resultPart)
end
function p.getSkillUnlockTable(frame)
local skillName = frame.args ~= nil and frame.args[1]
return p._getSkillUnlockTable(skillName)
end
return p