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)1 views