Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- local UserInputService = game:GetService("UserInputService")
- local RunService = game:GetService("RunService")
- local HttpService = game:GetService("HttpService")
- local CoreGui = game:GetService("CoreGui")
- local Players = game:GetService("Players")
- local Workspace = game:GetService("Workspace")
- local LocalPlayer = Players.LocalPlayer
- local Mouse = LocalPlayer:GetMouse() -- Mouse is still available for Target property
- local Keybinds = {
- AddSavestate = "BtnAddSavestate",
- RemoveSavestate = "BtnRemoveSavestate",
- BackSavestate = "BtnBackSavestate",
- GoFrameBack = "BtnGoFrameBack",
- GoFrameForward = "BtnGoFrameForward",
- SaveRun = "BtnSaveRun",
- UserPause = "BtnUserPause",
- CollisionToggler = "BtnCollisionToggler",
- ResetToNormal = "BtnResetToNormal",
- ViewTAS = "BtnViewTAS"
- }
- local Savestates = {}
- local PlayerInfo = {}
- local TimePaused = 0
- local Pause = true
- local TimePauseHolder
- local TimeStart
- local FrameCountLabel
- local SavestatesCountLabel
- local TimeTextLabel
- local CapLockPauseLabel -- This will be repurposed for a status label
- local KeyBindFrame
- local HUD
- local MainConnectionLoop
- local KeybindsConnect
- local InputEndConnect -- Less relevant for mobile buttons, but kept for potential future use
- local DiedConnect
- local MobileButtonFrame
- local CurrentAnimationState = { Name = "Idle", Weight = 0 }
- local function getCurrentCFrame()
- if LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
- return LocalPlayer.Character.HumanoidRootPart.CFrame
- end
- return CFrame.new()
- end
- local function getCurrentVelocity()
- if LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
- return LocalPlayer.Character.HumanoidRootPart.Velocity
- end
- return Vector3.new()
- end
- local function getCurrentCameraCFrame()
- return Workspace.CurrentCamera.CFrame
- end
- local function ReturnPlayerInfo()
- return {
- CFrame = getCurrentCFrame(),
- CameraCFrame = getCurrentCameraCFrame(),
- Velocity = getCurrentVelocity(),
- Animation = CurrentAnimationState,
- Time = tick() - TimeStart - TimePaused,
- }
- end
- local function UpdateGUIText()
- if SavestatesCountLabel then
- SavestatesCountLabel.Text = "Savestates: " .. #Savestates
- end
- if FrameCountLabel then
- FrameCountLabel.Text = "Frames: " .. #PlayerInfo
- end
- end
- local function FormatTime(TimeValue)
- local m = math.floor(TimeValue / 60)
- local s = math.floor(TimeValue % 60)
- local ms = math.floor((TimeValue * 1000) % 1000)
- local msStr = tostring(ms)
- local sStr = tostring(s)
- while #msStr < 3 do msStr = '0' .. msStr end
- while #sStr < 2 do sStr = '0' .. sStr end
- return m .. ":" .. sStr .. "." .. msStr
- end
- local function UpdateTimeGUI()
- if TimeTextLabel then
- local TimePlayed = tick() - TimeStart - TimePaused
- TimeTextLabel.Text = FormatTime(TimePlayed)
- end
- end
- local function SetCharacterState(InfoState)
- if not LocalPlayer.Character or not LocalPlayer.Character:FindFirstChild("HumanoidRootPart") or not LocalPlayer.Character:FindFirstChild("Humanoid") then return end
- local Hum = LocalPlayer.Character.Humanoid
- local RootPart = LocalPlayer.Character.HumanoidRootPart
- RootPart.CFrame = InfoState.CFrame
- RootPart.Velocity = InfoState.Velocity
- Workspace.CurrentCamera.CFrame = InfoState.CameraCFrame
- CurrentAnimationState = InfoState.Animation or { Name = "Idle", Weight = 0 }
- if CurrentAnimationState.Name and Hum then
- local Animator = Hum:FindFirstChildOfClass("Animator")
- if Animator then
- local PlayingTracks = Animator:GetPlayingAnimationTracks()
- for _, track in ipairs(PlayingTracks) do
- if track.Name == CurrentAnimationState.Name then
- track:AdjustSpeed(CurrentAnimationState.Weight or 1)
- -- Note: Setting TimePosition directly might not always look smooth,
- -- but it's the closest we can get to syncing animation state.
- -- More advanced animation handling might be needed for complex animations.
- -- track.TimePosition = 0 -- Consider if you always want to reset animation
- break
- end
- end
- end
- end
- end
- local function SetUpGui()
- if HUD then HUD:Destroy() end
- HUD = Instance.new("ScreenGui", CoreGui)
- HUD.Name = "TASRecorderGUI"
- HUD.ResetOnSpawn = false
- HUD.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
- local MainFrame = Instance.new("Frame", HUD)
- MainFrame.AnchorPoint = Vector2.new(0.5, 1)
- MainFrame.Position = UDim2.new(0.5, 0, 1, -10)
- MainFrame.Size = UDim2.new(0.4, 0, 0, 100) -- Adjusted size for mobile
- MainFrame.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
- MainFrame.BackgroundTransparency = 0.3
- MainFrame.BorderColor3 = Color3.fromRGB(200, 200, 200)
- MainFrame.BorderSizePixel = 1
- local UIListLayout = Instance.new("UIListLayout", MainFrame)
- UIListLayout.Padding = UDim.new(0, 5)
- UIListLayout.SortOrder = Enum.SortOrder.LayoutOrder
- UIListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
- TimeTextLabel = Instance.new("TextLabel", MainFrame)
- TimeTextLabel.Name = "TimeText"
- TimeTextLabel.LayoutOrder = 1
- TimeTextLabel.Size = UDim2.new(1, -10, 0, 20)
- TimeTextLabel.Position = UDim2.new(0, 5, 0, 5)
- TimeTextLabel.BackgroundTransparency = 1
- TimeTextLabel.Font = Enum.Font.SourceSansBold
- TimeTextLabel.Text = "0:00.000"
- TimeTextLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
- TimeTextLabel.TextScaled = true
- TimeTextLabel.TextXAlignment = Enum.TextXAlignment.Left
- SavestatesCountLabel = Instance.new("TextLabel", MainFrame)
- SavestatesCountLabel.Name = "SavestatesCount"
- SavestatesCountLabel.LayoutOrder = 2
- SavestatesCountLabel.Size = UDim2.new(1, -10, 0, 15)
- SavestatesCountLabel.BackgroundTransparency = 1
- SavestatesCountLabel.Font = Enum.Font.SourceSans
- SavestatesCountLabel.Text = "Savestates: 0"
- SavestatesCountLabel.TextColor3 = Color3.fromRGB(220, 220, 220)
- SavestatesCountLabel.TextScaled = true
- SavestatesCountLabel.TextXAlignment = Enum.TextXAlignment.Left
- FrameCountLabel = Instance.new("TextLabel", MainFrame)
- FrameCountLabel.Name = "FrameCount"
- FrameCountLabel.LayoutOrder = 3
- FrameCountLabel.Size = UDim2.new(1, -10, 0, 15)
- FrameCountLabel.BackgroundTransparency = 1
- FrameCountLabel.Font = Enum.Font.SourceSans
- FrameCountLabel.Text = "Frames: 0"
- FrameCountLabel.TextColor3 = Color3.fromRGB(220, 220, 220)
- FrameCountLabel.TextScaled = true
- FrameCountLabel.TextXAlignment = Enum.TextXAlignment.Left
- CapLockPauseLabel = Instance.new("TextLabel", MainFrame) -- Repurposed for status
- CapLockPauseLabel.Name = "StatusLabel"
- CapLockPauseLabel.LayoutOrder = 4
- CapLockPauseLabel.Size = UDim2.new(1, -10, 0, 15)
- CapLockPauseLabel.BackgroundTransparency = 1
- CapLockPauseLabel.Font = Enum.Font.SourceSansBold
- CapLockPauseLabel.Text = "Status: Paused"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
- CapLockPauseLabel.TextScaled = true
- CapLockPauseLabel.TextXAlignment = Enum.TextXAlignment.Left
- -- Mobile Buttons Frame
- MobileButtonFrame = Instance.new("Frame", HUD)
- MobileButtonFrame.Name = "MobileButtonFrame"
- MobileButtonFrame.AnchorPoint = Vector2.new(0, 0.5)
- MobileButtonFrame.Position = UDim2.new(0, 10, 0.5, 0)
- MobileButtonFrame.Size = UDim2.new(0, 80, 0.6, 0) -- Adjusted size and position
- MobileButtonFrame.BackgroundColor3 = Color3.fromRGB(30, 30, 30)
- MobileButtonFrame.BackgroundTransparency = 0.5
- MobileButtonFrame.BorderColor3 = Color3.fromRGB(200, 200, 200)
- MobileButtonFrame.BorderSizePixel = 1
- local ButtonListLayout = Instance.new("UIListLayout", MobileButtonFrame)
- ButtonListLayout.Padding = UDim.new(0, 5)
- ButtonListLayout.SortOrder = Enum.SortOrder.LayoutOrder
- ButtonListLayout.FillDirection = Enum.FillDirection.Vertical
- ButtonListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
- local function CreateMobileButton(name, text, layoutOrder)
- local Button = Instance.new("TextButton", MobileButtonFrame)
- Button.Name = name
- Button.LayoutOrder = layoutOrder
- Button.Size = UDim2.new(1, -10, 0, 25) -- Adjusted button size
- Button.BackgroundColor3 = Color3.fromRGB(60, 60, 60)
- Button.TextColor3 = Color3.fromRGB(255, 255, 255)
- Button.Text = text
- Button.Font = Enum.Font.SourceSansBold
- Button.TextScaled = true
- Button.BorderSizePixel = 0
- return Button
- end
- local BtnUserPause = CreateMobileButton(Keybinds.UserPause, "Pause", 1)
- local BtnAddSavestate = CreateMobileButton(Keybinds.AddSavestate, "Add SS", 2)
- local BtnBackSavestate = CreateMobileButton(Keybinds.BackSavestate, "Back SS", 3)
- local BtnGoFrameBack = CreateMobileButton(Keybinds.GoFrameBack, "<< Frame", 4)
- local BtnGoFrameForward = CreateMobileButton(Keybinds.GoFrameForward, "Frame >>", 5)
- local BtnSaveRun = CreateMobileButton(Keybinds.SaveRun, "Save Run", 6)
- local BtnCollisionToggler = CreateMobileButton(Keybinds.CollisionToggler, "Toggle Collide", 7)
- local BtnViewTAS = CreateMobileButton(Keybinds.ViewTAS, "View TAS", 8)
- local BtnResetToNormal = CreateMobileButton(Keybinds.ResetToNormal, "Stop", 9)
- -- Connect button events
- BtnUserPause.MouseButton1Click:Connect(UserPauseToggle)
- BtnAddSavestate.MouseButton1Click:Connect(AddSavestate)
- BtnBackSavestate.MouseButton1Click:Connect(BackSavestate)
- BtnSaveRun.MouseButton1Click:Connect(SaveRun)
- BtnCollisionToggler.MouseButton1Click:Connect(CollisionToggler)
- BtnViewTAS.MouseButton1Click:Connect(function()
- if not ViewingTAS then
- local CurrentTASData = PrepareTasData()
- ViewTASPlayback(CurrentTASData)
- end
- end)
- BtnResetToNormal.MouseButton1Click:Connect(DisconnectAll)
- -- Frame forward/back can use InputBegan/InputEnded for hold functionality
- BtnGoFrameForward.InputBegan:Connect(function(input)
- if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
- task.spawn(FrameForwardStart)
- end
- end)
- BtnGoFrameForward.InputEnded:Connect(function(input)
- if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
- isFrameForwardHeld = false
- end
- end)
- BtnGoFrameBack.InputBegan:Connect(function(input)
- if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
- task.spawn(FrameBackStart)
- end
- end)
- BtnGoFrameBack.InputEnded:Connect(function(input)
- if input.UserInputType == Enum.UserInputType.Touch or input.UserInputType == Enum.UserInputType.MouseButton1 then
- isFrameBackHeld = false
- end
- end)
- -- Keybinds frame is likely not needed for mobile, but keeping the structure
- -- in case you want to adapt it later or keep it for PC.
- KeyBindFrame = Instance.new("Frame", HUD)
- KeyBindFrame.Name = "KeyBindFrame"
- KeyBindFrame.AnchorPoint = Vector2.new(0.5, 1)
- KeyBindFrame.Position = UDim2.new(0.5, 0, 1, -120)
- KeyBindFrame.Size = UDim2.new(0, 250, 0, 200)
- KeyBindFrame.BackgroundColor3 = Color3.fromRGB(40, 40, 40)
- KeyBindFrame.BackgroundTransparency = 0.2
- KeyBindFrame.BorderColor3 = Color3.fromRGB(200, 200, 200)
- KeyBindFrame.BorderSizePixel = 1
- KeyBindFrame.Visible = false -- Hidden by default on mobile
- KeyBindFrame.ClipsDescendants = true
- local KeybindListLayout = Instance.new("UIListLayout", KeyBindFrame)
- KeybindListLayout.Padding = UDim.new(0, 2)
- KeybindListLayout.HorizontalAlignment = Enum.HorizontalAlignment.Center
- KeybindListLayout.SortOrder = Enum.SortOrder.LayoutOrder
- local function CreateKeybindLabel(Key, Action)
- local Label = Instance.new("TextLabel", KeyBindFrame)
- Label.Name = Action .. "Label"
- Label.Size = UDim2.new(1, -10, 0, 15)
- Label.Position = UDim2.new(0, 5, 0, 0)
- Label.BackgroundTransparency = 1
- Label.Font = Enum.Font.SourceSans
- Label.Text = Key .. " : " .. Action
- Label.TextColor3 = Color3.fromRGB(230, 230, 230)
- Label.TextScaled = true
- Label.TextXAlignment = Enum.TextXAlignment.Left
- return Label
- end
- -- These labels are for the hidden KeyBindFrame (PC view)
- CreateKeybindLabel("CapsLock", "Pause/Unpause")
- CreateKeybindLabel("One", "Add Savestate")
- CreateKeybindLabel("Two", "Remove Savestate")
- CreateKeybindLabel("Eight", "Go To Last Savestate")
- CreateKeybindLabel("Four", "Go Frame Back")
- CreateKeybindLabel("Five", "Go Frame Forward")
- CreateKeybindLabel("Six", "Save Run")
- CreateKeybindLabel("C", "Toggle Collision")
- CreateKeybindLabel("Zero", "View TAS")
- CreateKeybindLabel("Delete", "Stop Recording")
- -- A button to show/hide the keybinds frame (optional for mobile)
- local KeybindsButton = Instance.new("TextButton", MainFrame)
- KeybindsButton.Name = "KeybindsButton"
- KeybindsButton.LayoutOrder = 5
- KeybindsButton.Size = UDim2.new(1, -10, 0, 20)
- KeybindsButton.BackgroundColor3 = Color3.fromRGB(80, 80, 80)
- KeybindsButton.TextColor3 = Color3.fromRGB(255, 255, 255)
- KeybindsButton.Text = "Show Keybinds (PC)"
- KeybindsButton.Font = Enum.Font.SourceSansBold
- KeybindsButton.TextScaled = true
- KeybindsButton.Visible = false -- Hide by default on mobile
- -- Check if the device is mobile and adjust visibility
- if UserInputService.TouchEnabled and not UserInputService.KeyboardEnabled and not UserInputService.GamepadEnabled then
- KeybindsButton.Visible = false
- KeyBindFrame.Visible = false
- MobileButtonFrame.Visible = true
- else
- KeybindsButton.Visible = true
- MobileButtonFrame.Visible = false
- KeybindsButton.MouseButton1Click:Connect(function()
- KeyBindFrame.Visible = not KeyBindFrame.Visible
- if KeyBindFrame.Visible then
- KeybindsButton.Text = "Hide Keybinds (PC)"
- else
- KeybindsButton.Text = "Show Keybinds (PC)"
- end
- end)
- end
- end
- local function UserPauseToggle()
- Pause = not Pause
- if LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
- LocalPlayer.Character.HumanoidRootPart.Anchored = Pause
- end
- if Pause then
- TimePauseHolder = tick()
- if TimeTextLabel then TimeTextLabel.TextColor3 = Color3.fromRGB(255, 255, 0) end
- if CapLockPauseLabel then
- CapLockPauseLabel.Text = "Status: Paused"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
- end
- else
- if TimePauseHolder then
- TimePaused = TimePaused + (tick() - TimePauseHolder)
- TimePauseHolder = nil
- end
- if TimeTextLabel then TimeTextLabel.TextColor3 = Color3.fromRGB(255, 255, 255) end
- if CapLockPauseLabel then
- CapLockPauseLabel.Text = "Status: Recording"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 255)
- end
- end
- end
- local function AddSavestate()
- -- Save the current PlayerInfo as a new savestate
- if #PlayerInfo > 0 then
- table.insert(Savestates, PlayerInfo)
- PlayerInfo = {} -- Start a new frame sequence for the next savestate
- UpdateGUIText()
- print("Savestate added. Total savestates: " .. #Savestates)
- else
- print("No frame data to save as a savestate.")
- end
- end
- local function RemoveSavestate()
- if #Savestates > 0 then
- -- Remove the last savestate
- local lastSavestate = table.remove(Savestates)
- print("Last savestate removed. Total savestates: " .. #Savestates)
- -- Attempt to restore state from the new last savestate if one exists
- if #Savestates > 0 then
- local lastFrameInLastSavestate = Savestates[#Savestates][#Savestates[#Savestates]]
- PlayerInfo = {} -- Clear current frames
- Pause = true
- TimePauseHolder = tick()
- TimeStart = tick() - lastFrameInLastSavestate.Time
- TimePaused = 0
- SetCharacterState(lastFrameInLastSavestate)
- UpdateTimeGUI()
- if CapLockPauseLabel then
- CapLockPauseLabel.Text = "Status: Paused (Restored SS)"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
- end
- else
- -- If no savestates left, reset entirely
- PlayerInfo = {}
- TimeStart = tick()
- TimePaused = 0
- if LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
- LocalPlayer.Character.HumanoidRootPart.Anchored = true
- end
- Pause = true
- TimePauseHolder = tick()
- if CapLockPauseLabel then
- CapLockPauseLabel.Text = "Status: Paused (Reset)"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
- end
- end
- UpdateGUIText()
- else
- print("No savestates to remove.")
- end
- end
- local function BackSavestate()
- if #Savestates > 0 and Savestates[#Savestates] and #Savestates[#Savestates] > 0 then
- local InfoState = Savestates[#Savestates][#Savestates[#Savestates]]
- PlayerInfo = {} -- Clear current frames
- Pause = true
- if LocalPlayer.Character and LocalPlayer.Character:FindFirstChild("HumanoidRootPart") then
- LocalPlayer.Character.HumanoidRootPart.Anchored = true
- end
- TimePauseHolder = tick()
- TimeStart = tick() - InfoState.Time
- TimePaused = 0
- SetCharacterState(InfoState)
- if TimeTextLabel then TimeTextLabel.TextColor3 = Color3.fromRGB(255, 255, 0) end
- if CapLockPauseLabel then
- CapLockPauseLabel.Text = "Status: Paused (Go Back SS)"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
- end
- UpdateTimeGUI()
- UpdateGUIText()
- print("Returned to last savestate.")
- else
- print("No savestates to go back to.")
- end
- end
- local function GoFrameForward()
- if Pause then
- -- To step forward one frame, we need to temporarily unpause,
- -- wait for one physics frame, then re-pause.
- UserPauseToggle() -- Unpause
- RunService.Heartbeat:Wait() -- Wait for one frame
- UserPauseToggle() -- Repause
- print("Stepped forward one frame.")
- else
- print("Cannot step frame forward while unpaused.")
- end
- end
- local isFrameForwardHeld = false
- local FrameForwardTask = nil
- local function FrameForwardStart()
- if not Pause then
- print("Cannot step frame forward while unpaused.")
- return
- end
- if isFrameForwardHeld then return end -- Prevent multiple tasks
- isFrameForwardHeld = true
- print("Starting frame forward hold...")
- FrameForwardTask = task.spawn(function()
- GoFrameForward() -- Step forward one frame immediately
- while task.wait(0.05) and isFrameForwardHeld do -- Wait and check if still held
- GoFrameForward()
- end
- print("Frame forward hold ended.")
- end)
- end
- local isFrameBackHeld = false
- local FrameBackTask = nil
- local function GoFrameBack()
- if LocalPlayer.Character then
- local TargetFrameInfo = nil
- local frameSource = nil -- To track where the frame came from (PlayerInfo or Savestates)
- if #PlayerInfo > 0 then
- -- If there are frames in the current sequence, go back within them
- TargetFrameInfo = table.remove(PlayerInfo)
- frameSource = "PlayerInfo"
- if #PlayerInfo > 0 then
- TargetFrameInfo = PlayerInfo[#PlayerInfo]
- elseif #Savestates > 0 and #Savestates[#Savestates] > 0 then
- -- If PlayerInfo is now empty, go to the last frame of the last savestate
- TargetFrameInfo = Savestates[#Savestates][#Savestates[#Savestates]]
- frameSource = "Savestates"
- end
- elseif #Savestates > 0 and #Savestates[#Savestates] > 0 then
- -- If PlayerInfo is empty, go back within the last savestate
- table.remove(Savestates[#Savestates]) -- Remove the last frame from the last savestate
- if #Savestates[#Savestates] > 0 then
- TargetFrameInfo = Savestates[#Savestates][#Savestates[#Savestates]]
- frameSource = "Savestates"
- elseif #Savestates > 1 then
- -- If the last savestate is now empty, remove it and go to the last frame of the new last savestate
- table.remove(Savestates)
- if #Savestates > 0 and #Savestates[#Savestates] > 0 then
- TargetFrameInfo = Savestates[#Savestates][#Savestates[#Savestates]]
- frameSource = "Savestates"
- end
- end
- end
- if TargetFrameInfo then
- if not Pause then UserPauseToggle() end -- Ensure paused before setting state
- TimePauseHolder = tick()
- TimeStart = tick() - TargetFrameInfo.Time
- TimePaused = 0
- SetCharacterState(TargetFrameInfo)
- UpdateTimeGUI()
- UpdateGUIText()
- if CapLockPauseLabel then
- CapLockPauseLabel.Text = "Status: Paused (Go Frame Back)"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
- end
- print("Stepped back one frame. Source: " .. (frameSource or "N/A"))
- else
- print("Cannot step frame back. No previous frames or savestates.")
- end
- end
- end
- local function FrameBackStart()
- if not Pause then
- print("Cannot step frame back while unpaused.")
- return
- end
- if isFrameBackHeld then return end -- Prevent multiple tasks
- isFrameBackHeld = true
- print("Starting frame back hold...")
- FrameBackTask = task.spawn(function()
- GoFrameBack() -- Step back one frame immediately
- while task.wait(0.05) and isFrameBackHeld do -- Wait and check if still held
- GoFrameBack()
- end
- print("Frame back hold ended.")
- end)
- end
- local function CollisionToggler()
- local Target = Mouse.Target -- Mouse.Target still works for finding what the camera is looking at
- if Target and Target:IsA("BasePart") then
- Target.CanCollide = not Target.CanCollide
- Target.Transparency = Target.CanCollide and 0 or 0.7
- print("Toggled collision for: " .. Target.Name .. " (CanCollide: " .. tostring(Target.CanCollide) .. ")")
- else
- print("No valid BasePart found to toggle collision.")
- end
- end
- local function PrepareTasData()
- local FullTAS = {}
- -- Add frames from existing savestates
- for i = 1, #Savestates do
- for j = 1, #Savestates[i] do
- local Frame = Savestates[i][j]
- local MinFrame = {}
- local cfX, cfY, cfZ, cfR00, cfR01, cfR02, cfR10, cfR11, cfR12, cfR20, cfR21, cfR22 = Frame.CFrame:GetComponents()
- local camX, camY, camZ, camR00, camR01, camR02, camR10, camR11, camR12, camR20, camR21, camR22 = Frame.CameraCFrame:GetComponents()
- MinFrame.CCFrame = {cfX, cfY, cfZ, cfR00, cfR01, cfR02, cfR10, cfR11, cfR12, cfR20, cfR21, cfR22}
- MinFrame.CCameraCFrame = {camX, camY, camZ, camR00, camR01, camR02, camR10, camR11, camR12, camR20, camR21, camR22}
- MinFrame.VVelocity = {Frame.Velocity.X, Frame.Velocity.Y, Frame.Velocity.Z}
- MinFrame.AAnimation = Frame.Animation or {Name="Idle", Weight=0}
- MinFrame.time = Frame.Time
- table.insert(FullTAS, MinFrame)
- end
- end
- -- Add frames from the current, unsaved sequence
- for i = 1, #PlayerInfo do
- local Frame = PlayerInfo[i]
- local MinFrame = {}
- local cfX, cfY, cfZ, cfR00, cfR01, cfR02, cfR10, cfR11, cfR12, cfR20, cfR21, cfR22 = Frame.CFrame:GetComponents()
- local camX, camY, camZ, camR00, camR01, camR02, camR10, camR11, camR12, camR20, camR21, camR22 = Frame.CameraCFrame:GetComponents()
- MinFrame.CCFrame = {cfX, cfY, cfZ, cfR00, cfR01, cfR02, cfR10, cfR11, cfR12, cfR20, cfR21, cfR22}
- MinFrame.CCameraCFrame = {camX, camY, camZ, camR00, camR01, camR02, camR10, camR11, camR12, camR20, camR21, camR22}
- MinFrame.VVelocity = {Frame.Velocity.X, Frame.Velocity.Y, Frame.Velocity.Z}
- MinFrame.AAnimation = Frame.Animation or {Name="Idle", Weight=0}
- MinFrame.time = Frame.Time
- table.insert(FullTAS, MinFrame)
- end
- -- Sort the frames by time to ensure correct playback order
- table.sort(FullTAS, function(a, b)
- return a.time < b.time
- end)
- return FullTAS
- end
- local function SaveRun()
- local FullTAS = PrepareTasData()
- if #FullTAS > 0 then
- local MapName = "Generic" -- You might want to find a way to get a meaningful map name
- local GameName = "RobloxGame" -- Default name
- local Success, Info = pcall(game.GetService(game, "MarketplaceService").GetProductInfo, game.GetService(game, "MarketplaceService"), game.PlaceId)
- if Success and Info then
- GameName = Info.Name
- end
- GameName = GameName:gsub("[^%w%s]", ""):gsub("%s+", "_") -- Sanitize name
- local FileName = GameName .. "_" .. MapName .. "_TAS.json"
- local FilePath = "TAS_Recorder/" .. FileName
- local EncodedData = HttpService:JSONEncode(FullTAS)
- if writefile then -- Check if the environment supports file writing (e.g., exploit context)
- if not isfolder("TAS_Recorder") then
- makefolder("TAS_Recorder")
- end
- writefile(FilePath, EncodedData)
- print("TAS saved to: " .. FilePath)
- else
- print("File saving is not supported in this environment.")
- print("TAS Data (JSON):")
- print(EncodedData)
- end
- else
- print("No TAS data to save.")
- end
- end
- local ViewingTAS = false
- local function ViewTASPlayback(TAS)
- if ViewingTAS or #TAS == 0 then
- if #TAS == 0 then print("No TAS data to play.") end
- return
- end
- ViewingTAS = true
- print("Starting TAS Playback...")
- local StartTime = tick()
- local CurrentFrameIndex = 1
- local PlaybackConnection
- -- Ensure character is present and accessible
- if not LocalPlayer.Character then
- LocalPlayer.CharacterAdded:Wait()
- end
- local Character = LocalPlayer.Character
- if not Character or not Character:FindFirstChild("HumanoidRootPart") then
- print("Character not found for playback.")
- ViewingTAS = false
- return
- end
- local RootPart = Character.HumanoidRootPart
- local Hum = Character:FindFirstChildOfClass("Humanoid")
- if not Hum then
- print("Humanoid not found for playback.")
- ViewingTAS = false
- return
- end
- local Animator = Hum:FindFirstChildOfClass("Animator")
- -- Ensure player is not controlled by user input during playback
- if Hum then
- Hum.AutoRotate = false -- Prevent automatic rotation
- Hum.WalkSpeed = 0
- Hum.JumpPower = 0
- -- You might need to disable built-in movement scripts here
- -- This is game-specific and can be tricky.
- -- Example: Disable scripts in Character.CharacterScripts
- end
- RootPart.Anchored = true -- Anchor the root part for precise positioning
- if not Pause then UserPauseToggle() end -- Ensure paused before starting playback
- local function StopPlayback()
- if PlaybackConnection then
- PlaybackConnection:Disconnect()
- PlaybackConnection = nil
- end
- if Hum then
- Hum.AutoRotate = true -- Restore default behavior
- Hum.WalkSpeed = 16 -- Restore default walk speed
- Hum.JumpPower = 50 -- Restore default jump power
- -- Re-enable built-in movement scripts if you disabled them
- end
- RootPart.Anchored = false -- Unanchor the root part
- ViewingTAS = false
- print("TAS Playback finished.")
- task.wait() -- Give a moment before potentially re-pausing
- -- UserPauseToggle() -- Decide if you want to automatically re-pause
- end
- PlaybackConnection = RunService.Heartbeat:Connect(function()
- local ElapsedTime = tick() - StartTime
- local TargetFrame = nil
- -- Find the correct frame based on elapsed time
- -- We iterate through frames to catch up if the game lags
- while CurrentFrameIndex <= #TAS and TAS[CurrentFrameIndex].time <= ElapsedTime do
- TargetFrame = TAS[CurrentFrameIndex]
- CurrentFrameIndex = CurrentFrameIndex + 1
- end
- if TargetFrame then
- local cfData = TargetFrame.CCFrame
- local camData = TargetFrame.CCameraCFrame
- RootPart.CFrame = CFrame.new(cfData[1], cfData[2], cfData[3], cfData[4], cfData[5], cfData[6], cfData[7], cfData[8], cfData[9], cfData[10], cfData[11], cfData[12])
- RootPart.Velocity = Vector3.new(TargetFrame.VVelocity[1], TargetFrame.VVelocity[2], TargetFrame.VVelocity[3])
- Workspace.CurrentCamera.CFrame = CFrame.new(camData[1], camData[2], camData[3], camData[4], camData[5], camData[6], camData[7], camData[8], camData[9], camData[10], camData[11], camData[12])
- local AnimInfo = TargetFrame.AAnimation
- if AnimInfo and Animator then
- -- Attempt to find and play the correct animation track
- local foundTrack = nil
- for _, track in ipairs(Animator:GetPlayingAnimationTracks()) do
- if track.Name == AnimInfo.Name then
- foundTrack = track
- break
- end
- end
- -- If the track isn't playing, try finding it among the humanoid's children
- if not foundTrack then
- local potentialTrack = Hum:FindFirstChild(AnimInfo.Name, true)
- if potentialTrack and potentialTrack:IsA("AnimationTrack") then
- foundTrack = potentialTrack
- end
- end
- if foundTrack then
- if not foundTrack.IsPlaying then foundTrack:Play() end
- foundTrack:AdjustSpeed(AnimInfo.Weight or 1)
- -- Note: Setting TimePosition precisely for playback is complex
- -- based on how Roblox handles animation replication and blending.
- -- You might need to experiment or use a custom animation system
- -- for perfect animation sync.
- -- foundTrack.TimePosition = (ElapsedTime - (TargetFrame.time - (1/60))) % foundTrack.Length -- Rough attempt at syncing position
- else
- -- Optionally print a warning if an animation track isn't found
- -- print("Warning: Animation track not found for:", AnimInfo.Name)
- end
- end
- end
- -- Stop playback when all frames are processed
- if CurrentFrameIndex > #TAS then
- StopPlayback()
- end
- end)
- end
- local function DisconnectAll()
- if MainConnectionLoop then MainConnectionLoop:Disconnect() end
- if KeybindsConnect then KeybindsConnect:Disconnect() end
- if InputEndConnect then InputEndConnect:Disconnect() end
- if DiedConnect then DiedConnect:Disconnect() end
- if HUD then HUD:Destroy() end
- -- Ensure character state is reset
- if LocalPlayer.Character then
- local RootPart = LocalPlayer.Character:FindFirstChild("HumanoidRootPart")
- if RootPart then
- RootPart.Anchored = false
- end
- local Hum = LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
- if Hum then
- Hum.AutoRotate = true -- Restore default behavior
- Hum.WalkSpeed = 16 -- Restore default walk speed
- Hum.JumpPower = 50 -- Restore default jump power
- -- Re-enable built-in movement scripts if you disabled them
- end
- end
- -- Stop any active tasks
- if FrameForwardTask and coroutine.status(FrameForwardTask) ~= "dead" then
- task.cancel(FrameForwardTask)
- end
- if FrameBackTask and coroutine.status(FrameBackTask) ~= "dead" then
- task.cancel(FrameBackTask)
- end
- isFrameForwardHeld = false
- isFrameBackHeld = false
- ViewingTAS = false -- Ensure this is reset
- print("TAS Recorder Stopped.")
- end
- local function Initialize()
- -- Disconnect any existing connections just in case
- DisconnectAll()
- Savestates = {}
- PlayerInfo = {}
- TimePaused = 0
- Pause = true
- TimeStart = tick()
- TimePauseHolder = tick()
- -- Initial state saved as the first frame of the first savestate
- -- This allows returning to the start point.
- local initialCFrame = getCurrentCFrame()
- local initialCamCFrame = getCurrentCameraCFrame()
- local initialVelocity = getCurrentVelocity()
- local initialAnimation = { Name = "Idle", Weight = 0 } -- Attempt to get initial animation
- -- Try to get the current animation state more accurately
- local Hum = LocalPlayer.Character and LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
- if Hum then
- local Animator = Hum:FindFirstChildOfClass("Animator")
- if Animator then
- local PlayingTracks = Animator:GetPlayingAnimationTracks()
- for _, track in ipairs(PlayingTracks) do
- -- Prioritize tracks with weight > 0, or just pick the first playing one
- if track.Weight > 0 then
- initialAnimation = { Name = track.Name, Weight = track.Weight }
- break
- elseif #PlayingTracks > 0 then
- initialAnimation = { Name = PlayingTracks[1].Name, Weight = PlayingTracks[1].Weight }
- end
- end
- end
- end
- table.insert(Savestates, {{
- CFrame = initialCFrame,
- CameraCFrame = initialCamCFrame,
- Velocity = initialVelocity,
- Animation = initialAnimation,
- Time = 0 -- Time starts at 0 for the initial state
- }})
- SetUpGui()
- MainConnectionLoop = RunService.Heartbeat:Connect(function(deltaTime)
- if not Pause and not ViewingTAS then -- Only record when not paused and not viewing playback
- UpdateTimeGUI()
- local currentInfo = ReturnPlayerInfo()
- -- Attempt to get current animation state while recording
- local currentHum = LocalPlayer.Character and LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
- if currentHum then
- local currentAnimator = currentHum:FindFirstChildOfClass("Animator")
- if currentAnimator then
- local PlayingTracks = currentAnimator:GetPlayingAnimationTracks()
- local activeAnim = { Name = "Idle", Weight = 0 }
- for _, track in ipairs(PlayingTracks) do
- -- Capture the most relevant animation (e.g., highest weight)
- if track.Weight > activeAnim.Weight then
- activeAnim = { Name = track.Name, Weight = track.Weight }
- end
- end
- currentInfo.Animation = activeAnim
- end
- end
- table.insert(PlayerInfo, currentInfo)
- UpdateGUIText()
- end
- end)
- -- Keep keyboard input for PC users, but prioritize mobile buttons
- KeybindsConnect = UserInputService.InputBegan:Connect(function(Input, Typing)
- if Typing or UserInputService.TouchEnabled then return end -- Ignore keyboard input on touch devices
- local KeyCode = Input.KeyCode
- if KeyCode == Enum.KeyCode[Keybinds.UserPause] then
- UserPauseToggle()
- elseif KeyCode == Enum.KeyCode.One then -- Using direct KeyCode for PC
- AddSavestate()
- elseif KeyCode == Enum.KeyCode.Two then
- RemoveSavestate()
- elseif KeyCode == Enum.KeyCode.Eight then
- BackSavestate()
- elseif KeyCode == Enum.KeyCode.C then
- CollisionToggler()
- elseif KeyCode == Enum.KeyCode.Six then
- SaveRun()
- elseif KeyCode == Enum.KeyCode.Five then
- task.spawn(FrameForwardStart)
- elseif KeyCode == Enum.KeyCode.Four then
- task.spawn(FrameBackStart)
- elseif KeyCode == Enum.KeyCode.Delete then
- DisconnectAll()
- elseif KeyCode == Enum.KeyCode.Zero then
- if not ViewingTAS then
- local CurrentTASData = PrepareTasData()
- ViewTASPlayback(CurrentTASData)
- end
- end
- end)
- InputEndConnect = UserInputService.InputEnded:Connect(function(Input, Typing)
- if Typing or UserInputService.TouchEnabled then return end -- Ignore keyboard input on touch devices
- local KeyCode = Input.KeyCode
- if KeyCode == Enum.KeyCode.Four then
- isFrameBackHeld = false
- elseif KeyCode == Enum.KeyCode.Five then
- isFrameForwardHeld = false
- end
- end)
- -- Handle player death - reconnect the Died event if the character respawns
- local function setupDeathHandler(char)
- local Hum = char:FindFirstChildOfClass("Humanoid")
- if Hum then
- if DiedConnect then DiedConnect:Disconnect() end -- Disconnect old handler if any
- DiedConnect = Hum.Died:Connect(function()
- print("Character died. Attempting to respawn and go to last savestate.")
- task.wait(0.1) -- Small wait for character to fully die
- if not Pause then UserPauseToggle() end -- Pause on death
- local charAddedConn
- charAddedConn = LocalPlayer.CharacterAdded:Connect(function(newChar)
- charAddedConn:Disconnect() -- Disconnect this handler once character is added
- task.wait(1) -- Wait for character to load and humanoid to be ready
- BackSavestate() -- Go back to the last savestate
- -- Decide if you want to automatically unpause after returning
- -- if not Pause then UserPauseToggle() end
- end)
- end)
- end
- end
- -- Initial setup for death handler if character exists
- if LocalPlayer.Character then
- setupDeathHandler(LocalPlayer.Character)
- end
- -- Connect CharacterAdded to set up the death handler for future characters
- LocalPlayer.CharacterAdded:Connect(setupDeathHandler)
- print("TAS Recorder Initialized.")
- -- Anchor the character initially until the user unpauses
- if LocalPlayer.Character then
- local RootPart = LocalPlayer.Character:FindFirstChild("HumanoidRootPart")
- if RootPart then
- RootPart.Anchored = true
- end
- end
- if TimeTextLabel then TimeTextLabel.TextColor3 = Color3.fromRGB(255, 255, 0) end
- if CapLockPauseLabel then
- CapLockPauseLabel.Text = "Status: Paused"
- CapLockPauseLabel.TextColor3 = Color3.fromRGB(255, 255, 0)
- end
- -- Initial state is paused
- UserPauseToggle()
- if not Pause then UserPauseToggle() end -- Ensure it starts paused
- end
- Initialize(
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement