TASability
Key RequiredUniversal

TASability

Universal Scriptserickserick 2.3K views 113 executesUpdated Dec 3, 2025
Unrated
00
Lua Script•2974 lines (92732 chars)
--[[
By plusgiant5, IMGUI port by 0_Void
Some code taken from original Replayability so credit to dong too


Tasability V1.2.1

-- Improved Reading loop to fix all slowdowns
-- Fixed animations bugs and delays.
-- Added custom target fps.
-- Fixed shiftlock freezes.
-- Fixed some animations bugs.
-- Added support for CSCD, ETOH ETC.

AHKConnection is required to do mouse scroll inputs

Get AHKConnection here: https://github.com/plusgiant5/Libraries/raw/d96e61f24d2439f0f9405087054f169e5a50e1c4/AHKConnection/AHKConnection.exe
If you dont trust the exe, compile it yourself: https://raw.githubusercontent.com/plusgiant5/Libraries/main/AHKConnection/AHKConnection.ahk

AHKConnection does not work well unless compiled
]]

-- Config
local FPS = 120 -- Put whatever FPS you want here, changes fps cap for the tas.
local PlaybackInputs = false -- Sets if you want replays to playback your inputs when playing them (AHK connection is required for mouse scroll playback)
local PlaybackMouseLocation = false -- Sets if you want replays to move your mouse when playing them (glitchy when loading checkpoints)
local RoundDigits = 5 -- Rounds all numbers when writing, to greatly decrease file size (set to 50 to disable rounding)
local ReplayStartTime = 1 -- Number of seconds to wait before starting to read the replay
local FrameBacktrackCount = 500 -- Number of frames to backtrack when frozen to see which keys are currently pressed. Increase as much as your computer can handle
local MinimumJSONFPS = 1/10 -- Lowest you want your FPS to go while encoding/decoding (higher = faster encoding/decoding, lower = better fps) 1/30: 30 fps, 1/60: 60 fps\
local BypassAntiExploit = true -- If this is true games with anti cheat (like beans) will not kick you, but there is a chance animations will be broken


-- Advanced config

-- Inputs that will not be recorded
local InputBlacklist = {
	["R"] = true;
	["T"] = true;
	["H"] = true;
	["G"] = true;
	["E"] = true;
}

-- Color codes for the color code frame
local ColorCodes = {
	WaitingForInput = Color3.new(1,1,0);
	Recording = Color3.new(1,0,0);
	Reading = Color3.new(0,0,5,1);
	Idle = Color3.new(1,1,1);
	Frozen = Color3.new(0,1,1);
	
	None = Color3.new(0,0,0);
}

-- Data on Roblox's cursors
local Cursors = {
	["ArrowFarCursor"] = {
		Icon = "rbxasset://textures/Cursors/KeyboardMouse/ArrowFarCursor.png";
		Size = UDim2.fromOffset(64,64);
		Offset = Vector2.new(-32,4);
	};
	["MouseLockedCursor"] = {
		Icon = "rbxasset://textures/MouseLockedCursor.png";
		Size = UDim2.fromOffset(32,32);
		Offset = Vector2.new(-16,-16);
	};
}

-- End of config

-- Constants
local Version = "V1.2.1 IMGUI Edition"
local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")
local HttpService = game:GetService("HttpService")
local ContextActionService = game:GetService("ContextActionService")
local GuiService = game:GetService("GuiService")
local VirtualInputManager = game:GetService("VirtualInputManager")
local Player = game.Players.LocalPlayer
local Mouse = Player:GetMouse()
local random = math.random
local min = math.min
local max = math.max
local floor = math.floor
local ceil = math.ceil
local ReplayFileBeginning = "{\"Replay\":"
local ReplayFileEnding = "}"
local PlayerModule = Player.PlayerScripts:WaitForChild("PlayerModule")
local ShiftLockBoundKeys = PlayerModule:WaitForChild("CameraModule"):WaitForChild("MouseLockController"):WaitForChild("BoundKeys")
local ShiftLockEnabled = false
local GuiInset = GuiService:GetGuiInset()

-- Variables
local ExecutionTick = tick()
local PlaceId = game.PlaceId
-- These will be set later --
local Character = nil
local Humanoid = nil
local RootPart = nil
local DefaultGravity = nil
local DefaultJumpPower = nil
local DefaultWalkSpeed = nil
local Resolution = nil
local ConsoleMessage = print
-----------------------------
local Reading = false
local Writing = false
local Saving = false
local AnimateDisabled = false
local Checkpoints = {}
local RenderSteppedConnections = {}
local SteppedConnections = {}
local FolderPath = "Tasability\\"..tostring(PlaceId)
local ReplayPath = isfolder(FolderPath) and #listfiles(FolderPath) >= 1 and listfiles(FolderPath)[1] or FolderPath.."\\Replay.json"
local AHKConnectionFolderPath = "Replayability+_AHK"
local AHKConnectionRequestPath = "Replayability+_AHK/Request"
local ReplayTable = {} -- Should always be used instead of the json string except when encoding or decoding
local RecordingTable = {} -- List of frames that will be added to ReplayTable if recording is saved
local ReplayTableIndex = 0 -- The index in ReplayTable that will be read from
local AnimationQueue = {} -- Functions that were called by the animation script (clear every frame)
local RunSpeed = 0 -- Set in the onRunning function, reset to 0 every frame (AnimationId 2)
local ClimbSpeed = 0 -- Set in the onClimbing function, reset to 0 every frame (AnimationId 4)
local HumanoidStateQueue = {} -- States that were activated on the humanoid (clear every frame)
local InputBeganQueue = {} -- Inputs that have just began (for recording inputs) (clear every frame)
local InputEndedQueue = {} -- Inputs that have just ended (for recording inputs) (clear every frame)
local Cursor = Instance.new("ImageLabel") -- Fake cursor so the icon doesnt change all the time
local CursorIcon = nil -- Icon of the cursor
local CursorSize = nil -- Size of the cursor
local CursorOffset = nil -- Offset of the cursor from UserInputService:GetMouseLocation()
local Dead = false -- If the player is dead this is true
local CameraCFrame = workspace.CurrentCamera.CFrame -- Used when reading so that nothing else can change the camera's CFrame
local Pressed = {} -- Current keys that are pressed
local IgnoreGameProcessed = false -- To ignore GameProcessed in InputBegan, InputChanged, InputEnded

-- Tasability update
local Frozen = false
local FreezeFrame = 1 -- Frame to render while frozen
local SeekDirection = 0 -- Stays 0 normally, -1 when going backwards while frozen, 1 when going fowards
local SeekDirectionMultiplier = 1 -- To go faster or slower when seeking with R and T

-- Converting inputs
-- To add to this table, use https://docs.microsoft.com/en-us/windows/win32/inputdev/virtual-key-codes
local InputCodes = {
	["A"] = 0x41;
	["B"] = 0x42;
	["C"] = 0x43;
	["D"] = 0x44;
	["E"] = 0x45;
	["F"] = 0x46;
	["G"] = 0x47;
	["H"] = 0x48;
	["I"] = 0x49;
	["J"] = 0x4A;
	["K"] = 0x4B;
	["L"] = 0x4C;
	["M"] = 0x4D;
	["N"] = 0x4E;
	["O"] = 0x4F;
	["P"] = 0x50;
	["Q"] = 0x51;
	["R"] = 0x52;
	["S"] = 0x53;
	["T"] = 0x54;
	["U"] = 0x55;
	["V"] = 0x56;
	["W"] = 0x57;
	["X"] = 0x58;
	["Y"] = 0x59;
	["Z"] = 0x5A;
	["Space"] = 0x20;
	["LeftShift"] = 0x10;
	["RightShift"] = 0x10;
}

-- Compatibility
mouse1press = mouse1press or mouse1down
mouse2press = mouse2press or mouse2down
mouse1release = mouse1release or mouse1up
mouse2release = mouse2release or mouse2up
keypress = keypress or keydown
keyrelease = keyrelease or keyup

-- Variables used in Animate script
local pose = "Standing" -- The pose that is used in the move function
local currentAnimSpeed = 1.0 -- Animation speed

-- Other
local GUIParent = Player:WaitForChild("PlayerGui")
local json
do -- Overwriting JSON
	json = (function()
																			--
																			-- json.lua
																			--
																			-- Copyright (c) 2020 rxi
																			--
																			-- Permission is hereby granted, free of charge, to any person obtaining a copy of
																			-- this software and associated documentation files (the "Software"), to deal in
																			-- the Software without restriction, including without limitation the rights to
																			-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
																			-- of the Software, and to permit persons to whom the Software is furnished to do
																			-- so, subject to the following conditions:
																			--
																			-- The above copyright notice and this permission notice shall be included in all
																			-- copies or substantial portions of the Software.
																			--
																			-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
																			-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
																			-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
																			-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
																			-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
																			-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
																			-- SOFTWARE.
																			--

																			local json = { _version = "0.1.2" }
																			
																			local t = tick()
																			local currentstr
																			local lasti
																			local function checkwait(i)
																				if tick() - t > MinimumJSONFPS then
																					lasti = lasti or i
																					if i >= lasti then
																						local Type = (type(currentstr) == "table" and "En") or (type(currentstr) == "string" and "De")
																						if Type then
																							ConsoleMessage(Type.."coding... ("..tostring(i).."/"..tostring(#currentstr)..")")
																						end
																						game:GetService("RunService").Stepped:Wait()
																						t = tick()
																						lasti = i
																					end
																				end
																			end

																			-------------------------------------------------------------------------------
																			-- Encode
																			-------------------------------------------------------------------------------

																			

																			local encode

																			local escape_char_map = {
																				[ "\\" ] = "\\",
																				[ "\"" ] = "\"",
																				[ "\b" ] = "b",
																				[ "\f" ] = "f",
																				[ "\n" ] = "n",
																				[ "\r" ] = "r",
																				[ "\t" ] = "t",
																			}

																			local escape_char_map_inv = { [ "/" ] = "/" }
																			for k, v in pairs(escape_char_map) do
																				escape_char_map_inv[v] = k
																			end


																			local function escape_char(c)
																				return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
																			end


																			local function encode_nil(val)
																				return "null"
																			end


																			local function encode_table(val, stack)
																				local res = {}
																				stack = stack or {}

																				-- Circular reference?
																				if stack[val] then error("circular reference") end

																				stack[val] = true

																				if rawget(val, 1) ~= nil or next(val) == nil then
																					-- Treat as array -- check keys are valid and it is not sparse
																					local n = 0
																					for k in pairs(val) do
																						if type(k) ~= "number" then
																							error("invalid table: mixed or invalid key types")
																						end
																						n = n + 1
																					end
																					if n ~= #val then
																						error("invalid table: sparse array")
																					end
																					-- Encode
																					for i, v in ipairs(val) do
																						checkwait(i)
																						table.insert(res, encode(v, stack))
																					end
																					stack[val] = nil
																					
																					return "[" .. table.concat(res, ",") .. "]"

																				else
																					-- Treat as an object
																					local i = 0
																					for k, v in pairs(val) do
																						i = i + 1
																						if type(k) ~= "string" then
																							error("invalid table: mixed or invalid key types")
																						end
																						checkwait(i)
																						table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
																					end
																					stack[val] = nil
																					
																					return "{" .. table.concat(res, ",") .. "}"
																				end
																			end


																			local function encode_string(val)
																				return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
																			end


																			local function encode_number(val)
																				-- Check for NaN, -inf and inf
																				if val ~= val or val <= -math.huge or val >= math.huge then
																					error("unexpected number value '" .. tostring(val) .. "'")
																				end
																				return string.format("%.14g", val)
																			end


																			local type_func_map = {
																				[ "nil"     ] = encode_nil,
																				[ "table"   ] = encode_table,
																				[ "string"  ] = encode_string,
																				[ "number"  ] = encode_number,
																				[ "boolean" ] = tostring,
																			}


																			encode = function(val, stack)
																				local t = type(val)
																				local f = type_func_map[t]
																				if f then
																					t = tick()
																					return f(val, stack)
																				end
																				error("unexpected type '" .. t .. "'")
																			end


																			function json.encode(val)
																				currentstr = val
																				lasti = nil
																				return ( encode(val) )
																			end


																			-------------------------------------------------------------------------------
																			-- Decode
																			-------------------------------------------------------------------------------

																			local parse

																			local function create_set(...)
																				local res = {}
																				for i = 1, select("#", ...) do
																					res[ select(i, ...) ] = true
																				end
																				return res
																			end

																			local space_chars   = create_set(" ", "\t", "\r", "\n")
																			local delim_chars   = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
																			local escape_chars  = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
																			local literals      = create_set("true", "false", "null")

																			local literal_map = {
																				[ "true"  ] = true,
																				[ "false" ] = false,
																				[ "null"  ] = nil,
																			}


																			local function next_char(str, idx, set, negate)
																				for i = idx, #str do
																					if set[str:sub(i, i)] ~= negate then
																						return i
																					end
																				end
																				return #str + 1
																			end


																			local function decode_error(str, idx, msg)
																				local line_count = 1
																				local col_count = 1
																				for i = 1, idx - 1 do
																					col_count = col_count + 1
																					if str:sub(i, i) == "\n" then
																						line_count = line_count + 1
																						col_count = 1
																					end
																				end
																				error( string.format("%s at line %d col %d", msg, line_count, col_count) )
																			end



																			local function codepoint_to_utf8(n)
																				-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
																				local f = math.floor
																				if n <= 0x7f then
																					return string.char(n)
																				elseif n <= 0x7ff then
																					return string.char(f(n / 64) + 192, n % 64 + 128)
																				elseif n <= 0xffff then
																					return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
																				elseif n <= 0x10ffff then
																					return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
																						f(n % 4096 / 64) + 128, n % 64 + 128)
																				end
																				error( string.format("invalid unicode codepoint '%x'", n) )
																			end


																			local function parse_unicode_escape(s)
																				local n1 = tonumber( s:sub(1, 4),  16 )
																				local n2 = tonumber( s:sub(7, 10), 16 )
																				-- Surrogate pair?
																				if n2 then
																					return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
																				else
																					return codepoint_to_utf8(n1)
																				end
																			end


																			local function parse_string(str, i)
																				local res = ""
																				local j = i + 1
																				local k = j

																				while j <= #str do
																					local x = str:byte(j)

																					if x < 32 then
																						decode_error(str, j, "control character in string")

																					elseif x == 92 then -- `\`: Escape
																						res = res .. str:sub(k, j - 1)
																						j = j + 1
																						local c = str:sub(j, j)
																						if c == "u" then
																							local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
																								or str:match("^%x%x%x%x", j + 1)
																								or decode_error(str, j - 1, "invalid unicode escape in string")
																							res = res .. parse_unicode_escape(hex)
																							j = j + #hex
																						else
																							if not escape_chars[c] then
																								decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
																							end
																							res = res .. escape_char_map_inv[c]
																						end
																						k = j + 1

																					elseif x == 34 then -- `"`: End of string
																						res = res .. str:sub(k, j - 1)
																						return res, j + 1
																					end

																					j = j + 1
																					checkwait(i)
																				end

																				decode_error(str, i, "expected closing quote for string")
																			end


																			local function parse_number(str, i)
																				local x = next_char(str, i, delim_chars)
																				local s = str:sub(i, x - 1)
																				local n = tonumber(s)
																				if not n then
																					decode_error(str, i, "invalid number '" .. s .. "'")
																				end
																				checkwait(i)
																				return n, x
																			end


																			local function parse_literal(str, i)
																				local x = next_char(str, i, delim_chars)
																				local word = str:sub(i, x - 1)
																				if not literals[word] then
																					decode_error(str, i, "invalid literal '" .. word .. "'")
																				end
																				checkwait(i)
																				return literal_map[word], x
																			end


																			local function parse_array(str, i)
																				local res = {}
																				local n = 1
																				i = i + 1
																				while 1 do
																					local x
																					i = next_char(str, i, space_chars, true)
																					-- Empty / end of array?
																					if str:sub(i, i) == "]" then
																						i = i + 1
																						break
																					end
																					-- Read token
																					x, i = parse(str, i)
																					res[n] = x
																					n = n + 1
																					-- Next token
																					i = next_char(str, i, space_chars, true)
																					local chr = str:sub(i, i)
																					i = i + 1
																					checkwait(i)
																					if chr == "]" then break end
																					if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
																				end
																				return res, i
																			end


																			local function parse_object(str, i)
																				local res = {}
																				i = i + 1
																				while 1 do
																					local key, val
																					i = next_char(str, i, space_chars, true)
																					-- Empty / end of object?
																					if str:sub(i, i) == "}" then
																						i = i + 1
																						break
																					end
																					-- Read key
																					if str:sub(i, i) ~= '"' then
																						decode_error(str, i, "expected string for key")
																					end
																					key, i = parse(str, i)
																					-- Read ':' delimiter
																					i = next_char(str, i, space_chars, true)
																					if str:sub(i, i) ~= ":" then
																						decode_error(str, i, "expected ':' after key")
																					end
																					i = next_char(str, i + 1, space_chars, true)
																					-- Read value
																					val, i = parse(str, i)
																					-- Set
																					res[key] = val
																					-- Next token
																					i = next_char(str, i, space_chars, true)
																					local chr = str:sub(i, i)
																					i = i + 1
																					--print(tick() - t, 1/60)
																					checkwait(i)
																					if chr == "}" then break end
																					if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
																				end
																				return res, i
																			end


																			local char_func_map = {
																				[ '"' ] = parse_string,
																				[ "0" ] = parse_number,
																				[ "1" ] = parse_number,
																				[ "2" ] = parse_number,
																				[ "3" ] = parse_number,
																				[ "4" ] = parse_number,
																				[ "5" ] = parse_number,
																				[ "6" ] = parse_number,
																				[ "7" ] = parse_number,
																				[ "8" ] = parse_number,
																				[ "9" ] = parse_number,
																				[ "-" ] = parse_number,
																				[ "t" ] = parse_literal,
																				[ "f" ] = parse_literal,
																				[ "n" ] = parse_literal,
																				[ "[" ] = parse_array,
																				[ "{" ] = parse_object,
																			}


																			parse = function(str, idx)
																				local chr = str:sub(idx, idx)
																				local f = char_func_map[chr]
																				if f then
																					return f(str, idx)
																				end
																				decode_error(str, idx, "unexpected character '" .. chr .. "'")
																			end


																			function json.decode(str)
																				t = tick()
																				currentstr = str
																				lasti = nil
																				if type(str) ~= "string" then
																					error("expected argument of type string, got " .. type(str))
																				end
																				local res, idx = parse(str, next_char(str, 1, space_chars, true))
																				idx = next_char(str, idx, space_chars, true)
																				if idx <= #str then
																					decode_error(str, idx, "trailing garbage")
																				end
																				return res
																			end


																			return json
	end)()
end

-- Functions
-- General Functions
-- Fast conversion functions for better performance
local function FastTableToCFrame(t)
	return CFrame.new(t[1], t[2], t[3], t[4], t[5], t[6], t[7], t[8], t[9], t[10], t[11], t[12])
end

local function FastTableToVector3(t)
	return Vector3.new(t[1], t[2], t[3])
end

local function FastTableToVector2(t)
	return Vector2.new(t[1], t[2])
end
local RandomString --RandomString() -> string
local RoundNumber -- RoundNumber(Number,Digits) -> number
local Vector3ToTable -- Vector3ToTable(Vector3) -> table
local TableToVector3 -- TableToVector3(Table) -> vector3
local CFrameToTable -- CFrameToTable(CFrame) -> table
local TableToCFrame -- TableToCFrame(Table) -> cframe
local RoundVector3 -- RoundVector3(Vector3,Digits) -> vector3
local RoundCFrame -- RoundCFrame(CFrame,Digits) -> cframe
local FindListIndex -- FindListIndex(Table,Search) -> number
local WaitForInput -- WaitForInput() -> nil
do
	RandomString = function()
		local str = ""
		for _ = 1,random(1,20) do
			local type = random(1,3)
			if type == 1 then
				str = str..string.char(random(97,122)) -- Lowercase
			elseif type == 2 then
				str = str..string.char(random(65,90)) -- Uppercase
			elseif type == 3 then
				str = str..string.char(random(48,57)) -- Numbers
			end
		end
		return str
	end
	RoundNumber = function(Number,Digits)
		local Mult = 10^max(tonumber(Digits) or 0,0)
		return floor(Number*Mult+0.5)/Mult
	end
	Vector3ToTable = function(V3)
		return {V3.X,V3.Y,V3.Z}
	end
	TableToVector3 = function(Table)
		return Vector3.new(unpack(Table))
	end
	Vector2ToTable = function(V2)
		return {V2.X,V2.Y}
	end
	TableToVector2 = function(Table)
		return Vector2.new(unpack(Table))
	end
	CFrameToTable = function(CF)
		return {CF:GetComponents()}
	end
	TableToCFrame = function(Table)
		return CFrame.new(unpack(Table))
	end
	RoundTable = function(Table,Digits)
		local RoundedTable = {}
		for Index,Number in pairs(Table) do
			RoundedTable[Index] = RoundNumber(Number,Digits)
		end
		return RoundedTable
	end
	FindListIndex = function(Table,Search)
		for Index,Value in pairs(Table) do
			if Value == Search then
				return Index
			end
		end
	end
	WaitForInput = function()
		local KeyPressed = Instance.new("BindableEvent")
		local InputBeganConnection
		InputBeganConnection = UserInputService.InputBegan:Connect(function(Input)
			if Input.UserInputType == Enum.UserInputType.Keyboard then
				RunService.RenderStepped:Wait()
				KeyPressed:Fire()
			end
		end)
		KeyPressed.Event:Wait()
		InputBeganConnection:Disconnect()
		KeyPressed:Destroy()
	end
end

-- Making GUI
local InsertService = game:GetService("InsertService")

local ReGui = loadstring(game:HttpGet('https://raw.githubusercontent.com/depthso/Dear-ReGui/refs/heads/main/ReGui.lua'))()
local PrefabsId = "rbxassetid://" .. ReGui.PrefabsId

--// Declare the Prefabs asset
ReGui:Init({
	Prefabs = InsertService:LoadLocalAsset(PrefabsId)
})
ReGui:DefineElement("Textbox", {
	Base = {
		Value = "",
		Placeholder = "",
		Label = "Input text",
		Callback = EmptyFunction,
		MultiLine = false,
		NoAutoTag = true,
		Disabled = false
	},
	Create = function(Canvas, Config: InputText): InputText
		--// Unpack configuration
		local MultiLine = Config.MultiLine
		local Placeholder = Config.Placeholder
		local Label = Config.Label
		local Disabled = Config.Disabled
		local Value = Config.Value

		--// Create Text input object
		local Object = ReGui:InsertPrefab("InputBox", Config)
		local Frame = Object.Frame
		local TextBox = Frame.Input

		local Class = ReGui:MergeMetatables(Config, Object)

		Canvas:Label({
			Parent = Object,
			Text = Label,
			AutomaticSize = Enum.AutomaticSize.X,
			Size = UDim2.fromOffset(0, 19),
			Position = UDim2.new(1, 4),
			LayoutOrder = 2
		})

		ReGui:SetProperties(TextBox, {
			PlaceholderText = Placeholder,
			MultiLine = MultiLine
		})

		local function Callback(...)
			local Func = Config.Callback
			Func(Class, ...)
		end

		function Config:SetValue(Value: string?)
			TextBox.Text = tostring(Value)
			self.Value = Value
			return self
		end

		function Config:SetDisabled(Disabled: boolean)
			self.Disabled = Disabled
			Object.Interactable = not Disabled
			Canvas:SetColorTags({
				[Label] = Disabled and "LabelDisabled" or "Label"
			}, true)
			return self
		end

		function Config:Clear()
			TextBox.Text = ""
			return self
		end

		local function TextChanged()
			local Value = TextBox.Text
			Config.Value = Value
			Callback(Value)
		end

		--// Connect events
		TextBox.FocusLost:Connect(function(enter, inputObject)
            TextChanged()
        end)

		--// Update object state
		Config:SetDisabled(Disabled)
		Config:SetValue(Value)

		Canvas:TagElements({
			[TextBox] = "Frame"
		})

		return Class, Object
	end,
})
local Window = ReGui:Window({
	Title = "Tasability FIX BY 9KWR "..Version,
	Size = UDim2.new(0, 600, 0, 400),
	-- If you have a window with a single element with Fill enabled, you should disable Scroll on the window 
	-- Otherwise it will not fit correctly as automatic canvas size is enabled for the scrolling functionality
	NoScroll = false
})

--Console window
local ConsoleWindow = ReGui:Window({
    Title = "Console",
    Size = UDim2.new(0, 400, 0, 300),
    -- If you have a window with a single element with Fill enabled, you should disable Scroll on the window 
    -- Otherwise it will not fit correctly as automatic canvas size is enabled for the scrolling functionality
    NoScroll = true
})
local console = ConsoleWindow:Console({
    Enabled = true,
    ReadOnly = true,
})
local ConsoleInput = ConsoleWindow:Textbox({
    Label = "Enter command here",
})
ConsoleWindow:ToggleVisibility()

--Menu bar

local menubar = Window:MenuBar()
local Menu = menubar:MenuItem({
    Text = "Menu"
})
local CreateFile = Menu:Selectable({
    Text = "Create File",
    Callback = function()
        local popup = Window:PopupModal({
            Title = "Create file"
        })
        popup:Textbox({
            Text = "Enter file name",
            Placeholder = "File name...",
            Callback = function(_,name)
                if #name > 0 then
                    writefile(FolderPath.."./"..name..".json", "")
                    ReplayTable = {}
                    ReplayPath = FolderPath.."./"..name..".json"
                end
            end
        })
        popup:Button({
            Text = "Done",
            Callback = function()
                popup:ClosePopup()
            end
        })
    end
})
local SaveToFileButton = Menu:Selectable({
    Text = "Save to File",
    Callback = function()
	    SaveToFile()
    end 
})
local LoadFileButton = Menu:Selectable({
    Text = "Load File",
    Callback = function()
        local popup = Window:PopupModal({
            Title = "Load file"
        })
        popup:Combo({
            Text = "Select file",
            Placeholder = "Select file here",
            GetItems = function() return listfiles(FolderPath) end,
            Callback = function(_,path)
                print(path)
                ReplayPath = path
                ReplayTable = #GetReplayFile() > 0 and ReplayDecode(GetReplayFile()) or {}
            end
        })
        popup:Button({
            Text = "Done",
            Callback = function()
                popup:ClosePopup()
            end
        })
    end 
})
local DeleteFileButton = Menu:Selectable({
    Text = "Delete File",
    Callback = function()
        local popup = Window:PopupModal({
            Title = "Delete file"
        })
        local filepath = popup:Combo({
            Text = "Select file",
            Placeholder = "Select file here",
            GetItems = function() return listfiles(FolderPath) end,
        })
        popup:Button({
            Text = "Done",
            Callback = function()
                popup:ClosePopup()
                if #filepath.Value > 0 then
                    delfile(filepath.Value)
                end
            end
        })
    end 
})
local ConsoleButton = Menu:Selectable({
    Text = "Console",
    Callback = function()
        ConsoleWindow:ToggleVisibility()
    end
})


--Main
local InfoHeader = Window:CollapsingHeader({
    Title = "Info"
})
RecordedFramesLabel = InfoHeader:Label({
    Text = "Frames: 0"
})
local PressedKeysLabel = InfoHeader:Label({
    Text = "Pressed keys: |"
})
local WritingPressedKeysLabel = InfoHeader:Label({
    Text = "Writing Pressed keys: |"
})
local ColorCodeFrame = InfoHeader:Label({
    Text = "Status: "
})
local CurrentFile = InfoHeader:Label({
    Text = "Current File: "
})
task.spawn(function()
    repeat task.wait() until type(ReplayPath) == "string"
    print(ReplayPath)
    local oldfilename = ReplayPath
    CurrentFile.Text = "Current File: "..string.split(ReplayPath, "\\")[3]
    while task.wait() do
        if ReplayPath ~= oldfilename then
            CurrentFile.Text = "Current File: "..string.split(ReplayPath, "\\")[3]
            oldfilename = ReplayPath
        end
    end
end)
local ConnectedLabel = InfoHeader:Label({
    Text = "AHK folder not found",
    TextColor3 = Color3.fromRGB(255,0,0)
})
local CurrentPlaceIdButton; CurrentPlaceIdButton = InfoHeader:Button({
    Text = "Place id: "..tostring(PlaceId),
    Callback = function()
        if string.find(CurrentPlaceIdButton.Text, "Place") then
            local OldText = CurrentPlaceIdButton.Text
            setclipboard(tostring(PlaceId))
            CurrentPlaceIdButton.Text = "Copied id"
            wait(.5)
            CurrentPlaceIdButton.Text = OldText
        end
    end
})

local ControlsHeader = Window:CollapsingHeader({
    Title = "Controls"
})
local Frozentoidlerow = ControlsHeader:Row()
Frozentoidlerow:Button({
    Text = "Frozen to Idle",
    Callback = function()
        IdleButton_MouseButton1Click()
    end
})
local Frozenkeybind = Frozentoidlerow:Keybind({
    Label = "Keybind",
    Value = Enum.KeyCode.M,
})
local IgnoreGameProcessedButton_MouseButton1Click
local IgnoreGameProcessedButton = ControlsHeader:Checkbox({
    Label = "Ignore Game Processed",
    Callback = function()
        IgnoreGameProcessed = not IgnoreGameProcessed
    end
})
local movecameraonfroze = ControlsHeader:Checkbox({
    Label = "Move camera while frozen"
})
local ReadButtonrow = ControlsHeader:Row()
ReadButtonrow:Button({
    Text = "Read",
    Callback = function()
        ReadButton_MouseButton1Click()
    end
})
local Readkeybind = ReadButtonrow:Keybind({
    Label = "Keybind",
    Value = Enum.KeyCode.Z,
})
local Abortrow = ControlsHeader:Row()
local StopReading
Abortrow:Button({
    Text = "Abort",
    Callback = function()
        StopReading()
    end
})
local Abortkeybind = Abortrow:Keybind({
    Label = "Keybind",
    Value = Enum.KeyCode.L,
})
local Hideuikeybind = ControlsHeader:Keybind({
    Label = "Hide ui",
    Value = Enum.KeyCode.U
})
local Recordkeybind = ControlsHeader:Keybind({
    Label = "Record/Freeze keybind",
    Value = Enum.KeyCode.E
})
local Goforwardkeybind = ControlsHeader:Keybind({
    Label = "Go forward keybind",
    Value = Enum.KeyCode.H
})
local Gobackwardskeybind = ControlsHeader:Keybind({
    Label = "Go backwards keybind",
    Value = Enum.KeyCode.R
})
local Frameadvanceforwardkeybind = ControlsHeader:Keybind({
    Label = "Frame advance forward keybind",
    Value = Enum.KeyCode.T
})
local Frameadvancebackwardskeybind = ControlsHeader:Keybind({
    Label = "Frame advance backwards keybind",
    Value = Enum.KeyCode.G
})
local Savekeybind = ControlsHeader:Keybind({
    Label = "Save to file keybind",
    Value = Enum.KeyCode.P
})



-- GUI Functions
--local ConsoleMessage -- ConsoleMessage(...) -> nil
local SetColorCodeFrame -- SetColorCodeFrame(Name) -> nil
local GetColorCodeFrame -- GetColorCodeFrame() -> string
do
	ConsoleMessage = function(...)
        setthreadidentity(8)
        console:AppendText(...)
	end
	
	ConsoleMessage("Tasability loading...")
	SetColorCodeFrame = function(Name)
		ColorCodeFrame.TextColor3 = ColorCodes[Name] or ColorCodes.None
		ColorCodeFrame.Text = "Status: "..(ColorCodes[Name] and Name or "None")
	end
	GetColorCodeFrame = function()
		return string.sub(ColorCodeFrame.Text, 9, #ColorCodeFrame.Text) 
	end
end

do -- Anticheat bypasses
	do -- standard anti kick
		--// Variables
		
		local Players = game:GetService("Players")
		local OldNameCall = nil

		--// Anti Kick Hook
		local plr = game.Players.LocalPlayer
		OldNameCall = hookmetamethod(game, "__namecall", function(Self, ...)
			local NameCallMethod = getnamecallmethod()

			if not checkcaller() and Self == plr and NameCallMethod == "Kick" then
				if getgenv().SendNotifications == true then
					game:GetService("StarterGui"):SetCore("SendNotification", {
						Title = "Almost Kicked",
						Text = tostring(({...})[1]),
						Icon = "rbxassetid://6238540373",
						Duration = 3,
					})
				end
				
				return nil
			end
			
			return OldNameCall(Self, ...)
		end)
	end
	pcall(function() -- Practice anticheat bypass
		game.ReplicatedStorage.Remotes.Send:Destroy()
	end)
	pcall(function() -- Slad anticheat bypass
		local sendremote = game.ReplicatedStorage.DefaultChatSystemChatEvents.ChannelNameColorUpdated
		local oldspawn
		oldspawn = hookfunction(getrenv().spawn, function(...)
			if not checkcaller() and (tostring(getcallingscript()) == "Animate" or tostring(getcallingscript()) == "RbxAnimateScript") then
				return oldspawn(function()
					
				end)
			end
			return oldspawn(...)
		end)
		sendremote:Destroy()
	end)

	-- ADONIS BYPASS 

local g = getinfo or debug.getinfo
local d = false
local h = {}

local x, y

setthreadidentity(2)

for i, v in getgc(true) do
    if typeof(v) == "table" then
        local a = rawget(v, "Detected")
        local b = rawget(v, "Kill")
    
        if typeof(a) == "function" and not x then
            x = a
            
            local o; o = hookfunction(x, function(c, f, n)
                if c ~= "_" then
                    if d then
                        warn(`Adonis AntiCheat flagged\nMethod: {c}\nInfo: {f}`)
                    end
                end
                
                return true
            end)

            table.insert(h, x)
        end

        if rawget(v, "Variables") and rawget(v, "Process") and typeof(b) == "function" and not y then
            y = b
            local o; o = hookfunction(y, function(f)
                if d then
                    warn(`Adonis AntiCheat tried to kill (fallback): {f}`)
                end
            end)

            table.insert(h, y)
        end
    end
end

local o; o = hookfunction(getrenv().debug.info, newcclosure(function(...)
    local a, f = ...

    if x and a == x then
        if d then
            warn(`zins adonis bypassed`)
        end

        return coroutine.yield(coroutine.running())
    end
    
    return o(...)
end))

setthreadidentity(7)

end

-- Animation Functions
local StopAllAnimations -- StopAllAnimations() -> nil
local Reanimate -- Reanimate(Character) -> nil

local GetAnimationFunctionFromId -- GetAnimationFunctionFromId(Id) -> function
local onDied -- onDied() -> nil
local onRunning -- onRunning(Speed) -> nil
local onJumping -- onJumping() -> nil
local onClimbing -- onClimbing(Speed) -> nil
local onGettingUp -- onGettingUp() -> nil
local onFreeFall -- onFreeFall() -> nil
local onFallingDown -- onFallingDown() -> nil
local onSeated -- onSeated() -> nil
local onPlatformStanding -- onPlatformStanding() -> nil
local onSwimming -- onSwimming() -> nil

local PlayAnimation -- PlayAnimation() -> nil
local setAnimationSpeed -- setAnimationSpeed() -> nil
do
	StopAllAnimations = function()
		for _,v in pairs(Humanoid:GetPlayingAnimationTracks()) do 
			v:Stop()
		end
	end
	GetAnimationFunctionFromId = function(Id)
		return ({
			[1] = OnDied;
			[2] = onRunning;
			[3] = onJumping;
			[4] = onClimbing;
			[5] = onGettingUp;
			[6] = onFreeFall;
			[7] = onFallingDown;
			[8] = onSeated;
			[9] = onPlatformStanding;
			[10] = onSwimming;
		})[Id]
	end
	Reanimate = function(Character)
		if Character:WaitForChild("Animate",3) then
			for _, Animate in ipairs(Character:GetDescendants()) do
				if Animate:IsA("LocalScript") and Animate.Name == "Animate" then
					for _,Connection in pairs(getconnections(Animate.Changed)) do
						Connection:Disconnect()
					end
					if BypassAntiExploit then
						Animate.Disabled = true
						if setparentinternal then
							setparentinternal(Animate, game.Lighting)
						else
							ConsoleMessage("Your exploit does not support setparentinternal, expect animation glitches")
						end
					else
						Animate:Destroy()
					end
				end
			end
		else
			ConsoleMessage("[ERROR] Animate script could not be found, you probably executed twice")
			error("Animate script could not be found, you probably executed twice")
		end
		StopAllAnimations()
		do -- Animate script
								local Figure = Character
								local Torso = Figure:WaitForChild("Torso")
								local RightShoulder = Torso:WaitForChild("Right Shoulder")
								local LeftShoulder = Torso:WaitForChild("Left Shoulder")
								local RightHip = Torso:WaitForChild("Right Hip")
								local LeftHip = Torso:WaitForChild("Left Hip")
								local Neck = Torso:WaitForChild("Neck")
								local Humanoid = Figure:WaitForChild("Humanoid")

								local currentAnim = ""
								local currentAnimInstance = nil
								local currentAnimTrack = nil
								local currentAnimKeyframeHandler = nil
								--local currentAnimSpeed = 1.0
								local animTable = {}
								local animNames = { 
									idle = 	{	
												{ id = "http://www.roblox.com/asset/?id=180435571", weight = 8 },
												{ id = "http://www.roblox.com/asset/?id=180435792", weight = 1 }
											},
									walk = 	{ 	
												{ id = "http://www.roblox.com/asset/?id=180426354", weight = 10 } 
											}, 
									run = 	{
												{ id = "run.xml", weight = 10 } 
											}, 
									jump = 	{
												{ id = "http://www.roblox.com/asset/?id=125750702", weight = 12 } 
											}, 
									fall = 	{
												{ id = "http://www.roblox.com/asset/?id=180436148", weight = 9 } 
											}, 
									climb = {
												{ id = "http://www.roblox.com/asset/?id=180436334", weight = 10 } 
											}, 
									sit = 	{
												{ id = "http://www.roblox.com/asset/?id=178130996", weight = 10 } 
											},	
									toolnone = {
												{ id = "http://www.roblox.com/asset/?id=182393478", weight = 10 } 
											},
									toolslash = {
												{ id = "http://www.roblox.com/asset/?id=129967390", weight = 10 } 
								--				{ id = "slash.xml", weight = 10 } 
											},
									toollunge = {
												{ id = "http://www.roblox.com/asset/?id=129967478", weight = 10 } 
											},
									wave = {
												{ id = "http://www.roblox.com/asset/?id=128777973", weight = 10 } 
											},
									point = {
												{ id = "http://www.roblox.com/asset/?id=128853357", weight = 10 } 
											},
									dance1 = {
												{ id = "http://www.roblox.com/asset/?id=182435998", weight = 10 }, 
												{ id = "http://www.roblox.com/asset/?id=182491037", weight = 10 }, 
												{ id = "http://www.roblox.com/asset/?id=182491065", weight = 10 } 
											},
									dance2 = {
												{ id = "http://www.roblox.com/asset/?id=182436842", weight = 10 }, 
												{ id = "http://www.roblox.com/asset/?id=182491248", weight = 10 }, 
												{ id = "http://www.roblox.com/asset/?id=182491277", weight = 10 } 
											},
									dance3 = {
												{ id = "http://www.roblox.com/asset/?id=182436935", weight = 10 }, 
												{ id = "http://www.roblox.com/asset/?id=182491368", weight = 10 }, 
												{ id = "http://www.roblox.com/asset/?id=182491423", weight = 10 } 
											},
									laugh = {
												{ id = "http://www.roblox.com/asset/?id=129423131", weight = 10 } 
											},
									cheer = {
												{ id = "http://www.roblox.com/asset/?id=129423030", weight = 10 } 
											},
								}
								local dances = {"dance1", "dance2", "dance3"}

								-- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
								local emoteNames = { wave = false, point = false, dance1 = true, dance2 = true, dance3 = true, laugh = false, cheer = false}

								function configureAnimationSet(name, fileList)
									if (animTable[name] ~= nil) then
										for _, connection in pairs(animTable[name].connections) do
											connection:disconnect()
										end
									end
									animTable[name] = {}
									animTable[name].count = 0
									animTable[name].totalWeight = 0	
									animTable[name].connections = {}

									-- check for config values

									-- fallback to defaults
									if (animTable[name].count <= 0) then
										for idx, anim in pairs(fileList) do
											animTable[name][idx] = {}
											animTable[name][idx].anim = Instance.new("Animation")
											animTable[name][idx].anim.Name = name
											animTable[name][idx].anim.AnimationId = anim.id
											animTable[name][idx].weight = anim.weight
											animTable[name].count = animTable[name].count + 1
											animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
								--			print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
										end
									end
								end

								-- Setup animation objects

								-- Clear any existing animation tracks
								-- Fixes issue with characters that are moved in and out of the Workspace accumulating tracks
								local animator = Humanoid and Humanoid:FindFirstChildOfClass("Animator") or nil
								if animator then
									local animTracks = animator:GetPlayingAnimationTracks()
									for i,track in ipairs(animTracks) do
										track:Stop(0)
										track:Destroy()
									end
								end


								for name, fileList in pairs(animNames) do 
									configureAnimationSet(name, fileList)
								end	

								-- ANIMATION

								-- declarations
								local toolAnim = "None"
								local toolAnimTime = 0

								local jumpAnimTime = 0
								local jumpAnimDuration = 0.3

								local toolTransitionTime = 0.1
								local fallTransitionTime = 0.3
								local jumpMaxLimbVelocity = 0.75

								-- functions

								function stopAllAnimations()
									local oldAnim = currentAnim

									-- return to idle if finishing an emote
									if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
										oldAnim = "idle"
									end

									currentAnim = ""
									currentAnimInstance = nil
									if (currentAnimKeyframeHandler ~= nil) then
										currentAnimKeyframeHandler:disconnect()
									end

									if (currentAnimTrack ~= nil) then
										currentAnimTrack:Stop()
										currentAnimTrack:Destroy()
										currentAnimTrack = nil
									end
									return oldAnim
								end

								setAnimationSpeed = function(speed)
									if speed ~= currentAnimSpeed then
										currentAnimSpeed = speed
										currentAnimTrack:AdjustSpeed(currentAnimSpeed)
									end
								end

								function keyFrameReachedFunc(frameName)
									if (frameName == "End") then

										local repeatAnim = currentAnim
										-- return to idle if finishing an emote
										if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
											repeatAnim = "idle"
										end
										
										local animSpeed = currentAnimSpeed
										playAnimation(repeatAnim, 0.0, Humanoid)
										setAnimationSpeed(animSpeed)
									end
								end

								-- Preload animations
								playAnimation = function(animName, transitionTime, humanoid, bypassAnimateDisabled) 
									--print(animName, transitionTime, humanoid, bypassAnimateDisabled)
									pcall(function()
									if AnimateDisabled and not bypassAnimateDisabled then
										return
									end
									
									table.insert(AnimationQueue,{animName,transitionTime})
									
									local roll = math.random(1, animTable[animName].totalWeight) 
									local origRoll = roll
									local idx = 1
									while (roll > animTable[animName][idx].weight) do
										roll = roll - animTable[animName][idx].weight
										idx = idx + 1
									end
								--		print(animName .. " " .. idx .. " [" .. origRoll .. "]")
									local anim = animTable[animName][idx].anim

									-- switch animation		
									if (anim ~= currentAnimInstance) then
										
										if (currentAnimTrack ~= nil) then
											currentAnimTrack:Stop(transitionTime)
											currentAnimTrack:Destroy()
										end

										currentAnimSpeed = 1.0
									
										-- load it to the humanoid; get AnimationTrack
										currentAnimTrack = humanoid:LoadAnimation(anim)
										currentAnimTrack.Priority = Enum.AnimationPriority.Core
										 
										-- play the animation
										currentAnimTrack:Play(transitionTime)
										currentAnim = animName
										currentAnimInstance = anim

										-- set up keyframe name triggers
										if (currentAnimKeyframeHandler ~= nil) then
											currentAnimKeyframeHandler:disconnect()
										end
										currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
										
									end
								end)

								end

								-------------------------------------------------------------------------------------------
								-------------------------------------------------------------------------------------------

								local toolAnimName = ""
								local toolAnimTrack = nil
								local toolAnimInstance = nil
								local currentToolAnimKeyframeHandler = nil

								function toolKeyFrameReachedFunc(frameName)
									if (frameName == "End") then
								--		print("Keyframe : ".. frameName)	
										playToolAnimation(toolAnimName, 0.0, Humanoid)
									end
								end


								function playToolAnimation(animName, transitionTime, humanoid, priority)	 
										
										local roll = math.random(1, animTable[animName].totalWeight) 
										local origRoll = roll
										local idx = 1
										while (roll > animTable[animName][idx].weight) do
											roll = roll - animTable[animName][idx].weight
											idx = idx + 1
										end
								--		print(animName .. " * " .. idx .. " [" .. origRoll .. "]")
										local anim = animTable[animName][idx].anim

										if (toolAnimInstance ~= anim) then
											
											if (toolAnimTrack ~= nil) then
												toolAnimTrack:Stop()
												toolAnimTrack:Destroy()
												transitionTime = 0
											end
													
											-- load it to the humanoid; get AnimationTrack
											toolAnimTrack = humanoid:LoadAnimation(anim)
											if priority then
												toolAnimTrack.Priority = priority
											end
											 
											-- play the animation
											toolAnimTrack:Play(transitionTime)
											toolAnimName = animName
											toolAnimInstance = anim

											currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
										end
								end

								function stopToolAnimations()
									local oldAnim = toolAnimName

									if (currentToolAnimKeyframeHandler ~= nil) then
										currentToolAnimKeyframeHandler:disconnect()
									end

									toolAnimName = ""
									toolAnimInstance = nil
									if (toolAnimTrack ~= nil) then
										toolAnimTrack:Stop()
										toolAnimTrack:Destroy()
										toolAnimTrack = nil
									end


									return oldAnim
								end

								-------------------------------------------------------------------------------------------
								-------------------------------------------------------------------------------------------


								onRunning = function(speed)
									if speed > 0.01 then
										playAnimation("walk", 0.1, Humanoid)
										if currentAnimInstance and currentAnimInstance.AnimationId == "http://www.roblox.com/asset/?id=180426354" then
											setAnimationSpeed(speed / 14.5)
										end
										pose = "Running"
									else
										if emoteNames[currentAnim] == nil then
											playAnimation("idle", 0.1, Humanoid)
											pose = "Standing"
										end
									end
								end

								onDied = function()
									pose = "Dead"
								end

								onJumping = function()
									playAnimation("jump", 0.1, Humanoid)
									jumpAnimTime = jumpAnimDuration
									pose = "Jumping"
								end

								onClimbing = function(speed)
									playAnimation("climb", 0.1, Humanoid)
									setAnimationSpeed(speed / 12.0)
									pose = "Climbing"
								end

								onGettingUp = function()
									pose = "GettingUp"
								end

								onFreeFall = function()
									if (jumpAnimTime <= 0) then
										playAnimation("fall", fallTransitionTime, Humanoid)
									end
									pose = "FreeFall"
								end

								onFallingDown = function()
									pose = "FallingDown"
								end

								onSeated = function()
									pose = "Seated"
								end

								onPlatformStanding = function()
									pose = "PlatformStanding"
								end

								onSwimming = function(speed)
									if speed > 0 then
										pose = "Running"
									else
										pose = "Standing"
									end
								end

								function getTool()	
									for _, kid in ipairs(Figure:GetChildren()) do
										if kid.className == "Tool" then return kid end
									end
									return nil
								end

								function getToolAnim(tool)
									for _, c in ipairs(tool:GetChildren()) do
										if c.Name == "toolanim" and c.className == "StringValue" then
											return c
										end
									end
									return nil
								end

								function animateTool()
									
									if (toolAnim == "None") then
										playToolAnimation("toolnone", toolTransitionTime, Humanoid, Enum.AnimationPriority.Idle)
										return
									end

									if (toolAnim == "Slash") then
										playToolAnimation("toolslash", 0, Humanoid, Enum.AnimationPriority.Action)
										return
									end

									if (toolAnim == "Lunge") then
										playToolAnimation("toollunge", 0, Humanoid, Enum.AnimationPriority.Action)
										return
									end
								end

								function moveSit()
									RightShoulder.MaxVelocity = 0.15
									LeftShoulder.MaxVelocity = 0.15
									RightShoulder:SetDesiredAngle(3.14 /2)
									LeftShoulder:SetDesiredAngle(-3.14 /2)
									RightHip:SetDesiredAngle(3.14 /2)
									LeftHip:SetDesiredAngle(-3.14 /2)
								end

								local lastTick = 0

								function move(time)
									if AnimateDisabled then
										return
									end
									
									local amplitude = 1
									local frequency = 1
									local deltaTime = time - lastTick
									lastTick = time

									local climbFudge = 0
									local setAngles = false

									if (jumpAnimTime > 0) then
										jumpAnimTime = jumpAnimTime - deltaTime
									end

									if (pose == "FreeFall" and jumpAnimTime <= 0) then
										playAnimation("fall", fallTransitionTime, Humanoid)
									elseif (pose == "Seated") then
										playAnimation("sit", 0.5, Humanoid)
										return
									elseif (pose == "Running") then
										playAnimation("walk", 0.1, Humanoid)
									elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
								--		print("Wha " .. pose)
										stopAllAnimations()
										amplitude = 0.1
										frequency = 1
										setAngles = true
									end

									if (setAngles) then
										local desiredAngle = amplitude * math.sin(time * frequency)

										RightShoulder:SetDesiredAngle(desiredAngle + climbFudge)
										LeftShoulder:SetDesiredAngle(desiredAngle - climbFudge)
										RightHip:SetDesiredAngle(-desiredAngle)
										LeftHip:SetDesiredAngle(-desiredAngle)
									end

									-- Tool Animation handling
									local tool = getTool()
									if tool and tool:FindFirstChild("Handle") then
									
										local animStringValueObject = getToolAnim(tool)

										if animStringValueObject then
											toolAnim = animStringValueObject.Value
											-- message recieved, delete StringValue
											animStringValueObject.Parent = nil
											toolAnimTime = time + .3
										end

										if time > toolAnimTime then
											toolAnimTime = 0
											toolAnim = "None"
										end

										animateTool()		
									else
										stopToolAnimations()
										toolAnim = "None"
										toolAnimInstance = nil
										toolAnimTime = 0
									end
								end

								-- connect events
								Humanoid.Died:connect(function(...)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,1)
									onDied(...)
								end)
								Humanoid.Running:connect(function(Speed)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,2)
									--RunSpeed = Speed
									onRunning(Speed)
								end)
								Humanoid.Jumping:connect(onJumping)
								Humanoid.Climbing:connect(function(Speed)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,4)
									--ClimbSpeed = Speed
									onClimbing(Speed)
								end)
								Humanoid.GettingUp:connect(function(...)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,5)
									onGettingUp(...)
								end)
								Humanoid.FreeFalling:connect(function(...)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,6)
									onFreeFall(...)
								end)
								Humanoid.FallingDown:connect(function(...)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,7)
									onFallingDown(...)
								end)
								Humanoid.Seated:connect(function(...)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,8)
									onSeated(...)
								end)
								Humanoid.PlatformStanding:connect(function(...)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,9)
									onPlatformStanding(...)
								end)
								Humanoid.Swimming:connect(function(...)
									if AnimateDisabled then
										return
									end
									--table.insert(AnimationQueue,10)
									onSwimming(...)
								end)

								-- setup emote chat hook
								game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
									local emote = ""
									if msg == "/e dance" then
										emote = dances[math.random(1, #dances)]
									elseif (string.sub(msg, 1, 3) == "/e ") then
										emote = string.sub(msg, 4)
									elseif (string.sub(msg, 1, 7) == "/emote ") then
										emote = string.sub(msg, 8)
									end
									
									if (pose == "Standing" and emoteNames[emote] ~= nil) then
										playAnimation(emote, 0.1, Humanoid)
									end

								end)


								-- main program

								-- initialize to idle
								playAnimation("idle", 0.1, Humanoid)
								pose = "Standing"

								spawn(function()
									while Figure.Parent ~= nil do
										local _, time = wait(0.1)
										move(time)
									end
								end)
		end
	end
end

-- Camera/Input Functions
local GetZoom -- GetZoom() -> number
local SetZoom -- SetZoom(Zoom) -> nil

local GetShiftLockEnabled -- GetShiftLockEnabled() -> bool
local SetShiftLockEnabled -- SetShiftLockEnabled(Enabled) -> nil

local SetCameraCFrame -- SetCameraCFrame(NewCFrame) -> nil

local BlockInputs -- BlockInputs() -> nil
local UnlockInputs -- UnlockInputs() -> nil

local SetCursorIcon -- SetCursorIcon(Icon) -> nil
local SetCursorSize -- SetCursorSize(Size) -> nil
local SetCursorOffset -- SetCursorOffset(Offset) -> nil
local SetCursor -- SetCursorIcon(CursorName) -> nil
do
	-- Load mouse lock action
	VirtualInputManager:SendKeyEvent(true, 304, false, workspace)
	wait()
	VirtualInputManager:SendKeyEvent(true, 304, false, workspace)
	wait()
	
	local ZoomControllers = {}
	
	do -- Get ZoomControllers from getgc
		for _,Table in pairs(getgc(true)) do
			if type(Table) == "table" then
				pcall(function()
					if type(Table.SetCameraToSubjectDistance) == "function"
					and type(Table.GetCameraToSubjectDistance) == "function"
					and Table.FIRST_PERSON_DISTANCE_THRESHOLD
					and Table.lastCameraTransform then
						table.insert(ZoomControllers,Table)
					end
				end)
			end
		end
		ConsoleMessage(tostring(#ZoomControllers).." ZoomController"..(#ZoomControllers == 1 and "" or "s"))
	end
	GetZoom = function()
		for _,ZoomController in pairs(ZoomControllers) do
			local Zoom = ZoomController:GetCameraToSubjectDistance()
			if Zoom and Zoom ~= 12.5 then -- Sometimes a ZoomController returns 12.5 so check if theres any other zoom levels before picking 12.5
				return Zoom
			end
		end
		return 12.5
	end
	SetZoom = function(Zoom)
		for _,ZoomController in pairs(ZoomControllers) do
			pcall(function() -- Sometimes a ZoomController errors and breaks the whole script
				ZoomController:SetCameraToSubjectDistance(Zoom)
			end)
		end
	end
	
	-- from original replayability
	-- from original replayability (optimized)
GetShiftLockEnabled = function()
	return ShiftLockEnabled
end

-- Cache the mouse lock controller to avoid searching every time
local cachedMouseLockController = nil
local function getMouseLockController()
	if cachedMouseLockController and cachedMouseLockController.DoMouseLockSwitch then
		return cachedMouseLockController
	end
	
	-- Only search if cache is invalid
	for _, obj in getgc(true) do 
		if type(obj) == "table" and rawget(obj, "activeMouseLockController") then 
			cachedMouseLockController = obj.activeMouseLockController
			return cachedMouseLockController
		end
	end
	return nil
end

function shiftLock(active)
	local mouseLockController = getMouseLockController()
	if not mouseLockController then return end
	
	local isLocked = mouseLockController:GetIsMouseLocked()
	if (active and not isLocked) or (not active and isLocked) then
		mouseLockController:DoMouseLockSwitch("MouseLockSwitchAction", Enum.UserInputState.Begin, game)
	end
end

SetShiftLockEnabled = function(Enabled)
	if ShiftLockEnabled ~= Enabled then
		ShiftLockEnabled = Enabled
		if Enabled then
			SetCursor("MouseLockedCursor")
		else
			SetCursor("ArrowFarCursor")
		end
		shiftLock(Enabled)
	end
end
	
	SetCameraCFrame = function(NewCFrame)
		CameraCFrame = NewCFrame
		workspace.CurrentCamera.CFrame = NewCFrame
	end
	
	local BlockGui = Instance.new("ScreenGui")
	local BlockFrame = Instance.new("TextButton")
	BlockFrame.Text = ""
	BlockFrame.BackgroundTransparency = 1
	BlockFrame.Size = UDim2.fromScale(1,1)
	BlockFrame.Selectable = false
	BlockFrame.Selected = false
	BlockFrame.Parent = BlockGui
	BlockGui.Enabled = false
	BlockGui.Parent = GUIParent
	BlockInputs = function()
		BlockGui.Enabled = true
	end
	UnblockInputs = function()
		BlockGui.Enabled = false
	end
	
	local CursorHolder = Instance.new("ScreenGui")
	Cursor.BackgroundTransparency = 1
	Cursor.ZIndex = 9999
	Cursor.Parent = CursorHolder
	CursorHolder.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
	CursorHolder.Parent = gethui()
    CursorHolder.DisplayOrder = 9999
	Resolution = CursorHolder.AbsoluteSize
	SetCursor = function(CursorName)
		local CursorData = Cursors[CursorName]
		CursorIcon = CursorData.Icon
		CursorSize = CursorData.Size
		CursorOffset = CursorData.Offset
	end
end

-- AHK Functions
local IsInstalled -- IsInstalled() -> bool
local SendSignal -- SendSignal(Signal) -> nil
do
	IsInstalled = function()
		return isfolder(AHKConnectionFolderPath)
	end
	SendSignal = function(Signal)
		if IsInstalled() then
			writefile(AHKConnectionRequestPath,Signal)
		else
			ConsoleMessage("AHK folder not found")
		end
	end
end



local Freeze -- Defined earlier so it can be used in replay functions


-- Replay Functions
local ReplayEncode -- ReplayEncode(Table) -> string
local RecordReplay -- RecordReplay() -> nil [Event]
local StartRecording -- StartRecording() -> nil
local StopRecording -- StopRecording() -> nil
local SaveRecording -- SaveRecording() -> nil
local DiscardRecording -- DiscardRecording() -> nil

local StartReading -- StartReading() -> nil

local GetCheckpoint -- GetCheckpoint(CheckpointNumber?) -> number
local SetCheckpoint -- SetCheckpoint(FrameIndex?) -> nil

local GotoFrame -- GotoFrame(Index) -> nil
do
	GetReplayFile = function()
		if not isfolder(string.split(FolderPath,"/")[1]) then
			makefolder(string.split(FolderPath,"/")[1])
		end
		if not isfolder(FolderPath) then
			makefolder(FolderPath)
		end
		if not isfile(ReplayPath) then
			writefile(ReplayPath,ReplayFileBeginning)
			return ReplayFileBeginning
		end
		return readfile(ReplayPath)
	end

	ReplayEncode = function(Table)
		ConsoleMessage("Encoding "..tostring(#Table).." frames")
		local StartTick = tick()
		local Encoded = json.encode(Table)
		ConsoleMessage("Done encoding in",RoundNumber(tick()-StartTick,2),"seconds")
		return ReplayFileBeginning..Encoded..ReplayFileEnding
	end
	ReplayDecode = function(String)
		if String == ReplayFileBeginning or String == ReplayFileBeginning..ReplayFileEnding then
			ConsoleMessage("Nothing to read")
			return
		end
		ConsoleMessage("Decoding "..tostring(#String).." characters")
		local StartTick = tick()
		local Decoded = json.decode(String)
		ConsoleMessage("Done decoding in",RoundNumber(tick()-StartTick,2),"seconds")
		return Decoded.Replay
	end
	
	
	RecordReplay = function()
		ConsoleMessage("Waiting for input")
		if Writing then
			ConsoleMessage("Recording stopped")
			StopRecording()
			return
		end
		SetColorCodeFrame("WaitingForInput")
		WaitForInput()
		StartRecording()
		ConsoleMessage("Recording started")
	end
	StartRecording = function()
		if not Reading then
			SetColorCodeFrame("Recording")
			Writing = true
		end
	end
	StopRecording = function()
		if not Reading then
			Writing = false
		end
	end
	
	SaveToFile = function()
		local ReplayEncoded = ReplayEncode(ReplayTable)
		writefile(ReplayPath,ReplayEncoded)
	end
	
	SaveRecording = function()
		if #RecordingTable > 0 then
			local old = #ReplayTable
			for _,Frame in pairs(RecordingTable) do
				table.insert(ReplayTable,Frame)
			end
			RecordingTable = {}
			ConsoleMessage("Saved")
		end
	end
	DiscardRecording = function()
		if #RecordingTable > 0 then
			RecordingTable = {}
			ConsoleMessage("Discarded")
		end
	end
	StartReading = function()
		if not Reading then
			SaveToFile()
			ReplayTable = ReplayDecode(GetReplayFile()) -- Decode replay from file
			if ReplayTable then
				-- Decoding successful
				Freeze(false) -- Unfreeze
				AnimateDisabled = true -- Disable fake animate script
				Workspace.Gravity = 0
				ReplayTableIndex = 1
				BlockInputs() -- Disable scrolling and clicks
				Reading = true
				SetColorCodeFrame("Reading")
				ConsoleMessage("Reading started")
				ConsoleMessage("Length: "..RoundNumber(tostring(#ReplayTable/60)).." seconds")
			else
				-- Decoding failed
				ReplayTable = {}
				SetColorCodeFrame("Idle")
			end
		else
			ConsoleMessage("You are already reading")
		end
	end
	StopReading = function()
		if Reading then
			UnblockInputs() -- Enable scrolling and clicks
			Character.Head.CanCollide = true -- Fix character collisions
			Character.Torso.CanCollide = true -- Fix character collisions
			Character.HumanoidRootPart.CanCollide = true -- Fix character collisions
			AnimateDisabled = false -- Enable fake animate script
			Reading = false
			Character.Humanoid.JumpPower = DefaultJumpPower
			Character.Humanoid.WalkSpeed = DefaultWalkSpeed
			Workspace.Gravity = DefaultGravity
			SetColorCodeFrame("Idle")
			ConsoleMessage("Reading stopped")
		else
			ConsoleMessage("You are not reading")
		end
	end
end

-- Tasability functions
--local Freeze -- Freeze(NewFrozen) -> nil
do
	Freeze = function(NewFrozen,DoNotRecord)
		if Frozen ~= NewFrozen and not Reading then
			SeekDirection = 0
			if NewFrozen then
				Frozen = true
				StopRecording()
				SaveRecording()
				FreezeFrame = #ReplayTable
				SetColorCodeFrame("Frozen")
			else
				
				--ConsoleMessage(#ReplayTable)
				--ConsoleMessage(FreezeFrame)
				
				if DoNotRecord then
					Frozen = false
					SetColorCodeFrame("Idle")
				else
					for Index = #ReplayTable,FreezeFrame,-1 do
						--ConsoleMessage(Index)
						ReplayTable[Index] = nil
					end
					Frozen = false
					StartRecording()
					SetColorCodeFrame("Recording")
				end
			end
		end
	end
end
-- Commands
local Commands = {}
do
	Commands["help"] = function(Args)
		if Args == "help" then
			ConsoleMessage("help <command>: Shows a list of all commands, or a specific command")
		else
			local Command = Args[1]
			if Command then
				Command = string.lower(Command)
				if Commands[Command] then
					Commands[Command]("help")
				else
					ConsoleMessage("Command", Command, "was not found")
				end
			else
				for _,Command in pairs(Commands) do
					Command("help")
				end
			end
		end
	end
	Commands["erase"] = function(Args)
		if Args == "help" then
			ConsoleMessage("erase: Erases all data from the folder",PlaceId)
		else
			writefile(ReplayPath,ReplayFileBeginning)
			ReplayTable = {}
			return ReplayPath.." has been erased"
		end
	end
	Commands["setsdm"] = function(Args)
		if Args == "help" then
			ConsoleMessage("setsdm <number SeekDirectionMultiplier>: Sets speed multiplier when using R and T while frozen")
		else
			local Number = tonumber(Args[1]) or 1
			if Number then
				local OldValue = SeekDirectionMultiplier
				SeekDirectionMultiplier = Number
				return "SeekDirectionMultiplier has been set from "..tostring(OldValue).." to "..tostring(Number)
			end
		end
	end
	Commands["rejoin"] = function(Args)
		if Args == "help" then
			ConsoleMessage("rejoin <bool SaveReplay>: Sets one of the configs at the top of the script (PlaybackInputs, etc)")
		else
			local SaveReplay = Args[1] and string.lower(Args[1])
			ConsoleMessage("Saving...")
			if SaveReplay == "true" or SaveReplay == "yes" or SaveReplay == "1" or SaveReplay == "save" then
				SaveToFile()
			end
			ConsoleMessage("Rejoining...")
			if #game.Players:GetPlayers() <= 1 then
				game.Players.LocalPlayer:Kick("\nRejoining...")
				wait()
				game:GetService("TeleportService"):Teleport(game.PlaceId, game.Players.LocalPlayer)
			else
				game:GetService("TeleportService"):TeleportToPlaceInstance(game.PlaceId, game.JobId, game.Players.LocalPlayer)
			end
			return "Sent request to rejoin"
		end
	end
	Commands["invite"] = function(Args)
		if Args == "help" then
			ConsoleMessage("invite: Invites you to Tasability Discord")
		else
			request({
				Url = "http://127.0.0.1:6463/rpc?v=1",
				Method = "POST",
				Headers = {
					["Content-Type"] = "application/json",
					["origin"] = "https://discord.com",
				},
				Body = game:GetService("HttpService"):JSONEncode({
					["args"] = {
						["code"] = "Shyfsc2cJ9",
					},
					["cmd"] = "INVITE_BROWSER",
					["nonce"] = "."
				})
			})
			return "Sent invite (if your exploit blocked it the invite is https://discord.gg/Shyfsc2cJ9)"
		end
	end
end

-- Connection Functions
local StateChanged
local CharacterAdded
local InputBegan
local RenderStepped
local Stepped
local CurrentCamera_Changed
do
	StateChanged = function(_,State)
		table.insert(HumanoidStateQueue,State.Value)
	end
	CharacterAdded = function(NewCharacter)
		Humanoid = NewCharacter:WaitForChild("Humanoid")
		Humanoid.StateChanged:Connect(StateChanged)
		RootPart = NewCharacter:WaitForChild("HumanoidRootPart")
		DefaultJumpPower = Humanoid.JumpPower
		DefaultWalkSpeed = Humanoid.WalkSpeed
		Reanimate(NewCharacter)
		Character = NewCharacter
		Humanoid.Died:Connect(function()
			Dead = true
		end)
		Dead = false
	end
	InputBegan = function(Input,GameProcessed)
		if IgnoreGameProcessed then
			GameProcessed = false
		end
		
		if Input.UserInputType == Enum.UserInputType.MouseButton1 then
			table.insert(InputBeganQueue,"b1")
		elseif Input.UserInputType == Enum.UserInputType.MouseButton2 then
			table.insert(InputBeganQueue,"b2")
		elseif Input.UserInputType == Enum.UserInputType.Keyboard then
			local InputName = string.split(tostring(Input.KeyCode),".")[3]
			if not InputBlacklist[InputName] then
				table.insert(InputBeganQueue,InputName)
			end
		end
		
		if Input.KeyCode == Enum.KeyCode.LeftShift and not Reading and not GameProcessed then
			SetShiftLockEnabled(not ShiftLockEnabled)
		end
		
		if Input.KeyCode == Recordkeybind.Value and not GameProcessed then
			-- Freeze/Unfreeze
			Freeze(not Frozen)
		elseif Input.KeyCode == Gobackwardskeybind.Value and not GameProcessed then
			if not Reading then
				Freeze(true)
				if SeekDirection == 0 then
					SeekDirection = -1*SeekDirectionMultiplier -- Backwards
				end
			end
		elseif Input.KeyCode == Goforwardkeybind.Value and not GameProcessed then
			-- Seek fowards
			if not Reading then
				Freeze(true)
				if SeekDirection == 0 then
					SeekDirection = 1*SeekDirectionMultiplier -- Fowards
				end
			end
		elseif Input.KeyCode == Frameadvancebackwardskeybind.Value and not GameProcessed then
			-- Go 1 frame backwards
			Freeze(true)
			if Frozen and SeekDirection == 0 then
				local NewFreezeFrame = FreezeFrame - 1
				if NewFreezeFrame > 0 and NewFreezeFrame <= #ReplayTable then
					FreezeFrame = NewFreezeFrame
				end
			end
		elseif Input.KeyCode == Frameadvanceforwardkeybind.Value and not GameProcessed then
			-- Go 1 frame fowards
			Freeze(true)
			if Frozen and SeekDirection == 0 then
				local NewFreezeFrame = FreezeFrame + 1
				if NewFreezeFrame > 0 and NewFreezeFrame <= #ReplayTable then
					FreezeFrame = NewFreezeFrame
				end
			end
		elseif Input.KeyCode == Hideuikeybind.Value and not GameProcessed then
			-- Toggle UI
			Window:ToggleVisibility()
		elseif Input.KeyCode == Abortkeybind.Value and not GameProcessed then
			-- Stop reading
			StopReading()
		elseif Input.KeyCode == Savekeybind.Value and not GameProcessed then
			-- Save to file
			SaveToFile()
		elseif Input.KeyCode == Frozenkeybind.Value and not GameProcessed then
			-- Save to file
			IdleButton_MouseButton1Click()
        elseif Input.KeyCode == Readkeybind.Value and not GameProcessed then
            ReadButton_MouseButton1Click()
		end
	end
	InputChanged = function(Input,GameProcessed)
		if Input.UserInputType == Enum.UserInputType.MouseWheel then
			if Input.Position.Z > 0 then
				table.insert(InputBeganQueue,"u")
			else
				table.insert(InputBeganQueue,"d")
			end
		end
	end
	InputEnded = function(Input,GameProcessed)
		if Input.UserInputType == Enum.UserInputType.MouseButton1 then
			table.insert(InputEndedQueue,"b1")
		elseif Input.UserInputType == Enum.UserInputType.MouseButton2 then
			table.insert(InputEndedQueue,"b2")
		elseif Input.UserInputType == Enum.UserInputType.MouseWheel then
			if Input.Position.Z > 0 then
				table.insert(InputEndedQueue,"u")
			else
				table.insert(InputEndedQueue,"d")
			end
		elseif Input.UserInputType == Enum.UserInputType.Keyboard then
			local InputName = string.split(tostring(Input.KeyCode),".")[3]
			table.insert(InputEndedQueue,InputName)
		end
		
		if Input.KeyCode == Gobackwardskeybind.Value then
			-- Stop seeking backwards
			if SeekDirection == -1*SeekDirectionMultiplier then
				SeekDirection = 0
			end
		elseif Input.KeyCode == Goforwardkeybind then
			-- Stop seeking fowards
			if SeekDirection == 1*SeekDirectionMultiplier then
				SeekDirection = 0
			end
		end
	end
	RenderStepped = function(...)
		for _,Function in pairs(RenderSteppedConnections) do
			Function(...)
		end
	end
	Stepped = function(...)
		for _,Function in pairs(SteppedConnections) do
			Function(...)
		end
	end
	ReadButton_MouseButton1Click = function()
		if ReplayStartTime >= 1 then
			for i = ReplayStartTime,1,-1 do
				ConsoleMessage("Reading in "..tostring(i).." seconds")
				wait(1)
			end
		end
		StartReading()
	end
	IdleButton_MouseButton1Click = function()
		if GetColorCodeFrame() == "Frozen" then
			Freeze(false,true)
		end
	end
	CurrentCamera_Changed = function()
		if Reading then
			workspace.CurrentCamera.CFrame = CameraCFrame
		end
	end
	ConsoleInput.Callback = function(self, value)
		local Input = value
		local InputSplit = string.split(Input," ")
		local Command = Commands[string.lower(InputSplit[1])]
		if Command then
			table.remove(InputSplit,1)
			local ReturnMessage = Command(InputSplit)
			if ReturnMessage then
				ConsoleMessage(ReturnMessage)
			end
		else
			ConsoleMessage("Command",InputSplit[1],"was not found")
		end
		self:Clear()
	end
end

-- RenderStepped/Stepped connections
do
	RenderSteppedConnections.UpdateFreezeFrame = function()
		RecordedFramesLabel.Text = "Frames: "..RoundNumber(FreezeFrame,0)
	end
	RenderSteppedConnections.SeekDirectionHandler = function()
		if Frozen then
			local NewFreezeFrame = FreezeFrame + SeekDirection
			if NewFreezeFrame < 1 then
				FreezeFrame = 1
			elseif NewFreezeFrame > #ReplayTable then
				FreezeFrame = #ReplayTable
			else
				FreezeFrame = NewFreezeFrame
			end
		end
	end
	local PressedWriting = {}
	SteppedConnections.UpdateInputPreview = function()
		for _,Input in pairs(InputBeganQueue) do
			if Input == "u" or Input == "d" then
				return
			end
			Pressed[Input] = true
		end
		for _,Input in pairs(InputEndedQueue) do
			Pressed[Input] = nil
		end
		PressedKeysLabel.Text = "Pressed keys: |"
		for Input,_ in pairs(Pressed) do
			PressedKeysLabel.Text = PressedKeysLabel.Text..Input.."|"
		end
		
		-- MOVED TO FREEZE LOOP
		--[[if Writing then
			for _,Input in pairs(InputBeganQueue) do
				if Input == "u" or Input == "d" then
					return
				end
				PressedWriting[Input] = true
			end
			for _,Input in pairs(InputEndedQueue) do
				PressedWriting[Input] = nil
			end
			WritingPressedKeysLabel.Text = "|"
			for Input,_ in pairs(PressedWriting) do
				WritingPressedKeysLabel.Text = WritingPressedKeysLabel.Text..Input.."|"
			end
		end]]
	end
end

do -- Connections
	UserInputService.InputBegan:Connect(InputBegan)
	UserInputService.InputChanged:Connect(InputChanged)
	UserInputService.InputEnded:Connect(InputEnded)
	RunService.RenderStepped:Connect(RenderStepped)
	RunService.Stepped:Connect(Stepped)
	Player.CharacterAdded:Connect(CharacterAdded)
	workspace.CurrentCamera.Changed:Connect(CurrentCamera_Changed)
end

do -- Setup
	GetReplayFile() -- Create folders and files for Replayability+ if needed
	SetCursor("ArrowFarCursor") -- Add fake cursor
	UserInputService.MouseIconEnabled = false -- Remove real cursor
	DefaultGravity = Workspace.Gravity -- Set DefaultGravity
	ShiftLockBoundKeys.Value = "" -- Remove shift lock keybinds
	CharacterAdded(Player.Character) -- Set character
	SetColorCodeFrame("Idle") -- Set color code
end

local function findAnimateScript(character)
    if not character then return nil end

    -- common names
    local names = {
        "Animate",
        "AnimationScript",
        "CharacterAnimate",
        "PlayerAnimate"
    }

    -- check common names
    for _, name in ipairs(names) do
        local s = character:FindFirstChild(name, true)
        if s and s:IsA("LocalScript") then
            return s
        end
    end

    -- smart detection
    for _, obj in ipairs(character:GetDescendants()) do
        if obj:IsA("LocalScript") then
            for _, sub in ipairs(obj:GetChildren()) do
                local n = sub.Name:lower()
                if n == "idle" or n == "walk" or n == "run" or n == "jump" or n == "fall" then
                    return obj
                end
            end
        end
    end

    return nil
end


spawn(function() -- Reading Loop lol

	local frameCache = {}
	local lastAnimations = {} 
	local currentAnimTracks = {} 
	
	while true do
		if Reading then
			local Frame = ReplayTable[ReplayTableIndex]
			
			-- Handle special frames (void/death)
			if Frame == 0 then
				Humanoid:ChangeState(15)
				for _,Descendant in pairs(Character:GetDescendants()) do
					if Descendant:IsA("BasePart") then
						Descendant:Destroy()
					end
				end
				repeat wait() until not Dead
				RunService.Heartbeat:Wait()
				ReplayTableIndex = ReplayTableIndex + 1
				continue
			elseif Frame == 1 then
				Humanoid:ChangeState(15)
				workspace.Gravity = DefaultGravity
				repeat wait() until not Dead
				RunService.Heartbeat:Wait()
				ReplayTableIndex = ReplayTableIndex + 1
				continue
			end
			
			if not Frame or typeof(Frame) == "string" then
				StopReading()
				continue
			end
			
				local animateScript = findAnimateScript(Character)
						if animateScript then
   					 animateScript.Disabled = true
				end

			AnimateDisabled = true
			workspace.Gravity = 0
			Character.Humanoid.JumpPower = 0
			Character.Humanoid.WalkSpeed = 0
			
			if not Character:FindFirstChild("HumanoidRootPart") then
				RunService.Heartbeat:Wait()
				continue
			end
			
			local HRP = Character.HumanoidRootPart
			
			
			for _,v in pairs(Character:GetChildren()) do
				if v:IsA("BasePart") then
					v.CanCollide = false
				end
			end
			
			Humanoid.PlatformStand = true
			
			-- Cache
			local cache = frameCache[ReplayTableIndex]
			if not cache then
				cache = {
					hrpCFrame = FastTableToCFrame(Frame[1]),
					camCFrame = FastTableToCFrame(Frame[7]),
					hrpVel = FastTableToVector3(Frame[5]),
					hrpRotVel = FastTableToVector3(Frame[6]),
					mouseLocation = FastTableToVector2(Frame[11])
				}
				frameCache[ReplayTableIndex] = cache
			end
			
			local Animations = Frame[2]
			local AnimationSpeed = Frame[3]
			local HumanoidState = Frame[4]
			local Zoom = Frame[8]
			local AnimatePose = Frame[9]
			local ShiftLockEnabled = (Frame[10] == 1)
			local InputBeganQueue = Frame[12][1]
			local InputEndedQueue = Frame[12][2]
			
			SetCameraCFrame(cache.camCFrame)
			SetZoom(Zoom)
			
			pose = AnimatePose

			Humanoid:ChangeState(HumanoidState)

			for _, Arguments in pairs(Animations) do
				local animName = Arguments[1]
				local transitionTime = Arguments[2]
				playAnimation(animName, transitionTime, Humanoid, true)
			end

			pcall(setAnimationSpeed, AnimationSpeed)

			if ShiftLockEnabled ~= GetShiftLockEnabled() then
				SetShiftLockEnabled(ShiftLockEnabled)
			end

			if PlaybackMouseLocation and not ShiftLockEnabled and Zoom > 0.52 then
				mousemoveabs(cache.mouseLocation.X, cache.mouseLocation.Y)
			else
				local CurrentGuiInset = GuiService:GetGuiInset()
				mousemoveabs(
					(Resolution.X/2) + CursorOffset.X - CurrentGuiInset.X,
					(Resolution.Y/2) + CursorOffset.Y - CurrentGuiInset.Y - 36
				)
			end
			
			-- Handle input playback
			if PlaybackInputs then
				local Signal = {}
				for _, Input in pairs(InputBeganQueue) do
					local Code = InputCodes[Input]
					if Code then
						keypress(Code)
					elseif Input == "b1" then
						mouse1press()
					elseif Input == "b2" then
						mouse2press()
					elseif Input == "u" or Input == "d" then
						table.insert(Signal, Input)
					end
				end
				for _, Input in pairs(InputEndedQueue) do
					local Code = InputCodes[Input]
					if Code then
						keyrelease(Code)
					elseif Input == "b1" then
						mouse1release()
					elseif Input == "b2" then
						mouse2release()
					end
				end
				if #Signal > 0 then
					SendSignal(table.concat(Signal, ","))
				end
			end
			
			local alignPos = HRP:FindFirstChild("TAS_AlignPosition")
			local alignOrient = HRP:FindFirstChild("TAS_AlignOrientation")
			local attachment = HRP:FindFirstChild("TAS_Attachment")
			
			if not attachment then
				attachment = Instance.new("Attachment")
				attachment.Name = "TAS_Attachment"
				attachment.Parent = HRP
			end
			
			if not alignPos then
				alignPos = Instance.new("AlignPosition")
				alignPos.Name = "TAS_AlignPosition"
				alignPos.Attachment0 = attachment
				alignPos.Mode = Enum.PositionAlignmentMode.OneAttachment
				alignPos.MaxForce = 9e9
				alignPos.MaxVelocity = 9e9
				alignPos.Responsiveness = 200
				alignPos.RigidityEnabled = true
				alignPos.Parent = HRP
			end
			
			if not alignOrient then
				alignOrient = Instance.new("AlignOrientation")
				alignOrient.Name = "TAS_AlignOrientation"
				alignOrient.Attachment0 = attachment
				alignOrient.Mode = Enum.OrientationAlignmentMode.OneAttachment
				alignOrient.MaxTorque = 9e9
				alignOrient.MaxAngularVelocity = 9e9
				alignOrient.Responsiveness = 200
				alignOrient.RigidityEnabled = true
				alignOrient.Parent = HRP
			end
			
			alignPos.Position = cache.hrpCFrame.Position
			alignOrient.CFrame = cache.hrpCFrame

			HRP.Velocity = cache.hrpVel
			HRP.RotVelocity = cache.hrpRotVel

			HRP.CFrame = cache.hrpCFrame
			
			ReplayTableIndex = ReplayTableIndex + 1

-- Clear old cache

			if ReplayTableIndex > 100 then
				frameCache[ReplayTableIndex - 100] = nil
				for animName, frameIndex in pairs(lastAnimations) do
					if frameIndex < ReplayTableIndex - 100 then
						lastAnimations[animName] = nil
					end
				end
			end
		else
			workspace.Gravity = DefaultGravity
			pcall(function()
				if Character and Character:FindFirstChild("HumanoidRootPart") then
					local HRP = Character.HumanoidRootPart
					if HRP:FindFirstChild("TAS_AlignPosition") then
						HRP.TAS_AlignPosition:Destroy()
					end
					if HRP:FindFirstChild("TAS_AlignOrientation") then
						HRP.TAS_AlignOrientation:Destroy()
					end
					if HRP:FindFirstChild("TAS_Attachment") then
						HRP.TAS_Attachment:Destroy()
					end
					Character.Humanoid.PlatformStand = false
				end
			end)
			frameCache = {}
			lastAnimations = {}
			currentAnimTracks = {}
		end
		
		RunService.Heartbeat:Wait()
	end
end)


spawn(function() -- Writing
	while true do
		--print(currentAnimSpeed)
		--print(GetShiftLockEnabled())
		--table.foreach(InputEndedQueue,print)
		--print(GetZoom())
		if Writing then
			if (not Character or not Character.Parent) or (not Character:FindFirstChild("HumanoidRootPart")) then
				if type(RecordingTable[#RecordingTable]) == "table" then
					table.insert(RecordingTable,0) -- Voided
				end
				RunService.RenderStepped:Wait()
				continue
			end
			if (Humanoid.Health == 0) then
				if type(RecordingTable[#RecordingTable]) == "table" then
					table.insert(RecordingTable,1) -- Dead
				end
				RunService.RenderStepped:Wait()
				continue
			end
			local Frame = {}
			Frame[1] = RoundTable(CFrameToTable(Character.HumanoidRootPart.CFrame),RoundDigits)
			Frame[2] = AnimationQueue
			Frame[3] = RoundNumber(currentAnimSpeed,RoundDigits)
			Frame[4] = Humanoid:GetState().Value
			Frame[5] = RoundTable(Vector3ToTable(Character.HumanoidRootPart.Velocity),RoundDigits)
			Frame[6] = RoundTable(Vector3ToTable(Character.HumanoidRootPart.RotVelocity),RoundDigits)
			Frame[7] = RoundTable(CFrameToTable(workspace.CurrentCamera.CFrame),RoundDigits)
			Frame[8] = RoundNumber(GetZoom(),RoundDigits)
			Frame[9] = pose
			Frame[10] = (GetShiftLockEnabled() and 1) or 0
			Frame[11] = RoundTable(Vector2ToTable(UserInputService:GetMouseLocation()),RoundDigits)
			Frame[12] = {InputBeganQueue,InputEndedQueue}
			table.insert(RecordingTable,Frame)
		end
		AnimationQueue = {}
		RunSpeed = 0
		ClimbSpeed = 0
		HumanoidStateQueue = {}
		if setfpscap then
			setfpscap(FPS)
		end
		RunService.RenderStepped:Wait()
	end
end)

-- Clear input queues
RunService.Heartbeat:Connect(function()
	InputBeganQueue = {}
	InputEndedQueue = {}
end)

spawn(function() -- Check if connected
	while true do
		wait(1)
		if not Reading then
			local Installed = IsInstalled()
			if Installed then
				ConnectedLabel.Text = "AHK folder found"
				ConnectedLabel.TextColor3 = Color3.new(0,0.8,0)
			else
				ConnectedLabel.Text = "AHK folder not found"
				ConnectedLabel.TextColor3 = Color3.new(0.8,0,0)
			end
		end
	end
end)

spawn(function() -- Update cursor
	while true do
		Cursor.Image = CursorIcon
		Cursor.Size = CursorSize
		local MouseLocation = UserInputService:GetMouseLocation()
		local CurrentGuiInset = GuiService:GetGuiInset()
		
		if ShiftLockEnabled or UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then
			
			Cursor.Position = UDim2.fromOffset(
				(Resolution.X / 2) + CursorOffset.X - CurrentGuiInset.X,
				(Resolution.Y / 2) + CursorOffset.Y - CurrentGuiInset.Y - 36
			)
		else
			
			Cursor.Position = UDim2.fromOffset(
				MouseLocation.X + CursorOffset.X - CurrentGuiInset.X,
				MouseLocation.Y + CursorOffset.Y - CurrentGuiInset.Y - 36
			)
		end
		RunService.RenderStepped:Wait()
	end
end)

--[[local oldprint
oldprint = hookfunction(print,function(...)
	if checkcaller() then
		return oldprint(...)
	end
end)]]

spawn(function() -- Handling freezing
	while true do
		if Frozen then
			Character.HumanoidRootPart.Anchored = true
			if FreezeFrame > 0 and FreezeFrame <= #ReplayTable then
				local RoundedFreezeFrame = RoundNumber(FreezeFrame,0)
                print(FreezeFrame, RoundedFreezeFrame)
				local Frame = ReplayTable[RoundedFreezeFrame]
				
				if type(Frame) == "table" then
					
					local AnimatePose -- -2
					local Animation -- -1
					
					
					for Index = RoundedFreezeFrame,1,-1 do
						if AnimatePose and Animation then
							break
						end
						local Frame = ReplayTable[Index]
						if type(Frame) == "table" then
							AnimatePose = Frame[9]
							Animation = Frame[2][#Frame[2]]
						end
					end
					
					
					local CurrentPressedKeys = {}
					for Index = RoundedFreezeFrame-math.max(FrameBacktrackCount,0),RoundedFreezeFrame do
						local Frame = ReplayTable[Index]
						if Frame and type(Frame) == "table" then
							local BeganInputs,EndedInputs = unpack(Frame[12])
							
							for _,Key in pairs(BeganInputs) do
								if Key ~= "u" and Key ~= "d" then -- Exclude scroll inputs
									CurrentPressedKeys[Key] = true
								end
							end
							
							for _,Key in pairs(EndedInputs) do
								-- Scroll inputs don't show up in EndedInputs
								CurrentPressedKeys[Key] = nil
							end
						end
					end
					
					-- Display keys pressed on WritingPressedKeysLabel
					WritingPressedKeysLabel.Text = "Writing Pressed keys: |"
					for Input,_ in pairs(CurrentPressedKeys) do
						WritingPressedKeysLabel.Text = WritingPressedKeysLabel.Text..Input.."|"
					end
					
					local HumanoidRootPartCFrame = TableToCFrame(Frame[1]) -- 4
					local AnimationSpeed = Frame[3] -- 9
					local HumanoidState = Frame[4] -- 1
					local HumanoidRootPartVelocity = TableToVector3(Frame[5]) -- 2
					local HumanoidRootPartRotVelocity = TableToVector3(Frame[6]) -- 3
					local CameraCFrame = TableToCFrame(Frame[7]) -- 5
					local Zoom = Frame[8] -- 6
					local ShiftLockEnabled = (Frame[10] == 1 and true) or false -- 7
					local MouseLocation = TableToVector2(Frame[11]) -- 8
					
					local CurrentState = Humanoid:GetState().Value
					
					-- -1
					if Animation then
						if Animation[1] == "walk" then
							if Humanoid.FloorMaterial ~= Enum.Material.Air and CurrentState ~= 3 then
								playAnimation("walk",Animation[2],Humanoid,true)
							end
						else
							playAnimation(Animation[1],Animation[2],Humanoid,true)
						end
					end
					pcall(setAnimationSpeed,AnimationSpeed) -- 9
					pose = AnimatePose -- -2
					
					Humanoid:ChangeState(HumanoidState) -- 1
					
					Character.HumanoidRootPart.Velocity = HumanoidRootPartVelocity -- 2
					Character.HumanoidRootPart.RotVelocity = HumanoidRootPartRotVelocity -- 3
					Character.HumanoidRootPart.CFrame = HumanoidRootPartCFrame -- 4
                    if not movecameraonfroze.Value then
                        workspace.CurrentCamera.CFrame = CameraCFrame --5
                        SetZoom(Zoom) -- 6
                        if ShiftLockEnabled ~= GetShiftLockEnabled() then
                            SetShiftLockEnabled(ShiftLockEnabled) -- 7
                        end
                    end
					if PlaybackMouseLocation then
						mousemoveabs(MouseLocation.X,MouseLocation.Y) -- 8
					end
				else
					-- Frame is not a table
					RunService.RenderStepped:Wait()
				end
			else
				--ConsoleMessage("FreezeFrame is",FreezeFrame,"(not in range)")
			end
		else
			pcall(function()
				Character.HumanoidRootPart.Anchored = false
			end)
		end
		RunService.RenderStepped:Wait()
	end
end)

do -- Set checkpoint
	ConsoleMessage("Loading from file...")
	ReplayTable = ReplayDecode(GetReplayFile()) -- Decode replay from file
	if not ReplayTable then
		ReplayTable = {}
		ConsoleMessage("There is no replay folder for",PlaceId)
	end
end

ConsoleMessage("Tasability",Version,"loaded in",RoundNumber(tick()-ExecutionTick,2),"seconds")
ConsoleMessage("Type help to see all commands")

Features & Information

TASability UPDATE ANIMATION FIXES AND MORE TAS/MACRO ANY R6 GAME/OBBY - FRAME ADVANCE - GO BACKWARD/FORWARD - SAVE/LOAD FILE - AND MORE -- UPDATE BY 9kwr --
#TAS#MACRO#macro#replay#REPLAY

Community Comments (2)

sa*avrilchuk1sa*avrilchuk1Dec 10, 2025

IT DOESN'T WORK FOR ME, I DON'T KNOW WHY. I PRESS THE BOUND KEYS BUT NOTHING HAPPENS, AND THIS SCRIPT WORKS ON SOLAR

serickserickDec 12, 2025

doesnt work on solara, also join discord for support

More Scripts for Universal Scripts