this code.
local currentTimer = nil
local timerActive = false
function createTimerGui(player, triggeringPart)
if timerActive then return end
timerActive = true
timerStatusEvent:FireClient(player, true, false)
local playerGui = player:WaitForChild("PlayerGui")
if playerGui:FindFirstChild("TimerGui") then return end
local cfg = Config.TimerGui
local duration = (triggeringPart and triggeringPart:GetAttribute("TimerDuration")) or Config.MainTimerDuration
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "TimerGui"
screenGui.ResetOnSpawn = false
screenGui.IgnoreGuiInset = true
screenGui.Parent = playerGui
local backdrop = Instance.new("Frame")
backdrop.Size = UDim2.new(1, 0, 1, 0)
backdrop.BackgroundColor3 = cfg.backdropColor
backdrop.BackgroundTransparency = cfg.backdropTransparency
backdrop.ZIndex = 1
backdrop.Parent = screenGui
local container = Instance.new("Frame")
container.Size = UDim2.new(0.4, 0, 0.4, 0)
container.AnchorPoint = Vector2.new(0.5, 0.5)
container.Position = UDim2.new(0.5, 0, -0.5, 0) -- Start offscreen
container.BackgroundTransparency = 1
container.ZIndex = 2
container.Parent = screenGui
local ring = Instance.new("ImageLabel")
ring.Name = "CountdownRing"
ring.Size = UDim2.new(1, 0, 1, 0)
ring.AnchorPoint = Vector2.new(0.5, 0.5)
ring.Position = UDim2.new(0.5, 0, 0.5, 0)
ring.BackgroundTransparency = 1
ring.Image = cfg.ringImageId
ring.ImageColor3 = cfg.ringColor
ring.ScaleType = Enum.ScaleType.Slice
ring.SliceCenter = Rect.new(64, 64, 192, 192)
ring.SliceScale = 0.5
ring.Parent = container
local timerLabel = Instance.new("TextLabel")
timerLabel.Name = "TimerLabel"
timerLabel.AnchorPoint = Vector2.new(0.5, 0.5)
timerLabel.Position = UDim2.new(0.5, 0, 0.5, 0)
timerLabel.Size = UDim2.new(0.8, 0, 0.5, 0)
timerLabel.BackgroundTransparency = 1
timerLabel.Font = cfg.labelFont
timerLabel.TextSize = cfg.labelTextSize
timerLabel.TextColor3 = cfg.labelTextColor
timerLabel.TextStrokeColor3 = cfg.labelStrokeColor
timerLabel.TextStrokeTransparency = 0
timerLabel.Text = string.format("%d:%02d", math.floor(duration/60), duration%60)
timerLabel.Parent = container
local barBg = Instance.new("Frame")
barBg.AnchorPoint = Vector2.new(0.5, 1)
barBg.Position = UDim2.new(0.5, 0, 1, -cfg.barPadding)
barBg.Size = UDim2.new(0.6, 0, 0, cfg.barHeight)
barBg.BackgroundColor3 = cfg.barBgColor
barBg.BackgroundTransparency = 0.3
barBg.Parent = container
local bar = Instance.new("Frame")
bar.Name = "ProgressBar"
bar.Size = UDim2.new(1, 0, 1, 0)
bar.BackgroundColor3 = cfg.barColor
bar.Parent = barBg
-- Animate slide in
TweenService:Create(container, TweenInfo.new(1, Enum.EasingStyle.Quad), {
Position = UDim2.new(0.5, 0, 0.4, 0)
}):Play()
local tickSound = Instance.new("Sound", screenGui)
tickSound.SoundId = cfg.tickSoundId
tickSound.Volume = cfg.tickVolume
tickSound.Looped = false
local remaining = duration
local function updateUI()
timerLabel.Text = string.format("%d:%02d", math.floor(remaining / 60), remaining % 60)
local pct = remaining / duration
ring.ImageTransparency = 1 - pct
bar.Size = UDim2.new(pct, 0, 1, 0)
if pct > 0.5 then
bar.BackgroundColor3 = Color3.new(0, 1, 0)
elseif pct > 0.25 then
bar.BackgroundColor3 = Color3.new(1, 1, 0)
else
bar.BackgroundColor3 = Color3.new(1, 0, 0)
end
tickSound:Play()
if remaining <= 10 then
TweenService:Create(container, TweenInfo.new(0.15), {Size = UDim2.new(0.42, 0, 0.42, 0)}):Play()
TweenService:Create(container, TweenInfo.new(0.15), {Size = UDim2.new(0.4, 0, 0.4, 0)}):Play()
end
if remaining == 30 and cfg.dingSoundId then
local ding = Instance.new("Sound", screenGui)
ding.SoundId = cfg.dingSoundId
ding.Volume = cfg.dingVolume
ding:Play()
Debris:AddItem(ding, 3)
end
end
local function onComplete()
TweenService:Create(container, TweenInfo.new(1), {
Position = UDim2.new(0.5, 0, -0.5, 0)
}):Play()
TweenService:Create(backdrop, TweenInfo.new(1), {BackgroundTransparency = 1}):Play()
task.delay(1, function()
screenGui:Destroy()
timerActive = false
timerStatusEvent:FireClient(player, false, false)
end)
end
currentTimer = Timer.new(duration, timerLabel, player, onComplete, (triggeringPart and not triggeringPart:GetAttribute("DisableOvertime")))
currentTimer._onTick = updateUI
function currentTimer:start()
task.spawn(function()
while not self.cancelled do
if self.remaining > 0 then
self.remaining -= 1
if self._onTick then self._onTick() end
else
break
end
task.wait(1)
end
if not self.cancelled and self.onComplete then
self.onComplete()
end
end)
end
currentTimer:start()
end
give the gui an entire overhaul of details and improved gui
--[[
TimerDemonSystem.server.lua
Modular Roblox Timer Demon System (Overtime Spawns Demon and Effects)
Place this script in ServerScriptService
All configuration and logic is in this single file for easy modification.
]]
---------------------------------------------------------------------
-- CONFIGURATION SECTION
---------------------------------------------------------------------
local Config = {}
-- Main timer duration (in seconds)
Config.MainTimerDuration = 103
-- Audio presets for timer activation
Config.AudioPresets = {
preset1 = {
soundId = "rbxassetid://133014492553616",
volume = 10,
pitch = 1.05,
looped = false,
},
preset2 = {
soundId = "rbxassetid://71472790339951",
volume = 15,
pitch = 1.15,
looped = false,
},
}
-- Demon spawn settings
Config.Demon = {
offset = Vector3.new(0, 10, -15), -- relative to player look direction
size = Vector3.new(10, 10, 10),
color = Color3.fromRGB(17, 17, 17),
material = Enum.Material.Neon,
shape = Enum.PartType.Ball,
particleTexture = "http://www.roblox.com/asset/?id=2996538390",
smileTexture = "rbxassetid://120549545145829",
}
-- Overtime sound settings
Config.OvertimeSounds = {
run = {
soundId = "rbxassetid://1034263055",
volume = 0.5,
pitch = 1,
effects = {
DistortionSoundEffect = { Level = 0.4 },
PitchShiftSoundEffect = { Octave = 0.35 },
EqualizerSoundEffect = { LowGain = 10, MidGain = 10 },
ReverbSoundEffect = { WetLevel = 2.5 },
}
},
bell = {
soundId = "rbxassetid://78781475442496",
volume = 0.5,
pitch = 0.9,
effects = {
DistortionSoundEffect = { Level = 0.35 },
EqualizerSoundEffect = { LowGain = 10, MidGain = 10 },
PitchShiftSoundEffect = { Octave = 0.5 },
ReverbSoundEffect = { WetLevel = 2.5 },
}
},
laugh = {
soundId = "rbxassetid://18186817573",
volume = 1,
pitch = 0.75,
minDistance = 500,
maxDistance = 1500,
effects = {
DistortionSoundEffect = { Level = 0.65 },
}
}
}
-- Death sounds
Config.DeathSounds = {
{ soundId = "rbxassetid://169310273", volume = 0.5, pitch = 0.8, maxDistance = 750, minDistance = 0 },
{ soundId = "rbxassetid://82176913611683", volume = 1, pitch = 1, maxDistance = 750, minDistance = 0 },
}
-- Decal settings
Config.Decal = {
defaultId = "rbxassetid://834811660",
winSoundId = "rbxassetid://4879566787",
winSoundVolume = 1,
winSoundPitch = 1,
displayTime = 5,
}
-- GUI settings
Config.TimerGui = {
labelSize = UDim2.new(1, 0, 0.05, 0),
labelPosition = UDim2.new(0, 0, -0.1, 0),
labelFont = Enum.Font.SourceSansBold,
labelTextSize = 48,
labelTextColor = Color3.new(1, 1, 1),
labelBackgroundColor = Color3.new(0, 0, 0),
labelBackgroundTransparency = 0.5,
tweenTime = 3,
tweenPosition = UDim2.new(0, 0, 0.6, 80),
}
-- Overtime flash colors
Config.OvertimeFlashColors = { Color3.new(1, 0, 0), Color3.new(0.5, 0, 1) }
Config.OvertimeFlashInterval = 0.25
---------------------------------------------------------------------
-- SERVICES
---------------------------------------------------------------------
local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Debris = game:GetService("Debris")
---------------------------------------------------------------------
-- REMOTE EVENT
---------------------------------------------------------------------
local timerStatusEvent = ReplicatedStorage:FindFirstChild("TimerStatusEvent")
if not timerStatusEvent then
timerStatusEvent = Instance.new("RemoteEvent")
timerStatusEvent.Name = "TimerStatusEvent"
timerStatusEvent.Parent = ReplicatedStorage
end
---------------------------------------------------------------------
-- STATE
---------------------------------------------------------------------
local timerActive = false
local soundPlayed = false
local currentTimer = nil
local activeDecalPlayers = {}
---------------------------------------------------------------------
-- UTILITY: SOUND EFFECTS
---------------------------------------------------------------------
local function applySoundEffects(sound, effects)
for effectType, props in pairs(effects or {}) do
local effect = Instance.new(effectType)
for k, v in pairs(props) do
effect[k] = v
end
effect.Parent = sound
end
end
---------------------------------------------------------------------
-- DEMON SPAWN & OVERTIME EFFECTS
---------------------------------------------------------------------
local function spawnOrbitalDemonWithEffects(player)
-- Overtime sounds
for _, soundConfig in pairs({Config.OvertimeSounds.run, Config.OvertimeSounds.bell}) do
local sound = Instance.new("Sound")
sound.Name = "OvertimeSound"
sound.SoundId = soundConfig.soundId
sound.Volume = soundConfig.volume
sound.Pitch = soundConfig.pitch
sound.Looped = false
sound.Parent = player:FindFirstChild("PlayerGui") or game:GetService("SoundService")
applySoundEffects(sound, soundConfig.effects)
sound:Play()
Debris:AddItem(sound, 20)
end
-- Red flash GUI
local flashGui = Instance.new("ScreenGui")
flashGui.Name = "FlashGui"
flashGui.ResetOnSpawn = false
flashGui.IgnoreGuiInset = true
flashGui.Parent = player:WaitForChild("PlayerGui")
local redFlash = Instance.new("Frame")
redFlash.Size = UDim2.new(1, 0, 1, 0)
redFlash.Position = UDim2.new(0, 0, 0, 0)
redFlash.BackgroundColor3 = Color3.new(1, 0, 0)
redFlash.BackgroundTransparency = 0
redFlash.ZIndex = 10
redFlash.Parent = flashGui
local redFlashTween = TweenService:Create(redFlash, TweenInfo.new(5, Enum.EasingStyle.Linear), {
BackgroundTransparency = 1
})
redFlashTween:Play()
redFlashTween.Completed:Connect(function()
flashGui:Destroy()
end)
-- Demon spawn
local character = player.Character
if not character or not character:FindFirstChild("HumanoidRootPart") then return end
local hrp = character.HumanoidRootPart
local lookVec = hrp.CFrame.LookVector
local spawnPos = hrp.Position + Vector3.new(0, Config.Demon.offset.Y, 0) - lookVec * math.abs(Config.Demon.offset.Z)
local demon = Instance.new("Part")
demon.Name = "Orbital Demon"
demon.CFrame = CFrame.new(spawnPos)
demon.BottomSurface = Enum.SurfaceType.Smooth
demon.CanCollide = false
demon.TopSurface = Enum.SurfaceType.Smooth
demon.Color = Config.Demon.color
demon.Material = Config.Demon.material
demon.Size = Config.Demon.size
demon.Anchored = true
demon.Shape = Config.Demon.shape
local hitbox = Instance.new("Part")
hitbox.Name = "Orbital hitbox"
hitbox.CFrame = CFrame.new(spawnPos)
hitbox.BottomSurface = Enum.SurfaceType.Smooth
hitbox.CanCollide = false
hitbox.TopSurface = Enum.SurfaceType.Smooth
hitbox.Size = Config.Demon.size / 3.5
hitbox.Anchored = true
hitbox.Shape = Config.Demon.shape
hitbox.Parent = demon
local hitboxweld = Instance.new("WeldConstraint")
hitboxweld.Part0 = hitbox
hitboxweld.Part1 = demon
hitboxweld.Parent = hitbox
local heliight = Instance.new("Highlight")
heliight.Parent = hitbox
-- Attachments and particles
local RedVoidcircle = Instance.new("Attachment")
RedVoidcircle.Name = "RedVoidcircle"
RedVoidcircle.CFrame = CFrame.new(0, -0.25, 0)
RedVoidcircle.Position = Vector3.new(0, -0.25, 0)
RedVoidcircle.Parent = demon
local Redcirclevoid = Instance.new("ParticleEmitter")
Redcirclevoid.Name = "Redcirclevoid"
Redcirclevoid.Lifetime = NumberRange.new(5, 5)
Redcirclevoid.LockedToPart = true
Redcirclevoid.Transparency = NumberSequence.new({NumberSequenceKeypoint.new(0, 0), NumberSequenceKeypoint.new(0.5, 1), NumberSequenceKeypoint.new(1, 0)})
Redcirclevoid.Color = ColorSequence.new(Color3.fromRGB(255, 0, 0))
Redcirclevoid.Speed = NumberRange.new(0.175, 0.175)
Redcirclevoid.Brightness = 10
Redcirclevoid.Size = NumberSequence.new({NumberSequenceKeypoint.new(0, 4), NumberSequenceKeypoint.new(0.5, 10), NumberSequenceKeypoint.new(1, 4)})
Redcirclevoid.VelocityInheritance = 1
Redcirclevoid.Rate = 100
Redcirclevoid.Texture = Config.Demon.particleTexture
Redcirclevoid.RotSpeed = NumberRange.new(380, 380)
Redcirclevoid.Orientation = Enum.ParticleOrientation.VelocityPerpendicular
Redcirclevoid.Parent = RedVoidcircle
local Center = Instance.new("Attachment")
Center.Name = "Center"
Center.Parent = demon
local Redholeblackglow = Instance.new("ParticleEmitter")
Redholeblackglow.Name = "Redholeblackglow"
Redholeblackglow.Lifetime = NumberRange.new(5, 5)
Redholeblackglow.LockedToPart = true
Redholeblackglow.LightEmission = 0.8
Redholeblackglow.Color = ColorSequence.new(Color3.fromRGB(0, 0, 0))
Redholeblackglow.Squash = NumberSequence.new(0.5)
Redholeblackglow.Speed = NumberRange.new(0, 0)
Redholeblackglow.Size = NumberSequence.new(4)
Redholeblackglow.ZOffset = -0.5
Redholeblackglow.VelocityInheritance = 1
Redholeblackglow.Rate = 100
Redholeblackglow.Texture = Config.Demon.particleTexture
Redholeblackglow.RotSpeed = NumberRange.new(380, 380)
Redholeblackglow.Parent = Center
local Redholeredglow = Instance.new("ParticleEmitter")
Redholeredglow.Name = "Redholeredglow"
Redholeredglow.Lifetime = NumberRange.new(5, 5)
Redholeredglow.LockedToPart = true
Redholeredglow.LightEmission = 0.8
Redholeredglow.Color = ColorSequence.new(Color3.fromRGB(255, 0, 0))
Redholeredglow.Squash = NumberSequence.new(0.5)
Redholeredglow.Speed = NumberRange.new(0, 0)
Redholeredglow.Size = NumberSequence.new(4.25)
Redholeredglow.ZOffset = -0.75
Redholeredglow.VelocityInheritance = 1
Redholeredglow.Rate = 100
Redholeredglow.Texture = Config.Demon.particleTexture
Redholeredglow.RotSpeed = NumberRange.new(380, 380)
Redholeredglow.Parent = Center
local RedholeSmile = Instance.new("ParticleEmitter")
RedholeSmile.Name = "RedholeSmile"
RedholeSmile.Lifetime = NumberRange.new(0.25, 0.25)
RedholeSmile.LockedToPart = true
RedholeSmile.Color = ColorSequence.new(Color3.fromRGB(255, 0, 0))
RedholeSmile.Drag = 100
RedholeSmile.Speed = NumberRange.new(0, 0)
RedholeSmile.Size = NumberSequence.new(5)
RedholeSmile.ZOffset = 7
RedholeSmile.VelocityInheritance = 1
RedholeSmile.Texture = Config.Demon.smileTexture
RedholeSmile.RotSpeed = NumberRange.new(-1, 1)
RedholeSmile.Parent = Center
local Redholeshine = Instance.new("ParticleEmitter")
Redholeshine.Name = "Redholeshine"
Redholeshine.LightInfluence = 1
Redholeshine.LockedToPart = true
Redholeshine.Transparency = NumberSequence.new({NumberSequenceKeypoint.new(0, 1), NumberSequenceKeypoint.new(0.5037407, 0.96875), NumberSequenceKeypoint.new(1, 1)})
Redholeshine.LightEmission = 1
Redholeshine.Color = ColorSequence.new(Color3.fromRGB(255, 0, 4))
Redholeshine.Drag = 100
Redholeshine.Speed = NumberRange.new(0, 0)
Redholeshine.Size = NumberSequence.new(1, 10)
Redholeshine.ZOffset = 6
Redholeshine.VelocityInheritance = 1
Redholeshine.RotSpeed = NumberRange.new(-15, 15)
Redholeshine.Parent = Center
-- Laugh sound
local laughConfig = Config.OvertimeSounds.laugh
local SpawnLaugh = Instance.new("Sound")
SpawnLaugh.Name = "SpawnLaugh"
SpawnLaugh.Volume = laughConfig.volume
SpawnLaugh.Pitch = laughConfig.pitch
SpawnLaugh.MinDistance = laughConfig.minDistance
SpawnLaugh.MaxDistance = laughConfig.maxDistance
SpawnLaugh.SoundId = laughConfig.soundId
applySoundEffects(SpawnLaugh, laughConfig.effects)
SpawnLaugh.Parent = demon
SpawnLaugh:Play()
demon.Parent = Workspace
hitbox.Touched:Connect(function(hit)
local touchedPlayer = Players:GetPlayerFromCharacter(hit.Parent)
if touchedPlayer and touchedPlayer == player then
local humanoid = player.Character and player.Character:FindFirstChild("Humanoid")
if humanoid then
humanoid.BreakJointsOnDeath = true
for _, soundConfig in ipairs(Config.DeathSounds) do
local deathSound = Instance.new("Sound")
deathSound.SoundId = soundConfig.soundId
deathSound.Volume = soundConfig.volume
deathSound.Pitch = soundConfig.pitch
deathSound.MaxDistance = soundConfig.maxDistance
deathSound.MinDistance = soundConfig.minDistance
deathSound.Parent = humanoid
deathSound:Play()
end
local function createBloodEffect(part)
local bloody = Instance.new("ParticleEmitter")
bloody.Name = "bloody"
bloody.LightInfluence = 1
bloody.Lifetime = NumberRange.new(0.35, 0.35)
bloody.SpreadAngle = Vector2.new(360, 360)
bloody.LockedToPart = false
bloody.Transparency = NumberSequence.new({NumberSequenceKeypoint.new(0, 0.59375), NumberSequenceKeypoint.new(0.5236908, 0.4125), NumberSequenceKeypoint.new(1, 0.74375)})
bloody.WindAffectsDrag = true
bloody.Drag = 25
bloody.VelocitySpread = 360
bloody.Squash = NumberSequence.new(-0.3000002, 0.2250001)
bloody.Speed = NumberRange.new(15, 15)
bloody.Size = NumberSequence.new(0, 3.4999995)
bloody.Acceleration = Vector3.new(0, -2, 0)
bloody.ZOffset = 1
bloody.VelocityInheritance = 0.25
bloody.Rate = 50
bloody.Texture = "rbxassetid://2787218577"
bloody.RotSpeed = NumberRange.new(-100, 100)
bloody.Parent = part
end
for _, part in pairs(player.Character:GetChildren()) do
if part:IsA("BasePart") then
createBloodEffect(part)
end
end
local randomDirection = Vector3.new(math.random(-100, 100), math.random(100, 200), math.random(-100, 100))
local velocity = Instance.new("BodyVelocity")
velocity.MaxForce = Vector3.new(10000, 10000, 10000)
velocity.Velocity = randomDirection
velocity.Parent = player.Character:FindFirstChild("HumanoidRootPart")
Debris:AddItem(velocity, 0.25)
humanoid.Health = 0
demon:Destroy()
wait(0.1)
for _, part in ipairs(player.Character:GetDescendants()) do
if part:IsA("BallSocketConstraint") or part:IsA("Motor6D") then
part:Destroy()
end
end
end
end
end)
local humanoidRootPart = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
if humanoidRootPart then
local currentVelocity = Vector3.zero
local function updateDemonMovement()
RunService.Heartbeat:Connect(function(dt)
if humanoidRootPart and humanoidRootPart.Parent and demon and demon.Parent then
local toTarget = humanoidRootPart.Position - demon.Position
local distance = toTarget.Magnitude
local desiredVelocity = toTarget.Unit * math.clamp(distance * 2.25, 0, math.huge)
local velocityDelta = desiredVelocity - currentVelocity
local isAccelerating = velocityDelta.Magnitude > 0.25 and currentVelocity:Dot(velocityDelta) > 0
-- Adjust acceleration/deceleration rates
local accelRate = math.clamp(dt * 16 * distance, 0, 0.45)
local maxDecel = 0.125 -- 🔽 significantly lowered for softer decel
local minDecel = 0 -- 🔽 very low base decel
local decelRate = math.clamp(dt * distance * 0.0625, minDecel, maxDecel)
local lerpAlpha = isAccelerating and accelRate or decelRate
currentVelocity = currentVelocity:Lerp(desiredVelocity, lerpAlpha)
demon.CFrame = demon.CFrame + currentVelocity * dt
hitbox.CFrame = demon.CFrame
end
end)
end
task.delay(5, function()
updateDemonMovement()
end)
end
end
---------------------------------------------------------------------
-- TIMER CLASS
---------------------------------------------------------------------
local Timer = {}
Timer.__index = Timer
function Timer.new(duration, guiLabel, player, onComplete, allowOvertime)
local self = setmetatable({}, Timer)
self.duration = duration
self.remaining = duration
self.guiLabel = guiLabel
self.player = player
self.onComplete = onComplete
self.cancelled = false
self.isOvertime = false
self.allowOvertime = allowOvertime ~= false
return self
end
function Timer:start(spawnDemonFunc)
task.spawn(function()
while not self.cancelled do
if self.remaining > 0 then
self.guiLabel.Text = string.format("%d:%02d", math.floor(self.remaining / 60), self.remaining % 60)
self.remaining = self.remaining - 1
else
if not self.allowOvertime then
self.guiLabel.Text = "0:00"
local character = self.player.Character
if character then
local humanoid = character:FindFirstChildOfClass("Humanoid")
if humanoid then
humanoid.Health = 0
else
self.player:LoadCharacter()
end
end
if self.onComplete then
self.onComplete()
end
break
end
if not self.isOvertime then
self.isOvertime = true
self.guiLabel.Text = "Overtime!"
timerStatusEvent:FireClient(self.player, true, true)
if spawnDemonFunc then
spawnDemonFunc(self.player)
end
self:flashTextColor()
end
end
task.wait(1)
end
if not self.cancelled and self.onComplete then
self.onComplete()
end
end)
end
function Timer:cancel()
self.cancelled = true
end
function Timer:flashTextColor()
local colors = Config.OvertimeFlashColors
local colorIndex = 1
while self.isOvertime and not self.cancelled do
self.guiLabel.TextColor3 = colors[colorIndex]
colorIndex = colorIndex == 1 and 2 or 1
task.wait(Config.OvertimeFlashInterval)
end
end
---------------------------------------------------------------------
-- DECAL & TELEPORT
---------------------------------------------------------------------
local function showDecalAndTeleport(player, part)
if activeDecalPlayers[player] then return end
activeDecalPlayers[player] = true
local playerGui = player:WaitForChild("PlayerGui")
local decalGui = Instance.new("ScreenGui")
decalGui.Name = "DecalGui"
decalGui.IgnoreGuiInset = true
decalGui.Parent = playerGui
local imageLabel = Instance.new("ImageLabel")
imageLabel.Size = UDim2.new(1, 0, 1, 0)
imageLabel.Position = UDim2.new(0, 0, 0, 0)
imageLabel.BackgroundTransparency = 1
imageLabel.Image = part:GetAttribute("DecalId") or Config.Decal.defaultId
imageLabel.Parent = decalGui
local Win = Instance.new("Sound")
Win.SoundId = Config.Decal.winSoundId
Win.Volume = Config.Decal.winSoundVolume
Win.Pitch = Config.Decal.winSoundPitch
Win.Parent = player
Win:Play()
wait(Config.Decal.displayTime)
decalGui:Destroy()
Win:Destroy()
activeDecalPlayers[player] = nil
if part:GetAttribute("TeleportToSpawn") then
player:LoadCharacter()
end
end
---------------------------------------------------------------------
-- PART APPEARANCE RESET
---------------------------------------------------------------------
local function resetPartAppearance(part)
if part:GetAttribute("DisappearOnTimerActivate") then
part.Transparency = 0
part.CanCollide = true
for _, descendant in ipairs(part:GetDescendants()) do
if descendant:IsA("BasePart") then
descendant.Transparency = 0
descendant.CanCollide = true
elseif descendant:IsA("TextLabel") or descendant:IsA("TextButton") then
descendant.TextTransparency = 0
elseif descendant:IsA("ParticleEmitter") then
descendant.Enabled = true
end
end
end
end
---------------------------------------------------------------------
-- GUI TIMER CREATION
---------------------------------------------------------------------
local function createTimerGui(player, triggeringPart)
if timerActive then return end
timerActive = true
timerStatusEvent:FireClient(player, true, false)
if player:FindFirstChild("PlayerGui"):FindFirstChild("TimerGui") then return end
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "TimerGui"
screenGui.Parent = player:FindFirstChild("PlayerGui")
local timerLabel = Instance.new("TextLabel")
timerLabel.Size = Config.TimerGui.labelSize
timerLabel.Position = Config.TimerGui.labelPosition
timerLabel.BackgroundTransparency = Config.TimerGui.labelBackgroundTransparency
timerLabel.BackgroundColor3 = Config.TimerGui.labelBackgroundColor
local duration = triggeringPart and triggeringPart:GetAttribute("TimerDuration") or Config.MainTimerDuration
timerLabel.Text = string.format("%d:%02d", math.floor(duration / 60), duration % 60)
timerLabel.Font = Config.TimerGui.labelFont
timerLabel.TextSize = Config.TimerGui.labelTextSize
timerLabel.TextColor3 = Config.TimerGui.labelTextColor
timerLabel.Parent = screenGui
local tween = TweenService:Create(timerLabel, TweenInfo.new(Config.TimerGui.tweenTime, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
{Position = Config.TimerGui.tweenPosition})
tween:Play()
local function onTimerComplete()
if triggeringPart and triggeringPart:GetAttribute("DisappearOnTimerActivate") then
triggeringPart.Transparency = 0
triggeringPart.CanCollide = true
for _, descendant in ipairs(triggeringPart:GetDescendants()) do
if descendant:IsA("BasePart") then
descendant.Transparency = 0
descendant.CanCollide = true
elseif descendant:IsA("TextLabel") or descendant:IsA("TextButton") then
descendant.TextTransparency = 0
elseif descendant:IsA("ParticleEmitter") then
descendant.Enabled = true
end
end
end
screenGui:Destroy()
timerActive = false
timerStatusEvent:FireClient(player, false, false)
currentTimer = nil
end
local allowOvertime = true
if triggeringPart and triggeringPart:GetAttribute("DisableOvertime") then
allowOvertime = false
end
currentTimer = Timer.new(duration, timerLabel, player, onTimerComplete, allowOvertime)
currentTimer:start(spawnOrbitalDemonWithEffects)
task.spawn(function()
while currentTimer and not currentTimer.cancelled and currentTimer.remaining > 0 do
if Workspace:FindFirstChild("CancelTimer") and Workspace.CancelTimer.Value then
currentTimer:cancel()
screenGui:Destroy()
timerActive = false
timerStatusEvent:FireClient(player, false, false)
currentTimer = nil
break
end
task.wait(0.1)
end
end)
end
---------------------------------------------------------------------
-- PART TOUCHED HANDLING
---------------------------------------------------------------------
local function onPartTouched(part, player)
if part:GetAttribute("ExitTimer") then
if currentTimer then
currentTimer:cancel()
if demon then
demon:Destroy()
end
timerStatusEvent:FireClient(player, false)
if part:GetAttribute("ShowDecalBeforeTeleport") then
task.spawn(function()
showDecalAndTeleport(player, part)
end)
else
if part:GetAttribute("TeleportToSpawn") then
wait(0.1)
player:LoadCharacter()
end
end
end
return
end
if part:GetAttribute("LocalTimeActivate") then
createTimerGui(player, part)
elseif part:GetAttribute("PublicTimeActivate") then
for _, plr in ipairs(Players:GetPlayers()) do
createTimerGui(plr, part)
end
end
if part:GetAttribute("PlaySoundOnTimerActivate") and not soundPlayed then
local presetNumber = part:GetAttribute("SoundPreset")
local preset = presetNumber and Config.AudioPresets["preset" .. presetNumber]
if preset then
local sound = part:FindFirstChildOfClass("Sound") or Instance.new("Sound", part)
sound.SoundId = preset.soundId
sound.Volume = preset.volume
sound.Pitch = preset.pitch
sound.Looped = preset.looped or false
sound:Play()
end
soundPlayed = true
end
if part:GetAttribute("DisappearOnTimerActivate") then
part.Transparency = 1
part.CanCollide = false
for _, descendant in ipairs(part:GetDescendants()) do
if descendant:IsA("BasePart") then
descendant.Transparency = 1
descendant.CanCollide = false
elseif descendant:IsA("TextLabel") or descendant:IsA("TextButton") then
descendant.TextTransparency = 1
elseif descendant:IsA("ParticleEmitter") then
descendant.Enabled = false
end
end
end
end
---------------------------------------------------------------------
-- CONNECTION HELPER
---------------------------------------------------------------------
local function connectPart(part)
if part:IsA("BasePart") then
part.Touched:Connect(function(hit)
local player = Players:GetPlayerFromCharacter(hit.Parent)
if player then
onPartTouched(part, player)
end
end)
end
end
for _, part in ipairs(workspace:GetChildren()) do
connectPart(part)
end
workspace.ChildAdded:Connect(connectPart)
---------------------------------------------------------------------
-- RESET TIMER AND PARTS ON PLAYER RESPAWN / JOIN
---------------------------------------------------------------------
Players.PlayerAdded:Connect(function(player)
player.CharacterAdded:Connect(function()
timerActive = false
soundPlayed = false
if currentTimer then
currentTimer:cancel()
currentTimer = nil
end
activeDecalPlayers[player] = nil
for _, part in ipairs(workspace:GetChildren()) do
resetPartAppearance(part)
end
timerStatusEvent:FireClient(player, false, false)
end)
end)