Boost (dynamic number manipulation)!

Boost


By @avodey (my main account)!

:package: Module

This uses an Assert module, and Event module, both of which I made not specifically for this. It would help if you found bugs in those!

Summary (v1.0.1)


Ever wanted to easily add some sort of value modifiers? You know, like these?

image
An amulet which boosts certain game aspects, in Bee Swarm Simulator.

image
A fruit which boosts your attack damage, in Pet Simulator 99.

Boost makes it easy! You’ll be able to add, increase by percentage, multiply, and clamp a base value of your choice. Base values can be numbers, or NumberRanges. This can be used in multiple ways to account for a variety of use cases!


A Boost object has 5 important methods.

SetAdd: Add to the result value!
SetPercent: Add by a percentage of the result value!
SetScale: Multiply the result value!
SetClamp: Set the maximum result value!
SetActive: Set whether any modifier is active!

We will get to all of them later! API will also be provided later.

Why use a module?


So you just wanted to use basic math, huh? Let’s follow that through with Bee Swarm Simulator!

local beetleAmuletWalkspeed = 3
local antAmuletWalkspeed = 2
local hasteWalkspeed = 2
local hasteplusWalkspeed = 5
local moonAmuletWalkspeed = 0
local snailAmuletWalkspeed = 1
local beeWalkspeed = 2
-- etc...

humanoid.Walkspeed = 16 + beetleAmuletWalkspeed + antAmuletWalkspeed + hasteWalkspeed + hasteplusWalkspeed + moonAmuletWalkspeed + snailAmuletWalkspeed + beeWalkspeed -- etc...

Not super fun, or readable. With the boost module, you can dynamically set all these modifiers outside the script, which makes your code future proof!

This module was created specifically with Bee Swarm Simulator in mind, because they use a lot of boosters. My goal is to allow you to make something like that!

Use Cases


1: Potions, Amulets, and Charms

This use case will allow potions or some sort of ingame booster to change your values.

View Examples
local Boost = require(game.ReplicatedStorage:WaitForChild("Boost"))
local Walkspeed = Boost.new(16)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 16

Walkspeed:SetAdd("WalkspeedPotion", 4)

print(`My walkspeed now is {Walkspeed:Get()}!`) --> 20

In this code example, you can see a potion is increasing your walkspeed by 4 studs/second. How does it work? Let me explain each line.

1: Requiring the Boost module
2: Creating a new boost with the base value 16
4: Print the result value (16)
6: Set the WalkspeedPotion flag to the rule Add 4
8: Print the result value (20)

These flags allow you to control the modifiers dynamically. We wouldn’t want to push a rule with no name because we can’t remove it that way!

The SetAdd method adds to the result value. These are calculated first.


What if we wanted a bad potion? Like in minecraft, you could be hit with slowness or poison.

-- code from before

Walkspeed:SetPercent("SlownessPotion", -0.75)

print(`My walkspeed now is {Walkspeed:Get()}!`) --> 8

As you can see, your walkspeed went down by 75%. The SetPercent counts 1 as 100%, -1 as -100%, and everything in between as well. These are calculated second.


Let’s make it more drastic. Let’s freeze the player next!

local Boost = require(game.ReplicatedStorage:WaitForChild("Boost"))
local Walkspeed = Boost.new(16)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 16

Walkspeed:SetScale("FreezeEffect", 0)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 0

Great! The SetScale method allows you to multiply the result value. These are calculated third.


What if we went too far? Let’s also clamp the walkspeed so it must NOT go over a certain value.

local Boost = require(game.ReplicatedStorage:WaitForChild("Boost"))
local Walkspeed = Boost.new(16)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 16

Walkspeed:SetScale("Hyperspeed", 5)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 80

Walkspeed:SetAdd("WalkspeedPotion", 16)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 160

Walkspeed:SetClamp("TerminalVelocity", 92)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 92

The SetClamp value sets the maximum value the result must be. These are calculated fourth.

2: Upgrades and Levels

We can also use this to better automate value modifiers based on your level!

View Examples
local Boost = require(game.ReplicatedStorage:WaitForChild("Boost"))
local Walkspeed = Boost.new(0)

Walkspeed:SetAdd("Level1", 16)
Walkspeed:SetAdd("Level2", 20)
Walkspeed:SetAdd("Level3", 24)
Walkspeed:SetAdd("Level4", 28)

Walkspeed:SetActive("Level1")

print(`My walkspeed is {Walkspeed:Get()}!`) --> 16

Walkspeed:SetActive("Level2")

print(`My walkspeed is {Walkspeed:Get()}!`) --> 20

Walkspeed:SetActive("Level3")

-- etc...

In the code example, you can set the walkspeed depending on the users level. This can also be done with SetScale, but personally I will use this as it is more readable, and easier to softcode.

But wait, what is SetActive? Well, this method forces only a specific modifier to be active. All the other ones will not be used! This is useful for setting modifiers based on your level.

Too restricting, though? Use the second parameter that sets whether it is active or not! This will only affect the specific modifier.

local Boost = require(game.ReplicatedStorage:WaitForChild("Boost"))
local Walkspeed = Boost.new(0)

Walkspeed:SetAdd("Level1", 16)
Walkspeed:SetAdd("Level2", 20)
Walkspeed:SetAdd("Level3", 24)
Walkspeed:SetAdd("Level4", 28)

local function setLevel(x: number)
	for i = 1, 4 do
		Walkspeed:SetActive("Level" .. i, i == x)
	end
end

setLevel(1)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 16

setLevel(2)

print(`My walkspeed is {Walkspeed:Get()}!`) --> 20

setLevel(3)

-- etc...

Text Representation


Want to show the user what boosts they have? Use GetText()! This returns a list of strings, showing what all the modifiers are doing and what they are.

local Boost = require(game.ReplicatedStorage:WaitForChild("Boost"))
local Walkspeed = Boost.new(16)

Walkspeed:SetAdd("Speed Potion", 4)
Walkspeed:SetAdd("Group Reward", 2)

print(Walkspeed:GetText())

--[[ Output:
    [1] = "Speed Potion: +4",
    [2] = "Group Reward: +2"
]]

You’ll have to continue these strings with the word ‘Walkspeed’ in your code.

Detecting Changes


In each boost, there is a Changed event. You can connect to this when you add any modifiers, or set a new base value! This is likely where you will use the value, if not used in a while loop or stepped connection.

You can then use the Get() method to get the result value.

Calculation Logic


The result value starts off with a base. This is assigned as the first parameter in Boost.new(). You can also set a new base value later with Set(x).

Then, Adds are calculated. They add a number to the result value.
You can set new adds using SetAdd(flag, x).

Next, Percentages are calculated. They take the current result value, multiply it by the percentage, and then add it to itself. For example, +50% = x + x * 0.5 or x * 1.5.
You can set new percentages using SetPercent(flag, x).

Now, Scales are calculated. Simply, they multiply the result value.
You can set new scales using SetScale(flag, x).

Finally, Clamps are calculated. This prevents the result value from going over x.
You can set new clamps using SetClamp(flag, x)

After all this, you can get the result value using Get().

API


Hint: All number types also include NumberRanges!

Super.new(x: number)

Returns a new Boost object.
x: Base value

Boost:Set(x: number)

Set the base value.
x: New base value

Boost:SetAdd(flag: string, x: number|boolean)

Add a value to the result.
flag: The modifier’s name
x: (number) How much to add
x: (boolean) Whether the modifier should be enabled or not

Boost:SetPercentage(flag: string, x: number|boolean)

Add to the result by a percentage.
flag: The modifier’s name
x: (number) How much to add (100% is 1)
x: (boolean) Whether the modifier should be enabled or not

Boost:SetScale(flag: string, x: number|boolean)

Multiply the result value.
flag: The modifier’s name
x: (number) How much to multiply by
x: (boolean) Whether the modifier should be enabled or not

Boost:SetClamp(flag: string, x: number|boolean)

Clamp the result value.
flag: The modifier’s name
x: (number) The maximum result value
x: (boolean) Whether the modifier should be enabled or not

Boost:SetActive(flag: string, x: boolean?)

Set whether this flag is active, or the only one active.
flag: The modifier’s name
x: (boolean) Whether the modifier should be enabled or not
x: (nil) Set this as the only active modifier

Boost:Get(): number

Get the result value.

Boost:GetBase(): number

Get the base value.

Boost:GetText(prefix: boolean): {string}

Returns a text representation of the modifiers that will be applied.
prefix: Show the {name}: prefix in each text?

Boost:GetAdd(): number

Return the sum of all the add modifiers.

Boost:GetPercent(): number

Return the sum of all the percent modifiers.

Boost:GetScale(): number

Return the sum of all the scale modifiers, plus 1.

Boost:GetClamp(): number

Return the maximum value the clamp value can be.

tostring(Boost): string

Get the name of the boost. Format: <Booster {base}>

Boost(): number

Get the result value.

Conclusion


If you find any bugs, I will try and fix them! Just be sure to provide a reproduction script that causes the bug, and some context as well.

Any typos on this topic or in the code? That’s bad! DM about those so I can fix them.

Known Bugs

  • None

:+1:

39 Likes

Thank you so much! This is super useful for managing stats like changing walk speed without it resetting or being set to the wrong values. Many thanks for this resource and contribution to the community. :slight_smile:

5 Likes

my long-time theory is finally revealed to the public and it’s true…


also what do you mean by this:


I think I finally found a perfect module for placing statistics in the UI or smth idrk what to put there

2 Likes

Nice job! I myself was thinking about this issue several months ago, and it led me to discover that Entity Component Systems are a thing.

This is basically a very lite version of an ECS, with the various modifiers being systems that affect a single component (the value).

1 Like

Boost (v1.0.1)!


  • Optimized calculations (doesn’t recalculate on Get)
  • prefix: (boolean) on Boost:GetText method. Set to true if you want the prefix of each text show.
  • Added methods to get add/percent/scale/clamp sums

The module has been updated! Let me know if any of these new methods do not work.

Would you find it more useful if I modified the module to be more like that?

Instead of 4 hardcoded lists (Adds, Percents, Scales, Clamps), it would instead be one list full of modifier functions that run in order. These functions would have priorities so that the behavior is the same, but allows adding your own formulas to modify the result value!

This is honestly an amazing resource, it helps with the pain of having every single possible script manage their own value without breaking everything.

1 Like

I plan on using this module but may need some assistance. So basically our game has guns that I plan on adding speed reduction to, and I stumbled across this module as I also want to handle boosts as well (e.g. item boost). If I wanted to incorporate the speed reduction, how would I do so? What would your approach be?

Much appreciated.

Well, I guess I’m just not understanding correctly how to interact with the Walkspeed object from an external script. Because if I do .new() in one script, and attempt to interact with it in another, it wouldn’t work as it’s referencing a whole other object, not the one I want, right?

I see. Well, this is how I’d do it!

local Super = {}
local Controller = {}
Controller.__index = Controller

--\\ Modules

local Boost = require(game.ReplicatedStorage.Modules.Boost)

--\\ Public Methods

local cache = {}
function Super.new(char)
    if cache[char] then return cache[char] end
    
    local self = setmetatable({}, Controller)
    
    self.Walkspeed = Boost.new(16)
    self.Character = char
    
    --\\ Connections

    self._C1 = self.Walkspeed.Changed:Connect(function()
        char.Humanoid.Walkspeed = self.Walkspeed:Get()
    end)

    --\\ Handle Cache

    char.Destroyed:Connect(function()
        self:Destroy()
    end)
    
    return self
end

function Super:Get(char)
    return cache[char]
end

--\\ Instance Methods

function Controller:Destroy()
    if table.find(cache, self) then
        table.remove(cache, table.find(cache, self))
        self.Character = nil
        self._C1:Disconnect()
    end
end

This code creates a character controller. From there, you can modify the boost to change the speed of the character! It should work, but I haven’t tested it. If you already have some sort of character handler in your code, you don’t need this entire script!

I have an idea - appreciate the help. I’ll update you when I’m finished.

@bluebxrrybot I solved the issue by creating a wrapper to handle movement-related values like sprinting, crouching, and eventually gun speed reduction.

Thanks for your help and thanks for the resource!

1 Like

Any chance to publish the library on GitHub? This library is cool and I want to integrate it to my Rojo project!

Sorry, but I’m against using GitHub, Rojo, and VSC for Roblox.

2 Likes

A large portion of my game uses a lot of boosters and such. I tried creating something similar to this but it used a lot of resources and had flaws that could let the user infinitely increase a stat. I’m genuinely grateful for what you’ve made.

1 Like

Can you give an example of how to use it? I’m not really well versed with this type of scripting.

There’s use cases listed in the original topic!

Sorry for not clarifying in my original post, but I want a way to change the values from an external script similar to the one i’m replying to. Only difference is that I have multiple values and if you can, can you also provide an example of how I would use the functions?

EDIT:

i’m gonna try to use _G, i’ll update you if it works. It didn’t work.

You can store these Boost objects inside of a ModuleScript, or a dictionary returned by a module. They can be stored anywhere, but I use them like this because I use object oriented programming. I would suggest against using _G and shared because modules exist!

Got it working!

Code for anyone that wants to do the same :smile:

Script 1 (I parented it to the player but you can store it wherever):

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Boost = require(ReplicatedStorage.Boost)

local PlayerStats = {
	["TestValOne"] = Boost.new(5),
	["TestValTwo"] = Boost.new(10),
	["TestValThree"] = Boost.new(15),
}

return PlayerStats

Script 2 (External script):

local PlayerStats = require(Script1)

PlayerStats.TestValOne:Get(...)
PlayerStats.TestValTwo:SetAdd(...)
PlayerStats.TestValThree:SetScale(...)