PcoWSkbVqDnWTu_dm2ix
We use cookies on this site to enhance your user experience

Top Down Action: Boss Fight

Top Down Action: Boss Fight

Jul 30 2018, 2:11 PM PST 10 min

The last thing we need to do is code a boss fight for our game. The fight will consist of two phases. In the first phase, the player must defeat the robot wizards that are powering the boss’s force field. After the wizards are gone, phase two starts, and the boss starts summoning robot summoners (who will in turn summon PiBots). If the player is able to defeat the boss in phase two, then they have won the game.

TDS_MrBoss.png

Boss AI Script

To make the Boss summon SummonerBots, we can actually just reuse the SummonerBot script. In the MrBoss Script inside of RobotScript enter the following code:

local AIController = require(game.ServerStorage.RobotScripts.RobotController)

local robot = script.Parent
local maxSummonDistance = 10
local summonAnimationTrack = robot.Humanoid:LoadAnimation(robot.SummonAnimation)
local summonActivationTime = 4

local configurations = {}
configurations.AggroDistance = 30
configurations.ActionDistance = 30
configurations.ActionCooldown = 4
configurations.LastAction = 0

configurations.Action = function(target)
	robot['Right Hand'].ParticleEmitter.Enabled = true
	robot['Left Hand'].ParticleEmitter.Enabled = true
	summonAnimationTrack:Play()
	wait(summonActivationTime)
	robot['Right Hand'].ParticleEmitter.Enabled = false
	robot['Left Hand'].ParticleEmitter.Enabled = false
	if robot.Humanoid.Health > 0 then
		local toTarget = target.Torso.Position - robot.Torso.Position
		local offsetDistance = math.min(maxSummonDistance, toTarget.magnitude/2)
		local summonOffset = toTarget.unit * offsetDistance
		local summon = game.ServerStorage.Robots.SummonerBot:Clone()
		summon:SetPrimaryPartCFrame(robot.Torso.CFrame + summonOffset)
		summon.Parent = game.Workspace.Robots
	end
end

configurations.Aggro = function(target)
	
end

AIController:RunAI(robot, configurations)

The one line change we made is enough to make MrBoss summon a different robot. Apart from that, he will behave just like the other SummonerBots.

Boss Fight Script

Now we need a script to manage our boss fight and transition between the two phases. Create a new Script|Script in ServerScriptService|ServerScriptService and name it BossFight. Enter the following code:

local bossFightFolder = game.Workspace.BossFight

local function startPhase2()
	

end

local function startPhase1()
	
	
end

startPhase1()

We are simply setting up a function for each phase, then we call the function for the first phase. We also will need a reference to the BossFight folder so we set up a variable for it.

Phase 1

TDS_Shieldbot.png

In the first phase, we are just waiting until all of the bots in BossFight.ShieldBots are defeated. To do this, we will bind a function to each of their Humanoid/Died|Died events:

local bossFightFolder = game.Workspace.BossFight

local function startPhase2()
	

end

local function startPhase1()
	local shieldBots = bossFightFolder.ShieldBots:GetChildren()
	local activeBots = #shieldBots
	for _, bot in pairs(shieldBots) do
		bot.Humanoid.Died:connect(function()
			activeBots = activeBots - 1
			if activeBots == 0 then
				startPhase2()
			end
			wait(1)
			bot:Destroy()
		end)
	end
end

startPhase1()

We start by getting a list of all the bots in the ShieldBots folder. We then store the number of robots in the variable activeBots. We then use a for loop to cycle through all of the robots in sheildBots. For each one, we connect a function to their Humanoid/Died|Died event. In this function, we subtract 1 from the activeBots variable. If activeBots reaches 0, then we know all of the SheildBots have been defeated and we can move to phase 2. After we run this check, we wait a bit of time and then clean up the ShieldBot with the Instance/Destroy|Destroy function.

Phase 2

In this phase, we need to remove the force field that surrounds the boss so he can see the player and act accordingly:

local bossFightFolder = game.Workspace.BossFight
local dialogEvent = game.ReplicatedStorage.ShowDialog

local function startPhase2()
	local forceField = bossFightFolder.ForceField
	forceField.Anchored = false
	forceField.CanCollide = false
	
	wait(2)
	dialogEvent:FireAllClients("Boss", "So, you think you have won? Robot wizards, to me!", 2)
end

local function startPhase1()
	local shieldBots = bossFightFolder.ShieldBots:GetChildren()
	local activeBots = #shieldBots
	for _, bot in pairs(shieldBots) do
		bot.Humanoid.Died:connect(function()
			activeBots = activeBots - 1
			if activeBots == 0 then
				startPhase2()
			end
			wait(1)
			bot:Destroy()
		end)
	end
end

startPhase1()

While we could simply use the Instance/Destroy|Destroy function to remove the force field, we can get a neat effect by disabling the collisions and unanchoring it. This will cause the force field to fall due to gravity, which will make it appear to be sinking into the floor.

We are also using ‘‘dialogEvent’’, which we made when coding the keys and doors, in order to have the boss say some dialog. But just like how we needed a thumbnail picture for the keys, we should have a thumbnail picture for the boss so the player can know where the dialog is coming from. Copy and paste the Key ImageLabel inside of ‘‘StarterGui.DialogGui.DialogFrame’’, and rename the copy Boss. In the ImageLabel/Image|Image property of Boss, insert the url: rbxassetid://323147912.

Now when Phase2 starts the player will see some dialog from the boss.

TDS_BossPhase2.png

The last thing we need to do is bind a function to the Humanoid/Died|Died event of the boss so we know when the player has won the game. We can use PointsService|PointsService to give players recognition for beating the game, which will also put them on the game’s leaderboard (which shows up automatically on the game’s page on the Roblox site).

local bossFightFolder = game.Workspace.BossFight
local dialogEvent = game.ReplicatedStorage.ShowDialog
local PointsService = game:GetService('PointsService')

local function startPhase2()
	local forceField = bossFightFolder.ForceField
	forceField.Anchored = false
	forceField.CanCollide = false
	
	wait(2)
	dialogEvent:FireAllClients("Boss", "So, you think you have won? Robot wizards, to me!", 2)
	
	game.Workspace.Robots.MrBoss.Humanoid.Died:connect(function()
		for _, player in pairs(game.Players:GetPlayers()) do
			pcall(function() PointsService:AwardPoints(player.UserId, 1) end)
		end
		dialogEvent:FireAllClients("Boss", "Ouch! You have bested me. This souffle has fallen!", 5)
	end)
end

local function startPhase1()
	local shieldBots = bossFightFolder.ShieldBots:GetChildren()
	local activeBots = #shieldBots
	for _, bot in pairs(shieldBots) do
		bot.Humanoid.Died:connect(function()
			activeBots = activeBots - 1
			if activeBots == 0 then
				startPhase2()
			end
			wait(1)
			bot:Destroy()
		end)
	end
end

startPhase1()

When MrBoss is defeated, the function will cycle through all of the players in the game and award them a point with the PointsService/AwardPoints|AwardPoints function. The PointsService|PointsService can only be called from a live game though and will not work in Studio. To make sure this restriction does not cause our code to crash, we wrap the code in a special function called pcall. If any code crashes inside of a pcall, the rest of the script can continue running without error.

After we award points, we send another dialog to all the players letting them know the boss has been defeated.

TDS_BossWin.png