--!strict
-- ============================================
-- TOP DONATION GUI CLIENT - LOCAL SCRIPT
-- Leaderboard des meilleurs donateurs
-- ============================================
-- EMPLACEMENT: StarterPlayer > StarterPlayerScripts > DonationSystem > TopDonationGuiClient
-- Services
local Players = game:GetService("Players")
local UserInputService = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- MANDATORY: Mobile Platform Detection
if not UserInputService.TouchEnabled then
warn("TopDonationGui requires mobile platform - initialization aborted")
return
end
local player = Players.LocalPlayer
local playerGui = player:WaitForChild("PlayerGui")
-- RemoteFunctions
local donationSystemFolder = ReplicatedStorage:WaitForChild("DonationSystem")
local getLeaderboardFunction = donationSystemFolder:WaitForChild("GetLeaderboard") :: RemoteFunction
local getPlayerStatsFunction = donationSystemFolder:WaitForChild("GetPlayerStats") :: RemoteFunction
-- ============================================
-- TYPES & CONFIGS
-- ============================================
type DonatorData = {
Username: string,
UserId: number,
TotalDonated: number,
Rank: number
}
local TWEEN_FAST = TweenInfo.new(0.3, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
local TWEEN_SMOOTH = TweenInfo.new(0.5, Enum.EasingStyle.Cubic, Enum.EasingDirection.Out)
local TWEEN_BOUNCE = TweenInfo.new(0.6, Enum.EasingStyle.Back, Enum.EasingDirection.Out)
-- ============================================
-- GUI CONSTRUCTION
-- ============================================
local leaderboardGui = Instance.new("ScreenGui")
leaderboardGui.Name = "TopDonationGui"
leaderboardGui.ResetOnSpawn = false
leaderboardGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
leaderboardGui.ScreenInsets = Enum.ScreenInsets.DeviceSafeInsets
leaderboardGui.Parent = playerGui
-- Overlay
local overlay = Instance.new("Frame")
overlay.Name = "Overlay"
overlay.Size = UDim2.fromScale(1, 1)
overlay.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
overlay.BackgroundTransparency = 1
overlay.BorderSizePixel = 0
overlay.Visible = false
overlay.ZIndex = 1
overlay.Parent = leaderboardGui
-- Toggle Button
local toggleButton = Instance.new("TextButton")
toggleButton.Name = "ToggleButton"
toggleButton.AnchorPoint = Vector2.new(1, 0)
toggleButton.Position = UDim2.fromScale(0.98, 0.12)
toggleButton.Size = UDim2.fromScale(0.2, 0.09)
toggleButton.BackgroundColor3 = Color3.fromRGB(255, 215, 0)
toggleButton.BorderSizePixel = 0
toggleButton.Font = Enum.Font.GothamBold
toggleButton.Text = "🏆 TOP"
toggleButton.TextColor3 = Color3.fromRGB(0, 0, 0)
toggleButton.TextScaled = true
toggleButton.ZIndex = 2
toggleButton.Parent = leaderboardGui
local toggleGradient = Instance.new("UIGradient")
toggleGradient.Color = ColorSequence.new{
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 215, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(218, 165, 32))
}
toggleGradient.Rotation = 45
toggleGradient.Parent = toggleButton
local toggleCorner = Instance.new("UICorner")
toggleCorner.CornerRadius = UDim.new(0.25, 0)
toggleCorner.Parent = toggleButton
local togglePadding = Instance.new("UIPadding")
togglePadding.PaddingLeft = UDim.new(0.1, 0)
togglePadding.PaddingRight = UDim.new(0.1, 0)
togglePadding.PaddingTop = UDim.new(0.2, 0)
togglePadding.PaddingBottom = UDim.new(0.2, 0)
togglePadding.Parent = toggleButton
local toggleStroke = Instance.new("UIStroke")
toggleStroke.Color = Color3.fromRGB(255, 255, 255)
toggleStroke.Transparency = 0.5
toggleStroke.Thickness = 2
toggleStroke.Parent = toggleButton
-- MainFrame (MANDATORY: 0.5, 0.5)
local mainFrame = Instance.new("Frame")
mainFrame.Name = "MainFrame"
mainFrame.AnchorPoint = Vector2.new(0.5, 0.5)
mainFrame.Position = UDim2.fromScale(0.5, 0.5)
mainFrame.Size = UDim2.fromScale(0.5, 0.5)
mainFrame.BackgroundColor3 = Color3.fromRGB(20, 20, 30)
mainFrame.BorderSizePixel = 0
mainFrame.Visible = false
mainFrame.ZIndex = 2
mainFrame.Parent = leaderboardGui
local mainGradient = Instance.new("UIGradient")
mainGradient.Color = ColorSequence.new{
ColorSequenceKeypoint.new(0, Color3.fromRGB(25, 25, 40)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(15, 15, 25))
}
mainGradient.Rotation = 135
mainGradient.Parent = mainFrame
local mainCorner = Instance.new("UICorner")
mainCorner.CornerRadius = UDim.new(0.05, 0)
mainCorner.Parent = mainFrame
local mainStroke = Instance.new("UIStroke")
mainStroke.Color = Color3.fromRGB(255, 215, 0)
mainStroke.Transparency = 0.4
mainStroke.Thickness = 3
mainStroke.Parent = mainFrame
local aspectRatio = Instance.new("UIAspectRatioConstraint")
aspectRatio.AspectRatio = 0.65
aspectRatio.Parent = mainFrame
-- Header
local header = Instance.new("Frame")
header.Name = "Header"
header.Size = UDim2.fromScale(1, 0.12)
header.Position = UDim2.fromScale(0, 0)
header.BackgroundColor3 = Color3.fromRGB(255, 215, 0)
header.BorderSizePixel = 0
header.ZIndex = 3
header.Parent = mainFrame
local headerGradient = Instance.new("UIGradient")
headerGradient.Color = ColorSequence.new{
ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 215, 0)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(218, 165, 32))
}
headerGradient.Rotation = 90
headerGradient.Parent = header
local headerCorner = Instance.new("UICorner")
headerCorner.CornerRadius = UDim.new(0.05, 0)
headerCorner.Parent = header
-- Title
local titleLabel = Instance.new("TextLabel")
titleLabel.Name = "Title"
titleLabel.Size = UDim2.fromScale(0.75, 1)
titleLabel.Position = UDim2.fromScale(0, 0)
titleLabel.BackgroundTransparency = 1
titleLabel.Font = Enum.Font.GothamBold
titleLabel.Text = "🏆 TOP DONATEURS"
titleLabel.TextColor3 = Color3.fromRGB(0, 0, 0)
titleLabel.TextScaled = true
titleLabel.ZIndex = 4
titleLabel.Parent = header
local titlePadding = Instance.new("UIPadding")
titlePadding.PaddingLeft = UDim.new(0.08, 0)
titlePadding.PaddingTop = UDim.new(0.15, 0)
titlePadding.PaddingBottom = UDim.new(0.15, 0)
titlePadding.Parent = titleLabel
-- Close Button
local closeButton = Instance.new("TextButton")
closeButton.Name = "CloseButton"
closeButton.AnchorPoint = Vector2.new(1, 0.5)
closeButton.Position = UDim2.fromScale(0.95, 0.5)
closeButton.Size = UDim2.fromScale(0.15, 0.7)
closeButton.BackgroundColor3 = Color3.fromRGB(220, 53, 69)
closeButton.BorderSizePixel = 0
closeButton.Font = Enum.Font.GothamBold
closeButton.Text = "✕"
closeButton.TextColor3 = Color3.fromRGB(255, 255, 255)
closeButton.TextScaled = true
closeButton.ZIndex = 5
closeButton.Parent = header
local closeCorner = Instance.new("UICorner")
closeCorner.CornerRadius = UDim.new(0.5, 0)
closeCorner.Parent = closeButton
-- ============================================
-- PODIUM SECTION
-- ============================================
local podiumContainer = Instance.new("Frame")
podiumContainer.Name = "PodiumContainer"
podiumContainer.Position = UDim2.fromScale(0.05, 0.14)
podiumContainer.Size = UDim2.fromScale(0.9, 0.35)
podiumContainer.BackgroundTransparency = 1
podiumContainer.ZIndex = 3
podiumContainer.Parent = mainFrame
local podiumPositions = {
{Rank = 2, XPos = 0, Height = 0.7, Medal = "🥈", Color = Color3.fromRGB(192, 192, 192)},
{Rank = 1, XPos = 0.35, Height = 0.95, Medal = "🥇", Color = Color3.fromRGB(255, 215, 0)},
{Rank = 3, XPos = 0.7, Height = 0.5, Medal = "🥉", Color = Color3.fromRGB(205, 127, 50)}
}
local podiumFrames: {[number]: Frame} = {}
for _, config in podiumPositions do
local podiumFrame = Instance.new("Frame")
podiumFrame.Name = `Podium{config.Rank}`
podiumFrame.AnchorPoint = Vector2.new(0.5, 1)
podiumFrame.Position = UDim2.fromScale(config.XPos + 0.15, 1)
podiumFrame.Size = UDim2.fromScale(0.28, config.Height)
podiumFrame.BackgroundColor3 = Color3.fromRGB(40, 40, 60)
podiumFrame.BorderSizePixel = 0
podiumFrame.ZIndex = 4
podiumFrame.Parent = podiumContainer
local podiumGradient = Instance.new("UIGradient")
podiumGradient.Color = ColorSequence.new{
ColorSequenceKeypoint.new(0, config.Color),
ColorSequenceKeypoint.new(1, Color3.new(config.Color.R * 0.7, config.Color.G * 0.7, config.Color.B * 0.7))
}
podiumGradient.Rotation = 90
podiumGradient.Parent = podiumFrame
local podiumCorner = Instance.new("UICorner")
podiumCorner.CornerRadius = UDim.new(0.08, 0)
podiumCorner.Parent = podiumFrame
local podiumStroke = Instance.new("UIStroke")
podiumStroke.Color = config.Color
podiumStroke.Thickness = 2
podiumStroke.Transparency = 0.3
podiumStroke.Parent = podiumFrame
local medalLabel = Instance.new("TextLabel")
medalLabel.Name = "Medal"
medalLabel.Size = UDim2.fromScale(1, 0.25)
medalLabel.Position = UDim2.fromScale(0, 0.05)
medalLabel.BackgroundTransparency = 1
medalLabel.Font = Enum.Font.GothamBold
medalLabel.Text = config.Medal
medalLabel.TextScaled = true
medalLabel.ZIndex = 5
medalLabel.Parent = podiumFrame
local usernameLabel = Instance.new("TextLabel")
usernameLabel.Name = "Username"
usernameLabel.Size = UDim2.fromScale(0.9, 0.2)
usernameLabel.Position = UDim2.fromScale(0.05, 0.32)
usernameLabel.BackgroundTransparency = 1
usernameLabel.Font = Enum.Font.GothamBold
usernameLabel.Text = "---"
usernameLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
usernameLabel.TextScaled = true
usernameLabel.TextWrapped = true
usernameLabel.ZIndex = 5
usernameLabel.Parent = podiumFrame
local amountLabel = Instance.new("TextLabel")
amountLabel.Name = "Amount"
amountLabel.Size = UDim2.fromScale(0.9, 0.18)
amountLabel.Position = UDim2.fromScale(0.05, 0.55)
amountLabel.BackgroundTransparency = 1
amountLabel.Font = Enum.Font.Gotham
amountLabel.Text = "0 R$"
amountLabel.TextColor3 = Color3.fromRGB(200, 200, 220)
amountLabel.TextScaled = true
amountLabel.ZIndex = 5
amountLabel.Parent = podiumFrame
local rankBadge = Instance.new("TextLabel")
rankBadge.Name = "RankBadge"
rankBadge.AnchorPoint = Vector2.new(0.5, 1)
rankBadge.Size = UDim2.fromScale(0.4, 0.2)
rankBadge.Position = UDim2.fromScale(0.5, 0.98)
rankBadge.BackgroundColor3 = config.Color
rankBadge.Font = Enum.Font.GothamBold
rankBadge.Text = `#{config.Rank}`
rankBadge.TextColor3 = Color3.fromRGB(0, 0, 0)
rankBadge.TextScaled = true
rankBadge.ZIndex = 6
rankBadge.Parent = podiumFrame
local rankCorner = Instance.new("UICorner")
rankCorner.CornerRadius = UDim.new(0.3, 0)
rankCorner.Parent = rankBadge
podiumFrames[config.Rank] = podiumFrame
end
-- ============================================
-- LIST SECTION (RANKS 4-10)
-- ============================================
local listContainer = Instance.new("Frame")
listContainer.Name = "ListContainer"
listContainer.Position = UDim2.fromScale(0.05, 0.51)
listContainer.Size = UDim2.fromScale(0.9, 0.35)
listContainer.BackgroundColor3 = Color3.fromRGB(30, 30, 45)
listContainer.BackgroundTransparency = 0.5
listContainer.BorderSizePixel = 0
listContainer.ZIndex = 3
listContainer.Parent = mainFrame
local listCorner = Instance.new("UICorner")
listCorner.CornerRadius = UDim.new(0.04, 0)
listCorner.Parent = listContainer
local scrollFrame = Instance.new("ScrollingFrame")
scrollFrame.Name = "ScrollFrame"
scrollFrame.Size = UDim2.fromScale(1, 1)
scrollFrame.BackgroundTransparency = 1
scrollFrame.BorderSizePixel = 0
scrollFrame.ScrollBarThickness = 4
scrollFrame.ScrollBarImageColor3 = Color3.fromRGB(255, 215, 0)
scrollFrame.CanvasSize = UDim2.fromScale(0, 0)
scrollFrame.AutomaticCanvasSize = Enum.AutomaticSize.Y
scrollFrame.ZIndex = 4
scrollFrame.Parent = listContainer
local listLayout = Instance.new("UIListLayout")
listLayout.SortOrder = Enum.SortOrder.LayoutOrder
listLayout.Padding = UDim.new(0.02, 0)
listLayout.Parent = scrollFrame
local listPadding = Instance.new("UIPadding")
listPadding.PaddingTop = UDim.new(0.02, 0)
listPadding.PaddingBottom = UDim.new(0.02, 0)
listPadding.PaddingLeft = UDim.new(0.03, 0)
listPadding.PaddingRight = UDim.new(0.03, 0)
listPadding.Parent = scrollFrame
-- ============================================
-- PLAYER STATS
-- ============================================
local playerStatsFrame = Instance.new("Frame")
playerStatsFrame.Name = "PlayerStats"
playerStatsFrame.Position = UDim2.fromScale(0.05, 0.88)
playerStatsFrame.Size = UDim2.fromScale(0.9, 0.1)
playerStatsFrame.BackgroundColor3 = Color3.fromRGB(139, 69, 255)
playerStatsFrame.BorderSizePixel = 0
playerStatsFrame.ZIndex = 3
playerStatsFrame.Parent = mainFrame
local statsGradient = Instance.new("UIGradient")
statsGradient.Color = ColorSequence.new{
ColorSequenceKeypoint.new(0, Color3.fromRGB(139, 69, 255)),
ColorSequenceKeypoint.new(1, Color3.fromRGB(88, 44, 204))
}
statsGradient.Rotation = 45
statsGradient.Parent = playerStatsFrame
local statsCorner = Instance.new("UICorner")
statsCorner.CornerRadius = UDim.new(0.15, 0)
statsCorner.Parent = playerStatsFrame
local playerStatsLabel = Instance.new("TextLabel")
playerStatsLabel.Name = "StatsLabel"
playerStatsLabel.Size = UDim2.fromScale(1, 1)
playerStatsLabel.BackgroundTransparency = 1
playerStatsLabel.Font = Enum.Font.GothamBold
playerStatsLabel.Text = "Votre Position: #-- | Total: 0 R$"
playerStatsLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
playerStatsLabel.TextScaled = true
playerStatsLabel.ZIndex = 4
playerStatsLabel.Parent = playerStatsFrame
local statsPadding = Instance.new("UIPadding")
statsPadding.PaddingLeft = UDim.new(0.05, 0)
statsPadding.PaddingRight = UDim.new(0.05, 0)
statsPadding.PaddingTop = UDim.new(0.2, 0)
statsPadding.PaddingBottom = UDim.new(0.2, 0)
statsPadding.Parent = playerStatsLabel
-- ============================================
-- HELPER FUNCTIONS
-- ============================================
local function formatNumber(num: number): string
if num >= 1000000 then
return string.format("%.1fM", num / 1000000)
elseif num >= 1000 then
return string.format("%.1fK", num / 1000)
else
return tostring(num)
end
end
local function updatePodium(data: {DonatorData})
for rank = 1, 3 do
local donator = data[rank]
local frame = podiumFrames[rank]
if donator and frame then
local username = frame:FindFirstChild("Username") :: TextLabel
local amount = frame:FindFirstChild("Amount") :: TextLabel
if username then
username.Text = donator.Username
end
if amount then
amount.Text = `{formatNumber(donator.TotalDonated)} R$`
end
end
end
end
local function createListEntry(donator: DonatorData): Frame
local entry = Instance.new("Frame")
entry.Name = `Rank{donator.Rank}`
entry.Size = UDim2.fromScale(0.96, 0.22)
entry.BackgroundColor3 = Color3.fromRGB(40, 40, 60)
entry.BorderSizePixel = 0
entry.LayoutOrder = donator.Rank
entry.ZIndex = 5
local entryCorner = Instance.new("UICorner")
entryCorner.CornerRadius = UDim.new(0.15, 0)
entryCorner.Parent = entry
local rankLabel = Instance.new("TextLabel")
rankLabel.Name = "Rank"
rankLabel.Size = UDim2.fromScale(0.15, 0.8)
rankLabel.Position = UDim2.fromScale(0.05, 0.1)
rankLabel.BackgroundTransparency = 1
rankLabel.Font = Enum.Font.GothamBold
rankLabel.Text = `#{donator.Rank}`
rankLabel.TextColor3 = Color3.fromRGB(255, 215, 0)
rankLabel.TextScaled = true
rankLabel.ZIndex = 6
rankLabel.Parent = entry
local nameLabel = Instance.new("TextLabel")
nameLabel.Name = "Username"
nameLabel.Size = UDim2.fromScale(0.5, 0.8)
nameLabel.Position = UDim2.fromScale(0.22, 0.1)
nameLabel.BackgroundTransparency = 1
nameLabel.Font = Enum.Font.Gotham
nameLabel.Text = donator.Username
nameLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
nameLabel.TextScaled = true
nameLabel.TextXAlignment = Enum.TextXAlignment.Left
nameLabel.ZIndex = 6
nameLabel.Parent = entry
local amountLabel = Instance.new("TextLabel")
amountLabel.Name = "Amount"
amountLabel.AnchorPoint = Vector2.new(1, 0.5)
amountLabel.Size = UDim2.fromScale(0.22, 0.7)
amountLabel.Position = UDim2.fromScale(0.95, 0.5)
amountLabel.BackgroundTransparency = 1
amountLabel.Font = Enum.Font.GothamBold
amountLabel.Text = `{formatNumber(donator.TotalDonated)} R$`
amountLabel.TextColor3 = Color3.fromRGB(50, 200, 100)
amountLabel.TextScaled = true
amountLabel.ZIndex = 6
amountLabel.Parent = entry
return entry
end
local function updateLeaderboard()
-- Fetch from server
local success, leaderboardData = pcall(function()
return getLeaderboardFunction:InvokeServer(10)
end)
if not success or not leaderboardData then
warn("[TopDonationGui] Failed to fetch leaderboard")
return
end
-- Clear existing entries
for _, child in scrollFrame:GetChildren() do
if child:IsA("Frame") then
child:Destroy()
end
end
-- Update podium (Top 3)
updatePodium(leaderboardData)
-- Update list (Ranks 4-10)
for i = 4, math.min(#leaderboardData, 10) do
local entry = createListEntry(leaderboardData[i])
entry.Parent = scrollFrame
end
-- Update player stats
local statsSuccess, playerStats = pcall(function()
return getPlayerStatsFunction:InvokeServer(player.UserId)
end)
if statsSuccess and playerStats then
local rank = playerStats.Rank > 0 and tostring(playerStats.Rank) or "--"
playerStatsLabel.Text = `Votre Position: #{rank} | Total: {formatNumber(playerStats.TotalDonated)} R$`
end
end
-- ============================================
-- TOGGLE FUNCTIONALITY
-- ============================================
local function toggleLeaderboard()
if mainFrame.Visible then
local fadeTween = TweenService:Create(overlay, TWEEN_FAST, {BackgroundTransparency = 1})
local slideTween = TweenService:Create(mainFrame, TWEEN_SMOOTH, {Position = UDim2.fromScale(0.5, -0.6)})
fadeTween:Play()
slideTween:Play()
slideTween.Completed:Wait()
mainFrame.Visible = false
overlay.Visible = false
mainFrame.Position = UDim2.fromScale(0.5, 0.5)
toggleButton.Text = "🏆 TOP"
else
overlay.Visible = true
mainFrame.Visible = true
mainFrame.Position = UDim2.fromScale(0.5, -0.6)
updateLeaderboard()
local fadeTween = TweenService:Create(overlay, TWEEN_SMOOTH, {BackgroundTransparency = 0.5})
local slideTween = TweenService:Create(mainFrame, TWEEN_BOUNCE, {Position = UDim2.fromScale(0.5, 0.5)})
fadeTween:Play()
slideTween:Play()
toggleButton.Text = "✕"
end
end
toggleButton.MouseButton1Click:Connect(toggleLeaderboard)
closeButton.MouseButton1Click:Connect(toggleLeaderboard)
overlay.InputBegan:Connect(function(input)
if input.UserInputType == Enum.UserInputType.Touch then
toggleLeaderboard()
end
end)
-- Auto-refresh every 30 seconds when open
task.spawn(function()
while true do
task.wait(30)
if mainFrame.Visible then
updateLeaderboard()
end
end
end)
print("✅ TopDonationGui initialized successfully")