Module:LevelUpTable/Data: Difference between revisions

From Melvor Idle
(Add cooking data)
(Remove deprecated code)
 
(6 intermediate revisions by the same user not shown)
Line 9: Line 9:
local Shared = require('Module:Shared')
local Shared = require('Module:Shared')
local Constants = require('Module:Constants')
local Constants = require('Module:Constants')
local FL = require('Module:FunList')


local UnlockData = {}
local UnlockData = {}
UnlockData.__index = UnlockData
UnlockData.__index = UnlockData


function UnlockData.new(id, name, level, skill)
function UnlockData.new(id, name, level, skill, category)
     local self = setmetatable({}, UnlockData)
     local self = setmetatable({}, UnlockData)
     self.name = name
     self.name = name
     self.level = level
     self.level = level
     self.skill = skill
     self.skill = skill
    self.category = category or skill
     self.id = id
     self.id = id
     self.otherRequirements = nil
     self.otherRequirements = nil
     return self
     return self
end
--==== Helper functions ====--
-- Mimicks the C# Select method.
function Select(list, selector)
    local result = {}
    for _, item in ipairs(list) do
        table.insert(result, selector(item))
    end
    return result
end
function First(list, predicate)
for _, item in ipairs(list) do
if predicate(item) == true then
return item
end
end
error('No item found that matches predicate.')
end
-- Mimicks the C# Where method.
function Where(tbl, predicate)
    local result = {}
    for _, v in ipairs(tbl) do
        if predicate(v) then
            table.insert(result, v)
        end
    end
    return result
end
function Concat(...)
    local args = {...}
    local result = {}
    for _, tbl in ipairs(args) do
        for _, value in ipairs(tbl) do
            table.insert(result, value)
        end
    end
    return result
end
end


Line 71: Line 32:
return 'melvorD:Melvor'
return 'melvorD:Melvor'
end
end
end
function getDataSet(skillData, realm)
return GameData.getEntities(skillData, function(x) return Skills.getRecipeRealm(x) == realm.id end)
end
function getDataSetId(skillData, realm)
return GameData.getEntities(skillData, function(x) return getRealmFromID(x) == realm.id end)
end
end


Line 84: Line 37:
function getWoodcuttingData(realm)
function getWoodcuttingData(realm)
local skillID = 'Woodcutting'
local skillID = 'Woodcutting'
local data = getDataSet(SkillData.Woodcutting.trees, realm)
 
return FL.new(SkillData.Woodcutting.trees)
return Select(data, function(x)  
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
end)
:toTable()
end
end


function getMiningData(realm)
function getMiningData(realm)
local skillID = 'Mining'
local skillID = 'Mining'
local data = getDataSet(SkillData.Mining.rockData, realm)
 
return FL.new(SkillData.Woodcutting.rockData)
return Select(data, function(x)  
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
end)
:toTable()
end
end


function getFishingData(realm)
function getFishingData(realm)
local skillID = 'Fishing'
local skillID = 'Fishing'
local data = getDataSet(SkillData.Fishing.fish, realm)
 
return FL.new(SkillData.Woodcutting.fish)
return Select(data, function(x)  
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
end)
:toTable()
end
end


function getThievingData(realm)
function getThievingData(realm)
local skillID = 'Thieving'
local skillID = 'Thieving'
local data = getDataSet(SkillData.Thieving.npcs, realm)
 
return FL.new(SkillData.Woodcutting.npcs)
return Select(data, function(x)  
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
end)
:toTable()
end
end


function getFarmingData(realm)
function getFarmingData(realm)
local skillID = 'Farming'
local skillID = 'Farming'
local data = getDataSet(SkillData.Farming.recipes, realm)
-- Create lookup for plot category name.
-- Create lookup for plot category name.
local plotLookup = {}
local plotLookup = FL.new(SkillData.Farming.categories)
for _, plot in ipairs(SkillData.Farming.categories) do
:toDictionary(function(k) return k.id end,
        plotLookup[plot.id] = plot.name
  function(v) return v.name end)
    end
 
local seeds = FL.new(SkillData.Farming.recipes)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local productItem = Items.getItemByID(x.productId)
return UnlockData.new(productItem.id, productItem.name, Skills.getRecipeLevel(skillID, x), skillID)
end)


local seeds = Select(data, function(x)  
local plots = FL.new(SkillData.Farming.plots)
local productItem = Items.getItemByID(x.productId)
:where(function(x) return getRealmFromID(x) == realm.id end)
return UnlockData.new(productItem.id, productItem.name, Skills.getRecipeLevel(skillID, x), skillID)
:select(function(x)  
end)
return UnlockData.new(x.id, plotLookup[x.categoryID], Skills.getRecipeLevel(skillID, x), skillID, "Farming Plot")
end)
local plotData = getDataSetId(SkillData.Farming.plots, realm)
local plots = Select(plotData, function(x)
return UnlockData.new(x.id, plotLookup[x.categoryID], Skills.getRecipeLevel(skillID, x), "Farming Plot")
end)


return Concat(seeds, plots)
return seeds:concat(plots)
:toTable()
end
end


function getAstrologyData(realm)
function getAstrologyData(realm)
local skillID = 'Astrology'
local skillID = 'Astrology'
local data = getDataSet(SkillData.Astrology.recipes, realm)
 
return FL.new(SkillData.Woodcutting.recipes)
return Select(data, function(x)  
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
end)
:toTable()
end
end


function getAgilityData(realm)
function getAgilityData(realm)
return getAgilityRequirements('Agility', realm)
local skillID = 'Agility'
local pillarLevels = {
melvorF = 99,
melvorTotH = 120,
melvorItA = 60
}
local pillars = FL.new(SkillData.Agility.pillars)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local ns, _ = Shared.getLocalID(x.id)
return UnlockData.new(x.id, x.name, pillarLevels[ns], skillID)
end)
 
return pillars:concat(getAgilityRequirements(skillID, realm))
  :toTable()
end
end


function getFiremakingData(realm)
function getFiremakingData(realm)
local skillID = 'Firemaking'
local skillID = 'Firemaking'
local data = getDataSet(SkillData.Firemaking.logs, realm)


return Select(data, function(x)  
return FL.new(SkillData.Firemaking.logs)
local logs = Items.getItemByID(x.logID)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
return UnlockData.new(x.id, logs.name, Skills.getRecipeLevel(skillID, x), skillID)
:select(function(x)
end)
local logs = Items.getItemByID(x.logID)
return UnlockData.new(x.id, logs.name, Skills.getRecipeLevel(skillID, x), skillID)
end):toTable()
end
end


function getCookingData(realm)
function getCookingData(realm)
local skillID = 'Cooking'
local skillID = 'Cooking'
local data = getDataSet(SkillData.Cooking.recipes, realm)


return Select(data, function(x)  
return FL.new(SkillData.Cooking.recipes)
local foodItem = Items.getItemByID(x.productID)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
return UnlockData.new(foodItem.id, foodItem.name, Skills.getRecipeLevel(skillID, x), x.categoryID)
:select(function(x)
end)
local foodItem = Items.getItemByID(x.productID)
return UnlockData.new(foodItem.id, foodItem.name, Skills.getRecipeLevel(skillID, x), skillID, x.categoryID)
end):toTable()
end
 
function getSmithingData(realm)
local skillID = 'Smithing'
return FL.new(SkillData.Smithing.recipes)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local product = Items.getItemByID(x.productID)
return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID, x.categoryID)
end):toTable()
end
 
function getFletchingData(realm)
local skillID = 'Fletching'
return FL.new(SkillData.Fletching.recipes)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local product = Items.getItemByID(x.productID)
return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
end):toTable()
end
 
function getCraftingData(realm)
local skillID = 'Crafting'
return FL.new(SkillData.Crafting.recipes)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local product = Items.getItemByID(x.productID)
return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
end):toTable()
end
 
function getRunecraftingData(realm)
local skillID = 'Runecrafting'
return FL.new(SkillData.Runecrafting.recipes)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local product = Items.getItemByID(x.productID)
return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
end):toTable()
end
 
function getHerbloreData(realm)
local skillID = 'Herblore'
 
return FL.new(SkillData.Herblore.recipes)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local lastPotID = FL.new(x.potionIDs):last()
local lastPot = Items.getItemByID(lastPotID)
return UnlockData.new(lastPot.id, lastPot.name, Skills.getRecipeLevel(skillID, x), skillID)
end):toTable()
end
 
function getSummoningData(realm)
local skillID = 'Summoning'
return FL.new(SkillData.Summoning.recipes)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
:select(function(x)
local product = Items.getItemByID(x.productID)
return UnlockData.new(x.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
end):toTable()
end
end


Line 181: Line 220:
function getUnlockReqs(constellation)
function getUnlockReqs(constellation)
if constellation.abyssalModifiers == nil then return nil end
if constellation.abyssalModifiers == nil then return nil end
for _, mod in ipairs(constellation.abyssalModifiers) do
local mod = FL.new(constellation.abyssalModifiers):firstOrDefault(function(mod)
        if mod.unlockRequirements then
return mod.unlockRequirements ~= nil and
            for _, req in ipairs(mod.unlockRequirements) do
  FL.new(mod.unlockRequirements):any(function(req) return req.skillID == skillID end)
                if req.skillID == skillID then
end, nil)
                -- Return the unlockRequirements table, so we can reuse this function later to retrieve unlock data.
                return mod.unlockRequirements
return mod and mod.unlockRequirements or nil
                end
                end
            end
end
    return nil
end
end


local filterFunc = function(x)
return FL.new(SkillData.Astrology.recipes)
return Skills.getRecipeRealm(x) == realm.id and getUnlockReqs(x)
:where(function(x) return Skills.getRecipeRealm(x) == realm.id and getUnlockReqs(x) end)
end
:select(function(const)  
local unlockReqs = FL.new(getUnlockReqs(const))
local constellations = GameData.getEntities(SkillData.Astrology.recipes, filterFunc)
local myUnlocks = unlockReqs:first(function(req) return req.skillID == skillID end)
return Select(constellations, function(const)
local otherUnlocks = unlockReqs:where(function(req) return req.skillID ~= skillID end)
local unlockReqs = getUnlockReqs(const)
local myUnlocks = First(unlockReqs, function(req) return req.skillID == skillID end)
local otherUnlocks = Where(unlockReqs, function(req) return req.skillID ~= skillID end)
local data = UnlockData.new(const.id, const.name, myUnlocks.level, skill)
local data = UnlockData.new(const.id, const.name, myUnlocks.level, skill, "Astrology")
data.otherRequirements = {}
data.otherRequirements = {}
table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, Skills.getRecipeLevel(AstroID, const), AstroID))
table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, Skills.getRecipeLevel(AstroID, const), AstroID))
for _, req in ipairs(otherUnlocks) do
for _, req in pairs(otherUnlocks) do
table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, req.level, req.type))
table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, req.level, req.type))
end
end
return data
return data
end)
end):toTable()
end
end


Line 218: Line 249:
local AgilitySkillID = Constants.getSkillID('Agility')
local AgilitySkillID = Constants.getSkillID('Agility')
local AgilityID = 'Agility'
local AgilityID = 'Agility'
 
function hasSkill(obstacle)
-- Filter obstacles by realm.
if obstacle.skillRequirements == nil then
local obstacles = FL.new(SkillData.Agility.obstacles)
return false
:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
end
for _, req in pairs(obstacle.skillRequirements) do
if req.skillID == skillID then return true end
end
return false
end
function flattenRequirements(obstacle)
local skillRequirements = {}
for _, req in pairs(obstacle.skillRequirements) do
table.insert(skillRequirements, { level = req.level, skillID = req.skillID })
end
table.insert(skillRequirements, { level = Skills.getRecipeLevelRealm('Agility', obstacle), skillID = AgilitySkillID })
-- If the skill is something other than the Agility skill, filter obstacles that contain
return skillRequirements
-- the provided skill as a requirement.
end
if skill ~= AgilityID then
obstacles:where(function(obstacle)
local filterFunc = function(x)
return obstacle.skillRequirements ~= nil and
return Skills.getRecipeRealm(x) == realm.id and hasSkill(x)
FL.new(obstacle.skillRequirements):any(function(req) return req.skillID == skillID end)
end)
end
end


local obstacles = nil
-- Add agility skill requirement to requirements list.
if skill == 'Agility' then
return obstacles:select(function(o)
obstacles = getDataSet(SkillData.Agility.obstacles, realm)
local reqs = FL.new(o.skillRequirements)
else
:select(function(req) return { level = req.level, skillID = req.skillID } end)
obstacles = GameData.getEntities(SkillData.Agility.obstacles, filterFunc)
:append({ level = Skills.getRecipeLevelRealm('Agility', o), skillID = AgilitySkillID})
end


-- Add agility skill requirement to requirements list.
local myUnlock = reqs:first(function(req) return req.skillID == skillID end)
return Select(obstacles, function(o)
local otherUnlocks = reqs:where(function(req) return req.skillID ~= skillID end)
local unlockReqs = flattenRequirements(o)
local myUnlock = First(unlockReqs, function(req) return req.skillID == skillID end)
local otherUnlocks = Where(unlockReqs, function(req) return req.skillID ~= skillID end)
local data = UnlockData.new(o.id, o.name, myUnlock.level, skill)
local data = UnlockData.new(o.id, o.name, myUnlock.level, skill, 'AgilityObstacle')
data.otherRequirements = {}
data.otherRequirements = otherUnlocks:select(function(req)
for _, req in ipairs(otherUnlocks) do
local _, skillName = GameData.getLocalID(req.skillID)
local _, skillName = GameData.getLocalID(req.skillID)
table.insert(data.otherRequirements, UnlockData.new(o.id, o.name, req.level, skillName))
return UnlockData.new(o.id, o.name, req.level, skillName)
end
end):toTable()
 
return data
return data
end)
end):toTable()
end
end


Line 269: Line 285:
local realmName = 'Abyssal Realm'
local realmName = 'Abyssal Realm'
local realm = Skills.getRealmFromName(realmName)
local realm = Skills.getRealmFromName(realmName)
Debug.log(getCookingData(realm))
Debug.log(getAstrologyRequirements('Herblore', realm))
end
end


p.UnlockData = UnlockData
p.UnlockData = UnlockData
return p
return p

Latest revision as of 16:45, 20 July 2024

Documentation for this module may be created at Module:LevelUpTable/Data/doc

-- SkillData object that acts as an interface for data shared between skills.
local p = {}

local Debug = require('Module:Debug') --  < Remove when module is finished.
local GameData = require('Module:GameData')
local SkillData = GameData.skillData
local Skills = require('Module:Skills')
local Items = require('Module:Items')
local Shared = require('Module:Shared')
local Constants = require('Module:Constants')
local FL = require('Module:FunList')

local UnlockData = {}
UnlockData.__index = UnlockData

function UnlockData.new(id, name, level, skill, category)
    local self = setmetatable({}, UnlockData)
    self.name = name
    self.level = level
    self.skill = skill
    self.category = category or skill
    self.id = id
    self.otherRequirements = nil
    return self
end

function getRealmFromID(obj)
	local ns, _ = Shared.getLocalID(obj.id)
	if ns == 'melvorItA' then
		return 'melvorItA:Abyssal'
	else
		return 'melvorD:Melvor'
	end
end

--==== Skill Data Collection ====--
function getWoodcuttingData(realm)
	local skillID = 'Woodcutting'

	return FL.new(SkillData.Woodcutting.trees)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
		:toTable()
end

function getMiningData(realm)
	local skillID = 'Mining'

	return FL.new(SkillData.Woodcutting.rockData)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
		:toTable()
end

function getFishingData(realm)
	local skillID = 'Fishing'

	return FL.new(SkillData.Woodcutting.fish)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
		:toTable()
end

function getThievingData(realm)
	local skillID = 'Thieving'

	return FL.new(SkillData.Woodcutting.npcs)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
		:toTable()
end

function getFarmingData(realm)
	local skillID = 'Farming'
	-- Create lookup for plot category name.
	local plotLookup = FL.new(SkillData.Farming.categories)
		:toDictionary(function(k) return k.id end,
					  function(v) return v.name end)

	local seeds = FL.new(SkillData.Farming.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local productItem = Items.getItemByID(x.productId)
			return UnlockData.new(productItem.id, productItem.name, Skills.getRecipeLevel(skillID, x), skillID)		
		end)

	local plots = FL.new(SkillData.Farming.plots)
		:where(function(x) return getRealmFromID(x) == realm.id end)
		:select(function(x) 
			return UnlockData.new(x.id, plotLookup[x.categoryID], Skills.getRecipeLevel(skillID, x), skillID, "Farming Plot")
		end)

	return seeds:concat(plots)
				:toTable()
end

function getAstrologyData(realm)
	local skillID = 'Astrology'

	return FL.new(SkillData.Woodcutting.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x) return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID) end)
		:toTable()
end

function getAgilityData(realm)
	local skillID = 'Agility'
	local pillarLevels = {
		melvorF = 99,
		melvorTotH = 120,
		melvorItA = 60
	}
	
	local pillars = FL.new(SkillData.Agility.pillars)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local ns, _ = Shared.getLocalID(x.id)
			return UnlockData.new(x.id, x.name, pillarLevels[ns], skillID)	
		end)

	return pillars:concat(getAgilityRequirements(skillID, realm))
				  :toTable()
end

function getFiremakingData(realm)
	local skillID = 'Firemaking'

	return FL.new(SkillData.Firemaking.logs)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x) 
			local logs = Items.getItemByID(x.logID)
			return UnlockData.new(x.id, logs.name, Skills.getRecipeLevel(skillID, x), skillID)
		end):toTable()
end

function getCookingData(realm)
	local skillID = 'Cooking'

	return FL.new(SkillData.Cooking.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local foodItem = Items.getItemByID(x.productID)
			return UnlockData.new(foodItem.id, foodItem.name, Skills.getRecipeLevel(skillID, x), skillID, x.categoryID)			
		end):toTable()
end

function getSmithingData(realm)
	local skillID = 'Smithing'
	
	return FL.new(SkillData.Smithing.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local product = Items.getItemByID(x.productID)
			return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID, x.categoryID)			
		end):toTable()
end

function getFletchingData(realm)
	local skillID = 'Fletching'
	
	return FL.new(SkillData.Fletching.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)	
		:select(function(x)
			local product = Items.getItemByID(x.productID)
			return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
		end):toTable()
end

function getCraftingData(realm)
	local skillID = 'Crafting'
	
	return FL.new(SkillData.Crafting.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local product = Items.getItemByID(x.productID)
			return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
		end):toTable()
end

function getRunecraftingData(realm)
	local skillID = 'Runecrafting'
	
	return FL.new(SkillData.Runecrafting.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local product = Items.getItemByID(x.productID)
			return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)		
		end):toTable()
end

function getHerbloreData(realm)
	local skillID = 'Herblore'

	return FL.new(SkillData.Herblore.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local lastPotID = FL.new(x.potionIDs):last()
			local lastPot = Items.getItemByID(lastPotID)
			return UnlockData.new(lastPot.id, lastPot.name, Skills.getRecipeLevel(skillID, x), skillID)
		end):toTable()
end

function getSummoningData(realm)
	local skillID = 'Summoning'
	
	return FL.new(SkillData.Summoning.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		:select(function(x)
			local product = Items.getItemByID(x.productID)
			return UnlockData.new(x.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)			
		end):toTable()
end

--==== Other Requirements Collection ====--
function getAstrologyRequirements(skill, realm)
	-- Only Abyssal Astrology has level unlock requirements as of V1.3
	local skillID = Constants.getSkillID(skill)
	local AstroID = 'Astrology'
	-- Find if this constellation has the provided skill as an unlock requirement.
	function getUnlockReqs(constellation)
		if constellation.abyssalModifiers == nil then return nil end
		local mod = FL.new(constellation.abyssalModifiers):firstOrDefault(function(mod)
				return mod.unlockRequirements ~= nil and 
					   FL.new(mod.unlockRequirements):any(function(req) return req.skillID == skillID end)
		end, nil)
		
		return mod and mod.unlockRequirements or nil
	end

	return FL.new(SkillData.Astrology.recipes)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id and getUnlockReqs(x)	end)
		:select(function(const) 
			local unlockReqs = FL.new(getUnlockReqs(const))
			local myUnlocks = unlockReqs:first(function(req) return req.skillID == skillID end)
			local otherUnlocks = unlockReqs:where(function(req) return req.skillID ~= skillID end)
		
			local data = UnlockData.new(const.id, const.name, myUnlocks.level, skill, "Astrology")
			data.otherRequirements = {}
			table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, Skills.getRecipeLevel(AstroID, const), AstroID))
			for _, req in pairs(otherUnlocks) do
				table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, req.level, req.type))
			end
			return data			
		end):toTable()
end

function getAgilityRequirements(skill, realm)
	local skillID = Constants.getSkillID(skill)
	local AgilitySkillID = Constants.getSkillID('Agility')
	local AgilityID = 'Agility'

	-- Filter obstacles by realm.
	local obstacles = FL.new(SkillData.Agility.obstacles)
		:where(function(x) return Skills.getRecipeRealm(x) == realm.id end)
		
	-- If the skill is something other than the Agility skill, filter obstacles that contain
	-- the provided skill as a requirement.
	if skill ~= AgilityID then
		obstacles:where(function(obstacle)
			return obstacle.skillRequirements ~= nil and
			FL.new(obstacle.skillRequirements):any(function(req) return req.skillID == skillID end)
		end)
	end

	-- Add agility skill requirement to requirements list.
	return obstacles:select(function(o)
		local reqs = FL.new(o.skillRequirements)
			:select(function(req) return { level = req.level, skillID = req.skillID } end)
			:append({ level = Skills.getRecipeLevelRealm('Agility', o), skillID = AgilitySkillID})

		local myUnlock = reqs:first(function(req) return req.skillID == skillID end)
		local otherUnlocks = reqs:where(function(req) return req.skillID ~= skillID end)
		
		local data = UnlockData.new(o.id, o.name, myUnlock.level, skill, 'AgilityObstacle')
		data.otherRequirements = otherUnlocks:select(function(req)
			local _, skillName = GameData.getLocalID(req.skillID)
			return UnlockData.new(o.id, o.name, req.level, skillName)
		end):toTable()

		return data
	end):toTable()
end

function p.test()
	local realmName = 'Abyssal Realm'
	local realm = Skills.getRealmFromName(realmName)
	Debug.log(getAstrologyRequirements('Herblore', realm))
end

p.UnlockData = UnlockData
return p