Created
March 20, 2024 15:15
-
-
Save gaymeowing/7870f86e8c31bd06fb4d1daa8c651e5c to your computer and use it in GitHub Desktop.
Class for experience and levels
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--!native | |
--!strict | |
-- Exp | |
-- Level and experience class modified from | |
-- https://rostrap.github.io/Libraries/Math/Leveler/ | |
-- @Kalrnlo | |
-- 20/03/2024 | |
local Players = game:GetService("Players") | |
local Bindings = {} :: {[number]: Exp} | |
Players.PlayerRemoving:Connect(function(Player) | |
Bindings[Player.UserId] = nil | |
end) | |
type ExpMethod<T> = ((self: Exp, Points: number) -> T) & ((self: Exp, ExpStruct: ExpStruct) -> T) | |
type Exp_Prototype = { | |
__add: ExpMethod<Exp>, | |
__sub: ExpMethod<Exp>, | |
__eq: ExpMethod<boolean>, | |
__lt: ExpMethod<boolean>, | |
__le: ExpMethod<boolean>, | |
} | |
export type ExpStruct = { | |
Experience: number, | |
Percent: number, | |
Level: number, | |
Total: number, | |
Next: number, | |
} | |
export type Exp = typeof(setmetatable({} :: ExpStruct, {} :: Exp_Prototype)) | |
-- Level equations taken from https://minecraft.gamepedia.com/Experience | |
-- returns number Exp the Exp required to reach the next level | |
local function ToNextLevel(CurrentLevel: number) | |
return math.abs( | |
if 0 <= CurrentLevel and CurrentLevel < 16 then | |
(2 * CurrentLevel) + 7 | |
elseif Lvl < 31 then | |
(5 * CurrentLevel) - 38 | |
else | |
(9 * CurrentLevel) - 158 | |
) | |
end | |
local function LevelFromExperience(self: Exp, Exp: number) | |
-- @param number Exp The Amount of Experience | |
-- @returns number Level, number experience within the current level | |
if Exp <= 0 then | |
return 0, 0 | |
elseif Exp <= 352 and Exp > 0 then --Lvl 16 or under | |
local Lvl = math.floor(((Exp + 9) ^ 0.5) - 3) | |
local Experience = Exp - (((Lvl * Lvl) + 6) * Lvl) | |
self.Experience = ExpInLvl | |
self.Level = Lvl | |
return Lvl, ExpInLvl | |
elseif Exp > 352 and Exp <= 1507 then | |
local Lvl = math.floor((81 + ((40 * Exp) - 7839) ^ 0.5) * 0.1) | |
local ExpInLvl = Exp - (((((2.5 * Lvl) * Lvl) - 40.5) * Lvl) + 360) | |
self.Experience = ExpInLvl | |
self.Level = Lvl | |
return Lvl, ExpInLvl | |
else | |
local Lvl = math.floor((325 + ((72 * Exp) - 54215) ^ 0.5) / 18) | |
local ExpInLvl = Exp - (((((4.5 * Lvl) * Lvl) - 162.5) * Lvl) + 2220) | |
self.Experience = ExpInLvl | |
self.Level = Lvl | |
return Lvl, ExpInLvl | |
end | |
end | |
local function Award(self: Exp, PointsOrExpStruct: number | ExpStruct) | |
local Points = if type(PointsOrExpStruct) == "table" then PointsOrExpStruct.Total else PointsOrExpStruct | |
if Points < 0 then | |
error(`[Exp] Cannot award negitive points {Points}`) | |
end | |
local Total = self.Total + Points | |
local Experience = LevelFromExperience(self, Total) | |
local Next = ToNextLevel(Experience) | |
self.Percent = Experience / Next | |
self.Total = Total | |
self.Next = Next | |
return self | |
end | |
local function Deduct(self: Exp, PointsOrExpStruct: number | ExpStruct) | |
local Total = self.Total - if type(PointsOrExpStruct) == "table" then PointsOrExpStruct.Total else PointsOrExpStruct | |
if Total > 0 then | |
local Experience = LevelFromExperience(self, Total) | |
local Next = ToNextLevel(Experience) | |
self.Percent = Experience / Next | |
self.Total = Total | |
self.Next = Next | |
else | |
self.Experience = 0 | |
self.Percent = 0 | |
self.Next = 158 | |
self.Total = 0 | |
self.Level = 0 | |
end | |
return self | |
end | |
local function LessThanOrEqualTo(self: Exp, PointsOrExpStruct: number | ExpStruct) | |
return self.Total <= if type(PointsOrExpStruct) == "table" then PointsOrExpStruct.Total else PointsOrExpStruct | |
end | |
local function LessThan(self: Exp, PointsOrExpStruct: number | ExpStruct) | |
return self.Total < if type(PointsOrExpStruct) == "table" then PointsOrExpStruct.Total else PointsOrExpStruct | |
end | |
local function Equal(self: Exp, PointsOrExpStruct: number | ExpStruct) | |
return self.Total == if type(PointsOrExpStruct) == "table" then PointsOrExpStruct.Total else PointsOrExpStruct | |
end | |
local Exp_Prototype = { | |
__le = LessThanOrEqualTo, | |
__lt = LessThan, | |
__sub = Deduct, | |
__add = Award, | |
__eq = Equal, | |
} | |
Exp_Prototype.__index = Exp_Prototype | |
table.freeze(Exp_Prototype) | |
local function Create(PointsOrExpStruct: (number | ExpStruct)?): Exp | |
if type(PointsOrExpStruct) == "table" then | |
local Total = PointsOrExpStruct.Total or 0 | |
local self = { | |
Percent = PointsOrExpStruct.Percent, | |
Level = PointsOrExpStruct.Level, | |
Next = PointsOrExpStruct.Next, | |
Experience = PointsOrExpStruct.Experience, | |
Total = Total, | |
} | |
if not (self.Percent or self.Level or self.Next or self.Experience) then | |
local Experience = LevelFromExperience(self, Total) | |
local Next = ToNextLevel(Exp) | |
self.Percent = self.Percent or Experience / Next | |
self.Next = Next | |
end | |
return setmetatable(self, Exp_Prototype) :: any | |
else | |
local Total = PointsOrExpStruct or 0 | |
local self = { | |
Percent = nil, | |
Total = Total, | |
Level = nil, | |
Next = nil, | |
Experience = nil, | |
} | |
local Experience = LevelFromExperience(self, Total) | |
local Next = ToNextLevel(Exp) | |
self.Percent = Experience / Next | |
self.Next = Next | |
return setmetatable(self, Exp_Prototype) :: any | |
end | |
end | |
local function Player(Player: Player, ExpStructOrPoints: (number | ExpStruct)?) | |
local Binding = Bindings[Player.UserId] | |
if Binding then | |
return Binding | |
else | |
local Exp = Create(ExpStructOrPoints) | |
Bindings[Player.UserId] = Exp | |
return Exp | |
end | |
end | |
local Exports = table.freeze { | |
player = Player :: ((Player: Player, Points: number?) -> Exp) & ((Player: Player, ExpStruct: ExpStruct?) -> Exp), | |
create = Create :: ((Points: number?) -> Exp) & ((ExpStruct: ExpStruct?) -> Exp), | |
} | |
return Exports |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment