JustPaste
HomeCategoriesAboutDonateContactTerms of UsePrivacy Policy
JustPaste

Free online notepad — write and share instantly

Navigate

  • Home
  • Timeline
  • Categories

Info

  • About
  • Donate
  • Contact

Legal

  • Terms of Use
  • Privacy Policy

© 2026 JustPaste.app. All rights reserved.

Made with ♥ by JustPaste

Untitled Page | JustPaste.app
10 days ago1 views
👨‍💻Programming

Untitled Page

local TweenService = game:GetService("TweenService")
local Players = game:GetService("Players")
local ContentProvider = game:GetService("ContentProvider")

local MarketplaceService = game:GetService("MarketplaceService")
local GoldenGamepassID = 1860854804

local player = Players.LocalPlayer
local gui = player:WaitForChild("PlayerGui")

local SoundService = game:GetService("SoundService")

local popupFrame = script.Parent:WaitForChild("PopupFrame")
local notificationTemplate = script:WaitForChild("Template")
local trueNotification = script:WaitForChild("TrueNotification")
local falseNotification = script:WaitForChild("FalseNotification")

local redDots = {
	script.Parent:WaitForChild("Menu"):WaitForChild("Frame"):WaitForChild("MoreButton"):WaitForChild("RedDot"),
	script.Parent:WaitForChild("MoreFrame"):WaitForChild("Frame"):WaitForChild("DailyButton"):WaitForChild("RedDot")
}


local notificationIcons = {
	error = "rbxassetid://139948444656176",
	warning = "rbxassetid://122758386097183",
	success = "rbxassetid://82312257774713",
	cancel = "rbxassetid://95899143299927"
}

task.spawn(function()
	local assetsToPreload = {}

	for _, imageId in ipairs(notificationIcons) do
		local image = Instance.new("ImageLabel")
		image.Image = imageId
		table.insert(assetsToPreload, image)
	end

	local success, err = pcall(function()
		ContentProvider:PreloadAsync(assetsToPreload)
	end)

	if not success then
		warn("Failed to preload notification images: " .. tostring(err))
	end

	for _, image in ipairs(assetsToPreload) do
		image:Destroy()
	end
end)

local activeNotifications = {}
local maxNotifications = 1

local function playNotificationSound(sound)
	local soundClone = sound:Clone()
	soundClone.Parent = SoundService
	soundClone:Play()

	soundClone.Ended:Once(function()
		soundClone:Destroy()
	end)

	task.delay(8, function()
		if soundClone.Parent then
			soundClone:Destroy()
		end
	end)
end


local notificationsEnabled = false

local function showNotification(message, notificationType, duration)
	if not notificationsEnabled then
		return
	end

	if typeof(message) ~= "string" or message == "" then
		return
	end

	notificationType = notificationType or "error"
	duration = math.clamp(tonumber(duration) or 2.5, 1, 6)

	if #activeNotifications >= maxNotifications then
		local oldestNotification = table.remove(activeNotifications, 1)

		if oldestNotification and oldestNotification.Parent then
			oldestNotification:Destroy()
		end
	end

	local notification = notificationTemplate:Clone()
	notification.Name = "Notification"
	notification.Visible = true
	notification.Parent = popupFrame

	local icon = notification:FindFirstChild("Icon", true)
	local messageLabel = notification:FindFirstChild("Message", true)

	if not icon or not icon:IsA("ImageLabel") then
		notification:Destroy()
		warn("Notification template is missing an ImageLabel named Icon.")
		return
	end

	if not messageLabel or not messageLabel:IsA("TextLabel") then
		notification:Destroy()
		warn("Notification template is missing a TextLabel named Message.")
		return
	end

	local iconId = notificationIcons[notificationType] or notificationIcons.error
	icon.Image = iconId
	messageLabel.Text = message

	table.insert(activeNotifications, notification)

	local originalSize = notification.Size
	local originalPosition = notification.Position
	local originalBackgroundTransparency = notification.BackgroundTransparency

	local transparencyProperties = {}

	for _, object in ipairs(notification:GetDescendants()) do
		local properties = {}

		if object:IsA("TextLabel") or object:IsA("TextButton") then
			properties.TextTransparency = object.TextTransparency
			properties.TextStrokeTransparency = object.TextStrokeTransparency
		end

		if object:IsA("ImageLabel") or object:IsA("ImageButton") then
			properties.ImageTransparency = object.ImageTransparency
		end

		if object:IsA("GuiObject") then
			properties.BackgroundTransparency = object.BackgroundTransparency
		end

		local stroke = object:FindFirstChildOfClass("UIStroke")
		if stroke then
			properties.StrokeTransparency = stroke.Transparency
		end

		if next(properties) then
			transparencyProperties[object] = properties
		end
	end

	notification.Size = UDim2.new(
		originalSize.X.Scale,
		originalSize.X.Offset,
		0,
		0
	)

	notification.Position = originalPosition + UDim2.fromOffset(35, 0)
	notification.BackgroundTransparency = 1

	for object, properties in pairs(transparencyProperties) do
		if object.Parent then
			for property in pairs(properties) do
				if property == "TextTransparency"
					or property == "TextStrokeTransparency"
					or property == "ImageTransparency"
					or property == "BackgroundTransparency" then
					object[property] = 1
				end
			end

			local stroke = object:FindFirstChildOfClass("UIStroke")
			if stroke and properties.StrokeTransparency then
				stroke.Transparency = 1
			end
		end
	end

	local showTween = TweenService:Create(
		notification,
		TweenInfo.new(0.25, Enum.EasingStyle.Quint, Enum.EasingDirection.Out),
		{
			Size = originalSize,
			Position = originalPosition,
			BackgroundTransparency = originalBackgroundTransparency
		}
	)

	showTween:Play()

	for object, properties in pairs(transparencyProperties) do
		if object.Parent then
			local goals = {}

			for property, value in pairs(properties) do
				if property ~= "StrokeTransparency" then
					goals[property] = value
				end
			end

			if next(goals) then
				TweenService:Create(
					object,
					TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.Out),
					goals
				):Play()
			end

			local stroke = object:FindFirstChildOfClass("UIStroke")
			if stroke and properties.StrokeTransparency then
				TweenService:Create(
					stroke,
					TweenInfo.new(0.2),
					{ Transparency = properties.StrokeTransparency }
				):Play()
			end
		end
	end

	playNotificationSound(
		notificationType == "success" and trueNotification or falseNotification
	)

	task.delay(duration, function()
		if not notification.Parent then
			return
		end

		local hideTween = TweenService:Create(
			notification,
			TweenInfo.new(0.2, Enum.EasingStyle.Quad, Enum.EasingDirection.In),
			{
				Position = notification.Position + UDim2.fromOffset(25, 0),
				BackgroundTransparency = 1
			}
		)

		for object, properties in pairs(transparencyProperties) do
			if object.Parent then
				local goals = {}

				for property in pairs(properties) do
					if property == "TextTransparency"
						or property == "TextStrokeTransparency"
						or property == "ImageTransparency"
						or property == "BackgroundTransparency" then
						goals[property] = 1
					end
				end

				if next(goals) then
					TweenService:Create(object, TweenInfo.new(0.2), goals):Play()
				end

				local stroke = object:FindFirstChildOfClass("UIStroke")
				if stroke and properties.StrokeTransparency then
					TweenService:Create(
						stroke,
						TweenInfo.new(0.2),
						{ Transparency = 1 }
					):Play()
				end
			end
		end

		hideTween:Play()
		hideTween.Completed:Once(function()
			local index = table.find(activeNotifications, notification)

			if index then
				table.remove(activeNotifications, index)
			end

			notification:Destroy()
		end)
	end)
end

local hoverSound = script:WaitForChild("Hover")
local clickSound = script:WaitForChild("Click")

local function setupSoundButton(button)
	if not button:IsA("GuiButton") then
		return
	end

	button.MouseEnter:Connect(function()
		hoverSound:Play()
	end)

	button.Activated:Connect(function()
		clickSound:Play()
	end)
end

for _, object in ipairs(gui:GetDescendants()) do
	setupSoundButton(object)
end

gui.DescendantAdded:Connect(function(object)
	setupSoundButton(object)
end)

local root = script.Parent
local menu = root.Menu
local menuFrame = menu.Frame

local frames = {
	MoreButton = root.MoreFrame,
	PlayButton = root.GameMode,
	ShopButton = root.ShopFrame,
	ProfileButton = root.ProfileFrame
}

local moreButtons = {
	SettingsButton = root.SettingsFrame,
	HowToPlayButton = root.HowToPlayFrame,
	LeaderboardButton = root.LeaderboardFrame,
	CreditsButton = root.CreditsFrame,
	DailyButton = root.DailyFrame
}

local hoverScl = 1.08
local clickScl = 0.92

local tHover = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
local tClick = TweenInfo.new(0.1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
local tTrans = TweenInfo.new(0.4, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
local tShine = TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
local tStroke = TweenInfo.new(0.2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)

local function scale(size, multiplier)
	return UDim2.new(
		size.X.Scale * multiplier,
		size.X.Offset * multiplier,
		size.Y.Scale * multiplier,
		size.Y.Offset * multiplier
	)
end

local function setupButton(btn, onClick)
	local origSize = btn.Size
	local shine = btn:WaitForChild("Shine").Frame
	local stroke = btn:FindFirstChildOfClass("UIStroke")

	shine.Position = UDim2.new(-1, 0, -0.5, 0)
	shine.BackgroundTransparency = 1

	local currentShineTween

	local function shineForward()
		if currentShineTween then
			currentShineTween:Cancel()
		end

		shine.Position = UDim2.new(-1, 0, -0.5, 0)

		currentShineTween = TweenService:Create(shine, tShine, {
			Position = UDim2.new(1, 0, -0.5, 0),
			BackgroundTransparency = 0.65
		})

		currentShineTween:Play()
	end

	local function shineReverse()
		if currentShineTween then
			currentShineTween:Cancel()
		end

		currentShineTween = TweenService:Create(shine, tShine, {
			Position = UDim2.new(-1, 0, -0.5, 0),
			BackgroundTransparency = 1
		})

		currentShineTween:Play()
	end

	btn.MouseEnter:Connect(function()
		TweenService:Create(btn, tHover, {
			Size = scale(origSize, hoverScl)
		}):Play()

		shineForward()

		if stroke then
			TweenService:Create(stroke, tStroke, {
				Color = Color3.fromRGB(0, 188, 255),
				Thickness = 0.03
			}):Play()
		end
	end)

	btn.MouseLeave:Connect(function()
		TweenService:Create(btn, tHover, {
			Size = origSize
		}):Play()

		shineReverse()

		if stroke then
			TweenService:Create(stroke, tStroke, {
				Color = Color3.fromRGB(0, 152, 198),
				Thickness = 0.02
			}):Play()
		end
	end)

	btn.MouseButton1Down:Connect(function()
		TweenService:Create(btn, tClick, {
			Size = scale(origSize, clickScl)
		}):Play()
	end)

	btn.MouseButton1Up:Connect(function()
		TweenService:Create(btn, tClick, {
			Size = scale(origSize, hoverScl)
		}):Play()

		onClick()
	end)
end

local function openFrame(currentFrame, frameToShow)
	local frameOut = TweenService:Create(currentFrame, tTrans, {
		Position = UDim2.new(0.5, 0, -0.5, 0)
	})

	frameOut:Play()

	frameOut.Completed:Connect(function()
		currentFrame.Visible = false

		frameToShow.Visible = true
		frameToShow.Position = UDim2.new(0.5, 0, -0.5, 0)

		TweenService:Create(frameToShow, tTrans, {
			Position = UDim2.new(0.5, 0, 0.5, 0)
		}):Play()
	end)
end

-- Menu visible par défaut
menu.Visible = true
menu.Position = UDim2.new(0.5, 0, 0.5, 0)

-- Tous les autres frames cachés
for _, frame in pairs(frames) do
	frame.Visible = false
	frame.Position = UDim2.new(0.5, 0, -0.5, 0)
end

for _, frame in pairs(moreButtons) do
	frame.Visible = false
	frame.Position = UDim2.new(0.5, 0, -0.5, 0)
end




-- Player Profile

local profileFrame = root:WaitForChild("ProfileFrame")
local profileContent = profileFrame:WaitForChild("Content")

local playerUsername = profileContent:WaitForChild("PlayerUsername")
local profileImage = playerUsername:WaitForChild("profileimg")
local usernameLabel = playerUsername:WaitForChild("username")

local highScoreNumber = profileContent:WaitForChild("HighScore"):WaitForChild("Number")
local highLevelNumber = profileContent:WaitForChild("HighLevel"):WaitForChild("Number")
local highLinesNumber = profileContent:WaitForChild("HighLines"):WaitForChild("Number")
local coinsNumber = profileContent:WaitForChild("Coins"):WaitForChild("Number")


local function getStatValue(statName, defaultValue)
	local stat

	if statName == "Coins" then
		stat = player:FindFirstChild("Coins")
	else
		local gameStats = player:FindFirstChild("GameStats")

		if gameStats then
			stat = gameStats:FindFirstChild(statName)
		end
	end

	if stat and (stat:IsA("IntValue") or stat:IsA("NumberValue")) then
		return stat.Value
	end

	return defaultValue
end

local function formatProfileNumber(value)
	local formatted = tostring(math.floor(tonumber(value) or 0))

	while true do
		local result, count = formatted:gsub(
			"^(-?%d+)(%d%d%d)",
			"%1,%2"
		)

		formatted = result

		if count == 0 then
			break
		end
	end

	return formatted
end

local VIPGamepassID = 1860854804
local vipLabel = profileContent:WaitForChild("PlayerUsername"):WaitForChild("VIP")

vipLabel.Visible = false

local function updateVIP()
	vipLabel.Visible = false

	local success, ownsVIP = pcall(function()
		return MarketplaceService:UserOwnsGamePassAsync(
			player.UserId,
			VIPGamepassID
		)
	end)

	if success then
		vipLabel.Visible = ownsVIP
	end
end

local function updateProfile()
	usernameLabel.Text = "@" .. player.Name

	local success, content = pcall(function()
		return Players:GetUserThumbnailAsync(
			player.UserId,
			Enum.ThumbnailType.HeadShot,
			Enum.ThumbnailSize.Size150x150
		)
	end)

	if success then
		profileImage.Image = content
	end

	highScoreNumber.Text = formatProfileNumber(
		getStatValue("HighScore", 0)
	)

	highLevelNumber.Text = formatProfileNumber(
		getStatValue("HighLevel", 1)
	)

	highLinesNumber.Text = formatProfileNumber(
		getStatValue("HighLines", 0)
	)

	coinsNumber.Text = formatProfileNumber(
		getStatValue("Coins", 0)
	)
	
	updateVIP()
end



-- Menu Buttons

for buttonName, frameToShow in pairs(frames) do
	local btn = menuFrame:WaitForChild(buttonName)

	setupButton(btn, function()
		if buttonName == "ProfileButton" then
			updateProfile()
		end

		openFrame(menu, frameToShow)
	end)
end

local refreshDailyUI


-- Boutons de MoreFrame
local moreFrame = root.MoreFrame
local moreFrameButtons = moreFrame.Frame

for buttonName, frameToShow in pairs(moreButtons) do
	local btn = moreFrameButtons:WaitForChild(buttonName)

	setupButton(btn, function()
		openFrame(moreFrame, frameToShow)

		if buttonName == "DailyButton" then
			task.defer(function()
				if type(refreshDailyUI) == "function" then
					refreshDailyUI()
				end
			end)
		end
	end)
end


-- GoBack
local function setupGoBack(frame, returnFrame)
	local goBack = frame:WaitForChild("GoBack")

	setupButton(goBack, function()
		openFrame(frame, returnFrame)
	end)
end

setupGoBack(root.MoreFrame, menu)
setupGoBack(root.GameMode, menu)
setupGoBack(root.ShopFrame, menu)
setupGoBack(root.ProfileFrame, menu)

setupGoBack(root.SettingsFrame, moreFrame)
setupGoBack(root.HowToPlayFrame, moreFrame)
setupGoBack(root.LeaderboardFrame, moreFrame)
setupGoBack(root.CreditsFrame, moreFrame)
setupGoBack(root.DailyFrame, moreFrame)

-- Animation du logo
local logo = menu:WaitForChild("Logo")
local startPos = logo.Position

local up = TweenService:Create(
	logo,
	TweenInfo.new(2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut),
	{
		Position = startPos - UDim2.new(0, 0, 0.015, 0)
	}
)

local down = TweenService:Create(
	logo,
	TweenInfo.new(2, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut),
	{
		Position = startPos + UDim2.new(0, 0, 0.015, 0)
	}
)

task.spawn(function()
	while true do
		up:Play()
		up.Completed:Wait()

		down:Play()
		down.Completed:Wait()
	end
end)

-- Background particles
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local bg = root:WaitForChild("Background")
local camera = workspace.CurrentCamera
local player = game.Players.LocalPlayer

local isMobile = UserInputService.TouchEnabled
local PARTICLE_AMOUNT = isMobile and 250 or 500

local particles = {}
local mouse = Vector2.new()

UserInputService.InputChanged:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.MouseMovement then
		mouse = input.Position
	end
end)

local function createParticle()
	local p = Instance.new("Frame")
	p.Parent = bg

	local size = math.random(2, 4)

	p.Size = UDim2.fromOffset(size, size)
	p.Position = UDim2.fromScale(math.random(), math.random())

	p.BackgroundColor3 = Color3.fromRGB(140, 220, 255)
	p.BackgroundTransparency = 0.4
	p.BorderSizePixel = 0

	local corner = Instance.new("UICorner")
	corner.CornerRadius = UDim.new(1, 0)
	corner.Parent = p

	local trail = Instance.new("Frame")
	trail.Parent = bg
	trail.Size = p.Size
	trail.BackgroundColor3 = p.BackgroundColor3
	trail.BackgroundTransparency = 0.85
	trail.BorderSizePixel = 0

	local trailCorner = Instance.new("UICorner")
	trailCorner.CornerRadius = UDim.new(1, 0)
	trailCorner.Parent = trail

	return {
		ui = p,
		trail = trail,
		pos = Vector2.new(math.random(), math.random()),
		last = Vector2.new(0, 0),
		offset = Vector2.new(
			math.random(-200, 200) / 100,
			math.random(-200, 200) / 100
		),
		speed = math.random(4, 10) / 1200,
		fade = math.random(),
		fadeSpeed = math.random(2, 4) / 100
	}
end

for i = 1, PARTICLE_AMOUNT do
	table.insert(particles, createParticle())
end

RunService.RenderStepped:Connect(function()
	if player:GetAttribute("PerformanceMode") then
		return
	end

	local view = camera.ViewportSize

	if view.X <= 0 or view.Y <= 0 then
		return
	end

	local mousePos = Vector2.new(
		mouse.X / view.X,
		mouse.Y / view.Y
	)

	for _, v in ipairs(particles) do
		local targetX = mousePos.X + v.offset.X
		local targetY = mousePos.Y + v.offset.Y

		v.pos = Vector2.new(
			v.pos.X + (targetX - v.pos.X) * v.speed,
			v.pos.Y + (targetY - v.pos.Y) * v.speed
		)

		local newPos = UDim2.fromScale(v.pos.X, v.pos.Y)

		v.trail.Position = v.ui.Position
		v.ui.Position = newPos

		v.fade += v.fadeSpeed

		local t = (math.sin(v.fade) + 1) * 0.5

		v.ui.BackgroundTransparency = 0.2 + t * 0.3
		v.trail.BackgroundTransparency = 0.75 + t * 0.2
	end
end)

-- ShopFrame Tabs
local shopFrame = root.ShopFrame
local tabs = shopFrame:WaitForChild("Tabs")

local transition = TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local tabHover = TweenInfo.new(0.12, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)

local currentShopFrame
local currentIndex = 1
local activeTweens = {}

local shopOrder = {
	Gamepasses = 1,
	Skins = 2,
	Currency = 3
}

local tabOriginalSizes = {}

local function playTween(object, info, properties)
	local previousTween = activeTweens[object]

	if previousTween then
		previousTween:Cancel()
	end

	local tween = TweenService:Create(object, info, properties)
	activeTweens[object] = tween
	tween:Play()

	tween.Completed:Once(function()
		if activeTweens[object] == tween then
			activeTweens[object] = nil
		end
	end)

	return tween
end

local function openShopTab(button)
	local frame = shopFrame:FindFirstChild(button.Name)
	if not frame then return end

	local newIndex = shopOrder[button.Name]
	if not newIndex or frame == currentShopFrame then return end

	for _, v in ipairs(tabs:GetChildren()) do
		if v:IsA("TextButton") or v:IsA("ImageButton") then
			v.BackgroundTransparency = 1
		end
	end

	button.BackgroundTransparency = 0

	local direction = newIndex > currentIndex and 1 or -1
	local targetY = frame.Position.Y

	frame.Visible = true
	frame.Position = UDim2.new(0.5 + direction, 0, targetY.Scale, targetY.Offset)

	playTween(frame, transition, {
		Position = UDim2.new(0.5, 0, targetY.Scale, targetY.Offset)
	})

	if currentShopFrame then
		local oldFrame = currentShopFrame
		local oldY = oldFrame.Position.Y

		local outTween = playTween(oldFrame, transition, {
			Position = UDim2.new(0.5 - direction, 0, oldY.Scale, oldY.Offset)
		})

		outTween.Completed:Once(function()
			if currentShopFrame ~= oldFrame then
				oldFrame.Visible = false
				oldFrame.Position = UDim2.new(0.5, 0, oldY.Scale, oldY.Offset)
			end
		end)
	end

	currentShopFrame = frame
	currentIndex = newIndex
end

for _, button in ipairs(tabs:GetChildren()) do
	if button:IsA("TextButton") or button:IsA("ImageButton") then
		local originalSize = button.Size
		tabOriginalSizes[button] = originalSize

		button.MouseEnter:Connect(function()
			playTween(button, tabHover, {
				Size = scale(originalSize, 1.04)
			})
		end)

		button.MouseLeave:Connect(function()
			playTween(button, tabHover, {
				Size = originalSize
			})
		end)

		button.MouseButton1Click:Connect(function()
			openShopTab(button)
		end)
	end
end

local defaultTab = tabs:FindFirstChild("Gamepasses")

if defaultTab then
	openShopTab(defaultTab)
end

-- Classic Button
local classicButton = root.GameMode.Frame:WaitForChild("ClassicButton")
local classicFrame = root:WaitForChild("ClassicFrame")

classicFrame.Visible = false
classicFrame.Position = UDim2.new(0.5, 0, -0.5, 0)

setupButton(classicButton, function()
	openFrame(root.GameMode, classicFrame)
end)

-- High Score
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local getHighScore = ReplicatedStorage:WaitForChild("Events"):WaitForChild("GetHighScore")
local highScoreLabel = classicButton.HighScore:WaitForChild("TextLabel")

local function updateHighScore()
	local success, result = pcall(function()
		return getHighScore:InvokeServer()
	end)

	if success then
		highScoreLabel.Text = "High Score: " .. tostring(result)
	else
		highScoreLabel.Text = "High Score: error"
		showNotification("Unable to load high score", "error", 3)
	end
end

updateHighScore()

task.spawn(function()
	while true do
		task.wait(60)
		updateHighScore()
	end
end)

local gameLevel = root:WaitForChild("GameLevel")
local maingame = root.Parent:WaitForChild("TetrisGamemod")

gameLevel.Visible = false
gameLevel.Position = UDim2.new(0.5, 0, -0.5, 0)

setupGoBack(classicFrame, root.GameMode)
setupGoBack(gameLevel, classicFrame)

local soloButton = classicFrame.Frame:WaitForChild("SoloButton")

setupButton(soloButton, function()
	openFrame(classicFrame, gameLevel)
end)

local bindableFunctionStart = ReplicatedStorage:WaitForChild("Events"):WaitForChild("bindableFunctionStart")

local levelWindow = gameLevel:WaitForChild("Window")

for _, levelButton in ipairs(levelWindow:GetChildren()) do
	if levelButton:IsA("TextButton") or levelButton:IsA("ImageButton") then
		local levelNumber = tonumber(levelButton.Name:match("%d+"))

		if levelNumber then
			local originalSize = levelButton.Size
			local shine = levelButton:WaitForChild("Shine"):WaitForChild("Frame")
			local stroke = levelButton:FindFirstChildOfClass("UIStroke")

			shine.Position = UDim2.new(-1, 0, -0.5, 0)
			shine.BackgroundTransparency = 1

			local shineTween

			levelButton.MouseEnter:Connect(function()
				TweenService:Create(levelButton, tHover, {
					Size = scale(originalSize, hoverScl)
				}):Play()

				if shineTween then
					shineTween:Cancel()
				end

				shine.Position = UDim2.new(-1, 0, -0.5, 0)

				shineTween = TweenService:Create(shine, tShine, {
					Position = UDim2.new(1, 0, -0.5, 0),
					BackgroundTransparency = 0.65
				})

				shineTween:Play()

				if stroke then
					TweenService:Create(stroke, tStroke, {
						Color = Color3.fromRGB(0, 188, 255),
						Thickness = 0.03
					}):Play()
				end
			end)

			levelButton.MouseLeave:Connect(function()
				TweenService:Create(levelButton, tHover, {
					Size = originalSize
				}):Play()

				if shineTween then
					shineTween:Cancel()
				end

				shineTween = TweenService:Create(shine, tShine, {
					Position = UDim2.new(-1, 0, -0.5, 0),
					BackgroundTransparency = 1
				})

				shineTween:Play()

				if stroke then
					TweenService:Create(stroke, tStroke, {
						Color = Color3.fromRGB(0, 152, 198),
						Thickness = 0.02
					}):Play()
				end
			end)

			levelButton.MouseButton1Down:Connect(function()
				TweenService:Create(levelButton, tClick, {
					Size = scale(originalSize, clickScl)
				}):Play()
			end)

			levelButton.MouseButton1Up:Connect(function()
				TweenService:Create(levelButton, tClick, {
					Size = scale(originalSize, hoverScl)
				}):Play()
			end)

			levelButton.MouseButton1Click:Connect(function()
				
				if maingame.Enabled == false then
					maingame.Enabled = true
				end

				gameLevel.Visible = false
				
				if bindableFunctionStart then
					bindableFunctionStart:Invoke(levelNumber)
				end
			end)
		end
	end
end

local tetrisSaved = root.Parent:WaitForChild("TetrisGamemod")
local gameFrame = tetrisSaved:WaitForChild("Game")
local buttonArea = gameFrame:WaitForChild("buttonArea")

local quitButton = buttonArea:WaitForChild("QuitButton")
local startButton = buttonArea:WaitForChild("StartButton")

local function setupSmallButton(btn)
	local originalSize = btn.Size
	local shine = btn:WaitForChild("Shine").Frame
	local stroke = btn:FindFirstChildOfClass("UIStroke")

	shine.Position = UDim2.new(-1, 0, -0.5, 0)
	shine.BackgroundTransparency = 1

	local shineTween

	btn.MouseEnter:Connect(function()
		TweenService:Create(btn, tHover, {
			Size = scale(originalSize, 1.03)
		}):Play()

		if shineTween then
			shineTween:Cancel()
		end

		shine.Position = UDim2.new(-1, 0, -0.5, 0)

		shineTween = TweenService:Create(shine, tShine, {
			Position = UDim2.new(1, 0, -0.5, 0),
			BackgroundTransparency = 0.65
		})

		shineTween:Play()

		if stroke then
			TweenService:Create(stroke, tStroke, {
				Color = Color3.fromRGB(0, 188, 255),
				Thickness = 0.03
			}):Play()
		end
	end)

	btn.MouseLeave:Connect(function()
		TweenService:Create(btn, tHover, {
			Size = originalSize
		}):Play()

		if shineTween then
			shineTween:Cancel()
		end

		shineTween = TweenService:Create(shine, tShine, {
			Position = UDim2.new(-1, 0, -0.5, 0),
			BackgroundTransparency = 1
		})

		shineTween:Play()

		if stroke then
			TweenService:Create(stroke, tStroke, {
				Color = Color3.fromRGB(0, 152, 198),
				Thickness = 0.02
			}):Play()
		end
	end)

	btn.MouseButton1Down:Connect(function()
		TweenService:Create(btn, tClick, {
			Size = scale(originalSize, 0.97)
		}):Play()
	end)

	btn.MouseButton1Up:Connect(function()
		TweenService:Create(btn, tClick, {
			Size = scale(originalSize, 1.03)
		}):Play()
	end)
end

setupSmallButton(quitButton)
setupSmallButton(startButton)


-- Settings

local player = game.Players.LocalPlayer

local getSettings = ReplicatedStorage:WaitForChild("Events"):WaitForChild("GetSettings")
local setSetting = ReplicatedStorage:WaitForChild("Events"):WaitForChild("SetSetting")

local savedSettings = getSettings:InvokeServer()

local musicOn = savedSettings.Music
local sfxOn = savedSettings.SFX
local ghostPieceOn = savedSettings.GhostPiece
local hardDropOn = savedSettings.HardDropEffect
local performanceOn = savedSettings.PerformanceMode

local musicToggle = root.SettingsFrame.CanvasGroup.Content.Music.Toggle
local musicDot = musicToggle:WaitForChild("Dot")
local musicGradient = musicToggle:WaitForChild("Gradient")
local musicSound = workspace:WaitForChild("Sound")

local sfxToggle = root.SettingsFrame.CanvasGroup.Content.SFX.Toggle
local sfxDot = sfxToggle:WaitForChild("Dot")
local sfxGradient = sfxToggle:WaitForChild("Gradient")

local ghostToggle = root.SettingsFrame.CanvasGroup.Content.GhostPiece.Toggle
local ghostDot = ghostToggle:WaitForChild("Dot")
local ghostGradient = ghostToggle:WaitForChild("Gradient")

local hardDropToggle = root.SettingsFrame.CanvasGroup.Content.HardDropEffect.Toggle
local hardDropDot = hardDropToggle:WaitForChild("Dot")
local hardDropGradient = hardDropToggle:WaitForChild("Gradient")

local performanceToggle = root.SettingsFrame.CanvasGroup.Content.PerformanceMode.Toggle
local performanceDot = performanceToggle:WaitForChild("Dot")
local performanceGradient = performanceToggle:WaitForChild("Gradient")

local toggleOnPos = UDim2.new(0.722, 0, 0.5, 0)
local toggleOffPos = UDim2.new(0.266, 0, 0.5, 0)

local toggleHoverTween = TweenInfo.new(
	0.14,
	Enum.EasingStyle.Quad,
	Enum.EasingDirection.Out
)

local togglePressTween = TweenInfo.new(
	0.08,
	Enum.EasingStyle.Quad,
	Enum.EasingDirection.Out
)

local toggleMoveTween = TweenInfo.new(
	0.25,
	Enum.EasingStyle.Back,
	Enum.EasingDirection.Out
)

local gradientTween = TweenInfo.new(
	0.35,
	Enum.EasingStyle.Quint,
	Enum.EasingDirection.InOut
)

local onGradient = ColorSequence.new({
	ColorSequenceKeypoint.new(0, Color3.fromRGB(143, 255, 152)),
	ColorSequenceKeypoint.new(0.5, Color3.fromRGB(199, 255, 203)),
	ColorSequenceKeypoint.new(1, Color3.fromRGB(143, 255, 152))
})

local offGradient = ColorSequence.new({
	ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 146, 146)),
	ColorSequenceKeypoint.new(0.5, Color3.fromRGB(255, 198, 198)),
	ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 146, 146))
})

local function setupToggleHover(toggle)
	local originalSize = toggle.Size

	toggle.MouseEnter:Connect(function()
		TweenService:Create(toggle, toggleHoverTween, {
			Size = scale(originalSize, 1.06)
		}):Play()
	end)

	toggle.MouseLeave:Connect(function()
		TweenService:Create(toggle, toggleHoverTween, {
			Size = originalSize
		}):Play()
	end)

	toggle.MouseButton1Down:Connect(function()
		TweenService:Create(toggle, togglePressTween, {
			Size = scale(originalSize, 0.98)
		}):Play()
	end)

	toggle.MouseButton1Up:Connect(function()
		TweenService:Create(toggle, togglePressTween, {
			Size = scale(originalSize, 1.06)
		}):Play()
	end)
end

local function lerpColor(a, b, alpha)
	return a:Lerp(b, alpha)
end

local function blendSequences(fromSequence, toSequence, alpha)
	local fromKeypoints = fromSequence.Keypoints
	local toKeypoints = toSequence.Keypoints

	return ColorSequence.new({
		ColorSequenceKeypoint.new(
			fromKeypoints[1].Time,
			lerpColor(fromKeypoints[1].Value, toKeypoints[1].Value, alpha)
		),
		ColorSequenceKeypoint.new(
			fromKeypoints[2].Time,
			lerpColor(fromKeypoints[2].Value, toKeypoints[2].Value, alpha)
		),
		ColorSequenceKeypoint.new(
			fromKeypoints[3].Time,
			lerpColor(fromKeypoints[3].Value, toKeypoints[3].Value, alpha)
		)
	})
end

local function animateGradient(targetGradient, fromSequence, toSequence)
	local value = Instance.new("NumberValue")
	value.Value = 0

	local connection = value:GetPropertyChangedSignal("Value"):Connect(function()
		targetGradient.Color = blendSequences(
			fromSequence,
			toSequence,
			value.Value
		)
	end)

	local tween = TweenService:Create(value, gradientTween, {
		Value = 1
	})

	tween.Completed:Connect(function()
		connection:Disconnect()
		value:Destroy()
		targetGradient.Color = toSequence
	end)

	tween:Play()
end

local function animateDot(dot, isOn)
	TweenService:Create(dot, toggleMoveTween, {
		Position = isOn and toggleOnPos or toggleOffPos
	}):Play()
end

local function saveSetting(name, value)
	task.spawn(function()
		local success = pcall(function()
			setSetting:InvokeServer(name, value)
		end)

		if not success then
			showNotification("Unable to save settings", "error", 3)
		end
	end)
end

setupToggleHover(musicToggle)
setupToggleHover(sfxToggle)
setupToggleHover(ghostToggle)
setupToggleHover(hardDropToggle)
setupToggleHover(performanceToggle)

musicDot.Position = musicOn and toggleOnPos or toggleOffPos
musicGradient.Color = musicOn and onGradient or offGradient
--musicSound.Playing = musicOn

script.Click.Volume = sfxOn and 6 or 0
script.Hover.Volume = sfxOn and 1 or 0
sfxDot.Position = sfxOn and toggleOnPos or toggleOffPos
sfxGradient.Color = sfxOn and onGradient or offGradient

player:SetAttribute("GhostPiece", ghostPieceOn)
ghostDot.Position = ghostPieceOn and toggleOnPos or toggleOffPos
ghostGradient.Color = ghostPieceOn and onGradient or offGradient

player:SetAttribute("HardDropEffect", hardDropOn)
hardDropDot.Position = hardDropOn and toggleOnPos or toggleOffPos
hardDropGradient.Color = hardDropOn and onGradient or offGradient

player:SetAttribute("PerformanceMode", performanceOn)
performanceDot.Position = performanceOn and toggleOnPos or toggleOffPos
performanceGradient.Color = performanceOn and onGradient or offGradient

musicToggle.MouseButton1Click:Connect(function()
	musicOn = not musicOn

	saveSetting("Music", musicOn)

	if musicOn then
		musicSound.Playing = true
		animateDot(musicDot, true)
		animateGradient(musicGradient, offGradient, onGradient)
	else
		musicSound.Playing = false
		animateDot(musicDot, false)
		animateGradient(musicGradient, onGradient, offGradient)
	end
end)

sfxToggle.MouseButton1Click:Connect(function()
	sfxOn = not sfxOn

	saveSetting("SFX", sfxOn)

	if sfxOn then
		script.Click.Volume = 6
		script.Hover.Volume = 1
		animateDot(sfxDot, true)
		animateGradient(sfxGradient, offGradient, onGradient)
	else
		script.Click.Volume = 0
		script.Hover.Volume = 0
		animateDot(sfxDot, false)
		animateGradient(sfxGradient, onGradient, offGradient)
	end
end)

ghostToggle.MouseButton1Click:Connect(function()
	ghostPieceOn = not ghostPieceOn

	saveSetting("GhostPiece", ghostPieceOn)
	player:SetAttribute("GhostPiece", ghostPieceOn)

	animateDot(ghostDot, ghostPieceOn)

	animateGradient(
		ghostGradient,
		ghostPieceOn and offGradient or onGradient,
		ghostPieceOn and onGradient or offGradient
	)
end)

hardDropToggle.MouseButton1Click:Connect(function()
	hardDropOn = not hardDropOn

	saveSetting("HardDropEffect", hardDropOn)
	player:SetAttribute("HardDropEffect", hardDropOn)

	animateDot(hardDropDot, hardDropOn)

	animateGradient(
		hardDropGradient,
		hardDropOn and offGradient or onGradient,
		hardDropOn and onGradient or offGradient
	)
end)

performanceToggle.MouseButton1Click:Connect(function()
	performanceOn = not performanceOn

	saveSetting("PerformanceMode", performanceOn)
	player:SetAttribute("PerformanceMode", performanceOn)

	animateDot(performanceDot, performanceOn)

	animateGradient(
		performanceGradient,
		performanceOn and offGradient or onGradient,
		performanceOn and onGradient or offGradient
	)
end)

-- How To Play Tabs

local howToPlayFrame = root:WaitForChild("HowToPlayFrame")
local howToPlayTabs = howToPlayFrame:WaitForChild("Tabs")

local desktopTab = howToPlayTabs:WaitForChild("Desktop")
local consoleTab = howToPlayTabs:WaitForChild("Console")

local desktopFrame = howToPlayFrame:WaitForChild("Container"):WaitForChild("Desktop")
local consoleFrame = howToPlayFrame:WaitForChild("Container"):WaitForChild("Console")

local howToPlayTransition = TweenInfo.new(
	0.25,
	Enum.EasingStyle.Quad,
	Enum.EasingDirection.Out
)

local howToPlayTabHover = TweenInfo.new(
	0.12,
	Enum.EasingStyle.Quad,
	Enum.EasingDirection.Out
)

local currentHowToPlayFrame

local function openHowToPlayTab(button, frame)
	if currentHowToPlayFrame == frame then
		return
	end

	for _, tab in ipairs(howToPlayTabs:GetChildren()) do
		if tab:IsA("TextButton") or tab:IsA("ImageButton") then
			tab.BackgroundTransparency = 1
		end
	end

	button.BackgroundTransparency = 0

	local direction = frame == consoleFrame and 1 or -1

	frame.Visible = true
	frame.Position = UDim2.new(
		0.5 + direction,
		0,
		frame.Position.Y.Scale,
		frame.Position.Y.Offset
	)

	TweenService:Create(frame, howToPlayTransition, {
		Position = UDim2.new(
			0.5,
			0,
			frame.Position.Y.Scale,
			frame.Position.Y.Offset
		)
	}):Play()

	if currentHowToPlayFrame then
		local oldFrame = currentHowToPlayFrame

		TweenService:Create(oldFrame, howToPlayTransition, {
			Position = UDim2.new(
				0.5 - direction,
				0,
				oldFrame.Position.Y.Scale,
				oldFrame.Position.Y.Offset
			)
		}):Play()

		task.delay(0.25, function()
			if oldFrame ~= frame then
				oldFrame.Visible = false
			end
		end)
	end

	currentHowToPlayFrame = frame
end

local function setupHowToPlayTab(button, frame)
	local originalSize = button.Size

	button.MouseEnter:Connect(function()
		TweenService:Create(button, howToPlayTabHover, {
			Size = scale(originalSize, 1.04)
		}):Play()
	end)

	button.MouseLeave:Connect(function()
		TweenService:Create(button, howToPlayTabHover, {
			Size = originalSize
		}):Play()
	end)

	button.MouseButton1Click:Connect(function()
		openHowToPlayTab(button, frame)
	end)
end

setupHowToPlayTab(desktopTab, desktopFrame)
setupHowToPlayTab(consoleTab, consoleFrame)

desktopFrame.Visible = false
consoleFrame.Visible = false

local UserInputService = game:GetService("UserInputService")

if UserInputService.TouchEnabled then
	openHowToPlayTab(consoleTab, consoleFrame)
else
	openHowToPlayTab(desktopTab, desktopFrame)
end


-- Leaderboard Tabs

local leaderboardFrame = root:WaitForChild("LeaderboardFrame")
local leaderboardTabs = leaderboardFrame:WaitForChild("Tabs")
local leaderboardContent = leaderboardFrame:WaitForChild("Content")
local yourBest = leaderboardFrame:WaitForChild("YourBest")
local yourBestLabel = yourBest:WaitForChild("TextLabel")

local getLeaderboard = ReplicatedStorage:WaitForChild("Events"):WaitForChild("GetLeaderboard")

local leaderboardOrder = {
	HighScores = 1,
	HighLines = 2,
	HighLevels = 3
}

local currentLeaderboardTab
local currentLeaderboardType = "HighScores"
local leaderboardTabTweens = {}

local leaderboardTransition = TweenInfo.new(
	0.25,
	Enum.EasingStyle.Quad,
	Enum.EasingDirection.Out
)

local leaderboardTabHover = TweenInfo.new(
	0.12,
	Enum.EasingStyle.Quad,
	Enum.EasingDirection.Out
)

local leaderboardTabOriginalSizes = {}

local function formatLeaderboardNumber(number)
	number = math.floor(tonumber(number) or 0)

	local formatted = tostring(number)

	while true do
		local newFormatted, count =
			formatted:gsub("^(-?%d+)(%d%d%d)", "%1,%2")

		formatted = newFormatted

		if count == 0 then
			break
		end
	end

	return formatted
end

local function playLeaderboardTabTween(object, info, properties)
	local previousTween = leaderboardTabTweens[object]

	if previousTween then
		previousTween:Cancel()
	end

	local tween = TweenService:Create(object, info, properties)
	leaderboardTabTweens[object] = tween
	tween:Play()

	tween.Completed:Once(function()
		if leaderboardTabTweens[object] == tween then
			leaderboardTabTweens[object] = nil
		end
	end)

	return tween
end




-- Leaderboard result animations (UIListLayout + UIStroke compatible)

local leaderboardTweenInfo = TweenInfo.new(
	0.3,
	Enum.EasingStyle.Quint,
	Enum.EasingDirection.Out
)

local leaderboardEntryTweens = {}

local function animateLeaderboardEntry(entry, delayTime)
	local previousTween = leaderboardEntryTweens[entry]

	if previousTween then
		previousTween:Cancel()
	end

	local stroke = entry:FindFirstChildWhichIsA("UIStroke")

	entry.TextTransparency = 1

	if stroke then
		stroke.Transparency = 1
	end

	task.delay(delayTime, function()
		if not entry.Parent then
			return
		end

		local tweens = {}

		local textTween = TweenService:Create(
			entry,
			leaderboardTweenInfo,
			{ TextTransparency = 0 }
		)

		table.insert(tweens, textTween)

		if stroke and stroke.Parent then
			local strokeTween = TweenService:Create(
				stroke,
				leaderboardTweenInfo,
				{ Transparency = 0 }
			)

			table.insert(tweens, strokeTween)
		end

		leaderboardEntryTweens[entry] = textTween

		for _, tween in ipairs(tweens) do
			tween:Play()
		end
	end)
end

local lastLeaderboardRequest = 0
local leaderboardRequestCooldown = 2
local leaderboardRequestInProgress = false

local leaderboardRefreshPending = false

local function updateLeaderboard(force)
	if leaderboardRequestInProgress then
		leaderboardRefreshPending = true
		return
	end

	local now = os.clock()

	if not force and now - lastLeaderboardRequest < leaderboardRequestCooldown then
		task.delay(
			leaderboardRequestCooldown - (now - lastLeaderboardRequest),
			function()
				updateLeaderboard(true)
			end
		)
		return
	end

	leaderboardRefreshPending = false
	lastLeaderboardRequest = now
	leaderboardRequestInProgress = true
	for i = 1, 10 do
		local entry = leaderboardContent:FindFirstChild(tostring(i))

		if entry and entry:IsA("TextLabel") then
			entry.Text = "Loading..."
		end
	end

	yourBestLabel.Text = "Loading..."

	local requestedType = currentLeaderboardType

	local success, data = pcall(function()
		return getLeaderboard:InvokeServer(requestedType)
	end)
	
	leaderboardRequestInProgress = false
	
	if leaderboardRefreshPending or requestedType ~= currentLeaderboardType then
		leaderboardRefreshPending = false
		updateLeaderboard(true)
		return
	end

	if not success or type(data) ~= "table" then
		showNotification("Unable to load leaderboard", "error", 3)
		for i = 1, 10 do
			local entry = leaderboardContent:FindFirstChild(tostring(i))

			if entry and entry:IsA("TextLabel") then
				entry.Text = "#" .. i .. "   Failed to load"
			end
		end

		yourBestLabel.Text = "Failed to load"
		warn("Failed to load leaderboard:", requestedType)
		return
	end

	local entries = data.Top or {}
	
	local entryPositions = {}

	for i = 1, 10 do
		local entry = leaderboardContent:FindFirstChild(tostring(i))

		if entry and entry:IsA("TextLabel") then
			entryPositions[i] = entry.Position
		end
	end

	for i = 1, 10 do
		local entry = leaderboardContent:FindFirstChild(tostring(i))

		if entry and entry:IsA("TextLabel") then
			local playerData = entries[i]

			if playerData then

				entry.Text =
					(i == 1 and "🥇 " or i == 2 and "🥈 " or i == 3 and "🥉 " or "#" .. i .. " ") ..
					tostring(playerData.Username or "Unknown") ..
					"       " .. formatLeaderboardNumber(playerData.Score)

				if i == 1 then
					entry.TextColor3 = Color3.fromRGB(255, 200, 0)
				elseif i == 2 then
					entry.TextColor3 = Color3.fromRGB(225, 236, 248)
				elseif i == 3 then
					entry.TextColor3 = Color3.fromRGB(227, 153, 96)
				else
					entry.TextColor3 = Color3.fromRGB(255, 255, 255)
				end

			else
				entry.Text = "#" .. i .. "   ---       0"
				entry.TextColor3 = Color3.fromRGB(255, 255, 255)
			end
			if entry and entryPositions[i] then
				animateLeaderboardEntry(entry, (i - 1) * 0.035)
			end
		end
	end
	

	local rankText = data.YourRank
		and ("#" .. tostring(data.YourRank))
		or "#--"

	yourBestLabel.Text =
		rankText ..
		"   " .. game.Players.LocalPlayer.Name ..
		"       " .. formatLeaderboardNumber(data.YourBest)
	
	yourBestLabel.TextTransparency = 1

	local yourBestTween = TweenService:Create(
		yourBestLabel,
		TweenInfo.new(0.3, Enum.EasingStyle.Quint, Enum.EasingDirection.Out),
		{
			TextTransparency = 0
		}
	)

	yourBestTween:Play()
end


local function openLeaderboardTab(button)
	local leaderboardType = button.Name

	if not leaderboardOrder[leaderboardType] then
		return
	end

	if currentLeaderboardTab == button then
		return
	end

	-- Update selected tab appearance
	for _, tab in ipairs(leaderboardTabs:GetChildren()) do
		if tab:IsA("TextButton") or tab:IsA("ImageButton") then
			tab.BackgroundTransparency = 1
		end
	end

	button.BackgroundTransparency = 0

	-- Only refresh the contents, not the whole window
	currentLeaderboardTab = button
	currentLeaderboardType = leaderboardType

	updateLeaderboard(true)
end

for _, button in ipairs(leaderboardTabs:GetChildren()) do
	if button:IsA("TextButton") or button:IsA("ImageButton") then
		local originalSize = button.Size
		leaderboardTabOriginalSizes[button] = originalSize

		button.MouseEnter:Connect(function()
			playLeaderboardTabTween(button, leaderboardTabHover, {
				Size = scale(originalSize, 1.04)
			})
		end)

		button.MouseLeave:Connect(function()
			playLeaderboardTabTween(button, leaderboardTabHover, {
				Size = originalSize
			})
		end)

		button.Activated:Connect(function()
			openLeaderboardTab(button)
		end)
	end
end

-- Open HighScores by default
local defaultLeaderboardTab = leaderboardTabs:WaitForChild("HighScores")
openLeaderboardTab(defaultLeaderboardTab)

-- Refresh the currently selected leaderboard periodically
task.spawn(function()
	while leaderboardFrame.Parent do
		task.wait(60)

		if leaderboardFrame.Visible then
			updateLeaderboard()
		end
	end
end)

-- Keyboard Controls

local keyboardControlsButton = root.SettingsFrame.CanvasGroup.Content.KeyboardControls.Open
local keyboardControlsFrame = root:WaitForChild("KeyboardControlsFrame")

keyboardControlsFrame.Visible = false
keyboardControlsFrame.Position = UDim2.new(0.5, 0, -0.5, 0)

setupButton(keyboardControlsButton, function()
	openFrame(root.SettingsFrame, keyboardControlsFrame)
end)

setupGoBack(keyboardControlsFrame, root.SettingsFrame)


local skinsStore = root:WaitForChild("ShopFrame"):WaitForChild("Skins")

local FADE_TIME = 0.45
local DISPLAY_TIME = 2

local function createCarousel(container)
	local examples = {}
	local activeTweens = {}

	for _, frame in ipairs(container:GetChildren()) do
		if frame:IsA("GuiObject") then
			local example = {
				Frame = frame,
				Objects = {}
			}

			local function saveObject(object)
				local props = {
					BackgroundTransparency = object.BackgroundTransparency
				}

				if object:IsA("TextLabel")
					or object:IsA("TextButton")
					or object:IsA("TextBox") then
					props.TextTransparency = object.TextTransparency
				end

				if object:IsA("ImageLabel")
					or object:IsA("ImageButton") then
					props.ImageTransparency = object.ImageTransparency
				end

				if object:IsA("ScrollingFrame") then
					props.ScrollBarImageTransparency = object.ScrollBarImageTransparency
				end

				table.insert(example.Objects, {
					Object = object,
					Properties = props
				})
			end

			saveObject(frame)

			for _, object in ipairs(frame:GetDescendants()) do
				if object:IsA("GuiObject") then
					saveObject(object)
				end
			end

			frame.Visible = false
			table.insert(examples, example)
		end
	end

	table.sort(examples, function(a, b)
		return (tonumber(a.Frame.Name) or 0) < (tonumber(b.Frame.Name) or 0)
	end)

	local function fade(example, visible)
		local previous = activeTweens[example]

		if previous then
			for _, tween in ipairs(previous) do
				tween:Cancel()
			end
		end

		local tweens = {}
		activeTweens[example] = tweens

		if visible then
			example.Frame.Visible = true

			for _, entry in ipairs(example.Objects) do
				if entry.Object.Parent then
					for property in pairs(entry.Properties) do
						entry.Object[property] = 1
					end
				end
			end
		end

		local info = TweenInfo.new(
			FADE_TIME,
			Enum.EasingStyle.Quad,
			Enum.EasingDirection.Out
		)

		for _, entry in ipairs(example.Objects) do
			local object = entry.Object

			if object.Parent then
				local goals = {}

				for property, original in pairs(entry.Properties) do
					goals[property] = visible and original or 1
				end

				local tween = TweenService:Create(object, info, goals)
				table.insert(tweens, tween)
				tween:Play()
			end
		end

		task.delay(FADE_TIME, function()
			if activeTweens[example] ~= tweens then
				return
			end

			if not visible then
				example.Frame.Visible = false
			end

			activeTweens[example] = nil
		end)
	end

	if #examples == 0 then
		return
	end

	task.spawn(function()
		local index = 1
		local previous

		while container.Parent and container:IsDescendantOf(skinsStore) do
			local current = examples[index]

			fade(current, true)

			if previous and previous ~= current then
				fade(previous, false)
			end

			task.wait(DISPLAY_TIME)

			previous = current
			index = index % #examples + 1
		end
	end)
end

for _, skinFrame in ipairs(skinsStore:GetChildren()) do
	local container = skinFrame:FindFirstChild("ExamplesContainer")

	if container then
		createCarousel(container)
	end
end




local skinRemote = ReplicatedStorage:WaitForChild("Events"):WaitForChild("BuyOrEquipSkin")
local getSkins = ReplicatedStorage:WaitForChild("Events"):WaitForChild("GetSkins")
local skinsFrame = root.ShopFrame:WaitForChild("Skins")

local skinNames = {
	"Classic",
	"France",
	"Japan",
	"Germany",
	"Italy",
	"Switzerland",
	"Checkerboard",
	"Cartoon",
	"Lava",
	"Frost",
	"Monochrome",
	"Golden"
}

local skinState = getSkins:InvokeServer()

local function updateSkinButtons()
	if not skinState then
		return
	end

	skinState.OwnedSkins = skinState.OwnedSkins or {}

	local success, ownsVIP = pcall(function()
		return MarketplaceService:UserOwnsGamePassAsync(player.UserId, VIPGamepassID)
	end)

	for _, skinName in ipairs(skinNames) do
		local skinFrame = skinsFrame:FindFirstChild(skinName)
		local buyButton = skinFrame and skinFrame:FindFirstChild("Buy")
		local content = buyButton
			and buyButton:FindFirstChild("Content")
			and buyButton.Content:FindFirstChild("Text")

		if content and content:IsA("TextLabel") then
			if skinState.EquippedSkin == skinName then
				content.Text = "Equipped"
			elseif skinName == "Golden" and success and ownsVIP then
				content.Text = "Equip"
			elseif skinState.OwnedSkins[skinName] then
				content.Text = "Equip"
			else
				content.Text = "Buy"
			end
		end
	end
end

local function requestSkin(skinName)
	if skinState and skinState.EquippedSkin == skinName then
		showNotification("Already equipped", "warning", 3)
		return
	end

	if skinName == "Golden" then
		local success, ownsPass = pcall(function()
			return MarketplaceService:UserOwnsGamePassAsync(
				player.UserId,
				GoldenGamepassID
			)
		end)

		if not success then
			warn("Unable to check Golden Game Pass ownership.")
			showNotification("Something went wrong", "error", 3)
			return
		end

		if not ownsPass then
			MarketplaceService:PromptGamePassPurchase(
				player,
				GoldenGamepassID
			)
			return
		end
	end

	local success, status = skinRemote:InvokeServer(skinName)

	if success then
		skinState = getSkins:InvokeServer()
		updateSkinButtons()

		if skinState and skinState.EquippedSkin == skinName then
			showNotification("Skin equipped", "success", 3)
		else
			showNotification("Skin purchased", "success", 3)
		end

	elseif status == "NotOwnedGamePass" then
		MarketplaceService:PromptGamePassPurchase(
			player,
			GoldenGamepassID
		)

	elseif status == "NotEnoughCoins" then
		showNotification("Not enough coins", "error", 3)

		skinState = getSkins:InvokeServer()
		updateSkinButtons()

	elseif status == "AlreadyOwned" then
		showNotification("Skin already owned", "warning", 3)

	elseif status == "AlreadyEquipped" then
		showNotification("Already equipped", "warning", 3)

	elseif status == "SomethingWentWrong" then
		showNotification("Something went wrong", "error", 3)

	else
		showNotification("Unable to purchase skin", "error", 3)
		warn("Skin request failed:", skinName, status)
	end
end

for _, skinName in ipairs(skinNames) do
	local skinFrame = skinsFrame:FindFirstChild(skinName)
	local buyButton = skinFrame and skinFrame:FindFirstChild("Buy")

	if buyButton and buyButton:IsA("GuiButton") then
		setupButton(buyButton, function()
			requestSkin(skinName)
		end)
	end
end

MarketplaceService.PromptGamePassPurchaseFinished:Connect(
	function(finishedPlayer, gamePassId, wasPurchased)
		showNotification("Purchase successful", "success", 3)
		if finishedPlayer ~= player
			or gamePassId ~= GoldenGamepassID
			or not wasPurchased then
			return
		end

		task.wait(1)

		local success, status = skinRemote:InvokeServer("Golden")

		if success then
			skinState = getSkins:InvokeServer()
			updateSkinButtons()
			showNotification("Skin equipped", "success", 3)
		else
			showNotification("Something went wrong", "error", 3)
			warn("Golden purchase detected, but equipping failed:", status)
		end
	end
)

updateSkinButtons()


-- Rainbow Text Effect for rarity

local RunService = game:GetService("RunService")

local rainbowSequence = ColorSequence.new({
	ColorSequenceKeypoint.new(0.00, Color3.fromRGB(255, 60, 60)),
	ColorSequenceKeypoint.new(0.17, Color3.fromRGB(255, 170, 40)),
	ColorSequenceKeypoint.new(0.33, Color3.fromRGB(255, 245, 80)),
	ColorSequenceKeypoint.new(0.50, Color3.fromRGB(70, 255, 120)),
	ColorSequenceKeypoint.new(0.67, Color3.fromRGB(50, 210, 255)),
	ColorSequenceKeypoint.new(0.83, Color3.fromRGB(150, 90, 255)),
	ColorSequenceKeypoint.new(1.00, Color3.fromRGB(255, 60, 180))
})

local function applyRainbowEffect(object)
	if not (
		object:IsA("TextLabel")
			or object:IsA("TextButton")
			or object:IsA("TextBox")
		) then
		return
	end

	if object:GetAttribute("RainbowEffect") ~= true then
		return
	end

	local gradient = object:FindFirstChildOfClass("UIGradient")

	if not gradient then
		gradient = Instance.new("UIGradient")
		gradient.Parent = object
	end

	object.TextColor3 = Color3.fromRGB(255, 255, 255)

	gradient.Color = ColorSequence.new({
		ColorSequenceKeypoint.new(0.00, Color3.fromRGB(255, 110, 150)),
		ColorSequenceKeypoint.new(0.16, Color3.fromRGB(255, 175, 70)),
		ColorSequenceKeypoint.new(0.32, Color3.fromRGB(235, 255, 90)),
		ColorSequenceKeypoint.new(0.48, Color3.fromRGB(65, 255, 160)),
		ColorSequenceKeypoint.new(0.64, Color3.fromRGB(55, 205, 255)),
		ColorSequenceKeypoint.new(0.80, Color3.fromRGB(125, 90, 255)),
		ColorSequenceKeypoint.new(0.92, Color3.fromRGB(220, 85, 255)),
		ColorSequenceKeypoint.new(1.00, Color3.fromRGB(255, 110, 150))
	})

	gradient.Rotation = 0
	gradient.Offset = Vector2.new(-1, 0)

	if gradient:GetAttribute("RainbowAnimating") then
		return
	end

	gradient:SetAttribute("RainbowAnimating", true)

	task.spawn(function()
		while gradient.Parent and object.Parent and object:GetAttribute("RainbowEffect") == true do
			gradient.Offset = Vector2.new(-1, 0)

			local tween = TweenService:Create(
				gradient,
				TweenInfo.new(2.5, Enum.EasingStyle.Linear),
				{ Offset = Vector2.new(1, 0) }
			)

			tween:Play()
			tween.Completed:Wait()
		end

		if gradient.Parent then
			gradient:SetAttribute("RainbowAnimating", nil)
		end
	end)
end

for _, object in ipairs(skinsStore:GetDescendants()) do
	applyRainbowEffect(object)
end

skinsStore.DescendantAdded:Connect(function(object)
	task.defer(function()
		if object.Parent then
			applyRainbowEffect(object)
		end
	end)
end)

for _, object in ipairs(skinsStore:GetDescendants()) do
	if object:IsA("TextLabel")
		or object:IsA("TextButton")
		or object:IsA("TextBox") then

		object:GetAttributeChangedSignal("RainbowEffect"):Connect(function()
			applyRainbowEffect(object)
		end)
	end
end

local gamepassesFrame = root.ShopFrame:WaitForChild("Gamepasses")

local function updateGamepassFrame(frame)
	local gamepassID = frame:FindFirstChild("GamepassID")
	local priceLabel = frame:FindFirstChild("Price")

	if not gamepassID or not priceLabel then
		return
	end

	local id = gamepassID.Value

	task.spawn(function()
		local success, ownsPass = pcall(function()
			return MarketplaceService:UserOwnsGamePassAsync(player.UserId, id)
		end)

		if not priceLabel.Parent then
			return
		end

		if success and ownsPass then
			priceLabel.Text = "Owned"
			return
		end

		local infoSuccess, info = pcall(function()
			return MarketplaceService:GetProductInfoAsync(id, Enum.InfoType.GamePass)
		end)

		if infoSuccess and info and info.PriceInRobux then
			priceLabel.Text = ""..tostring(info.PriceInRobux) .. ""
		else
			priceLabel.Text = "Unavailable"
		end
	end)
end

for _, frame in ipairs(gamepassesFrame:GetChildren()) do
	if frame:IsA("Frame") then
		local buyButton = frame:FindFirstChild("Buy")
		local gamepassID = frame:FindFirstChild("GamepassID")

		if buyButton and buyButton:IsA("GuiButton") and gamepassID then
			if not buyButton:GetAttribute("GamepassConnected") then
				buyButton:SetAttribute("GamepassConnected", true)

				setupButton(buyButton, function()
					local id = gamepassID.Value

					local success, ownsPass = pcall(function()
						return MarketplaceService:UserOwnsGamePassAsync(player.UserId, id)
					end)

					if success and ownsPass then
						updateGamepassFrame(frame)
						return
					end

					MarketplaceService:PromptGamePassPurchase(player, id)
				end)
			end
		end

		updateGamepassFrame(frame)
	end
end

MarketplaceService.PromptGamePassPurchaseFinished:Connect(function(finishedPlayer, gamePassId, wasPurchased)
	showNotification("Purchase successful", "success", 3)
	if finishedPlayer ~= player or not wasPurchased then
		return
	end

	task.wait(1)

	for _, frame in ipairs(gamepassesFrame:GetChildren()) do
		local id = frame:FindFirstChild("GamepassID")

		if id and id.Value == gamePassId then
			updateGamepassFrame(frame)
		end
	end

	if gamePassId == VIPGamepassID then
		updateVIP()
	end
end)


-- VIP Chat Tag

local TextChatService = game:GetService("TextChatService")

local ownsVIP = false

local success, result = pcall(function()
	return MarketplaceService:UserOwnsGamePassAsync(
		player.UserId,
		VIPGamepassID
	)
end)

if success then
	ownsVIP = result
end

TextChatService.OnIncomingMessage = function(message)
	local properties = Instance.new("TextChatMessageProperties")

	if not ownsVIP then
		return properties
	end

	local source = message.TextSource

	if not source then
		return properties
	end

	local messagePlayer = Players:GetPlayerByUserId(source.UserId)

	if not messagePlayer then
		return properties
	end

	properties.PrefixText = '<font color="#FFD700">[VIP]</font> ' .. message.PrefixText

	return properties
end

MarketplaceService.PromptGamePassPurchaseFinished:Connect(
	function(finishedPlayer, gamePassId, wasPurchased)
		showNotification("Purchase successful", "success", 3)
		if finishedPlayer == player
			and gamePassId == VIPGamepassID
			and wasPurchased then
			ownsVIP = true
		end
	end
)

-- Currency Shop

local currencyFrame = root.ShopFrame:WaitForChild("Currency")

local function updateCurrencyFrame(frame)
	local productID = frame:FindFirstChild("ProductID")
	local priceLabel = frame:FindFirstChild("Price")

	if not productID or not priceLabel then
		warn("Missing ProductID or Price in:", frame.Name)
		return
	end

	if not productID:IsA("IntValue")
		and not productID:IsA("NumberValue") then
		warn("ProductID must be an IntValue or NumberValue:", frame.Name)
		return
	end

	local id = productID.Value

	task.spawn(function()
		local success, info = pcall(function()
			return MarketplaceService:GetProductInfoAsync(
				id,
				Enum.InfoType.Product
			)
		end)

		if not priceLabel.Parent then
			return
		end

		if success and info and info.PriceInRobux then
			priceLabel.Text = "" .. tostring(info.PriceInRobux)
		else
			priceLabel.Text = "Unavailable"
			warn("Unable to load currency product:", id)
		end
	end)
end

for _, frame in ipairs(currencyFrame:GetChildren()) do
	if frame:IsA("Frame") then
		local buyButton = frame:FindFirstChild("Buy")
		local productID = frame:FindFirstChild("ProductID")

		if buyButton and buyButton:IsA("GuiButton") and productID then
			if not buyButton:GetAttribute("CurrencyConnected") then
				buyButton:SetAttribute("CurrencyConnected", true)

				setupButton(buyButton, function()
					local id = productID.Value

					if typeof(id) ~= "number" or id <= 0 then
						warn("Invalid Developer Product ID:", frame.Name)
						return
					end

					MarketplaceService:PromptProductPurchase(
						player,
						id
					)
				end)
			end
		end

		updateCurrencyFrame(frame)
	end
end

-- Daily Rewards

local dailyFrame = root:WaitForChild("DailyFrame")
local dailyContent = dailyFrame:WaitForChild("Content")
local dailyClaimButton = dailyFrame:WaitForChild("ClaimButton")
local dailyClaimLabel = dailyClaimButton
	:WaitForChild("Content")
	:WaitForChild("TextLabel")

local dailyRemotes = ReplicatedStorage:WaitForChild("Events")
local getDailyState = dailyRemotes:WaitForChild("GetDailyRewardsState")
local claimDailyReward = dailyRemotes:WaitForChild("ClaimDailyReward")

local function findDayFrame(dayNumber)
	return dailyContent:FindFirstChild("Day" .. dayNumber)
		or dailyContent:FindFirstChild("Day " .. dayNumber)
end

local function updateClaimedVisibility(state)
	local lastClaimedDay = tonumber(state.LastClaimedRewardDay) or 0

	for day = 1, 7 do
		local dayFrame = findDayFrame(day)

		if dayFrame then
			local claimed = dayFrame:FindFirstChild("Claimed", true)

			if claimed then
				claimed.Visible = day <= lastClaimedDay
			end
		end
	end
end

local dailyRewards = {50, 75, 100, 125, 175, 250, 500}
local DAILY_INTERVAL = 24 * 60 * 60

local normalDayStrokeColor = Color3.fromRGB(0, 152, 198)
local activeDayStrokeColor = Color3.fromRGB(90, 205, 255)

local dailyState
local dailyBusy = false
local dailyRefreshing = false

local nextClaimAt = 0
local serverTimeAtSync = 0
local localClockAtSync = 0
local lastZeroRefresh = false

local function updateDailyRewardDot()
	local success, state = pcall(function()
		return getDailyState:InvokeServer()
	end)

	if success and state then
		for _, redDot in ipairs(redDots) do
			redDot.Visible = state.CanClaim == true
		end
	end
end

local function getDayTime(dayFrame)
	local timeFrame = dayFrame:FindFirstChild("Time", true)

	if not timeFrame then
		return nil, nil
	end

	local label = timeFrame:FindFirstChild("Label", true)

	if label and (
		label:IsA("TextLabel")
			or label:IsA("TextButton")
		) then
		return timeFrame, label
	end

	return timeFrame, nil
end

local function formatRemaining(seconds)
	seconds = math.max(0, math.floor(seconds))

	local hours = math.floor(seconds / 3600)
	local minutes = math.floor((seconds % 3600) / 60)
	local remainingSeconds = seconds % 60

	return string.format(
		"%02d:%02d:%02d",
		hours,
		minutes,
		remainingSeconds
	)
end

-- Synchronise l'heure estimée avec celle du serveur.
local function syncDailyTimer(state)
	if type(state) ~= "table" or state.Success ~= true then
		return
	end

	nextClaimAt = tonumber(state.NextClaimAt) or 0
	serverTimeAtSync = tonumber(state.ServerTime) or os.time()
	localClockAtSync = os.clock()

	lastZeroRefresh = false
end

task.spawn(function()
	while true do
		if dailyState then
			syncDailyTimer(dailyState)
		end

		task.wait(1)
	end
end)

task.spawn(function()
	while true do
		updateDailyRewardDot()
		task.wait(10)
	end
end)

local function getServerTimeEstimate()
	if serverTimeAtSync <= 0 then
		return os.time()
	end

	return serverTimeAtSync + (os.clock() - localClockAtSync)
end

local function getSecondsUntilClaim()
	if not nextClaimAt or nextClaimAt <= 0 then
		return 0
	end

	local elapsed = os.clock() - localClockAtSync
	local estimatedServerTime = serverTimeAtSync + elapsed

	return math.max(0, nextClaimAt - estimatedServerTime)
end

local function updateDailyUI(state)
	if type(state) ~= "table" or state.Success ~= true then
		return
	end

	dailyState = state
	updateClaimedVisibility(state)
	syncDailyTimer(state)
	

	local canClaim = state.CanClaim == true
	local nextDay = math.clamp(
		math.floor(tonumber(state.NextDay) or 1),
		1,
		#dailyRewards
	)

	local secondsUntilClaim = getSecondsUntilClaim()

	for dayNumber = 1, #dailyRewards do
		local dayFrame = findDayFrame(dayNumber)

		if dayFrame then
			local rewardLabel = dayFrame:FindFirstChild("Reward", true)

			if rewardLabel and (
				rewardLabel:IsA("TextLabel")
					or rewardLabel:IsA("TextButton")
				) then
				rewardLabel.Text =
					"+" .. tostring(dailyRewards[dayNumber]) .. " Coins"
			end

			local offset = (dayNumber - nextDay) % #dailyRewards
			local isClaimable = canClaim and offset == 0
			local isAlreadyClaimed =
				dayNumber == (tonumber(state.LastClaimedRewardDay) or 0)

			local lastClaimedDay =
				tonumber(state.LastClaimedRewardDay) or 0

			local isAlreadyClaimed = dayNumber == lastClaimedDay

			local isFuture = not isAlreadyClaimed and (
				(canClaim and offset > 0)
					or (not canClaim and offset >= 0)
			)

			local timeFrame, timeLabel = getDayTime(dayFrame)

			if timeFrame then
				timeFrame.Visible = isFuture

				if timeLabel and isFuture then
					local daysUntilUnlock

					if canClaim then
						daysUntilUnlock = math.max(0, offset - 1)
					else
						daysUntilUnlock = offset
					end

					local remaining =
						secondsUntilClaim + daysUntilUnlock * DAILY_INTERVAL

					timeLabel.Text = formatRemaining(remaining)
				end
			end

			local stroke =
				dayFrame:FindFirstChildWhichIsA("UIStroke", true)

			if stroke then
				stroke.Thickness = isClaimable and 0.02 or 0.01
				stroke.Color = isClaimable
					and activeDayStrokeColor
					or normalDayStrokeColor
			end
		end
	end

	if canClaim then
		dailyClaimLabel.Text =
			"Claim +" .. tostring(state.Reward or dailyRewards[nextDay]) .. " Coins"
	else
		dailyClaimLabel.Text = "Claimed Today"
	end
end

-- Une seule fonction de rafraîchissement.
refreshDailyUI = function()
	if dailyRefreshing then
		return false
	end

	dailyRefreshing = true

	local success, state = pcall(function()
		return getDailyState:InvokeServer()
	end)

	dailyRefreshing = false
	
	

	if not success or type(state) ~= "table" or state.Success ~= true then
		if not dailyState then
			dailyClaimLabel.Text = "Try Again"
		end

		warn("Unable to refresh Daily Rewards:", state)
		return false
	end

	updateDailyUI(state)
	return true
end

-- Le bouton Daily utilise cette fonction définie localement.
-- Connecte-le après la déclaration de la fonction.
local function claimDailyRewardAction()
	if dailyBusy or not dailyState or dailyState.CanClaim ~= true then
		return
	end

	dailyBusy = true
	dailyClaimLabel.Text = "Claiming..."

	local success, result = pcall(function()
		return claimDailyReward:InvokeServer()
	end)

	if success and type(result) == "table" and result.Success == true then
		if type(result.State) == "table" then
			updateDailyUI(result.State)
			updateDailyRewardDot()
		else
			refreshDailyUI()
		end

		showNotification(
			result.Message or "Reward claimed!",
			"success",
			3
		)
	else
		local message = "Unable to claim reward. Please try again."

		if success and type(result) == "table" and result.Message then
			message = result.Message
		end

		showNotification(message, "error", 3)
		refreshDailyUI()
	end

	dailyBusy = false
end

setupButton(dailyClaimButton, claimDailyRewardAction)

-- Actualise les compteurs sans appeler le serveur chaque seconde.
task.spawn(function()
	while dailyFrame.Parent do
		task.wait(1)

		if dailyFrame.Visible and dailyState then
			local remaining = getSecondsUntilClaim()

			for dayNumber = 1, #dailyRewards do
				local dayFrame = findDayFrame(dayNumber)

				if dayFrame then
					local timeFrame, timeLabel = getDayTime(dayFrame)

					if timeFrame and timeFrame.Visible and timeLabel then
						local nextDay = math.clamp(
							math.floor(tonumber(dailyState.NextDay) or 1),
							1,
							#dailyRewards
						)

						local offset = (dayNumber - nextDay) % #dailyRewards
						local canClaim = dailyState.CanClaim == true

						local daysUntilUnlock

						if canClaim then
							daysUntilUnlock = math.max(0, offset - 1)
						else
							daysUntilUnlock = offset
						end

						timeLabel.Text = formatRemaining(
							remaining + daysUntilUnlock * DAILY_INTERVAL
						)
					end
				end
			end

			-- Une seule nouvelle vérification quand le compteur atteint zéro.
			if remaining <= 0 and not lastZeroRefresh then
				lastZeroRefresh = true

				task.spawn(function()
					refreshDailyUI()
				end)
			elseif remaining > 0 then
				lastZeroRefresh = false
			end
		end
	end
end)
← Back to timeline