[5 Attacks!] Boss Fight System | Control bosses from 1 script, easy customization, free premade abilities and more

How To Make A Boss Fight

☆⸻●⸻☆


The title summed up pretty much everything so maybe scroll down to see specific features in details.

[Reminder for me to put a video here]

:waving_hand: Introduction :waving_hand:

Hi everyone! This post was made in order to help you guys with designing interesting boss fights


It took me over 3 years of learning and about 2 years of developing my own game to finally make this possible, so hopefully you find it helpful!

New attacks will be added overtime so come back every once in a while!

I’ll occasionally provide updates to fix bugs or improve the system (Any help would be appreciated!)
During special occasions like Halloween or Christmas, you can expect to see even more premade bosses and attacks themed around those events, which are free for use (Basically similar to game events)

How this system works:
  1. All bosses are controlled from a main ModuleScript (BossController), which can be activated by requiring it from a Script and providing additional settings/configurations.
  2. In there, most of the basic functions and logic will run to make the bosses move, wander, find target, attack, regenerate their health, run animations, run death effects, etc...
  3. Bosses have Defense, Immunities like 🔥 Fire, 🧊 Ice, ⚡ Stun, 💀Poison. These are set as attributes. Without them, a boss can be stunned, frozen, slowed down or weakened, so make sure you balance the stats well.
  4. Bosses can use abilities/attacks. Each boss has their own global attack cooldown + local cooldown for each type of attack
  5. Attacks are activated through a RemoteEvent, so it's fine if the boss model gets destroyed while an attack is still happening

:collision: Free Ability/Attack Examples (? Presets/Templates) :water_pistol:

Please read the notice in Credits section below. Thanks! More attacks will be added overtime
All of them can be found in the Attack folder in ServerScriptService of the test place
You can find newly added and undocumented attacks by checking the test place!

Basic - The foundation of all other attacks
    Explosions

    Create an explosion to deal damage to all targets caught within its blast radius. Damage falloff scales with the distance. There are 10+ explosion effects for you to choose from, including making it invisible

    FunctionBank.CreateExplosion(
    	Creator,
    	Position,
    	Radius,
    	1.5, --Ratio between the outer and inner sphere, might be removed later once I can
    	Color3.fromRGB(255, 0, 0), --Color of the outer explosion sphere (Not all effects have the sphere so this will be the main color of their ParticleEmitters)
    	Color3.fromRGB(85, 0, 0), --Color of the inner explosion sphere
    	Damage,
    	Force,
    	false, --ForceScaleWithMass?
    	true, --DamagePlayer? 
    	"Realistic"
    )
    
    Status Effects

    Call this function and provide a target with an effect name as well as other settings. Check explanation to see all the effects and their behaviors

    local Effect = "Stun"
    local Duration = 5
    
    FunctionBank.AddEffect(Target, Effect, Duration, {
    	Creator = Caster.Character
    })
    
Melee - Close-ranged attacks
    Swing

    Play an animation before dealing damage to targets caught within the range

    • Method 1: Create an explosion using FunctionBank.CreateExplosion(…) right at the boss’s location, set the effect to invisible if you only need the animation, otherwise pick any fitting effect
    ["Swing"] = function(Caster, AttackSettings)
    		task.spawn(function()
    			if AttackSettings then		
    				FunctionBank.PlaySound(FunctionBank.Roll(HitSounds), Caster.Root, nil, 3, 1, false, false, nil, true)
    				FunctionBank.PlayAnimation(Caster.Character, Caster.Animator, ClientFunctionBank.CheckRigType(Caster.Character) == "R15" and FunctionBank.Roll(R15SwingAnims) or FunctionBank.Roll(R6SwingAnims), nil, "Action", false, true, false, nil, nil, 1.75)
    				
    				if string.find(Caster.Character.Name, "Headless Horseman") then
    					local Start = Caster.Root.Position
    					
    					FunctionBank.CreateExplosion(
    						Caster.Character,
    						Start,
    						AttackSettings.Range or 10,
    						1.25, 
    						Color3.fromRGB(255, 85, 0),
    						Color3.fromRGB(255, 170, 0),
    						AttackSettings.Damage or 25,
    						10, 
    						false,
    						true,
    						"Slash",
    						AttackSettings.Effect or nil
    					)
    					
    					task.wait(0.15)
    					for i = 1, 2 do
    						local RingRadius = 25 + (i - 1) * 50
    
    						for e = 1, 6 do
    							local Angle = (e / 6) * (math.pi * 2)
    							local Offset = Vector3.new(
    								math.cos(Angle) * RingRadius,
    								0,
    								math.sin(Angle) * RingRadius
    							)
    							
    							FunctionBank.CreateExplosion(
    								Caster.Character,
    								Start + Offset,
    								AttackSettings.Range / 3,
    								1.25, 
    								Color3.fromRGB(255, 85, 0),
    								Color3.fromRGB(255, 170, 0),
    								AttackSettings.Damage / 2,
    								10, 
    								false,
    								true,
    								"Slash",
    								AttackSettings.Effect or nil
    							)
    
    							task.wait(0.05)
    						end
    					end
    				else
    					FunctionBank.CreateExplosion(
    						Caster.Character,
    						Caster.Root.Position,
    						AttackSettings.Range or 10,
    						1.25, 
    						Color3.fromRGB(255, 255, 255),
    						Color3.fromRGB(255, 255, 255),
    						AttackSettings.Damage or 25,
    						10, 
    						false,
    						true,
    						"Invisible",
    						AttackSettings.Effect or nil
    					)
    				end
    			end
    		end)
    
    		return AttackSettings.Cooldown
    	end,
    
    • Method 2: Loop through all the descendants of the workspace, then check their distance from the boss and deal damage
    • Method 3: Call FunctionBank.GetPartsInPart(…) and provide the arguments needed, then check their distance and deal damage. You can also provide a custom hitbox part in this function if you have a sword or something
    Shockwaves/Slam/Smash

    Create a ring-shaped shockwave that gradually grows bigger. Can easily be avoided by jumping over the ring at the right moment

    • [WIP]
Ranged - Far-ranged attacks
    Throw Projectiles
    • [WIP]
    Fire Projectiles
    • [WIP]
    Shards
    • [WIP]
    Beam

    Shoot a beam straight at the goal position. This beam will deal damage to all targets inside it every tick until each target reach the limit for how many hits they can take

    • [WIP]
    Flamethrower/Fire Breath,/Blazing Breath
    • [WIP]
    Laser Projectiles
    • [WIP]
    Spinning Lasers
    • [WIP]
    Lightning Strike
    • [WIP]
    Lava Rise/The Floor Is Lava
    • [WIP]
    Missiles/Rockets
    • [WIP]
    Cluster Bombs
    • [WIP]
    Mini Nuke Launcher
    • [WIP]
Movement - Bosses perform physical actions and move to somewhere
    Rush/Charge
    • [WIP]
    Leap/Jump & Slam
    • [WIP]
    Leap Shockwaves
    • [WIP]
    Teleport/Warp
    • [WIP]
    Dash
    • [WIP]
Reality Altering - You can't do this in real life, so why not break it online?
    Stop Time
    • [WIP]
    Rewind Time
    • [WIP]
    Skip Time
    • [WIP]
    Accelerate/Decelerate Time
    • [WIP]
    Curruption
    • [WIP]
Defense - The strongest defense is the strongest offense
    Heal
    • [WIP]
    Hidden/Camoflag/Stealth/Void Cloak
    • [WIP]
    Energy Shield/Block Hits
    • [WIP]
    Thorn/Damage Reflect
    • [WIP]
Others - Attacks that do not need a range or even a target
    Body Control + Disable Gears
    • [WIP]
    Self Destruct
    • [WIP]
    Spawn Minions
    • [WIP]
    Jumpscare
    • [WIP]
    Send Friend Request
    • [WIP]
    Chat/Send Messages
    • [WIP]
    Dance
    • [WIP]
    Clones
    • [WIP]
    Deploy Decoys
    • [WIP]
    Shapeshift
    • [WIP]

Explanation:

Status effects, I tried making them as unique as possible. They can be applied to bosses or players with customizable settings

  • :high_voltage: Stun: Stuns and stops the target from moving with a strong force. For heavy targets or those with Stun Immunity, the target is not stunned but rather slowed down along with their animation speed. Can set tick, can set stun type (Zap/Shock/Paralyze) or color with variant. Effect emits once every tick. Tick, Force and AnimationSpeed factor are affected by Stun Immunity

  • :fire: Fire: Burns to deal DoT and melt defense

  • :test_tube: Acid: Corrodes to deal DoT and melt defense

  • :radioactive: Radiation: Deals DoT, also melts and ignores defense

  • :skull: Poison: Deals DoT while bypassing defense, damage (%) depends on the MaxHealth of the target

  • :drop_of_blood: Bleed: Deals DoT, damage grows after each tick until the limit is reached, also reduces the WalkSpeed and JumpPower of the target based on the % of Health remaining

  • :revolving_hearts: Heal: Heals target

  • :ice: Ice: Freezes the target to reduces defense, stops any movements or animations while retaining physics, creates a thin ice layer on the character. Utilizes my FreezeModule. If the target is 100% immune to this then :snowflake: Chill will be applied instead

  • :snowflake: Chill: Slows down chilled targets, freeze amount depends on FreezeAmount setting and the IceImmunity of the target

  • :red_question_mark: Confusion: If the target is a Player, their control is inverted (all devices), otherwise, the movement of the target is reversed/heavily altered

  • :milky_way: Void: Halves the damage that the target can deal when applied

  • :man_vampire:Lifesteal: Takes HP from the target to heal the attacker. Lifesteal amount is affected by Defense, Void immunity and is multiplied by LifestealBoost stat. Must provide Creator in the settings

  • :link: Love-Link: (Will be added one day): Electrocuts targets near the main target who is being affected by this effect, lightning strikes will link the affected targets together

  • :collision: Explosion: (Will be added on New Year Eve): Instantly kills the target and then creates an explosion afterwards

  • :nazar_amulet: Teleportation: (Will be added on ??? event): Randomly teleports the target to a random location at random time

Examples of status effects being applied

  1. You apply :fire: Fire with 100 damage per tick for 5 seconds to a Noob who has Defense = 50(%), FireImmunity = 50, he will only take 25 damage every tick for 2.5 seconds.
  2. You apply :ice: Ice for 2 seconds to a Snowman that has IceImmunity = 100, it will not be frozen at all.
  3. You apply :skull: Poison with 1% MaxHealth as damage per tick for 10 seconds to a Zombie who has Defense = 90, PoisonImmunity = 150, it will not take any damage, but heal up by 1.5% of its MaxHealth per tick for 10 seconds. Poison ignores Defense
  4. You apply :test_tube: Acid with 100 damage per tick for 5 seconds to a Robot that has Defense = 50, AcidImmunity = -50, it will take 75 damage every tick for 7.5 seconds.
  5. You apply :biohazard: Radiation with 100 damage per tick for 5 seconds to a Golem that has Defense = 100, no RadiationImmunity, it will take 100 damage every tick for 5 seconds. Radiation ignores Defense
  6. You apply :red_question_mark: Confusion for 5 seconds to a Player without ConfusionImmunity, their control will be inverted during that time (WASD → SDWA on PC, opposite movement direction on all devices)
  7. You apply :sob: Failure with infinite emotional damage per tick to a Player forever, they will not be happy and will most likely dislike your game

And now other things

  1. DoT: Damage over time. Damage is applied every Tick

  2. Defense: How many % of damage that a character can block. The effective damage dealt towards a character is decreased if its defense amount is higher than 0. However, Poison & Radiation can both bypass/ignore defense

  3. Immunity:: How many % of damage that a character can block from the same type of effect they are immune to, can stack with Defense (Full Damage → Reduced by Defense → Reduced by Immunity)

  4. Defense-melting: Reduce defense, each type of elemental status effect can only drop up to a certain amount of defense and does not stack with the same type of status effect

  5. Tick: The rate at which the effect deals damage or activates. Basically firerate

  6. Duration: How long the effect will be applied for. This timespan can be shorten by an immunity (0%-100%). If the immunity is higher than 100%, then the duration is not changed. Else if the immunity is lower than 0%, then the duration is increased. Duration is also stackable if an effect allows it

  7. Creator: The Player instance or the character of a player (Module auto checks), is used to tag targets and recognize total damage contributed and the final hit dealer. Does not tag if the Creator is not a player

  8. Variant: Customize an effect or give it a different mechanic. This is because you can’t send info to the CollectionService

  9. AoE/CC: Refering to area of effect or crowd control. Basically the ability to hit multiple targets at once with or without a limit in a specific range. Usually seen in explosive attacks


:skull: Free Bosses :skull:

🎃 Headless Horseman


Go through this portal in the test place
You can skip all other steps if you have already defeated the same boss in MBB


Find this statue and activate the proximity prompt


Defeat the boss to claim the model


:scroll: Source Code/Main Scripts :page_with_curl:

BossController (The main ModuleScript you require):
local BossTemplateController = {}
BossTemplateController.__index = BossTemplateController

local RS = game:GetService("ReplicatedStorage")
local SS = game:GetService("ServerStorage")
local SSS = game:GetService("ServerScriptService")
local TS = game:GetService("TweenService")
local Debris = require(SSS.DebrisGobbler.DebrisGobbler)
local Players = game:GetService("Players")
local Lighting = game:GetService("Lighting")
local Running = game:GetService("RunService")

local DebrisHolder = workspace:FindFirstChild("DebrisHolder")
local Attacks = SS:FindFirstChild("Attacks")
local Animations = RS:FindFirstChild("Animations")

local AttackServer = RS:FindFirstChild("RemoteEvents"):FindFirstChild("AttackServer")

local GameConfig = require(RS.GameConfig)
local FunctionBank = require(SS.ServerFunctionBank:FindFirstChild("FunctionBank"))
local ClientFunctionBank = require(RS:FindFirstChild("ClientFunctionBank"))
local StatsHandler = require(SSS.Main.StatsHandler)

local DefaultStats = GameConfig.DefaultStats

function BossTemplateController.New(Boss, Settings)
    local self = setmetatable({}, BossTemplateController)

    self.Character = Boss
	self.Settings = Settings or {}
	
    self.Humanoid = Boss:FindFirstChildOfClass("Humanoid")
	self.Animator = self.Humanoid and self.Humanoid:FindFirstChildOfClass("Animator")
	self.Root = self.Humanoid and self.Humanoid.RootPart or Boss:FindFirstChild("HumanoidRootPart")
	self.Head = Boss:FindFirstChild("Head")
	
    self.CanMove = true
    self.Attacking = false
    self.Walking = false
    self.Phase2 = false
	self.Cooldown = false
	
	self.OriginalPosition = self.Root and self.Root.Position
	self.PreviousState = Enum.HumanoidStateType.Physics
	
	self.Settings.Anims = self.Settings.Anims or {}
	
	self.WalkAnim = self.Settings.WalkAnim or self.Settings.Anims.Walk or Animations:FindFirstChild(Boss.Name .. " Walk")
	self.Settings.Anims.Idle = self.Settings.Anims.Idle or Animations:FindFirstChild(Boss.Name .. " Idle")
	
	if Boss.Name == "Fallen Emperor" then
		self.WalkAnim = Animations["Fallen Emperor Walk"]
		self.Settings.Anims.Idle = Animations["Fallen Emperor Idle"]
		self.Settings.Anims.RageWalk = Animations["Fallen Emperor Rage Walk"]
	end
	
    self:InitializeAttacks()
	self:InitializeBoss()
	self:StepRegenerate()

    return self
end

function BossTemplateController:InitializeAttacks()
	AttackServer.Event:Connect(function(Status, Boss)
		if Status == "Finished" and Boss == self.Character then
			self.CanMove = true
			self.Attacking = false
		end
	end)
end

function BossTemplateController:InitializeBoss()
	local Boss = self.Character
	local Humanoid = self.Humanoid
	local Animator = self.Animator
	local Root = self.Root
	
	Boss:SetAttribute("State", "Normal")
	if not Humanoid then return end
	
	if Boss.Name == "Fallen Emperor" then
		local Anim = FunctionBank:PlayAnimation(Boss, Animator, Animations["Fallen Emperor Spawn"], nil, "Action", false, false, true)
		Anim:GetMarkerReachedSignal("Landed"):Connect(function()
			FunctionBank:CreateExplosion(Boss, Root.Position, 150, 1.25, Color3.fromRGB(61, 95, 120), Color3.fromRGB(0, 85, 127), 250, 400, false, true, "Blue Fallen", "Stun 5 0", "JointBreak")
			FunctionBank:CreateShockwave(Boss, "Boss", 200, "Ground", 1, 300, 600, Root.Position, Color3.fromRGB(0, 0, 0), false, nil, "JointBreak")
		end)
		task.wait(2)
	end
	
	if self.Settings.MovementAnimation then
		FunctionBank:PlayAnimation(Boss, Animator, self.Settings.Anims.Idle, nil, "Idle", true, false, true)
	end

	local AnimationConnection
	AnimationConnection = Humanoid.Running:Connect(function(Speed)
		if not self.Settings.MovementAnimation or self.Attacking then return end

		local State = Humanoid:GetState()
		if State == Enum.HumanoidStateType.Running and Speed > 5 and self.CanMove then
			if not self.Walking then
				FunctionBank:PlayAnimation(Boss, Animator, self.WalkAnim, nil, "Movement", true, false, true, nil, nil, self.Phase2 and 2 or 1)
				self.Walking = true
			end
		elseif State == Enum.HumanoidStateType.Seated then
			Humanoid.PlatformStand = false
			Humanoid.Jump = true
			if self.Walking then
				FunctionBank:PlayAnimation(Boss, Animator, self.Settings.Anims.Idle, nil, "Idle", true, false, true)
				self.Walking = false
			end
		else
			if self.Walking and State ~= self.PreviousState then
				FunctionBank:PlayAnimation(Boss, Animator, self.Settings.Anims.Idle, nil, "Idle", true, false, true)
				self.Walking = false
			end
		end

		self.PreviousState = State
	end)

	local HealthConnection
	HealthConnection = Humanoid.HealthChanged:Connect(function()
		if Humanoid.Health <= Humanoid.MaxHealth * 0.8 then
			for i, v in ipairs(Boss:GetDescendants()) do
				if v:GetAttribute("Destroyable") then
					local Destroyable = v:GetAttribute("Destroyable")
					if Humanoid.Health <= Humanoid.MaxHealth * Destroyable then
						FunctionBank:CreateExplosion(Boss, v.Position, 5, 1.5, v.Color, v.Color, 0, 1, false, true, "VFX")
						v:Destroy()
					end
				end
			end
		end
		
		if Humanoid.Health <= Humanoid.MaxHealth * 0.5 then
			if self.Settings.Phase2 then
				if self.Settings.Anims.RageWalk then
					self.WalkAnim = self.Settings.Anims.RageWalk
				end

				if not self.Phase2 then
					self.Phase2 = true
					StatsHandler.AddModifier(Boss, "DamageBoost", "Phase2", self.Settings.DamageBoost)
					StatsHandler.AddModifier(Boss, "WalkSpeed", "Phase2", self.Settings.WalkSpeedBoost)

					if Boss.Name == "Fallen Emperor" then
						local Anim = FunctionBank:PlayAnimation(Boss, Animator, Animations["Fallen Emperor Rage"], nil, "Action", false, false, true)
						Anim:GetMarkerReachedSignal("Stomped"):Connect(function()
							FunctionBank:CreateExplosion(Boss, Root.Position, 150, 1.25, Color3.fromRGB(61, 95, 120), Color3.fromRGB(0, 85, 127), 250, 400, false, true, "Blue Fallen", "Stun 5 0", "JointBreak")
							FunctionBank:CreateShockwave(Boss, "Boss", 200, "Ground", 1, 300, 600, Root.Position, Color3.fromRGB(0, 0, 0), false, nil, "JointBreak")

							task.wait(0.25)
							Humanoid:ChangeState(Enum.HumanoidStateType.Landed)
						end)
					end
				end

				if Humanoid.Health <= 0 then
					if HealthConnection then
						HealthConnection:Disconnect()
						HealthConnection = nil
					end
					if AnimationConnection then
						AnimationConnection:Disconnect()
						AnimationConnection = nil
					end
					if self.Walking then
						self.Walking = false
					end
					if self.Attacking then
						self.Attacking = false
					end
				end
			end
		end
	end)
end

function BossTemplateController:MoveTo(TargetRoot)
	if not self:IsAlive() then return end
	
	local Hit, EndPosition = workspace:FindPartOnRayWithIgnoreList(Ray.new(self.Root.Position, self.Root.CFrame.LookVector.Unit * (self.Root.Size.Z * 2.5)), {self.Character})
	if Hit and Hit.Parent and Hit.Parent ~= self.Character then
		self.Humanoid.Jump = true
	end
	
	if typeof(TargetRoot) == "Vector3" then
		self.Humanoid:MoveTo(TargetRoot)
		self.Character:SetAttribute("CurrentCursorPosition", TargetRoot)
		self.CurrentCursorPosition = TargetRoot
	else
		self.Humanoid:MoveTo(TargetRoot.Position, TargetRoot)
		self.Character:SetAttribute("CurrentCursorPosition", TargetRoot.Position)
		self.CurrentCursorPosition = TargetRoot.Position
	end
end

function BossTemplateController:Wander()
	if not self:IsAlive() or not self.Settings.Wander then return end

	local WanderOffset = Vector3.new(math.random(-self.Settings.WalkRadius, self.Settings.WalkRadius), 0, math.random(-self.Settings.WalkRadius, self.Settings.WalkRadius))
	local TargetPosition = self.Root.Position + WanderOffset
	
	self:MoveTo(TargetPosition)
end

function BossTemplateController:StopMoving()
	if not self:IsAlive() then return end
	
	self:MoveTo(self.Root)
	self.CanMove = false
end

function BossTemplateController:IsAlive()
	if self.Settings.Object then return true end
	
	return self.Character and self.Character.Parent and self.Humanoid and self.Root and self.Humanoid.Health > 0
end

function BossTemplateController:StartAttackCycle()
	if not self:IsAlive() then return end
	
	local AttackCycle = self.Settings.AttackCycle
	local Boss = self.Character
	local TargetChar, TargetHum, TargetRoot = FunctionBank:FindTarget(Boss, self.Root, self.Settings.MaxFollowDistance, "All", "Closest")
	
	if TargetChar and TargetChar.Parent and TargetHum and TargetHum.Parent and TargetRoot and TargetRoot.Parent then
		local State = Boss:GetAttribute("State")
		local Distance = (self.Root.Position - TargetRoot.Position).Magnitude
		
		if State == "Normal" then
			if self.CanMove and self.Settings.Move then
				self:MoveTo(TargetRoot)
			end
			
			if not self.Cooldown then
				local SelectedAttack
				local AttackType = nil
				local AttackOrderKey = nil
				
				if Distance <= self.Settings.MeleeDistance and AttackCycle.Melee and next(AttackCycle.Melee) then
					AttackType = "Melee"
					AttackOrderKey = "MeleeAttackOrder"
				elseif Distance <= self.Settings.RangedDistance and AttackCycle.Ranged and next(AttackCycle.Ranged) then
					AttackType = "Ranged"
					AttackOrderKey = "RangedAttackOrder"
				end
				
				if AttackType then
					local AttackInfo = AttackCycle[AttackType]
					local AttackPattern = AttackInfo.AttackPattern or "Ordered"
					local Cooldown = AttackType == "Melee" and self.Settings.MeleeCD or self.Settings.RangedCD
					
					self[AttackOrderKey] = self[AttackOrderKey] or 1

					if AttackPattern == "Structured" and AttackInfo.Structured and #AttackInfo.Structured > 0 then
						local Entry = AttackInfo.Structured[self[AttackOrderKey]]
						if Entry and typeof(Entry) == "string" and string.sub(Entry, 1, 4):lower() == "wait" then
							local DelayTime = tonumber(string.match(Entry, "%d+")) or 1
							self.Cooldown = true
							
							task.delay(DelayTime, function()
								self.Cooldown = false
								self.CanMove = true
								self.Attacking = false
							end)
						else
							SelectedAttack = {Attack = Entry, Settings = {}}
						end

						self[AttackOrderKey] = (self[AttackOrderKey] % #AttackInfo.Structured) + 1

					elseif AttackPattern == "Random" then
						SelectedAttack = AttackInfo[math.random(1, #AttackInfo)]

					else
						SelectedAttack = AttackInfo[self[AttackOrderKey]]
						self[AttackOrderKey] = (self[AttackOrderKey] % #AttackInfo) + 1
					end

					if SelectedAttack then
						self.Cooldown = true
						
						task.delay(Cooldown, function()
							self.Cooldown = false
							self.CanMove = true
							self.Attacking = false
						end)

						if SelectedAttack.Settings then
							if SelectedAttack.Settings.StayStill then
								self:StopMoving()
							end
							SelectedAttack.Settings.CurrentCursorPosition = TargetRoot.Position
						end
						
						self.Attacking = true
						AttackServer:Fire(self, SelectedAttack.Attack, SelectedAttack.Settings or {})
					end
				end
			end
		elseif State == "Stunned" then
			task.wait(self.Settings.ResponseRate)
		end
	else
		self:Wander()
		task.wait(self.Settings.WanderDuration)
	end
end

function BossTemplateController:StepRegenerate()
	task.spawn(function()
		local RegenerateRate = DefaultStats.RegenerateRate
		local RegeneratePercent = DefaultStats.RegeneratePercent
		
		if not self.Settings.Regenerate then return end
		
		while self:IsAlive() do
			while self.Humanoid.Health < self.Humanoid.MaxHealth do
				local DeltaTime = task.wait(RegenerateRate)
				
				if self.Character:GetAttribute("RegeneratePercent") then
					RegeneratePercent = self.Character:GetAttribute("RegeneratePercent")
				else
					RegeneratePercent = DefaultStats.RegeneratePercent
				end
				
				if self.Character:GetAttribute("RegenerateRate") then
					RegenerateRate = self.Character:GetAttribute("RegenerateRate")
				else
					RegenerateRate = DefaultStats.RegenerateRate
				end

				local DeltaHealth = DeltaTime * (RegeneratePercent / 100) * self.Humanoid.MaxHealth

				if self.Humanoid.Health > 0 then
					self.Humanoid.Health = math.min(self.Humanoid.Health + DeltaHealth, self.Humanoid.MaxHealth)
				end

				if not self or not self.Humanoid.Parent or self.Humanoid.Health <= 0 then break end
			end

			self.Humanoid.HealthChanged:Wait()
		end
	end)
end

return BossTemplateController

Script (You have 1 in each boss model):
local Boss = script.Parent

local BossController = require(game:GetService("ServerScriptService").Main.BossController)
local ClientFunctionBank = require(game:GetService("ReplicatedStorage"):FindFirstChild("ClientFunctionBank"))

local BossSettings = {
	ResponseRate = 1/60,
	WanderDuration = 2,
	MaxFollowDistance = 2000,
	MeleeDistance = 80,
	RangedDistance = 1000,
	MeleeCD = 3,
	RangedCD = 10,
	WalkRadius = 150,
	
	DamageBoost = 50,
	WalkSpeedBoost = 4,
	
	Phase2 = true,
	MovementAnimation = true,
	Move = true,
	Wander = true,
	
	Anims = {},
	
	AttackCycle = {
		Melee = {
			[1] = {
				Attack = "Swing",
				Settings = {
					Damage = 45,
					Range = 80,
					Effect = "Burn 5 5",
					StayStill = true
				}
			},
		},
		Ranged = {
			AttackPattern = "Ordered",
			
			[1] = {
				Attack = "Axe Throw",
				Settings = {
					Cooldown = 1,
					Damage = 5,
					ExplosionDamage = 300,
					Radius = 45,
					Range = 300,
					Force = 30,
					TravelTime = 1,
					ReturnTime = 1.5,
					Curve = true,
					CurveFactor = 0.15,
					StayStill = false
				}
			},
			[3] = {
				Attack = "Slices",
				Settings = {
					Damage = 100,
					Cooldown = 3,
					Radius = 30,
					Force = 1.5,
					Delay = 0.05,
					Amount = 8,
					Duration = 2,
					Color1 = Color3.fromRGB(255, 85, 0),
					Color2 = Color3.fromRGB(255, 170, 0),
					StayStill = true
				}
			},
			[4] = {
				Attack = "Leap Shockwave",
				Settings = {
					Damage = 45,
					Cooldown = 5,
					Radius = 30,
					MaxRange = 200,
					Speed = 200,
					Force = 40,
					Amount = 3,
					Randomness = 100,
					StayStill = true
				}
			},
			[2] = {
				Attack = "E",
				Settings = {
					Damage = 25,
					Cooldown = 1,
					Radius = 30,
					Force = 1.5,
					Amount = 4,
					OuterColor = Color3.fromRGB(255, 85, 0),
					InnerColor = Color3.fromRGB(255, 170, 0),
					StayStill = true
				}
			},
		},
	}
}

local BossController = BossController.New(Boss, BossSettings)

ClientFunctionBank:BindToInstanceDestroyed(Boss, function()
	BossController = nil
end)

local ResponseRate = BossSettings.ResponseRate

while true do
	BossController:StartAttackCycle()
	task.wait(ResponseRate)
end
All other scripts:

Bruh you thought I can put the amount of text equivalent to 2 years of typing here huh?
Absolutely not! Go edit the uncopylocked place to see the rest as there are too much stuff for a single post with low character limit


:white_check_mark: Features :white_check_mark:

  • Pro OOP in the BossController module

  • Attacks shown are already made so you can just copy paste them into your game. They are also good for learning I suppose

  • Bosses have Defense (Take less damage), Immunities (Less vulnerable to damage or debuffs from status effects)

  • Most visual effects are handled on the client side → You can add easily add settings to reduce the quality or hide them, less stress on the server

  • Useful functions I’ve gathered over the years

  • Stats Handler - Manage layers of stats like WalkSpeed, JumpPower, Defense, etc… so that they don’t overlap/override each other. Useful for status effects and changing phases of bosses

  • Status Effect System - Deal damage over time, restrict movement, alter behavior, reduce defense or apply other unique mechanics depending on the effect type

  • Revamped Roblox’s TagHumanoid function - Know how much damage each player/boss have dealt in total + Final hit dealer

  • FreezeModule


:cross_mark: Limitations :cross_mark:

  • I’m a bad modeler, so my coding skill is limited. HOWEVER, the models I make are also not really good (Jack of all trades, master of none moment)

  • I cannot animate or make visual effects

  • There might be pretty outdated stuff or change of coding style from time to time as I’m still learning how to code, most of the more advanced scripts were only made a few months before this post

  • The optimization isn’t that great. You should not have more than 10 bosses spamming insane amount of attacks too fast (Though I don’t see any reason to have >1~3 bosses at once)


:movie_camera: Story :popcorn:

One day in 2022, I stumbed upon this tutorial by HowToRoblox, which was the beginning of everything

However, the biggest inspiration was a game called MEGA Boss Survival by Dinpey. As I was developing my fanmade game, Mega Boss Battles, I made this open-sourced FreezeModule for everyone to use with my little scripting knowledge

From then on, I kept learning more and more, making more public resources and improving until this boss fight system became somewhat acceptable


:bar_chart: Update Logs :chart_increasing:


:glowing_star: Credits :fire:

I feel like the mentions would be annoying so I removed @

  • Dinpey - The legend, unfortunately terminated for a false reason - Big motivation
  • Sub2HTR - Original boss fight tutorial - Big inspiration
  • Lightlimn - Original StatsHandler module - The StatsHandler system being used
  • Xan_TheDragon - FastCast module - Some attack projectiles use this
  • verret001 - DebrisGobbler module - Replacement for Roblox’s outdated Debris service which stops working at 1k objects limit
  • Mythamanx - Explosion damage calculation made easy - Realistic damage falloff for explosions
  • ketrab2004 - Damage equation graph demonstration in Desmos (#6 from the damage falloff post above) - Helpful
  • ThunderDaNub - Creator, made basically everything else - Please credit me if possible or let me know about your game when using this so I can try it out :D

Honorable mention:

  • Toolbox - Effects and animations
  • Roblox gears - Some attacks were heavily based on them

Notice about usage: If you plan to use any free premade attacks, make sure you have permission to use the animations, visual effect designs (ParticleEmitter, Trail, Beam), audio sound effects or boss models (UGC Accessory, Roblox Accessory). Some of them were taken from the Roblox Toolbox which might not always be safe for commercial use


Support Me - Check the game below to see this system in effect!

(Uncopylocked) Boss Fight System | Mega Boss Battles (Showcase) | Clothing Store | Youtube Channel
17 Likes

Any feedback and help to improve the system is always appreciated!

I’m slowly working on more attacks and add to this. I already have over 30 attacks that need some adjustments before transfering from my game to here
This post will keep being updated until the day I’m banned from Roblox Studio or in case my laptop breaks (It already broke A key and runs baseplate starting place with only 30 FPS, maybe this can help you see how much I try to optimize the performance optimization for the system)

Feel free to suggest ideas for a boss attack, a boss design, your custom animations or effects and I’ll turn them into new attacks
Nobody has hired me before so you can also do that to get bosses/attacks for your game :]

3 Likes

Stay tuned to get this boss for free. Yep, you heard that right, a fully functional Halloween themed boss for our first ever event in this post


8 Likes

It’s nearly done but this headless guy’s head is taking me way too much time
Plus I have to update a bunch of outdated things from like 2 years ago

Anyway here are the current abilities of the main body (The head is still being worked on)


This thing is giving me headache

2 Likes


Hi everyone, I just added our first ever boss, the Headless Horseman!
(Optional) You can claim the boss model by doing the steps shown in the “Free Bosses” section of the main post
All of the attacks are already added into the Attacks module in ServerScript Service. However, you need the boss model to use the Beheaded attack (Detach the Pumpkin head and let it attack alongside the Headless Horseman with abilities like Blazing Breath and Roar Shockwave)
Animations can be found in the rigs in workspace, please upload them and replace the IDs of the animation tracks in Animations folder in ReplicatedStorage

The boss has 2 melee attacks (Swing + Hellfire Halo, Blood Slash) and 3 ranged attacks (Axe Throw, Slices, Jumping Leap)
His Pumpkin head will detach at 50% HP (Phase 2), giving him a Damage boost & WalkSpeed boost. It will follow players to shoot out burning Shards at them, or to use Blazing Breath and Roar Shockwave attack in close range
I will show a video for each attack in the main post tomorrow hopefully

1 Like

03/12/2025

  • Added :man_vampire:Lifesteal in status effect system. This effect will take HP from the target to heal the attacker. Lifesteal amount is affected by Defense, Void immunity and is multiplied by LifestealBoost stat. Must provide Creator in the settings
  • Turned all assets into Packages (They finally added it :sob:)
2 Likes

This looks SUPER cool! I might try and remake a table-type moveset for a battlegrounds game I’m working on! :smile:

2 Likes

Hi,
I am checking out this post now, had replies in your other post.

I see updates in the uncopylocked testing place, on 1/22/26… what things did you update?

Also is there a way when the explosion happen, if you are like inside of a room and a wall is blocking the direct path of the boss and the explosion, it does not do damage? prob would have to use raycasting or something to see if the path is blocked?..

Thanks

Hi
I’m currently busy with making a sticker system, 2 bosses, turrets, drawing icons, etc… so I only opened the place to update the packages to the newest version and to find a more simple way to share animations
About the changes, there’s too many things I cannot even list them all here but mostly new attacks, bug fixes and heavy optimizations
Your suggestion gave me an idea, I will update the explosion when I have time to add a new shockwave damage option (You cannot block that with a wall)

NICE! This sounds fantastic!

Great job, let me know if you want me to help out on early testing, private DM me if you want.

Also the bosses I checked out are super tough to beat… needs to be a balance of what they can do, what a play has or over time can gain to defeat them , and or loose a few times and come back to the boss with more skills …

U rock! keep it up!

1 Like