Tutorial text Glitches out

Hey!

I have got a bug in my roblox game where if the player plays the tutorial and doesn’t guess the plane on the first round and guesses the plane correctly on the second or third, fourth etc. The text glitches out from the cause of multiple stages playing really quickly at the same time.

The video is cut and sped up to be under 10 MB. The bug occurs after the second plane is shown and i guess ‘Boeing 787’ which is the correct plane. From then the text on the tutorial UI glitches.

It does not look like it glitched but it had. Also, where it said the correct plane was the HawkT1, that was the first plane showed (the small red one) which was incorrect it should’ve said Boeing 787.

Video.

Tutorial local script

local Player = game.Players.LocalPlayer

local TS = game:GetService("TweenService")

local PlaneModule = require(game.ReplicatedStorage.Modules.PlaneData.PlaneModule)

local ScreenGui = Player:WaitForChild("PlayerGui"):WaitForChild("Tutorial")
local NextBTN = ScreenGui.MainFrame.DevBox.NextBTN
local MainText= ScreenGui.MainFrame.MainText

local SoundEffects = game.SoundService.SoundEffects
local Click = SoundEffects.Click
local Type = SoundEffects.Type
local Celebration = SoundEffects.Celebration

local TypingSpeed = 0.01

local WinRem = game.ReplicatedStorage.Events.Rounds.Winner.Win
local WaitLavaRem = game.ReplicatedStorage.Events.Rounds.Lava.WaitLava
local CorrectAnswerRem = game.ReplicatedStorage.Events.Rounds.CorrectAnswer.CorrectAnswer
local GiftBlocksRem = game.ReplicatedStorage.Events.Gifting.GiftBlocks

local BlocksStat = Player:WaitForChild("leaderstats"):WaitForChild("Blocks")
local HasPlayedTutorial = false
if BlocksStat.Value == 0 or Player.Name == "planeboy2021" and not HasPlayedTutorial then
	
	local StageSession = 0
	local activeConnections = {}

	function CancelAllStages()
		StageSession += 1
		for _, conn in ipairs(activeConnections) do
			if conn and conn.Disconnect then conn:Disconnect() end
		end
		activeConnections = {}
	end

	task.wait(2)
	local function Stage1()
		
		local session = StageSession
		CurrentStage = 1
		ScreenGui.Enabled = true
		local PlayerName = Player.Name
		if Player.DisplayName then
			PlayerName = Player.DisplayName
		end
		local Text = "Welcome, " .. PlayerName .. "! 🌟 I’m Planey, your slightly crazy tour guide and the proud creator of this madness. Fasten your seat belts!"

		for i = 1, #Text do
			if session ~= StageSession then return end
			MainText.Text = string.sub(Text, 1, i)
			Type:Play()
			wait(TypingSpeed)
		end
		
	end
	Stage1()
	
	
	
	local function Stage3()
		local session = StageSession
		CurrentStage = 3
		NextBTN.Visible = true
		
		for i, frame in ScreenGui.Parent.GuessGUI:GetChildren() do
			if frame.Name == "GreyFrame" then
				frame:Destroy()
			end
		end
		
		local Stage4Arrow = ScreenGui.Parent.GuessGUI.Arrow2
		Stage4Arrow.Visible = false
		
		local Arrow = ScreenGui.Parent.GuessGUI.Arrow1
		Arrow.Visible = true
		local GreyFrame = Instance.new("Frame")
		GreyFrame.Size = UDim2.new(1,0,1,0)
		GreyFrame.Parent = ScreenGui.Parent.GuessGUI
		GreyFrame.Name = "GreyFrame"
		GreyFrame.Transparency = 0.5
		GreyFrame.BackgroundColor3 = Color3.new(0, 0, 0)
		
		ScreenGui.Parent.GuessGUI.PlaneImage.ZIndex = 2
		
		
		
		local Text = "You see this Image of a plane?"
		
		for i = 1, #Text do
			if session ~= StageSession then return end
			MainText.Text = string.sub(Text, 1, i)
			Type:Play()
			wait(TypingSpeed)
		end
		
		
	end
	
	local function Stage2() -- Waits for the Intermission or Round to end
		local session = StageSession
		local IntermissionText = ScreenGui.Parent.Intermission.MainFrame.TextLabel
		if IntermissionText.Parent.Parent.Enabled == false then
			CurrentStage = 2
			NextBTN.Visible = false
			local Text = "Anyways, let’s wait for the Round to end, so we can start!"

			for i = 1, #Text do
				if session ~= StageSession then return end
				MainText.Text = string.sub(Text, 1, i)
				Type:Play()
				wait(TypingSpeed)
			end

			WinRem.OnClientEvent:Connect(function()
				if not HasPlayedTutorial then
					HasPlayedTutorial = true
					CancelAllStages()
					task.spawn(Stage3)
				else
					Player.Character.Humanoid.Health = 0
				end
				
			end)
		else
			CurrentStage = 2
			NextBTN.Visible = false
			
			local Text = "Anyways, let’s wait for the Intermission to end, so we can start!"

			for i = 1, #Text do
				if session ~= StageSession then return end
				MainText.Text = string.sub(Text, 1, i)
				Type:Play()
				wait(TypingSpeed)
			end

			
		end

		WaitLavaRem.OnClientEvent:Connect(function()
			CancelAllStages()
			Stage3()
			
		end)

	end
	
	local function Stage8() -- Player gets complimented and gets 20 block
		local session = StageSession
		CurrentStage = 8
		NextBTN.Visible = true
		
		local Text = "Well, that is the whole game! Pretty simple right? As a reward I will give you 20 Blocks!"

		for i = 1, #Text do
			if session ~= StageSession then return end
			MainText.Text = string.sub(Text, 1, i)
			Type:Play()
			wait(TypingSpeed)
		end
		
		GiftBlocksRem:FireServer(20)
		
		for i, frame in ScreenGui.Parent.GuessGUI:GetChildren() do
			if frame.Name == "GreyFrame" then
				frame:Destroy()
			end
		end
		
		local BlocksLabel = ScreenGui.Parent.AddGui.MainFrame.Blocks
		
		BlocksLabel.Visible = true
		print(BlocksLabel.Visible)
		BlocksLabel.Text = "+".. 20 .. " Blocks"
		BlocksLabel.TextTransparency = 0
		Celebration:Play()
		task.wait(3)

		local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Linear)
		local goal = { TextTransparency = 1 }

		local BlockTween = TS:Create(BlocksLabel, tweenInfo, goal)
		BlockTween:Play()
		
	end
	
	local function Stage5(PlaneName) -- Player gets answer correct
		local session = StageSession
		CurrentStage = 5
		
		NextBTN.Visible = true
		
		local Info = TweenInfo.new(
			0.5,
			Enum.EasingStyle.Cubic,
			Enum.EasingDirection.In,
			0,
			false,
			0.5
		)

		local goal = {
			Position = UDim2.new(0.162, 0,0.645, 0)
		}

		local MoveTween = TS:Create(ScreenGui.MainFrame, Info, goal)
		MoveTween:Play()
		
		local Text = "Yay! 🥳 You are already and aviation expert!"
	
		for i = 1, #Text do
			if session ~= StageSession then return end
			MainText.Text = string.sub(Text, 1, i)
			Type:Play()
			wait(TypingSpeed)
		end
		
		
	end
	
	local function Stage7(CorrectPlaneName) -- Player gets answer incorret and is given answer
		CurrentStage = 7
		local session = StageSession
		local Text = "That’s alright. These planes can get a little challenging. The plane is called the ".. CorrectPlaneName

		local Arrow = ScreenGui.Parent.GuessGUI.Arrow2


		for i = 1, #Text do
			if session ~= StageSession then return end
			MainText.Text = string.sub(Text, 1, i)
			Type:Play()
			wait(TypingSpeed)
		end
		local Textbox = ScreenGui.Parent.GuessGUI.TextBox.TextBox

		local CorrectPlaneName
		local CurrentImage = ScreenGui.Parent.GuessGUI.PlaneImage.Image

		for i, plane in PlaneModule do
			if plane.ID == CurrentImage then
				CorrectPlaneName = plane.Name

			end
		end

		Textbox.FocusLost:Connect(function(enterPressed)

			if enterPressed then
				local function normalize(text)
					return string.lower(string.gsub(text, "%s+", ""))
				end
				if normalize(Textbox.Text) == normalize(CorrectPlaneName) then
					Stage8()
				else 
					Stage7()
				end
			else

			end
		end)
	end
	
	local function Stage6(CorrectPlaneName) -- Player gets answer incorrect and tries again
		CurrentStage = 6
		local session = StageSession
		local Text = "So close! Try again."
		
		local Arrow = ScreenGui.Parent.GuessGUI.Arrow2

		
		for i = 1, #Text do
			if session ~= StageSession then return end
			MainText.Text = string.sub(Text, 1, i)
			Type:Play()
			wait(TypingSpeed)
		end
		local Textbox = ScreenGui.Parent.GuessGUI.TextBox.TextBox

		
		local CurrentImage = ScreenGui.Parent.GuessGUI.PlaneImage.Image

		for i, plane in PlaneModule do
			if plane.ID == CurrentImage then
				CorrectPlaneName = plane.Name

			end
		end
		
		Textbox.FocusLost:Connect(function(enterPressed)

			if enterPressed then
				local function normalize(text)
					return string.lower(string.gsub(text, "%s+", ""))
				end
				if normalize(Textbox.Text) == normalize(CorrectPlaneName) then
					Stage5(CorrectPlaneName)
					Textbox.Parent.ZIndex = 1
					Arrow.Visible = false
				else 
					Stage7(CorrectPlaneName)
				end
			else

			end
		end)
	end
	
	
	
	local function Stage4()
		
		ScreenGui.Parent.GuessGUI.Arrow1.Visible = false
		local session = StageSession
		CurrentStage = 4
		
		NextBTN.Visible = false
		
		local Arrow = ScreenGui.Parent.GuessGUI.Arrow2
		Arrow.Visible = true
		local GreyFrame = Instance.new("Frame")
		GreyFrame.Size = UDim2.new(1,0,1,0)
		GreyFrame.Parent = ScreenGui.Parent.GuessGUI
		GreyFrame.Name = "GreyFrame"
		GreyFrame.Transparency = 0.5
		GreyFrame.BackgroundColor3 = Color3.new(0, 0, 0)

		ScreenGui.Parent.GuessGUI.TextBox.TextBox.ZIndex = 2
		
		local Info = TweenInfo.new(
			0.5,
			Enum.EasingStyle.Cubic,
			Enum.EasingDirection.In,
			0,
			false,
			0.5
		)

		local goal = {
			Position = UDim2.new(0.16, 0,0.374, 0)
		}

		local MoveTween = TS:Create(ScreenGui.MainFrame, Info, goal)
		MoveTween:Play()
		
		local Text = "Type the name of the Plane in the Textbox at the bottom."
		local Textbox = ScreenGui.Parent.GuessGUI.TextBox.TextBox
		Textbox.Parent.ZIndex = 2
		
		task.spawn(function()
			for i = 1, #Text do
				if session ~= StageSession then return end
				MainText.Text = string.sub(Text, 1, i)
				Type:Play()
				wait(TypingSpeed)
			end
		end)
		
		
		local CorrectPlaneName
		local CurrentImage = ScreenGui.Parent.GuessGUI.PlaneImage.Image
		
		for i, plane in PlaneModule do
			if plane.ID == CurrentImage then
				CorrectPlaneName = plane.Name
				
			end
		end
		
		Textbox.FocusLost:Connect(function(enterPressed)
		
			if enterPressed then
				local function normalize(text)
					return string.lower(string.gsub(text, "%s+", ""))
				end
				if normalize(Textbox.Text) == normalize(CorrectPlaneName) then
					Stage5(CorrectPlaneName)
					Textbox.Parent.ZIndex = 1
					Arrow.Visible = false
				else 
					Stage6(CorrectPlaneName)
				end
			else
				
			end
		end)
	
	end
	
	
	
	NextBTN.MouseButton1Click:Connect(function()
		if CurrentStage == 1 then
			Stage2()
		elseif CurrentStage == 2 then
			Stage3()
		elseif CurrentStage == 3 then
			Stage4()
		elseif CurrentStage == 4 then
			Stage5()
		elseif CurrentStage == 5 then
			Stage6()
		elseif CurrentStage == 6 then
			Stage7()
		elseif CurrentStage == 7 then
			Stage8()
		end
	end)
	
	
end

I believe the cause is the Textbox.FocusLost event (and other events I may have missed) is not being disconnected from the previous stage(s), which causes the textbox to fire the current stage and previous stage functions at the same time.

Make sure you disconnect any associated connections you no longer need when a stage is considered complete.