Countdown script won't work

Hey so I’ve been trying to find a fix to this bug I’m experiencing, but I still couldn’t find one. Here’s both of the scripts for anybody who wants to examine it.

The bug makes the bindable events not connect with each other, so it prevents me from actually using the handlers. I know this because I was using print(), but again if anybody knows the fix to this please let me know.

Another thing is that these handlers are in ServerScriptService, and the SpecialEvents folder is located in ReplicatedStorage.

Main Handler:

local Players = game:GetService("Players")

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local SpecialEvents = ReplicatedStorage.SpecialEvents

local CountdownBegin = SpecialEvents.CountdownBegin
local CountdownStop = SpecialEvents.CountdownStop
local GameResults = SpecialEvents.GameResults
local GameHandlerRestart = SpecialEvents.GameHandlerRestart
local NighttimeFunction = SpecialEvents.NighttimeFunction

local Minutes = ReplicatedStorage.GameConfiguration.Minutes
local Seconds = ReplicatedStorage.GameConfiguration.Seconds
local GameHandlerMessage = ReplicatedStorage.GameConfiguration.GameHandlerMessage

local NighttimeRemote = ReplicatedStorage.Event.NighttimeRemote
local LastOneStandingRemote = ReplicatedStorage.Event.LastOneStanding

local NighttimeEvent = require(game.ServerStorage.Events.ModuleScript)

local playerList = {}

local IsIntermission = ReplicatedStorage.GameConfiguration.IsIntermission.Value
IsIntermission = true

function RoundHandler()
	local PlayersInGroup = Players:GetPlayers()
	if #PlayersInGroup > 1 then
		CountdownStop:Fire()
		Minutes.Value = 0
		Seconds.Value = 0
		GameHandlerMessage.Value = ""

		local randomIndex = math.random(1, #PlayersInGroup)
		local randomPlayer = PlayersInGroup[randomIndex]

		task.wait(2)
	
		Minutes.Value = 3
		Seconds.Value = 25
		CountdownBegin:Fire()

		NighttimeFunction:Fire()
	else
		GameHandlerMessage.Value = "Game requires another person to start the round."
		Minutes.Value = 0
		Seconds.Value = 30
		task.wait(30)
		RoundHandler()
	end	
end

local function IsIntermissionChecker()
	Minutes.Value = 1
	Seconds.Value = 25
	CountdownBegin:Fire()
	print("Hey I'm going through")
	task.wait(85)
	RoundHandler()
end

IsIntermissionChecker()

Countdown Handler:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Minutes = ReplicatedStorage.GameConfiguration.Minutes
local Seconds = ReplicatedStorage.GameConfiguration.Seconds

local TextColor = ReplicatedStorage.GameConfiguration.TextColor

local SpecialEvents = ReplicatedStorage.SpecialEvents

local CountdownBegin = SpecialEvents.CountdownBegin
local CountdownStop = SpecialEvents.CountdownStop

local Countdown = coroutine.create(function()
	while true do
		if Seconds.Value <= 0 then
			Minutes.Value = Minutes.Value - 1
			Seconds.Value = 59
		else
			Seconds.Value = Seconds.Value - 1
		end
		wait(1)
	end
end)

--[[if Minutes.Value < 1 and Seconds.Value <= 15 then
	TextColor.Value = Color3.new(1, 0.329412, 0.341176)
else
	TextColor.Value = Color3.new(1, 1, 1)
end]]

CountdownBegin.Event:Connect(function()
	coroutine.resume(Countdown)
	print("Went through")
end)

CountdownStop.Event:Connect(function()
	print("Went through")
	coroutine.yield(Countdown)
end)

1 Like

You could just use seconds instead of minutes and seconds:
os.time() returns the number of seconds since the unix epoch.

if we save one os.time() and subtract it from one os.time() in the future, we know how many seconds have passed between these points in time.

we can then do something like this:

local startTick = os.time()
while task.wait() do
    local currentSeconds = os.time() - startTick
end

This will be going up as time passes (by one each second) - however we want the timer to count down. We can accomplish that by subtracting a maximum by the current seconds.

local maximum  = 60 --one minute
local timeLeft = maximum - currentSeconds

To convert the seconds into a string with minutes and seconds we can use this function:

local function addLeadingZero(s)
	s = (s:len() == 1 and "0"..s) or s --if string length is 1 then string is gonna be "0"..rest of string
	return s
end

local function formatSeconds(seconds)
	seconds = math.clamp(seconds, 0, math.huge)
	local minutes = math.floor(seconds/60)
	seconds -= minutes*60
	return addLeadingZero(tostring(minutes))..":"..addLeadingZero(tostring(seconds))
end
print(formatSeconds(70)) --will print 01:10
1 Like

If you were to implement it this way, you wouldn’t need two scripts and you wouldn’t need any events

I thing is that I want to have a minutes and seconds system.

that is what this part is doing

oohh, my apologies for the misunderstanding.

Honestly I do find the code very complex for me to understand. I want to have a system that’s like the coroutine that I’ve set up. I want it to be turned off and turned back on without any change to it.

That’s not how coroutine.yield() works…

It must be inside the coroutine itself in order to yield. I recommend that you just put the while loop at the very end of the script instead of a coroutine, and create a variable that is like a toggle. Only run the loop when that variable is true.

Countdown Handler:

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local Minutes = ReplicatedStorage.GameConfiguration.Minutes
local Seconds = ReplicatedStorage.GameConfiguration.Seconds

local TextColor = ReplicatedStorage.GameConfiguration.TextColor

local SpecialEvents = ReplicatedStorage.SpecialEvents

local CountdownBegin = SpecialEvents.CountdownBegin
local CountdownStop = SpecialEvents.CountdownStop

--[[if Minutes.Value < 1 and Seconds.Value <= 15 then
	TextColor.Value = Color3.new(1, 0.329412, 0.341176)
else
	TextColor.Value = Color3.new(1, 1, 1)
end]]

CountdownBegin.Event:Connect(function()
	print("Went through")
	countdownToggle = true
end)

CountdownStop.Event:Connect(function()
	print("Went through")
	countdownToggle = false
end)

while true do
	if countdownToggle == true then
		if Seconds.Value <= 0 then
			Minutes.Value -= 1
			Seconds.Value = 59
		else
			Seconds.Value -= 1
		end
	end
	task.wait(1)
end

Is there a way to make the while loop turn back on without any crazy changes to the scripts I have?

local function loop()

end

local thread = task.spawn(loop) --continue or start it
task.cancel(thread) --stop or pause it

i edited my script so that the while loop can start again, though i will edit it later for better results (also to integrate @Nico_Nic77’s answer)

though if you use task.cancel() or coroutine.close() you can never run it again so you must create a new thread

and that would be over-engineering (unless you want to do something after starting the loop) so it’s recommended that you just create a toggle variable and only do something in the loop if that variable is true

It doesn’t seem to restart. I did get print logs of the game going through the bindable events, and it did tick down, but it doesn’t restart the loop after it was toggled off.

I made an OOP counter yesterday (for someone else on here) and I can send you the code if you want to try it.

It can be paused, continued, started/restarted and completely stopped

Uhm I mean sure, but I’m not that advanced in scripting so I’m going to be confused if it’s complex.

This is the module script (you don’t have to change anything in here):

local Countdown = {}
Countdown.__index = Countdown

local function addLeadingZero(s)
	s = (s:len() == 1 and "0"..s) or s --if string length is 1 then string is gonna be "0"..rest of string
	return s
end

local function format(unix)
	unix = math.clamp(unix, 0, math.huge)
	local minutes = math.floor(unix/60)
	unix -= minutes*60
	return addLeadingZero(tostring(minutes))..":"..addLeadingZero(tostring(unix))
end

function Countdown.new(maxTime : number, startWithMax : boolean)
	startWithMax = (startWithMax == nil and true) or startWithMax
	local self = {}
	self.MaxTime = maxTime
	self.TimeRemaining = {
		unix   = ((startWithMax == true and 0) or 1)*maxTime,
		format = format(self.MaxTime)
	}
	self.Finished = Instance.new("BindableEvent")
	self.Updated  = Instance.new("BindableEvent")

	setmetatable(self, Countdown)
	return self
end

function Countdown:Start(...)
	local args = {...}
	if self.PauseThread then
		task.cancel(self.PauseThread)
	end

	self.Thread = task.spawn(function()
		self.StartTick = args[1] or os.time()
		while task.wait() do
			local currentSeconds = os.time() - self.StartTick
			local remainder 	 = self.MaxTime - currentSeconds

			if remainder < 0 then
				self.TimeRemaining = {
					unix   = 0,
					format = format(0)
				}
				self.Updated:Fire()
				self.Finished:Fire() 

				return
			end
			if remainder ~= self.TimeRemaining.unix then
				self.TimeRemaining = {
					unix   = remainder,
					format = format(remainder)
				}
				self.Updated:Fire()

				continue
			end

			self.TimeRemaining = {
				unix   = remainder,
				format = format(remainder)
			}
		end
	end)
end

function Countdown:Stop()
	task.cancel(self.Thread)
	self.Finished:Fire()
end

function Countdown:Continue()
	task.cancel(self.PauseThread)
	self:Start(self.StartTick)
end

function Countdown:Pause()
	task.cancel(self.Thread)
	self.PauseThread = task.spawn(function()
		local distance = os.time() - self.StartTick

		while task.wait() do
			self.StartTick = os.time() - distance
		end
	end)
end

function Countdown:AddTime(delta)
	self.StartTick += delta
end

function Countdown:Destroy()
	setmetatable(self, nil)
	self = nil
end

return Countdown

And this is an example Server script:

local Counter = require(script.Counter)
local count   = Counter.new(70)
count:Start()
count:Pause()
count:Continue()

count.Updated.Event:Connect(function()
	print(count.TimeRemaining.format)
end)

count.Finished.Event:Connect(function()
	print("counter done!")
end)
1 Like

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.