-- Services local CoreGui = game:GetService("CoreGui") local Players = game:GetService("Players") local StarterGui = game:GetService("StarterGui") local UserInputService = game:GetService("UserInputService") local HttpService = game:GetService("HttpService") local TeleportService = game:GetService("TeleportService") local LocalPlayer = Players.LocalPlayer --------------------------------------------------------- -- [ RP & BIO SYSTEM ] --------------------------------------------------------- local function UpdateRP() pcall(function() local RE = game:GetService("ReplicatedStorage"):WaitForChild("RE", 5) if RE then RE:FindFirstChild("1RPNam1eTex1t"):FireServer("RolePlayName", "ȤeNix CHaT") RE:FindFirstChild("1RPNam1eTex1t"):FireServer("RolePlayBio", "By ZaIN, J00") RE:FindFirstChild("1RPNam1eColo1r"):FireServer("PickingRPNameColor", Color3.fromRGB(170, 85, 255)) pcall(function() RE:FindFirstChild("1RPNam1eColo1r"):FireServer("PickingRPBioColor", Color3.fromRGB(0, 100, 0)) end) end end) end task.spawn(function() task.wait(5) local RE = game:GetService("ReplicatedStorage"):WaitForChild("RE", 5) if RE then RE:FindFirstChild("1RPNam1eTex1t"):FireServer("RolePlayName", "ZeNiX CHaT π foR aLL") RE:FindFirstChild("1RPNam1eTex1t"):FireServer("RolePlayBio", "By ZaIN, J00") local Green = Color3.fromRGB(0, 255, 0) local Red = Color3.fromRGB(255, 0, 0) RE:FindFirstChild("1RPNam1eColo1r"):FireServer("PickingRPNameColor", Green) RE:FindFirstChild("1RPNam1eColo1r"):FireServer("PickingRPBioColor", Red) end end) --------------------------------------------------------- -- CONFIGURATION local FIREBASE_URL = "https://betterchat-rblx-default-rtdb.firebaseio.com/" local CURRENT_VERSION_STRING = "2" local FOLDER_NAME = "BetterChat" local VERSION_FILE = FOLDER_NAME .. "/version.txt" local RUNS_FILE = FOLDER_NAME .. "/runs.txt" local CHAT_PATH = "GlobalChat/Servers/" .. (game.JobId ~= "" and game.JobId or "Studio_Debug") local PRESENCE_PATH = "GlobalChat/OnlinePlayers" local ANNOUNCE_PATH = "GlobalChat/Announcement" local MAX_MESSAGES = 50 local PRESENCE_TTL = 90 -- FIX 1: safeRequest no longer falls back to HttpService local function safeRequest(options) local req = (syn and syn.request) or (http and http.request) or http_request or request if req then return req(options) else return {Success = false, Body = ""} end end local function sanitize(s) return tostring(s):gsub("[<>&]", {["<"] = "<", [">"] = ">", ["&"] = "&"}) end -- 1. BOOTSTRAPPER if not isfolder(FOLDER_NAME) then makefolder(FOLDER_NAME) end local function CheckForUpdates() local response = safeRequest({ Url = FIREBASE_URL .. "GlobalChat/LatestVersion.json", Method = "GET" }) if not response.Success then return false end if response.Body ~= "null" then local cloudVersion = string.gsub(response.Body, '["%s]', '') if CURRENT_VERSION_STRING ~= cloudVersion then return false end end return true end if not CheckForUpdates() then local parentGui = (gethui and gethui()) or CoreGui:FindFirstChild("RobloxGui") or CoreGui local UpdateGui = Instance.new("ScreenGui", parentGui) UpdateGui.Name = "BetterChatUpdater" local BG = Instance.new("Frame", UpdateGui) BG.Size = UDim2.new(1, 0, 1, 0) BG.BackgroundColor3 = Color3.fromRGB(0, 0, 0) local Warning = Instance.new("TextLabel", BG) Warning.Size = UDim2.new(1, 0, 1, 0) Warning.Text = "Script outdated, please update." Warning.TextColor3 = Color3.fromRGB(255, 80, 80) Warning.TextSize = 30 Warning.BackgroundTransparency = 1 return end writefile(VERSION_FILE, CURRENT_VERSION_STRING) -- 2. RUN COUNTER local runCount = 0 if isfile(RUNS_FILE) then runCount = tonumber(readfile(RUNS_FILE)) or 0 end runCount = runCount + 1 writefile(RUNS_FILE, tostring(runCount)) local isFirstRun = runCount == 1 -- 3. PRESENCE SYSTEM local MY_JOB_ID = game.JobId ~= "" and game.JobId or "Studio_Debug" local MY_PLACE_ID = game.PlaceId local function WritePresence() safeRequest({ Url = FIREBASE_URL .. PRESENCE_PATH .. "/" .. LocalPlayer.UserId .. ".json", Method = "PUT", Headers = {["Content-Type"] = "application/json"}, Body = HttpService:JSONEncode({ DisplayName = LocalPlayer.DisplayName, Username = LocalPlayer.Name, UserId = LocalPlayer.UserId, JobId = MY_JOB_ID, PlaceId = MY_PLACE_ID, LastSeen = os.time() }) }) end local function ClearPresence() safeRequest({ Url = FIREBASE_URL .. PRESENCE_PATH .. "/" .. LocalPlayer.UserId .. ".json", Method = "DELETE" }) end WritePresence() task.spawn(function() while task.wait(30) do WritePresence() end end) LocalPlayer.AncestryChanged:Connect(function() if not LocalPlayer:IsDescendantOf(game) then ClearPresence() end end) -- 4. UI SETUP local parentGui = (gethui and gethui()) or CoreGui:FindFirstChild("RobloxGui") or CoreGui if parentGui:FindFirstChild("GlobalChatGui") then parentGui.GlobalChatGui:Destroy() end local ScreenGui = Instance.new("ScreenGui", parentGui) ScreenGui.Name = "GlobalChatGui" ScreenGui.IgnoreGuiInset = true ScreenGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling local Container = Instance.new("Frame", ScreenGui) Container.Name = "mainframe" Container.BackgroundColor3 = Color3.new(0, 0, 0) Container.BackgroundTransparency = 0.2 Container.BorderColor3 = Color3.new(0, 0, 0) Container.BorderSizePixel = 0 Container.Position = UDim2.new(0, 10, 0, 60) Container.Size = UDim2.new(0, 340, 0, 202) Container.Visible = true local ContainerCorner = Instance.new("UICorner", Container) ContainerCorner.CornerRadius = UDim.new(0, 9) local ChatScroll = Instance.new("ScrollingFrame", Container) ChatScroll.BackgroundTransparency = 1 ChatScroll.Position = UDim2.new(0, 8, 0, 6) ChatScroll.Size = UDim2.new(1, -16, 1, -48) ChatScroll.AutomaticCanvasSize = Enum.AutomaticSize.Y ChatScroll.CanvasSize = UDim2.new(0, 0, 0, 0) ChatScroll.ScrollBarThickness = 2 ChatScroll.ScrollBarImageColor3 = Color3.fromRGB(255, 255, 255) local Layout = Instance.new("UIListLayout", ChatScroll) Layout.Padding = UDim.new(0, 4) local TextBox = Instance.new("TextBox", Container) TextBox.BackgroundColor3 = Color3.new(0, 0, 0) TextBox.BackgroundTransparency = 0.5 TextBox.BorderColor3 = Color3.new(0, 0, 0) TextBox.BorderSizePixel = 0 TextBox.Position = UDim2.new(0.0234042555, 0, 0.797029674, 0) TextBox.Size = UDim2.new(0, 300, 0, 33) TextBox.Font = Enum.Font.SourceSansBold TextBox.PlaceholderText = "To chat, click here" TextBox.Text = "" TextBox.TextColor3 = Color3.new(1, 1, 1) TextBox.TextSize = 23 TextBox.TextWrapped = true TextBox.TextXAlignment = Enum.TextXAlignment.Left TextBox.ClearTextOnFocus = false Instance.new("UICorner", TextBox) local ToggleButton = Instance.new("ImageButton", ScreenGui) ToggleButton.Name = "ChatToggle" ToggleButton.BackgroundColor3 = Color3.fromRGB(0, 0, 0) ToggleButton.BackgroundTransparency = 0.5 ToggleButton.AnchorPoint = Vector2.new(1, 0) ToggleButton.Position = UDim2.new(1, -50, 0, 4) ToggleButton.Size = UDim2.new(0, 32, 0, 32) ToggleButton.Image = "rbxasset://textures/ui/Chat/Chat.png" Instance.new("UICorner", ToggleButton).CornerRadius = UDim.new(0, 8) local UnreadBadge = Instance.new("TextLabel", ScreenGui) UnreadBadge.Name = "UnreadBadge" UnreadBadge.BackgroundColor3 = Color3.fromRGB(255, 60, 60) UnreadBadge.AnchorPoint = Vector2.new(1, 0) UnreadBadge.Position = UDim2.new(1, -44, 0, 34) UnreadBadge.Size = UDim2.new(0, 18, 0, 14) UnreadBadge.Font = Enum.Font.GothamBold UnreadBadge.Text = "" UnreadBadge.TextColor3 = Color3.fromRGB(255, 255, 255) UnreadBadge.TextSize = 9 UnreadBadge.TextXAlignment = Enum.TextXAlignment.Center UnreadBadge.Visible = false Instance.new("UICorner", UnreadBadge).CornerRadius = UDim.new(0, 4) local OnlineButton = Instance.new("TextButton", ScreenGui) OnlineButton.Name = "OnlineToggle" OnlineButton.BackgroundColor3 = Color3.fromRGB(0, 180, 100) OnlineButton.BackgroundTransparency = 0.4 OnlineButton.AnchorPoint = Vector2.new(1, 0) OnlineButton.Position = UDim2.new(1, -88, 0, 4) OnlineButton.Size = UDim2.new(0, 32, 0, 32) OnlineButton.Text = "👥" OnlineButton.TextSize = 18 Instance.new("UICorner", OnlineButton).CornerRadius = UDim.new(0, 8) local OnlinePanel = Instance.new("Frame", ScreenGui) OnlinePanel.BackgroundColor3 = Color3.fromRGB(0, 0, 0) OnlinePanel.BackgroundTransparency = 0.5 OnlinePanel.AnchorPoint = Vector2.new(0.5, 0.5) OnlinePanel.Position = UDim2.new(0.5, 0, 0.5, 0) OnlinePanel.Size = UDim2.new(0, 360, 0, 180) OnlinePanel.Visible = false Instance.new("UICorner", OnlinePanel) local OnlineTitle = Instance.new("TextLabel", OnlinePanel) OnlineTitle.BackgroundTransparency = 1 OnlineTitle.Position = UDim2.new(0, 8, 0, 4) OnlineTitle.Size = UDim2.new(1, -16, 0, 20) OnlineTitle.Font = Enum.Font.GothamBold OnlineTitle.Text = "🟢 Online Players" OnlineTitle.TextColor3 = Color3.fromRGB(100, 255, 150) OnlineTitle.TextSize = 14 OnlineTitle.TextXAlignment = Enum.TextXAlignment.Left local OnlineScroll = Instance.new("ScrollingFrame", OnlinePanel) OnlineScroll.BackgroundTransparency = 1 OnlineScroll.Position = UDim2.new(0, 8, 0, 28) OnlineScroll.Size = UDim2.new(1, -16, 1, -36) OnlineScroll.AutomaticCanvasSize = Enum.AutomaticSize.Y OnlineScroll.CanvasSize = UDim2.new(0, 0, 0, 0) OnlineScroll.ScrollBarThickness = 2 local OnlineLayout = Instance.new("UIListLayout", OnlineScroll) OnlineLayout.Padding = UDim.new(0, 4) -- 5. UNREAD LOGIC local unreadCount = 0 local function AddUnread() if Container.Visible then return end unreadCount += 1 UnreadBadge.Text = unreadCount > 99 and "99+" or tostring(unreadCount) UnreadBadge.Visible = true end local function ClearUnread() unreadCount = 0 UnreadBadge.Text = "" UnreadBadge.Visible = false end -- 6. CORE CHAT LOGIC local function trimMessages() local labels = {} for _, c in ipairs(ChatScroll:GetChildren()) do if c:IsA("TextLabel") then table.insert(labels, c) end end table.sort(labels, function(a, b) return a.AbsolutePosition.Y < b.AbsolutePosition.Y end) if #labels > MAX_MESSAGES then for i = 1, #labels - MAX_MESSAGES do labels[i]:Destroy() end end end local function CreateMessage(sender, text, isSystem) local TextLabel = Instance.new("TextLabel", ChatScroll) TextLabel.BackgroundTransparency = 1 TextLabel.Size = UDim2.new(1, 0, 0, 0) TextLabel.Font = Enum.Font.SourceSansBold TextLabel.TextSize = 16 TextLabel.RichText = true TextLabel.TextWrapped = true TextLabel.AutomaticSize = Enum.AutomaticSize.Y TextLabel.TextXAlignment = Enum.TextXAlignment.Left local nameColor = isSystem and "rgb(255, 80, 80)" or "rgb(150, 200, 255)" TextLabel.Text = string.format( '%s: %s', nameColor, sanitize(sender), sanitize(text) ) trimMessages() task.defer(function() ChatScroll.CanvasPosition = Vector2.new(0, ChatScroll.AbsoluteCanvasSize.Y) end) if not isSystem then AddUnread() end end local function CreateAnnouncement(sender, text) local toast = Instance.new("Frame", ScreenGui) toast.BackgroundColor3 = Color3.fromRGB(130, 130, 130) toast.AnchorPoint = Vector2.new(0.5, 0) toast.Position = UDim2.new(0.5, 0, 0, 10) toast.Size = UDim2.new(0, 320, 0, 80) Instance.new("UICorner", toast).CornerRadius = UDim.new(1, 0) local leftBulge = Instance.new("Frame", toast) leftBulge.BackgroundColor3 = Color3.fromRGB(130, 130, 130) leftBulge.BorderSizePixel = 0 leftBulge.AnchorPoint = Vector2.new(0.5, 0.5) leftBulge.Position = UDim2.new(0, 18, 0.5, 0) leftBulge.Size = UDim2.new(0, 72, 0, 72) Instance.new("UICorner", leftBulge).CornerRadius = UDim.new(1, 0) local rightBulge = Instance.new("Frame", toast) rightBulge.BackgroundColor3 = Color3.fromRGB(130, 130, 130) rightBulge.BorderSizePixel = 0 rightBulge.AnchorPoint = Vector2.new(0.5, 0.5) rightBulge.Position = UDim2.new(1, -18, 0.5, 0) rightBulge.Size = UDim2.new(0, 72, 0, 72) Instance.new("UICorner", rightBulge).CornerRadius = UDim.new(1, 0) local senderLabel = Instance.new("TextLabel", toast) senderLabel.BackgroundTransparency = 1 senderLabel.AnchorPoint = Vector2.new(0.5, 0.5) senderLabel.Position = UDim2.new(0.5, 0, 0.35, 0) senderLabel.Size = UDim2.new(0.8, 0, 0, 22) senderLabel.Font = Enum.Font.GothamMedium senderLabel.Text = sanitize(sender) senderLabel.TextColor3 = Color3.fromRGB(255, 255, 255) senderLabel.TextSize = 14 senderLabel.TextStrokeTransparency = 0 senderLabel.TextStrokeColor3 = Color3.fromRGB(0, 0, 0) senderLabel.TextXAlignment = Enum.TextXAlignment.Center local msgLabel = Instance.new("TextLabel", toast) msgLabel.BackgroundTransparency = 1 msgLabel.AnchorPoint = Vector2.new(0.5, 0.5) msgLabel.Position = UDim2.new(0.5, 0, 0.68, 0) msgLabel.Size = UDim2.new(0.8, 0, 0, 28) msgLabel.Font = Enum.Font.GothamBold msgLabel.Text = sanitize(text) msgLabel.TextColor3 = Color3.fromRGB(255, 255, 255) msgLabel.TextSize = 20 msgLabel.TextStrokeTransparency = 0 msgLabel.TextStrokeColor3 = Color3.fromRGB(0, 0, 0) msgLabel.TextXAlignment = Enum.TextXAlignment.Center msgLabel.TextWrapped = true local chatLabel = Instance.new("TextLabel", ChatScroll) chatLabel.BackgroundColor3 = Color3.fromRGB(255, 200, 0) chatLabel.BackgroundTransparency = 0.85 chatLabel.Size = UDim2.new(1, 0, 0, 0) chatLabel.Font = Enum.Font.SourceSansBold chatLabel.TextSize = 16 chatLabel.RichText = true chatLabel.TextWrapped = true chatLabel.AutomaticSize = Enum.AutomaticSize.Y chatLabel.TextXAlignment = Enum.TextXAlignment.Left Instance.new("UICorner", chatLabel).CornerRadius = UDim.new(0, 4) chatLabel.Text = string.format( '📢 %s: %s', sanitize(sender), sanitize(text) ) trimMessages() task.defer(function() ChatScroll.CanvasPosition = Vector2.new(0, ChatScroll.AbsoluteCanvasSize.Y) end) task.delay(6, function() if toast and toast.Parent then toast:Destroy() end end) end local function SendToFirebase(sender, message, msgId) safeRequest({ Url = FIREBASE_URL .. CHAT_PATH .. ".json", Method = "POST", Headers = {["Content-Type"] = "application/json"}, Body = HttpService:JSONEncode({ ["Sender"] = sender, ["Message"] = message, ["Time"] = os.time(), ["MsgId"] = msgId }) }) end local seenKeys = {} -- 7. ANNOUNCEMENT LOGIC local seenAnnouncements = {} local function SeedAnnouncements() local response = safeRequest({ Url = FIREBASE_URL .. ANNOUNCE_PATH .. ".json", Method = "GET" }) if response.Success and response.Body ~= "null" then local ok, data = pcall(HttpService.JSONDecode, HttpService, response.Body) if ok then for key, _ in pairs(data) do seenAnnouncements[key] = true end end end end local function CheckAnnouncement() local response = safeRequest({ Url = FIREBASE_URL .. ANNOUNCE_PATH .. ".json", Method = "GET" }) if not response.Success or response.Body == "null" then return end local ok, data = pcall(HttpService.JSONDecode, HttpService, response.Body) if not ok then return end local entries = {} for key, v in pairs(data) do if not seenAnnouncements[key] then table.insert(entries, {key = key, data = v}) end end table.sort(entries, function(a, b) return a.data.PostedAt < b.data.PostedAt end) for _, entry in ipairs(entries) do seenAnnouncements[entry.key] = true CreateAnnouncement(entry.data.PostedBy or "Owner", entry.data.Message or "") end end local function ShowBubble(senderName, text) local target = nil for _, p in ipairs(Players:GetPlayers()) do if p.DisplayName == senderName or p.Name == senderName then target = p break end end if not target or not target.Character then return end local head = target.Character:FindFirstChild("Head") if not head then return end local existing = head:FindFirstChild("BetterChatBubble") if existing then existing:Destroy() end local billboard = Instance.new("BillboardGui", head) billboard.Name = "BetterChatBubble" billboard.Size = UDim2.new(0, 200, 0, 50) billboard.StudsOffset = Vector3.new(0, 2.5, 0) billboard.AlwaysOnTop = false billboard.ResetOnSpawn = false local bg = Instance.new("Frame", billboard) bg.BackgroundColor3 = Color3.fromRGB(255, 255, 255) bg.BackgroundTransparency = 0.1 bg.Size = UDim2.new(1, 0, 1, 0) bg.BorderSizePixel = 0 Instance.new("UICorner", bg).CornerRadius = UDim.new(0, 8) local label = Instance.new("TextLabel", bg) label.BackgroundTransparency = 1 label.Size = UDim2.new(1, -10, 1, 0) label.Position = UDim2.new(0, 5, 0, 0) label.Font = Enum.Font.GothamMedium label.TextSize = 13 label.TextColor3 = Color3.fromRGB(0, 0, 0) label.TextWrapped = true label.Text = text label.TextXAlignment = Enum.TextXAlignment.Center billboard.Size = UDim2.new(0, 200, 0, math.clamp(label.TextBounds.Y + 16, 36, 80)) task.delay(5, function() if billboard and billboard.Parent then billboard:Destroy() end end) end -- FIX 2: StartPolling Optimized for Instant Chat local function StartPolling() task.spawn(function() while ScreenGui.Parent and task.wait(2.5) do local response = safeRequest({ Url = FIREBASE_URL .. CHAT_PATH .. ".json?orderBy=\"Time\"&limitToLast=10", Method = "GET" }) if response.Success and response.Body ~= "null" then local ok, data = pcall(HttpService.JSONDecode, HttpService, response.Body) if not ok then continue end local msgs = {} for key, v in pairs(data) do local uid = v.MsgId or key -- استخدام المعرف الفريد إن وجد if not seenKeys[uid] then table.insert(msgs, {uid = uid, data = v}) end end table.sort(msgs, function(a, b) return a.data.Time < b.data.Time end) for _, entry in ipairs(msgs) do seenKeys[entry.uid] = true CreateMessage(entry.data.Sender, entry.data.Message, false) ShowBubble(entry.data.Sender, entry.data.Message) end end end end) task.spawn(function() while ScreenGui.Parent and task.wait(5) do CheckAnnouncement() end end) end -- 8. ONLINE PLAYERS LOGIC local function ClearOnlineList() for _, c in ipairs(OnlineScroll:GetChildren()) do if not c:IsA("UIListLayout") then c:Destroy() end end end local function BuildPlayerRow(info) local row = Instance.new("Frame", OnlineScroll) row.BackgroundColor3 = Color3.fromRGB(255, 255, 255) row.BackgroundTransparency = 0.9 row.Size = UDim2.new(1, 0, 0, 32) Instance.new("UICorner", row) local nameLabel = Instance.new("TextLabel", row) nameLabel.BackgroundTransparency = 1 nameLabel.Position = UDim2.new(0, 8, 0, 0) nameLabel.Size = UDim2.new(1, -90, 1, 0) nameLabel.Font = Enum.Font.GothamMedium nameLabel.TextSize = 13 nameLabel.TextColor3 = Color3.fromRGB(220, 220, 220) nameLabel.TextXAlignment = Enum.TextXAlignment.Left nameLabel.TextTruncate = Enum.TextTruncate.AtEnd nameLabel.RichText = true local sameServer = info.JobId == MY_JOB_ID local tag = sameServer and " [Here]" or "" nameLabel.Text = sanitize(info.DisplayName) .. " (@" .. sanitize(info.Username) .. ")" .. tag if not sameServer then local joinBtn = Instance.new("TextButton", row) joinBtn.BackgroundColor3 = Color3.fromRGB(0, 180, 100) joinBtn.BackgroundTransparency = 0.3 joinBtn.Position = UDim2.new(1, -82, 0.5, -12) joinBtn.Size = UDim2.new(0, 74, 0, 24) joinBtn.Font = Enum.Font.GothamBold joinBtn.Text = "Join" joinBtn.TextColor3 = Color3.fromRGB(255, 255, 255) joinBtn.TextSize = 12 Instance.new("UICorner", joinBtn).CornerRadius = UDim.new(0, 6) joinBtn.MouseButton1Click:Connect(function() pcall(function() TeleportService:TeleportToPlaceInstance(info.PlaceId, info.JobId, LocalPlayer) end) end) end end local function RefreshOnlinePlayers() local response = safeRequest({ Url = FIREBASE_URL .. PRESENCE_PATH .. ".json", Method = "GET" }) ClearOnlineList() if not response.Success or response.Body == "null" then local empty = Instance.new("TextLabel", OnlineScroll) empty.BackgroundTransparency = 1 empty.Size = UDim2.new(1, 0, 0, 24) empty.Font = Enum.Font.Gotham empty.Text = "No players online." empty.TextColor3 = Color3.fromRGB(160, 160, 160) empty.TextSize = 13 return end local ok, data = pcall(HttpService.JSONDecode, HttpService, response.Body) if not ok then return end local now = os.time() for _, info in pairs(data) do if (now - (info.LastSeen or 0)) <= PRESENCE_TTL then BuildPlayerRow(info) end end end task.spawn(function() while ScreenGui.Parent and task.wait(15) do if OnlinePanel.Visible then RefreshOnlinePlayers() end end end) -- 9. COMMAND SYSTEM local flying = false local flyBody = nil local function StopFly() flying = false if flyBody then flyBody:Destroy() flyBody = nil end local char = LocalPlayer.Character if char then local hum = char:FindFirstChildOfClass("Humanoid") if hum then hum.PlatformStand = false end end end local function StartFly() local char = LocalPlayer.Character if not char then return end local hum = char:FindFirstChildOfClass("Humanoid") local root = char:FindFirstChild("HumanoidRootPart") if not hum or not root then return end flying = true hum.PlatformStand = true local bg = Instance.new("BodyGyro", root) bg.MaxTorque = Vector3.new(1e6, 1e6, 1e6) bg.P = 1e6 flyBody = bg local bv = Instance.new("BodyVelocity", root) bv.MaxForce = Vector3.new(1e6, 1e6, 1e6) bv.Velocity = Vector3.zero local cam = workspace.CurrentCamera local SPEED = 60 task.spawn(function() while flying and root.Parent do local cf = cam.CFrame local dir = Vector3.zero if UserInputService:IsKeyDown(Enum.KeyCode.W) then dir += cf.LookVector end if UserInputService:IsKeyDown(Enum.KeyCode.S) then dir -= cf.LookVector end if UserInputService:IsKeyDown(Enum.KeyCode.A) then dir -= cf.RightVector end if UserInputService:IsKeyDown(Enum.KeyCode.D) then dir += cf.RightVector end if UserInputService:IsKeyDown(Enum.KeyCode.Space) then dir += Vector3.new(0,1,0) end if UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) then dir -= Vector3.new(0,1,0) end bv.Velocity = dir.Magnitude > 0 and dir.Unit * SPEED or Vector3.zero bg.CFrame = cf task.wait() end bv:Destroy() end) end local function PlayDance() local char = LocalPlayer.Character if not char then return end local hum = char:FindFirstChildOfClass("Humanoid") if not hum then return end local animId = hum.RigType == Enum.HumanoidRigType.R15 and "rbxassetid://507771019" or "rbxassetid://182435998" local anim = Instance.new("Animation") anim.AnimationId = animId local track = hum:LoadAnimation(anim) track:Play() CreateMessage("System", "Dancing!", true) end local HELP_TEXT = { "━━━ BetterChat Commands ━━━", "/help — shows this list", "/clear — clears the chat window", "/e dance — makes your character dance", "/console — opens the developer console", "/fly — toggle flying (WASD + Space/Shift)", "/speed [n] — sets your walk speed", "/jump [n] — sets your jump power", "/leave — exits the game", "━━━━━━━━━━━━━━━━━━━━━━━━━━", } local function HandleCommand(input) local args = {} for word in input:gmatch("%S+") do table.insert(args, word) end local cmd = args[1] and args[1]:lower() if cmd == "/help" then for _, line in ipairs(HELP_TEXT) do CreateMessage("System", line, true) end elseif cmd == "/clear" then for _, c in ipairs(ChatScroll:GetChildren()) do if c:IsA("TextLabel") then c:Destroy() end end CreateMessage("System", "Chat cleared.", true) elseif cmd == "/e" and args[2] and args[2]:lower() == "dance" then PlayDance() elseif cmd == "/console" then StarterGui:SetCore("DevConsoleVisible", true) elseif cmd == "/fly" then if flying then StopFly() CreateMessage("System", "Fly disabled.", true) else StartFly() CreateMessage("System", "Fly enabled. WASD to move, Space/Shift for up/down.", true) end elseif cmd == "/speed" then local val = tonumber(args[2]) if not val then return true end local char = LocalPlayer.Character local hum = char and char:FindFirstChildOfClass("Humanoid") if hum then hum.WalkSpeed = val CreateMessage("System", "Walk speed set to " .. val, true) end elseif cmd == "/jump" then local val = tonumber(args[2]) if not val then return true end local char = LocalPlayer.Character local hum = char and char:FindFirstChildOfClass("Humanoid") if hum then hum.JumpPower = val hum.JumpHeight = val CreateMessage("System", "Jump power set to " .. val, true) end elseif cmd == "/leave" then TeleportService:Teleport(game.PlaceId) else return false end return true end -- 10. FIRST RUN TUTORIAL local function ShowTutorial() local overlay = Instance.new("Frame", ScreenGui) overlay.BackgroundColor3 = Color3.fromRGB(0, 0, 0) overlay.BackgroundTransparency = 0.4 overlay.Size = UDim2.new(1, 0, 1, 0) overlay.ZIndex = 10 local card = Instance.new("Frame", overlay) card.BackgroundColor3 = Color3.fromRGB(15, 15, 15) card.BackgroundTransparency = 0.1 card.AnchorPoint = Vector2.new(0.5, 0.5) card.Position = UDim2.new(0.5, 0, 0.5, 0) card.Size = UDim2.new(0, 340, 0, 220) card.ZIndex = 11 Instance.new("UICorner", card).CornerRadius = UDim.new(0, 12) local titleLabel = Instance.new("TextLabel", card) titleLabel.BackgroundTransparency = 1 titleLabel.Position = UDim2.new(0, 0, 0, 16) titleLabel.Size = UDim2.new(1, 0, 0, 28) titleLabel.Font = Enum.Font.GothamBold titleLabel.TextColor3 = Color3.fromRGB(100, 200, 255) titleLabel.TextSize = 18 titleLabel.TextXAlignment = Enum.TextXAlignment.Center titleLabel.ZIndex = 12 local bodyLabel = Instance.new("TextLabel", card) bodyLabel.BackgroundTransparency = 1 bodyLabel.Position = UDim2.new(0, 24, 0, 58) bodyLabel.Size = UDim2.new(1, -48, 0, 110) bodyLabel.Font = Enum.Font.GothamMedium bodyLabel.TextColor3 = Color3.fromRGB(220, 220, 220) bodyLabel.TextSize = 14 bodyLabel.TextWrapped = true bodyLabel.TextXAlignment = Enum.TextXAlignment.Center bodyLabel.TextYAlignment = Enum.TextYAlignment.Top bodyLabel.ZIndex = 12 local pageLabel = Instance.new("TextLabel", card) pageLabel.BackgroundTransparency = 1 pageLabel.Position = UDim2.new(0, 16, 1, -36) pageLabel.Size = UDim2.new(0.4, 0, 0, 30) pageLabel.Font = Enum.Font.Gotham pageLabel.TextColor3 = Color3.fromRGB(120, 120, 120) pageLabel.TextSize = 12 pageLabel.TextXAlignment = Enum.TextXAlignment.Left pageLabel.ZIndex = 12 local nextBtn = Instance.new("TextButton", card) nextBtn.BackgroundColor3 = Color3.fromRGB(0, 150, 255) nextBtn.AnchorPoint = Vector2.new(1, 1) nextBtn.Position = UDim2.new(1, -16, 1, -16) nextBtn.Size = UDim2.new(0, 100, 0, 30) nextBtn.Font = Enum.Font.GothamBold nextBtn.TextColor3 = Color3.fromRGB(255, 255, 255) nextBtn.TextSize = 14 nextBtn.ZIndex = 12 Instance.new("UICorner", nextBtn).CornerRadius = UDim.new(0, 8) local pages = { { title = "👋 Welcome to BetterChat", body = "BetterChat replaces Roblox's default chat with a global cross-server chat. You can talk to anyone running the script, no matter what server they're in." }, { title = "💬 How to Chat", body = "Type your message in the box at the bottom of the chat window and press Enter to send.\n\nPress / anywhere in-game to quickly focus the chat box." }, { title = "👁 Toggling the Chat", body = "Click the chat icon in the top-right to show or hide the chat window.\n\nA red badge will appear showing how many new messages you've missed while it's hidden." }, { title = "👥 Joining Other Players", body = "Click the green 👥 button to open the Online Players panel.\n\nAnyone else running BetterChat appears here. Press Join to teleport directly into their server." }, { title = "⌨️ Commands", body = "Type /help in chat to see all available commands.\n\nCommands include /fly, /speed, /jump, /clear, /leave, /e dance, and more." }, { title = "✅ You're all set!", body = "BetterChat is now active. This tutorial won't show again.\n\nEnjoy chatting!" } } local currentPage = 1 local function ShowPage(n) local p = pages[n] titleLabel.Text = p.title bodyLabel.Text = p.body pageLabel.Text = n .. " / " .. #pages nextBtn.Text = n == #pages and "Done ✓" or "Next →" end ShowPage(1) nextBtn.MouseButton1Click:Connect(function() if currentPage < #pages then currentPage += 1 ShowPage(currentPage) else overlay:Destroy() end end) end -- 11. CONNECTIONS ToggleButton.MouseButton1Click:Connect(function() Container.Visible = not Container.Visible if Container.Visible then ClearUnread() end end) OnlineButton.MouseButton1Click:Connect(function() OnlinePanel.Visible = not OnlinePanel.Visible if OnlinePanel.Visible then RefreshOnlinePlayers() end end) TextBox.FocusLost:Connect(function(enter) if enter and TextBox.Text ~= "" then local input = TextBox.Text TextBox.Text = "" if not HandleCommand(input) then local dName = LocalPlayer.DisplayName local msgId = HttpService:GenerateGUID(false) seenKeys[msgId] = true -- إظهار الرسالة للمستخدم في اللحظة نفسها (Optimistic UI) CreateMessage(dName, input, false) ShowBubble(dName, input) -- رفع الرسالة للسيرفر بالخلفية task.spawn(function() SendToFirebase(dName, input, msgId) end) end end end) UserInputService.InputBegan:Connect(function(i, g) if i.KeyCode == Enum.KeyCode.Slash and not g then if not Container.Visible then Container.Visible = true end task.wait() TextBox:CaptureFocus() end end) SeedAnnouncements() StartPolling() CreateMessage("System", "BetterChat V" .. CURRENT_VERSION_STRING .. " loaded. Type /help for commands.", true) if isFirstRun then ShowTutorial() end game:GetService("Players").PlayerAdded:Connect(function(player) if typeof(AddMessage) == "function" then AddMessage("System", player.Name .. " joined the chat", Color3.fromRGB(255, 255, 255)) end end)