Module:LevelUpTable/Data

From Melvor Idle
< Module:LevelUpTable
Revision as of 12:32, 8 July 2024 by Ricewind (talk | contribs) (Add agility pillar data)

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 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

--==== 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

function getRealmFromID(obj)
	local ns, _ = Shared.getLocalID(obj.id)
	if ns == 'melvorItA' then
		return 'melvorItA:Abyssal'
	else
		return 'melvorD:Melvor'
	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

--==== Skill Data Collection ====--
function getWoodcuttingData(realm)
	local skillID = 'Woodcutting'
	local data = getDataSet(SkillData.Woodcutting.trees, realm)
	
	return Select(data, function(x) 
		return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)
end

function getMiningData(realm)
	local skillID = 'Mining'
	local data = getDataSet(SkillData.Mining.rockData, realm)
	
	return Select(data, function(x) 
		return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)
end

function getFishingData(realm)
	local skillID = 'Fishing'
	local data = getDataSet(SkillData.Fishing.fish, realm)
	
	return Select(data, function(x) 
		return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)
end

function getThievingData(realm)
	local skillID = 'Thieving'
	local data = getDataSet(SkillData.Thieving.npcs, realm)
	
	return Select(data, function(x) 
		return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)
end

function getFarmingData(realm)
	local skillID = 'Farming'
	local data = getDataSet(SkillData.Farming.recipes, realm)
	-- Create lookup for plot category name.
	local plotLookup = {}
	for _, plot in ipairs(SkillData.Farming.categories) do
        plotLookup[plot.id] = plot.name
    end

	local seeds = Select(data, function(x) 
		local productItem = Items.getItemByID(x.productId)
		return UnlockData.new(productItem.id, productItem.name, Skills.getRecipeLevel(skillID, x), skillID)
	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), skillID, "Farming Plot")
	end)

	return Concat(seeds, plots)
end

function getAstrologyData(realm)
	local skillID = 'Astrology'
	local data = getDataSet(SkillData.Astrology.recipes, realm)
	
	return Select(data, function(x) 
		return UnlockData.new(x.id, x.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)
end

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

	local obstacles = getAgilityRequirements(skillID, realm)
	return Concat(obstacles, pillars)
end

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

	return Select(data, function(x) 
		local logs = Items.getItemByID(x.logID)
		return UnlockData.new(x.id, logs.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)
end

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

	return Select(data, function(x) 
		local foodItem = Items.getItemByID(x.productID)
		return UnlockData.new(foodItem.id, foodItem.name, Skills.getRecipeLevel(skillID, x), skillID, x.categoryID)
	end)	
end

function getSmithingData(realm)
	local skillID = 'Smithing'
	local data = getDataSet(SkillData.Smithing.recipes, realm)
	
	return Select(data, function(x) 
		local product = Items.getItemByID(x.productID)
		return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID, x.categoryID)
	end)	
end

function getFletchingData(realm)
	local skillID = 'Fletching'
	local data = getDataSet(SkillData.Fletching.recipes, realm)
	
	return Select(data, function(x) 
		local product = Items.getItemByID(x.productID)
		return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)	
end

function getCraftingData(realm)
	local skillID = 'Crafting'
	local data = getDataSet(SkillData.Crafting.recipes, realm)
	
	return Select(data, function(x) 
		local product = Items.getItemByID(x.productID)
		return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)	
end

function getRunecraftingData(realm)
	local skillID = 'Runecrafting'
	local data = getDataSet(SkillData.Runecrafting.recipes, realm)
	
	return Select(data, function(x) 
		local product = Items.getItemByID(x.productID)
		return UnlockData.new(product.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)	
end

function getHerbloreData(realm)
	local skillID = 'Herblore'
	local data = getDataSet(SkillData.Herblore.recipes, realm)
	
	function getLastPotion(potion)
		local lastID = nil
		for _, v in ipairs(potion.potionIDs) do
			lastID = v
		end
		return Items.getItemByID(lastID)
	end
	
	return Select(data, function(x) 
		local lastPot = getLastPotion(x)
		return UnlockData.new(lastPot.id, lastPot.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)	
end

function getSummoningData(realm)
	local skillID = 'Summoning'
	local data = getDataSet(SkillData.Summoning.recipes, realm)
	
	return Select(data, function(x) 
		local product = Items.getItemByID(x.productID)
		return UnlockData.new(x.id, product.name, Skills.getRecipeLevel(skillID, x), skillID)
	end)	
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
		for _, mod in ipairs(constellation.abyssalModifiers) do
        	if mod.unlockRequirements then
            	for _, req in ipairs(mod.unlockRequirements) do
                	if req.skillID == skillID then
                		-- Return the unlockRequirements table, so we can reuse this function later to retrieve unlock data.
                		return mod.unlockRequirements
                	end
                end
            end
		end
    	return nil
	end

	local filterFunc = function(x)
		return Skills.getRecipeRealm(x) == realm.id and getUnlockReqs(x)
	end
	
	local constellations = GameData.getEntities(SkillData.Astrology.recipes, filterFunc)
	return Select(constellations, function(const)
		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)
		data.otherRequirements = {}
		table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, Skills.getRecipeLevel(AstroID, const), AstroID))
		for _, req in ipairs(otherUnlocks) do
			table.insert(data.otherRequirements, UnlockData.new(const.id, const.name, req.level, req.type))
		end
		return data
	end)
end

function getAgilityRequirements(skill, realm)
	local skillID = Constants.getSkillID(skill)
	local AgilitySkillID = Constants.getSkillID('Agility')
	local AgilityID = 'Agility'
	
	function hasSkill(obstacle)
		if obstacle.skillRequirements == nil then
			return false
		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 })
		return skillRequirements
	end
	
	local filterFunc = function(x)
		return Skills.getRecipeRealm(x) == realm.id and hasSkill(x)
	end

	local obstacles = nil
	if skill == 'Agility' then
		obstacles = getDataSet(SkillData.Agility.obstacles, realm)
	else
		obstacles = GameData.getEntities(SkillData.Agility.obstacles, filterFunc)
	end

	-- Add agility skill requirement to requirements list.
	return Select(obstacles, function(o)
		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)
		data.otherRequirements = {}
		for _, req in ipairs(otherUnlocks) do
			local _, skillName = GameData.getLocalID(req.skillID)
			table.insert(data.otherRequirements, UnlockData.new(o.id, o.name, req.level, skillName))
		end
		return data
	end)
end

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

p.UnlockData = UnlockData
return p