Epic Player
Key Required

Epic Player

⬆️ GET TO THE TOP! ⬆️ [NEW SHOP!]AltgameAltgame 128.1K views 227 executesUpdated Aug 10, 2022
Unrated
00
Lua Script•14519 lines (590761 chars)
local Library = loadstring(game:HttpGet("https://raw.githubusercontent.com/xHeptc/Kavo-UI-Library/main/source.lua"))()
local Window = Library.CreateLib("Epic Player", "Synapse")
local Tab = Window:NewTab("Epic sutff")
local Section = Tab:NewSection("super epic")
Section:NewButton("Collect any touchable objects", "get stuff", function()
    getgenv().autofarm = true -- change autofarm to any name you want

while getgenv().autofarm == true and task.wait() do -- you can change task.wait() to wait()
    pcall( -- script is not being interrupted if errors occur in functions called with pcall
        function() -- normal function
            for _, v in pairs(game:GetService("Workspace"):GetDescendants()) do -- change the path of game:GetService("Workspace") to the path you got from Dark Dex
                if v:IsA("TouchTransmitter") then
                    firetouchinterest(game.Players.LocalPlayer.Character.HumanoidRootPart, v.Parent, 0)
                    wait()
                    firetouchinterest(game.Players.LocalPlayer.Character.HumanoidRootPart, v.Parent, 1)
            end
        end
    end
    )
end
end)

Section:NewButton("Vape V4", "Don't vape", function()
    loadstring(game:HttpGet("https://raw.githubusercontent.com/7GrandDadPGN/VapeV4ForRoblox/main/NewMainScript.lua", true))()
end)

Section:NewButton("Script Roblox esp", "see all players", function()
    --- Tut

local esp_settings = { ---- table for esp settings 
    textsize = 8,
    colour = 255,255,255
}

local gui = Instance.new("BillboardGui")
local esp = Instance.new("TextLabel",gui) ---- new instances to make the billboard gui and the textlabel



gui.Name = "Cracked esp"; ---- properties of the esp
gui.ResetOnSpawn = false
gui.AlwaysOnTop = true;
gui.LightInfluence = 0;
gui.Size = UDim2.new(1.75, 0, 1.75, 0);
esp.BackgroundColor3 = Color3.fromRGB(255, 255, 255);
esp.Text = ""
esp.Size = UDim2.new(0.0001, 0.00001, 0.0001, 0.00001);
esp.BorderSizePixel = 4;
esp.BorderColor3 = Color3.new(esp_settings.colour)
esp.BorderSizePixel = 0
esp.Font = "GothamSemibold"
esp.TextSize = esp_settings.textsize
esp.TextColor3 = Color3.fromRGB(esp_settings.colour) -- text colour

game:GetService("RunService").RenderStepped:Connect(function() ---- loops faster than a while loop :)
    for i,v in pairs (game:GetService("Players"):GetPlayers()) do
        if v ~= game:GetService("Players").LocalPlayer and v.Character.Head:FindFirstChild("Cracked esp")==nil  then -- craeting checks for team check, local player etc
            esp.Text = "{"..v.Name.."}"
            gui:Clone().Parent = v.Character.Head
    end
end
end)
end)

Section:NewButton("Walk On Walls", "go on walls", function()
    -- https://youtu.be/Z5Cl47UdF1s
-- https://controlc.com/19a39f78 ControlC backup
--[[
local _p = game:WaitForChild("Players")
local _plr = _p.ChildAdded:Wait()
if _plr == _p.LocalPlayer then
	_plr.ChildAdded:Connect(function(cccc)
		if c.Name == "PlayerScriptsLoader" then
			c.Disabled = true
		end
	end)
end
]]
repeat wait()
    a = pcall(function()
        game:WaitForChild("Players").LocalPlayer:WaitForChild("PlayerScripts").ChildAdded:Connect(function(c)
            if c.Name == "PlayerScriptsLoader"then
                c.Disabled = true
            end
        end)
        end)
        if a == true then break end
    until true == false
    game:WaitForChild("Players").LocalPlayer:WaitForChild("PlayerScripts").ChildAdded:Connect(function(c)
        if c.Name == "PlayerScriptsLoader"then
            c.Disabled = true
        end
    end)
    
    
    function _CameraUI()
        local Players = game:GetService("Players")
        local TweenService = game:GetService("TweenService")
        
        local LocalPlayer = Players.LocalPlayer
        if not LocalPlayer then
            Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
            LocalPlayer = Players.LocalPlayer
        end
        
        local function waitForChildOfClass(parent, class)
            local child = parent:FindFirstChildOfClass(class)
            while not child or child.ClassName ~= class do
                child = parent.ChildAdded:Wait()
            end
            return child
        end
        
        local PlayerGui = waitForChildOfClass(LocalPlayer, "PlayerGui")
        
        local TOAST_OPEN_SIZE = UDim2.new(0, 326, 0, 58)
        local TOAST_CLOSED_SIZE = UDim2.new(0, 80, 0, 58)
        local TOAST_BACKGROUND_COLOR = Color3.fromRGB(32, 32, 32)
        local TOAST_BACKGROUND_TRANS = 0.4
        local TOAST_FOREGROUND_COLOR = Color3.fromRGB(200, 200, 200)
        local TOAST_FOREGROUND_TRANS = 0
        
        -- Convenient syntax for creating a tree of instanes
        local function create(className)
            return function(props)
                local inst = Instance.new(className)
                local parent = props.Parent
                props.Parent = nil
                for name, val in pairs(props) do
                    if type(name) == "string" then
                        inst[name] = val
                    else
                        val.Parent = inst
                    end
                end
                -- Only set parent after all other properties are initialized
                inst.Parent = parent
                return inst
            end
        end
        
        local initialized = false
        
        local uiRoot
        local toast
        local toastIcon
        local toastUpperText
        local toastLowerText
        
        local function initializeUI()
            assert(not initialized)
        
            uiRoot = create("ScreenGui"){
                Name = "RbxCameraUI",
                AutoLocalize = false,
                Enabled = true,
                DisplayOrder = -1, -- Appears behind default developer UI
                IgnoreGuiInset = false,
                ResetOnSpawn = false,
                ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
        
                create("ImageLabel"){
                    Name = "Toast",
                    Visible = false,
                    AnchorPoint = Vector2.new(0.5, 0),
                    BackgroundTransparency = 1,
                    BorderSizePixel = 0,
                    Position = UDim2.new(0.5, 0, 0, 8),
                    Size = TOAST_CLOSED_SIZE,
                    Image = "rbxasset://textures/ui/Camera/CameraToast9Slice.png",
                    ImageColor3 = TOAST_BACKGROUND_COLOR,
                    ImageRectSize = Vector2.new(6, 6),
                    ImageTransparency = 1,
                    ScaleType = Enum.ScaleType.Slice,
                    SliceCenter = Rect.new(3, 3, 3, 3),
                    ClipsDescendants = true,
        
                    create("Frame"){
                        Name = "IconBuffer",
                        BackgroundTransparency = 1,
                        BorderSizePixel = 0,
                        Position = UDim2.new(0, 0, 0, 0),
                        Size = UDim2.new(0, 80, 1, 0),
        
                        create("ImageLabel"){
                            Name = "Icon",
                            AnchorPoint = Vector2.new(0.5, 0.5),
                            BackgroundTransparency = 1,
                            Position = UDim2.new(0.5, 0, 0.5, 0),
                            Size = UDim2.new(0, 48, 0, 48),
                            ZIndex = 2,
                            Image = "rbxasset://textures/ui/Camera/CameraToastIcon.png",
                            ImageColor3 = TOAST_FOREGROUND_COLOR,
                            ImageTransparency = 1,
                        }
                    },
        
                    create("Frame"){
                        Name = "TextBuffer",
                        BackgroundTransparency = 1,
                        BorderSizePixel = 0,
                        Position = UDim2.new(0, 80, 0, 0),
                        Size = UDim2.new(1, -80, 1, 0),
                        ClipsDescendants = true,
        
                        create("TextLabel"){
                            Name = "Upper",
                            AnchorPoint = Vector2.new(0, 1),
                            BackgroundTransparency = 1,
                            Position = UDim2.new(0, 0, 0.5, 0),
                            Size = UDim2.new(1, 0, 0, 19),
                            Font = Enum.Font.GothamSemibold,
                            Text = "Camera control enabled",
                            TextColor3 = TOAST_FOREGROUND_COLOR,
                            TextTransparency = 1,
                            TextSize = 19,
                            TextXAlignment = Enum.TextXAlignment.Left,
                            TextYAlignment = Enum.TextYAlignment.Center,
                        },
        
                        create("TextLabel"){
                            Name = "Lower",
                            AnchorPoint = Vector2.new(0, 0),
                            BackgroundTransparency = 1,
                            Position = UDim2.new(0, 0, 0.5, 3),
                            Size = UDim2.new(1, 0, 0, 15),
                            Font = Enum.Font.Gotham,
                            Text = "Right mouse button to toggle",
                            TextColor3 = TOAST_FOREGROUND_COLOR,
                            TextTransparency = 1,
                            TextSize = 15,
                            TextXAlignment = Enum.TextXAlignment.Left,
                            TextYAlignment = Enum.TextYAlignment.Center,
                        },
                    },
                },
        
                Parent = PlayerGui,
            }
        
            toast = uiRoot.Toast
            toastIcon = toast.IconBuffer.Icon
            toastUpperText = toast.TextBuffer.Upper
            toastLowerText = toast.TextBuffer.Lower
        
            initialized = true
        end
        
        local CameraUI = {}
        
        do
            -- Instantaneously disable the toast or enable for opening later on. Used when switching camera modes.
            function CameraUI.setCameraModeToastEnabled(enabled)
                if not enabled and not initialized then
                    return
                end
        
                if not initialized then
                    initializeUI()
                end
        
                toast.Visible = enabled
                if not enabled then
                    CameraUI.setCameraModeToastOpen(false)
                end
            end
        
            local tweenInfo = TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
        
            -- Tween the toast in or out. Toast must be enabled with setCameraModeToastEnabled.
            function CameraUI.setCameraModeToastOpen(open)
                assert(initialized)
        
                TweenService:Create(toast, tweenInfo, {
                    Size = open and TOAST_OPEN_SIZE or TOAST_CLOSED_SIZE,
                    ImageTransparency = open and TOAST_BACKGROUND_TRANS or 1,
                }):Play()
        
                TweenService:Create(toastIcon, tweenInfo, {
                    ImageTransparency = open and TOAST_FOREGROUND_TRANS or 1,
                }):Play()
        
                TweenService:Create(toastUpperText, tweenInfo, {
                    TextTransparency = open and TOAST_FOREGROUND_TRANS or 1,
                }):Play()
        
                TweenService:Create(toastLowerText, tweenInfo, {
                    TextTransparency = open and TOAST_FOREGROUND_TRANS or 1,
                }):Play()
            end
        end
        
        return CameraUI
    end
    
    function _CameraToggleStateController()
        local Players = game:GetService("Players")
        local UserInputService = game:GetService("UserInputService")
        local GameSettings = UserSettings():GetService("UserGameSettings")
        
        local LocalPlayer = Players.LocalPlayer
        if not LocalPlayer then
            Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
            LocalPlayer = Players.LocalPlayer
        end
        
        local Mouse = LocalPlayer:GetMouse()
        
        local Input = _CameraInput()
        local CameraUI = _CameraUI()
        
        local lastTogglePan = false
        local lastTogglePanChange = tick()
        
        local CROSS_MOUSE_ICON = "rbxasset://textures/Cursors/CrossMouseIcon.png"
        
        local lockStateDirty = false
        local wasTogglePanOnTheLastTimeYouWentIntoFirstPerson = false
        local lastFirstPerson = false
        
        CameraUI.setCameraModeToastEnabled(false)
        
        return function(isFirstPerson)
            local togglePan = Input.getTogglePan()
            local toastTimeout = 3
        
            if isFirstPerson and togglePan ~= lastTogglePan then
                lockStateDirty = true
            end
        
            if lastTogglePan ~= togglePan or tick() - lastTogglePanChange > toastTimeout then
                local doShow = togglePan and tick() - lastTogglePanChange < toastTimeout
        
                CameraUI.setCameraModeToastOpen(doShow)
        
                if togglePan then
                    lockStateDirty = false
                end
                lastTogglePanChange = tick()
                lastTogglePan = togglePan
            end
        
            if isFirstPerson ~= lastFirstPerson then
                if isFirstPerson then
                    wasTogglePanOnTheLastTimeYouWentIntoFirstPerson = Input.getTogglePan()
                    Input.setTogglePan(true)
                elseif not lockStateDirty then
                    Input.setTogglePan(wasTogglePanOnTheLastTimeYouWentIntoFirstPerson)
                end
            end
        
            if isFirstPerson then
                if Input.getTogglePan() then
                    Mouse.Icon = CROSS_MOUSE_ICON
                    UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
                    --GameSettings.RotationType = Enum.RotationType.CameraRelative
                else
                    Mouse.Icon = ""
                    UserInputService.MouseBehavior = Enum.MouseBehavior.Default
                    --GameSettings.RotationType = Enum.RotationType.CameraRelative
                end
        
            elseif Input.getTogglePan() then
                Mouse.Icon = CROSS_MOUSE_ICON
                UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
                GameSettings.RotationType = Enum.RotationType.MovementRelative
        
            elseif Input.getHoldPan() then
                Mouse.Icon = ""
                UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
                GameSettings.RotationType = Enum.RotationType.MovementRelative
        
            else
                Mouse.Icon = ""
                UserInputService.MouseBehavior = Enum.MouseBehavior.Default
                GameSettings.RotationType = Enum.RotationType.MovementRelative
            end
        
            lastFirstPerson = isFirstPerson
        end
    end
    
    function _CameraInput()
        local UserInputService = game:GetService("UserInputService")
        
        local MB_TAP_LENGTH = 0.3 -- length of time for a short mouse button tap to be registered
        
        local rmbDown, rmbUp
        do
            local rmbDownBindable = Instance.new("BindableEvent")
            local rmbUpBindable = Instance.new("BindableEvent")
        
            rmbDown = rmbDownBindable.Event
            rmbUp = rmbUpBindable.Event
        
            UserInputService.InputBegan:Connect(function(input, gpe)
                if not gpe and input.UserInputType == Enum.UserInputType.MouseButton2 then
                    rmbDownBindable:Fire()
                end
            end)
        
            UserInputService.InputEnded:Connect(function(input, gpe)
                if input.UserInputType == Enum.UserInputType.MouseButton2 then
                    rmbUpBindable:Fire()
                end
            end)
        end
        
        local holdPan = false
        local togglePan = false
        local lastRmbDown = 0 -- tick() timestamp of the last right mouse button down event
        
        local CameraInput = {}
        
        function CameraInput.getHoldPan()
            return holdPan
        end
        
        function CameraInput.getTogglePan()
            return togglePan
        end
        
        function CameraInput.getPanning()
            return togglePan or holdPan
        end
        
        function CameraInput.setTogglePan(value)
            togglePan = value
        end
        
        local cameraToggleInputEnabled = false
        local rmbDownConnection
        local rmbUpConnection
        
        function CameraInput.enableCameraToggleInput()
            if cameraToggleInputEnabled then
                return
            end
            cameraToggleInputEnabled = true
        
            holdPan = false
            togglePan = false
        
            if rmbDownConnection then
                rmbDownConnection:Disconnect()
            end
        
            if rmbUpConnection then
                rmbUpConnection:Disconnect()
            end
        
            rmbDownConnection = rmbDown:Connect(function()
                holdPan = true
                lastRmbDown = tick()
            end)
        
            rmbUpConnection = rmbUp:Connect(function()
                holdPan = false
                if tick() - lastRmbDown < MB_TAP_LENGTH and (togglePan or UserInputService:GetMouseDelta().Magnitude < 2) then
                    togglePan = not togglePan
                end
            end)
        end
        
        function CameraInput.disableCameraToggleInput()
            if not cameraToggleInputEnabled then
                return
            end
            cameraToggleInputEnabled = false
        
            if rmbDownConnection then
                rmbDownConnection:Disconnect()
                rmbDownConnection = nil
            end
            if rmbUpConnection then
                rmbUpConnection:Disconnect()
                rmbUpConnection = nil
            end
        end
        
        return CameraInput
    end
    
    function _BaseCamera()
        --[[
            BaseCamera - Abstract base class for camera control modules
            2018 Camera Update - AllYourBlox
        --]]
        
        --[[ Local Constants ]]--
        local UNIT_Z = Vector3.new(0,0,1)
        local X1_Y0_Z1 = Vector3.new(1,0,1)	--Note: not a unit vector, used for projecting onto XZ plane
        
        local THUMBSTICK_DEADZONE = 0.2
        local DEFAULT_DISTANCE = 12.5	-- Studs
        local PORTRAIT_DEFAULT_DISTANCE = 25		-- Studs
        local FIRST_PERSON_DISTANCE_THRESHOLD = 1.0 -- Below this value, snap into first person
        
        local CAMERA_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
        
        -- Note: DotProduct check in CoordinateFrame::lookAt() prevents using values within about
        -- 8.11 degrees of the +/- Y axis, that's why these limits are currently 80 degrees
        local MIN_Y = math.rad(-80)
        local MAX_Y = math.rad(80)
        
        local TOUCH_ADJUST_AREA_UP = math.rad(30)
        local TOUCH_ADJUST_AREA_DOWN = math.rad(-15)
        
        local TOUCH_SENSITIVTY_ADJUST_MAX_Y = 2.1
        local TOUCH_SENSITIVTY_ADJUST_MIN_Y = 0.5
        
        local VR_ANGLE = math.rad(15)
        local VR_LOW_INTENSITY_ROTATION = Vector2.new(math.rad(15), 0)
        local VR_HIGH_INTENSITY_ROTATION = Vector2.new(math.rad(45), 0)
        local VR_LOW_INTENSITY_REPEAT = 0.1
        local VR_HIGH_INTENSITY_REPEAT = 0.4
        
        local ZERO_VECTOR2 = Vector2.new(0,0)
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        
        local TOUCH_SENSITIVTY = Vector2.new(0.00945 * math.pi, 0.003375 * math.pi)
        local MOUSE_SENSITIVITY = Vector2.new( 0.002 * math.pi, 0.0015 * math.pi )
        
        local SEAT_OFFSET = Vector3.new(0,5,0)
        local VR_SEAT_OFFSET = Vector3.new(0,4,0)
        local HEAD_OFFSET = Vector3.new(0,1.5,0)
        local R15_HEAD_OFFSET = Vector3.new(0, 1.5, 0)
        local R15_HEAD_OFFSET_NO_SCALING = Vector3.new(0, 2, 0)
        local HUMANOID_ROOT_PART_SIZE = Vector3.new(2, 2, 1)
        
        local GAMEPAD_ZOOM_STEP_1 = 0
        local GAMEPAD_ZOOM_STEP_2 = 10
        local GAMEPAD_ZOOM_STEP_3 = 20
        
        local PAN_SENSITIVITY = 20
        local ZOOM_SENSITIVITY_CURVATURE = 0.5
        
        local abs = math.abs
        local sign = math.sign
        
        local FFlagUserCameraToggle do
            local success, result = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
            end)
            FFlagUserCameraToggle = success and result
        end
        
        local FFlagUserDontAdjustSensitvityForPortrait do
            local success, result = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserDontAdjustSensitvityForPortrait")
            end)
            FFlagUserDontAdjustSensitvityForPortrait = success and result
        end
        
        local FFlagUserFixZoomInZoomOutDiscrepancy do
            local success, result = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserFixZoomInZoomOutDiscrepancy")
            end)
            FFlagUserFixZoomInZoomOutDiscrepancy = success and result
        end
        
        local Util = _CameraUtils()
        local ZoomController = _ZoomController()
        local CameraToggleStateController = _CameraToggleStateController()
        local CameraInput = _CameraInput()
        local CameraUI = _CameraUI()
        
        --[[ Roblox Services ]]--
        local Players = game:GetService("Players")
        local UserInputService = game:GetService("UserInputService")
        local StarterGui = game:GetService("StarterGui")
        local GuiService = game:GetService("GuiService")
        local ContextActionService = game:GetService("ContextActionService")
        local VRService = game:GetService("VRService")
        local UserGameSettings = UserSettings():GetService("UserGameSettings")
        
        local player = Players.LocalPlayer 
        
        --[[ The Module ]]--
        local BaseCamera = {}
        BaseCamera.__index = BaseCamera
        
        function BaseCamera.new()
            local self = setmetatable({}, BaseCamera)
        
            -- So that derived classes have access to this
            self.FIRST_PERSON_DISTANCE_THRESHOLD = FIRST_PERSON_DISTANCE_THRESHOLD
        
            self.cameraType = nil
            self.cameraMovementMode = nil
        
            self.lastCameraTransform = nil
            self.rotateInput = ZERO_VECTOR2
            self.userPanningCamera = false
            self.lastUserPanCamera = tick()
        
            self.humanoidRootPart = nil
            self.humanoidCache = {}
        
            -- Subject and position on last update call
            self.lastSubject = nil
            self.lastSubjectPosition = Vector3.new(0,5,0)
        
            -- These subject distance members refer to the nominal camera-to-subject follow distance that the camera
            -- is trying to maintain, not the actual measured value.
            -- The default is updated when screen orientation or the min/max distances change,
            -- to be sure the default is always in range and appropriate for the orientation.
            self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
            self.currentSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
        
            self.inFirstPerson = false
            self.inMouseLockedMode = false
            self.portraitMode = false
            self.isSmallTouchScreen = false
        
            -- Used by modules which want to reset the camera angle on respawn.
            self.resetCameraAngle = true
        
            self.enabled = false
        
            -- Input Event Connections
            self.inputBeganConn = nil
            self.inputChangedConn = nil
            self.inputEndedConn = nil
        
            self.startPos = nil
            self.lastPos = nil
            self.panBeginLook = nil
        
            self.panEnabled = true
            self.keyPanEnabled = true
            self.distanceChangeEnabled = true
        
            self.PlayerGui = nil
        
            self.cameraChangedConn = nil
            self.viewportSizeChangedConn = nil
        
            self.boundContextActions = {}
        
            -- VR Support
            self.shouldUseVRRotation = false
            self.VRRotationIntensityAvailable = false
            self.lastVRRotationIntensityCheckTime = 0
            self.lastVRRotationTime = 0
            self.vrRotateKeyCooldown = {}
            self.cameraTranslationConstraints = Vector3.new(1, 1, 1)
            self.humanoidJumpOrigin = nil
            self.trackingHumanoid = nil
            self.cameraFrozen = false
            self.subjectStateChangedConn = nil
        
            -- Gamepad support
            self.activeGamepad = nil
            self.gamepadPanningCamera = false
            self.lastThumbstickRotate = nil
            self.numOfSeconds = 0.7
            self.currentSpeed = 0
            self.maxSpeed = 6
            self.vrMaxSpeed = 4
            self.lastThumbstickPos = Vector2.new(0,0)
            self.ySensitivity = 0.65
            self.lastVelocity = nil
            self.gamepadConnectedConn = nil
            self.gamepadDisconnectedConn = nil
            self.currentZoomSpeed = 1.0
            self.L3ButtonDown = false
            self.dpadLeftDown = false
            self.dpadRightDown = false
        
            -- Touch input support
            self.isDynamicThumbstickEnabled = false
            self.fingerTouches = {}
            self.dynamicTouchInput = nil
            self.numUnsunkTouches = 0
            self.inputStartPositions = {}
            self.inputStartTimes = {}
            self.startingDiff = nil
            self.pinchBeginZoom = nil
            self.userPanningTheCamera = false
            self.touchActivateConn = nil
        
            -- Mouse locked formerly known as shift lock mode
            self.mouseLockOffset = ZERO_VECTOR3
        
            -- [[ NOTICE ]] --
            -- Initialization things used to always execute at game load time, but now these camera modules are instantiated
            -- when needed, so the code here may run well after the start of the game
        
            if player.Character then
                self:OnCharacterAdded(player.Character)
            end
        
            player.CharacterAdded:Connect(function(char)
                self:OnCharacterAdded(char)
            end)
        
            if self.cameraChangedConn then self.cameraChangedConn:Disconnect() end
            self.cameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
                self:OnCurrentCameraChanged()
            end)
            self:OnCurrentCameraChanged()
        
            if self.playerCameraModeChangeConn then self.playerCameraModeChangeConn:Disconnect() end
            self.playerCameraModeChangeConn = player:GetPropertyChangedSignal("CameraMode"):Connect(function()
                self:OnPlayerCameraPropertyChange()
            end)
        
            if self.minDistanceChangeConn then self.minDistanceChangeConn:Disconnect() end
            self.minDistanceChangeConn = player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(function()
                self:OnPlayerCameraPropertyChange()
            end)
        
            if self.maxDistanceChangeConn then self.maxDistanceChangeConn:Disconnect() end
            self.maxDistanceChangeConn = player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(function()
                self:OnPlayerCameraPropertyChange()
            end)
        
            if self.playerDevTouchMoveModeChangeConn then self.playerDevTouchMoveModeChangeConn:Disconnect() end
            self.playerDevTouchMoveModeChangeConn = player:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
                self:OnDevTouchMovementModeChanged()
            end)
            self:OnDevTouchMovementModeChanged() -- Init
        
            if self.gameSettingsTouchMoveMoveChangeConn then self.gameSettingsTouchMoveMoveChangeConn:Disconnect() end
            self.gameSettingsTouchMoveMoveChangeConn = UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
                self:OnGameSettingsTouchMovementModeChanged()
            end)
            self:OnGameSettingsTouchMovementModeChanged() -- Init
        
            UserGameSettings:SetCameraYInvertVisible()
            UserGameSettings:SetGamepadCameraSensitivityVisible()
        
            self.hasGameLoaded = game:IsLoaded()
            if not self.hasGameLoaded then
                self.gameLoadedConn = game.Loaded:Connect(function()
                    self.hasGameLoaded = true
                    self.gameLoadedConn:Disconnect()
                    self.gameLoadedConn = nil
                end)
            end
        
            self:OnPlayerCameraPropertyChange()
        
            return self
        end
        
        function BaseCamera:GetModuleName()
            return "BaseCamera"
        end
        
        function BaseCamera:OnCharacterAdded(char)
            self.resetCameraAngle = self.resetCameraAngle or self:GetEnabled()
            self.humanoidRootPart = nil
            if UserInputService.TouchEnabled then
                self.PlayerGui = player:WaitForChild("PlayerGui")
                for _, child in ipairs(char:GetChildren()) do
                    if child:IsA("Tool") then
                        self.isAToolEquipped = true
                    end
                end
                char.ChildAdded:Connect(function(child)
                    if child:IsA("Tool") then
                        self.isAToolEquipped = true
                    end
                end)
                char.ChildRemoved:Connect(function(child)
                    if child:IsA("Tool") then
                        self.isAToolEquipped = false
                    end
                end)
            end
        end
        
        function BaseCamera:GetHumanoidRootPart()
            if not self.humanoidRootPart then
                if player.Character then
                    local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
                    if humanoid then
                        self.humanoidRootPart = humanoid.RootPart
                    end
                end
            end
            return self.humanoidRootPart
        end
        
        function BaseCamera:GetBodyPartToFollow(humanoid, isDead)
            -- If the humanoid is dead, prefer the head part if one still exists as a sibling of the humanoid
            if humanoid:GetState() == Enum.HumanoidStateType.Dead then
                local character = humanoid.Parent
                if character and character:IsA("Model") then
                    return character:FindFirstChild("Head") or humanoid.RootPart
                end
            end
        
            return humanoid.RootPart
        end
        
        function BaseCamera:GetSubjectPosition()
            local result = self.lastSubjectPosition
            local camera = game.Workspace.CurrentCamera
            local cameraSubject = camera and camera.CameraSubject
        
            if cameraSubject then
                if cameraSubject:IsA("Humanoid") then
                    local humanoid = cameraSubject
                    local humanoidIsDead = humanoid:GetState() == Enum.HumanoidStateType.Dead
        
                    if VRService.VREnabled and humanoidIsDead and humanoid == self.lastSubject then
                        result = self.lastSubjectPosition
                    else
                        local bodyPartToFollow = humanoid.RootPart
        
                        -- If the humanoid is dead, prefer their head part as a follow target, if it exists
                        if humanoidIsDead then
                            if humanoid.Parent and humanoid.Parent:IsA("Model") then
                                bodyPartToFollow = humanoid.Parent:FindFirstChild("Head") or bodyPartToFollow
                            end
                        end
        
                        if bodyPartToFollow and bodyPartToFollow:IsA("BasePart") then
                            local heightOffset
                            if humanoid.RigType == Enum.HumanoidRigType.R15 then
                                if humanoid.AutomaticScalingEnabled then
                                    heightOffset = R15_HEAD_OFFSET
                                    if bodyPartToFollow == humanoid.RootPart then
                                        local rootPartSizeOffset = (humanoid.RootPart.Size.Y/2) - (HUMANOID_ROOT_PART_SIZE.Y/2)
                                        heightOffset = heightOffset + Vector3.new(0, rootPartSizeOffset, 0)
                                    end
                                else
                                    heightOffset = R15_HEAD_OFFSET_NO_SCALING
                                end
                            else
                                heightOffset = HEAD_OFFSET
                            end
        
                            if humanoidIsDead then
                                heightOffset = ZERO_VECTOR3
                            end
        
                            result = bodyPartToFollow.CFrame.p + bodyPartToFollow.CFrame:vectorToWorldSpace(heightOffset + humanoid.CameraOffset)
                        end
                    end
        
                elseif cameraSubject:IsA("VehicleSeat") then
                    local offset = SEAT_OFFSET
                    if VRService.VREnabled then
                        offset = VR_SEAT_OFFSET
                    end
                    result = cameraSubject.CFrame.p + cameraSubject.CFrame:vectorToWorldSpace(offset)
                elseif cameraSubject:IsA("SkateboardPlatform") then
                    result = cameraSubject.CFrame.p + SEAT_OFFSET
                elseif cameraSubject:IsA("BasePart") then
                    result = cameraSubject.CFrame.p
                elseif cameraSubject:IsA("Model") then
                    if cameraSubject.PrimaryPart then
                        result = cameraSubject:GetPrimaryPartCFrame().p
                    else
                        result = cameraSubject:GetModelCFrame().p
                    end
                end
            else
                -- cameraSubject is nil
                -- Note: Previous RootCamera did not have this else case and let self.lastSubject and self.lastSubjectPosition
                -- both get set to nil in the case of cameraSubject being nil. This function now exits here to preserve the
                -- last set valid values for these, as nil values are not handled cases
                return
            end
        
            self.lastSubject = cameraSubject
            self.lastSubjectPosition = result
        
            return result
        end
        
        function BaseCamera:UpdateDefaultSubjectDistance()
            if self.portraitMode then
                self.defaultSubjectDistance = math.clamp(PORTRAIT_DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
            else
                self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
            end
        end
        
        function BaseCamera:OnViewportSizeChanged()
            local camera = game.Workspace.CurrentCamera
            local size = camera.ViewportSize
            self.portraitMode = size.X < size.Y
            self.isSmallTouchScreen = UserInputService.TouchEnabled and (size.Y < 500 or size.X < 700)
        
            self:UpdateDefaultSubjectDistance()
        end
        
        -- Listener for changes to workspace.CurrentCamera
        function BaseCamera:OnCurrentCameraChanged()
            if UserInputService.TouchEnabled then
                if self.viewportSizeChangedConn then
                    self.viewportSizeChangedConn:Disconnect()
                    self.viewportSizeChangedConn = nil
                end
        
                local newCamera = game.Workspace.CurrentCamera
        
                if newCamera then
                    self:OnViewportSizeChanged()
                    self.viewportSizeChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
                        self:OnViewportSizeChanged()
                    end)
                end
            end
        
            -- VR support additions
            if self.cameraSubjectChangedConn then
                self.cameraSubjectChangedConn:Disconnect()
                self.cameraSubjectChangedConn = nil
            end
        
            local camera = game.Workspace.CurrentCamera
            if camera then
                self.cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
                    self:OnNewCameraSubject()
                end)
                self:OnNewCameraSubject()
            end
        end
        
        function BaseCamera:OnDynamicThumbstickEnabled()
            if UserInputService.TouchEnabled then
                self.isDynamicThumbstickEnabled = true
            end
        end
        
        function BaseCamera:OnDynamicThumbstickDisabled()
            self.isDynamicThumbstickEnabled = false
        end
        
        function BaseCamera:OnGameSettingsTouchMovementModeChanged()
            if player.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
                if (UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.DynamicThumbstick
                    or UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.Default) then
                    self:OnDynamicThumbstickEnabled()
                else
                    self:OnDynamicThumbstickDisabled()
                end
            end
        end
        
        function BaseCamera:OnDevTouchMovementModeChanged()
            if player.DevTouchMovementMode.Name == "DynamicThumbstick" then
                self:OnDynamicThumbstickEnabled()
            else
                self:OnGameSettingsTouchMovementModeChanged()
            end
        end
        
        function BaseCamera:OnPlayerCameraPropertyChange()
            -- This call forces re-evaluation of player.CameraMode and clamping to min/max distance which may have changed
            self:SetCameraToSubjectDistance(self.currentSubjectDistance)
        end
        
        function BaseCamera:GetCameraHeight()
            if VRService.VREnabled and not self.inFirstPerson then
                return math.sin(VR_ANGLE) * self.currentSubjectDistance
            end
            return 0
        end
        
        function BaseCamera:InputTranslationToCameraAngleChange(translationVector, sensitivity)
            if not FFlagUserDontAdjustSensitvityForPortrait then
                local camera = game.Workspace.CurrentCamera
                if camera and camera.ViewportSize.X > 0 and camera.ViewportSize.Y > 0 and (camera.ViewportSize.Y > camera.ViewportSize.X) then
                    -- Screen has portrait orientation, swap X and Y sensitivity
                    return translationVector * Vector2.new( sensitivity.Y, sensitivity.X)
                end
            end
            return translationVector * sensitivity
        end
        
        function BaseCamera:Enable(enable)
            if self.enabled ~= enable then
                self.enabled = enable
                if self.enabled then
                    self:ConnectInputEvents()
                    self:BindContextActions()
        
                    if player.CameraMode == Enum.CameraMode.LockFirstPerson then
                        self.currentSubjectDistance = 0.5
                        if not self.inFirstPerson then
                            self:EnterFirstPerson()
                        end
                    end
                else
                    self:DisconnectInputEvents()
                    self:UnbindContextActions()
                    -- Clean up additional event listeners and reset a bunch of properties
                    self:Cleanup()
                end
            end
        end
        
        function BaseCamera:GetEnabled()
            return self.enabled
        end
        
        function BaseCamera:OnInputBegan(input, processed)
            if input.UserInputType == Enum.UserInputType.Touch then
                self:OnTouchBegan(input, processed)
            elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
                self:OnMouse2Down(input, processed)
            elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
                self:OnMouse3Down(input, processed)
            end
        end
        
        function BaseCamera:OnInputChanged(input, processed)
            if input.UserInputType == Enum.UserInputType.Touch then
                self:OnTouchChanged(input, processed)
            elseif input.UserInputType == Enum.UserInputType.MouseMovement then
                self:OnMouseMoved(input, processed)
            end
        end
        
        function BaseCamera:OnInputEnded(input, processed)
            if input.UserInputType == Enum.UserInputType.Touch then
                self:OnTouchEnded(input, processed)
            elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
                self:OnMouse2Up(input, processed)
            elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
                self:OnMouse3Up(input, processed)
            end
        end
        
        function BaseCamera:OnPointerAction(wheel, pan, pinch, processed)
            if processed then
                return
            end
        
            if pan.Magnitude > 0 then
                local inversionVector = Vector2.new(1, UserGameSettings:GetCameraYInvertValue())
                local rotateDelta = self:InputTranslationToCameraAngleChange(PAN_SENSITIVITY*pan, MOUSE_SENSITIVITY)*inversionVector
                self.rotateInput = self.rotateInput + rotateDelta
            end
        
            local zoom = self.currentSubjectDistance
            local zoomDelta = -(wheel + pinch)
        
            if abs(zoomDelta) > 0 then
                local newZoom
                if self.inFirstPerson and zoomDelta > 0 then
                    newZoom = FIRST_PERSON_DISTANCE_THRESHOLD
                else
                    if FFlagUserFixZoomInZoomOutDiscrepancy then
                        if (zoomDelta > 0) then
                            newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
                        else
                            newZoom = (zoom + zoomDelta) / (1 - zoomDelta*ZOOM_SENSITIVITY_CURVATURE)
                        end
                    else
                        newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
                    end
                end
        
                self:SetCameraToSubjectDistance(newZoom)
            end
        end
        
        function BaseCamera:ConnectInputEvents()
            self.pointerActionConn = UserInputService.PointerAction:Connect(function(wheel, pan, pinch, processed)
                self:OnPointerAction(wheel, pan, pinch, processed)
            end)
        
            self.inputBeganConn = UserInputService.InputBegan:Connect(function(input, processed)
                self:OnInputBegan(input, processed)
            end)
        
            self.inputChangedConn = UserInputService.InputChanged:Connect(function(input, processed)
                self:OnInputChanged(input, processed)
            end)
        
            self.inputEndedConn = UserInputService.InputEnded:Connect(function(input, processed)
                self:OnInputEnded(input, processed)
            end)
        
            self.menuOpenedConn = GuiService.MenuOpened:connect(function()
                self:ResetInputStates()
            end)
        
            self.gamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
                if self.activeGamepad ~= gamepadEnum then return end
                self.activeGamepad = nil
                self:AssignActivateGamepad()
            end)
        
            self.gamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum)
                if self.activeGamepad == nil then
                    self:AssignActivateGamepad()
                end
            end)
        
            self:AssignActivateGamepad()
            if not FFlagUserCameraToggle then
                self:UpdateMouseBehavior()
            end
        end
        
        function BaseCamera:BindContextActions()
            self:BindGamepadInputActions()
            self:BindKeyboardInputActions()
        end
        
        function BaseCamera:AssignActivateGamepad()
            local connectedGamepads = UserInputService:GetConnectedGamepads()
            if #connectedGamepads > 0 then
                for i = 1, #connectedGamepads do
                    if self.activeGamepad == nil then
                        self.activeGamepad = connectedGamepads[i]
                    elseif connectedGamepads[i].Value < self.activeGamepad.Value then
                        self.activeGamepad = connectedGamepads[i]
                    end
                end
            end
        
            if self.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
                self.activeGamepad = Enum.UserInputType.Gamepad1
            end
        end
        
        function BaseCamera:DisconnectInputEvents()
            if self.inputBeganConn then
                self.inputBeganConn:Disconnect()
                self.inputBeganConn = nil
            end
            if self.inputChangedConn then
                self.inputChangedConn:Disconnect()
                self.inputChangedConn = nil
            end
            if self.inputEndedConn then
                self.inputEndedConn:Disconnect()
                self.inputEndedConn = nil
            end
        end
        
        function BaseCamera:UnbindContextActions()
            for i = 1, #self.boundContextActions do
                ContextActionService:UnbindAction(self.boundContextActions[i])
            end
            self.boundContextActions = {}
        end
        
        function BaseCamera:Cleanup()
            if self.pointerActionConn then
                self.pointerActionConn:Disconnect()
                self.pointerActionConn = nil
            end
            if self.menuOpenedConn then
                self.menuOpenedConn:Disconnect()
                self.menuOpenedConn = nil
            end
            if self.mouseLockToggleConn then
                self.mouseLockToggleConn:Disconnect()
                self.mouseLockToggleConn = nil
            end
            if self.gamepadConnectedConn then
                self.gamepadConnectedConn:Disconnect()
                self.gamepadConnectedConn = nil
            end
            if self.gamepadDisconnectedConn then
                self.gamepadDisconnectedConn:Disconnect()
                self.gamepadDisconnectedConn = nil
            end
            if self.subjectStateChangedConn then
                self.subjectStateChangedConn:Disconnect()
                self.subjectStateChangedConn = nil
            end
            if self.viewportSizeChangedConn then
                self.viewportSizeChangedConn:Disconnect()
                self.viewportSizeChangedConn = nil
            end
            if self.touchActivateConn then
                self.touchActivateConn:Disconnect()
                self.touchActivateConn = nil
            end
        
            self.turningLeft = false
            self.turningRight = false
            self.lastCameraTransform = nil
            self.lastSubjectCFrame = nil
            self.userPanningTheCamera = false
            self.rotateInput = Vector2.new()
            self.gamepadPanningCamera = Vector2.new(0,0)
        
            -- Reset input states
            self.startPos = nil
            self.lastPos = nil
            self.panBeginLook = nil
            self.isRightMouseDown = false
            self.isMiddleMouseDown = false
        
            self.fingerTouches = {}
            self.dynamicTouchInput = nil
            self.numUnsunkTouches = 0
        
            self.startingDiff = nil
            self.pinchBeginZoom = nil
        
            -- Unlock mouse for example if right mouse button was being held down
            if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
                UserInputService.MouseBehavior = Enum.MouseBehavior.Default
            end
        end
        
        -- This is called when settings menu is opened
        function BaseCamera:ResetInputStates()
            self.isRightMouseDown = false
            self.isMiddleMouseDown = false
            self:OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters
        
            if UserInputService.TouchEnabled then
                --[[menu opening was causing serious touch issues
                this should disable all active touch events if
                they're active when menu opens.]]
                for inputObject in pairs(self.fingerTouches) do
                    self.fingerTouches[inputObject] = nil
                end
                self.dynamicTouchInput = nil
                self.panBeginLook = nil
                self.startPos = nil
                self.lastPos = nil
                self.userPanningTheCamera = false
                self.startingDiff = nil
                self.pinchBeginZoom = nil
                self.numUnsunkTouches = 0
            end
        end
        
        function BaseCamera:GetGamepadPan(name, state, input)
            if input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
        --		if self.L3ButtonDown then
        --			-- L3 Thumbstick is depressed, right stick controls dolly in/out
        --			if (input.Position.Y > THUMBSTICK_DEADZONE) then
        --				self.currentZoomSpeed = 0.96
        --			elseif (input.Position.Y < -THUMBSTICK_DEADZONE) then
        --				self.currentZoomSpeed = 1.04
        --			else
        --				self.currentZoomSpeed = 1.00
        --			end
        --		else
                    if state == Enum.UserInputState.Cancel then
                        self.gamepadPanningCamera = ZERO_VECTOR2
                        return
                    end
        
                    local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
                    if inputVector.magnitude > THUMBSTICK_DEADZONE then
                        self.gamepadPanningCamera = Vector2.new(input.Position.X, -input.Position.Y)
                    else
                        self.gamepadPanningCamera = ZERO_VECTOR2
                    end
                --end
                return Enum.ContextActionResult.Sink
            end
            return Enum.ContextActionResult.Pass
        end
        
        function BaseCamera:DoKeyboardPanTurn(name, state, input)
            if not self.hasGameLoaded and VRService.VREnabled then
                return Enum.ContextActionResult.Pass
            end
        
            if state == Enum.UserInputState.Cancel then
                self.turningLeft = false
                self.turningRight = false
                return Enum.ContextActionResult.Sink
            end
        
            if self.panBeginLook == nil and self.keyPanEnabled then
                if input.KeyCode == Enum.KeyCode.Left then
                    self.turningLeft = state == Enum.UserInputState.Begin
                elseif input.KeyCode == Enum.KeyCode.Right then
                    self.turningRight = state == Enum.UserInputState.Begin
                end
                return Enum.ContextActionResult.Sink
            end
            return Enum.ContextActionResult.Pass
        end
        
        function BaseCamera:DoPanRotateCamera(rotateAngle)
            local angle = Util.RotateVectorByAngleAndRound(self:GetCameraLookVector() * Vector3.new(1,0,1), rotateAngle, math.pi*0.25)
            if angle ~= 0 then
                self.rotateInput = self.rotateInput + Vector2.new(angle, 0)
                self.lastUserPanCamera = tick()
                self.lastCameraTransform = nil
            end
        end
        
        function BaseCamera:DoGamepadZoom(name, state, input)
            if input.UserInputType == self.activeGamepad then
                if input.KeyCode == Enum.KeyCode.ButtonR3 then
                    if state == Enum.UserInputState.Begin then
                        if self.distanceChangeEnabled then
                            local dist = self:GetCameraToSubjectDistance()
        
                            if dist > (GAMEPAD_ZOOM_STEP_2 + GAMEPAD_ZOOM_STEP_3)/2 then
                                self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_2)
                            elseif dist > (GAMEPAD_ZOOM_STEP_1 + GAMEPAD_ZOOM_STEP_2)/2 then
                                self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_1)
                            else
                                self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_3)
                            end
                        end
                    end
                elseif input.KeyCode == Enum.KeyCode.DPadLeft then
                    self.dpadLeftDown = (state == Enum.UserInputState.Begin)
                elseif input.KeyCode == Enum.KeyCode.DPadRight then
                    self.dpadRightDown = (state == Enum.UserInputState.Begin)
                end
        
                if self.dpadLeftDown then
                    self.currentZoomSpeed = 1.04
                elseif self.dpadRightDown then
                    self.currentZoomSpeed = 0.96
                else
                    self.currentZoomSpeed = 1.00
                end
                return Enum.ContextActionResult.Sink
            end
            return Enum.ContextActionResult.Pass
        --	elseif input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonL3 then
        --		if (state == Enum.UserInputState.Begin) then
        --			self.L3ButtonDown = true
        --		elseif (state == Enum.UserInputState.End) then
        --			self.L3ButtonDown = false
        --			self.currentZoomSpeed = 1.00
        --		end
        --	end
        end
        
        function BaseCamera:DoKeyboardZoom(name, state, input)
            if not self.hasGameLoaded and VRService.VREnabled then
                return Enum.ContextActionResult.Pass
            end
        
            if state ~= Enum.UserInputState.Begin then
                return Enum.ContextActionResult.Pass
            end
        
            if self.distanceChangeEnabled and player.CameraMode ~= Enum.CameraMode.LockFirstPerson then
                if input.KeyCode == Enum.KeyCode.I then
                    self:SetCameraToSubjectDistance( self.currentSubjectDistance - 5 )
                elseif input.KeyCode == Enum.KeyCode.O then
                    self:SetCameraToSubjectDistance( self.currentSubjectDistance + 5 )
                end
                return Enum.ContextActionResult.Sink
            end
            return Enum.ContextActionResult.Pass
        end
        
        function BaseCamera:BindAction(actionName, actionFunc, createTouchButton, ...)
            table.insert(self.boundContextActions, actionName)
            ContextActionService:BindActionAtPriority(actionName, actionFunc, createTouchButton,
                CAMERA_ACTION_PRIORITY, ...)
        end
        
        function BaseCamera:BindGamepadInputActions()
            self:BindAction("BaseCameraGamepadPan", function(name, state, input) return self:GetGamepadPan(name, state, input) end,
                false, Enum.KeyCode.Thumbstick2)
            self:BindAction("BaseCameraGamepadZoom", function(name, state, input) return self:DoGamepadZoom(name, state, input) end,
                false, Enum.KeyCode.DPadLeft, Enum.KeyCode.DPadRight, Enum.KeyCode.ButtonR3)
        end
        
        function BaseCamera:BindKeyboardInputActions()
            self:BindAction("BaseCameraKeyboardPanArrowKeys", function(name, state, input) return self:DoKeyboardPanTurn(name, state, input) end,
                false, Enum.KeyCode.Left, Enum.KeyCode.Right)
            self:BindAction("BaseCameraKeyboardZoom", function(name, state, input) return self:DoKeyboardZoom(name, state, input) end,
                false, Enum.KeyCode.I, Enum.KeyCode.O)
        end
        
        local function isInDynamicThumbstickArea(input)
            local playerGui = player:FindFirstChildOfClass("PlayerGui")
            local touchGui = playerGui and playerGui:FindFirstChild("TouchGui")
            local touchFrame = touchGui and touchGui:FindFirstChild("TouchControlFrame")
            local thumbstickFrame = touchFrame and touchFrame:FindFirstChild("DynamicThumbstickFrame")
        
            if not thumbstickFrame then
                return false
            end
        
            local frameCornerTopLeft = thumbstickFrame.AbsolutePosition
            local frameCornerBottomRight = frameCornerTopLeft + thumbstickFrame.AbsoluteSize
            if input.Position.X >= frameCornerTopLeft.X and input.Position.Y >= frameCornerTopLeft.Y then
                if input.Position.X <= frameCornerBottomRight.X and input.Position.Y <= frameCornerBottomRight.Y then
                    return true
                end
            end
        
            return false
        end
        
        ---Adjusts the camera Y touch Sensitivity when moving away from the center and in the TOUCH_SENSITIVTY_ADJUST_AREA
        function BaseCamera:AdjustTouchSensitivity(delta, sensitivity)
            local cameraCFrame = game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame
            if not cameraCFrame then
                return sensitivity
            end
            local currPitchAngle = cameraCFrame:ToEulerAnglesYXZ()
        
            local multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y
            if currPitchAngle > TOUCH_ADJUST_AREA_UP and delta.Y < 0 then
                local fractionAdjust = (currPitchAngle - TOUCH_ADJUST_AREA_UP)/(MAX_Y - TOUCH_ADJUST_AREA_UP)
                fractionAdjust = 1 - (1 - fractionAdjust)^3
                multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y - fractionAdjust * (
                    TOUCH_SENSITIVTY_ADJUST_MAX_Y - TOUCH_SENSITIVTY_ADJUST_MIN_Y)
            elseif currPitchAngle < TOUCH_ADJUST_AREA_DOWN and delta.Y > 0 then
                local fractionAdjust = (currPitchAngle - TOUCH_ADJUST_AREA_DOWN)/(MIN_Y - TOUCH_ADJUST_AREA_DOWN)
                fractionAdjust = 1 - (1 - fractionAdjust)^3
                multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y - fractionAdjust * (
                    TOUCH_SENSITIVTY_ADJUST_MAX_Y - TOUCH_SENSITIVTY_ADJUST_MIN_Y)
            end
        
            return Vector2.new(
                sensitivity.X,
                sensitivity.Y * multiplierY
            )
        end
        
        function BaseCamera:OnTouchBegan(input, processed)
            local canUseDynamicTouch = self.isDynamicThumbstickEnabled and not processed
            if canUseDynamicTouch then
                if self.dynamicTouchInput == nil and isInDynamicThumbstickArea(input) then
                    -- First input in the dynamic thumbstick area should always be ignored for camera purposes
                    -- Even if the dynamic thumbstick does not process it immediately
                    self.dynamicTouchInput = input
                    return
                end
                self.fingerTouches[input] = processed
                self.inputStartPositions[input] = input.Position
                self.inputStartTimes[input] = tick()
                self.numUnsunkTouches = self.numUnsunkTouches + 1
            end
        end
        
        function BaseCamera:OnTouchChanged(input, processed)
            if self.fingerTouches[input] == nil then
                if self.isDynamicThumbstickEnabled then
                    return
                end
                self.fingerTouches[input] = processed
                if not processed then
                    self.numUnsunkTouches = self.numUnsunkTouches + 1
                end
            end
        
            if self.numUnsunkTouches == 1 then
                if self.fingerTouches[input] == false then
                    self.panBeginLook = self.panBeginLook or self:GetCameraLookVector()
                    self.startPos = self.startPos or input.Position
                    self.lastPos = self.lastPos or self.startPos
                    self.userPanningTheCamera = true
        
                    local delta = input.Position - self.lastPos
                    delta = Vector2.new(delta.X, delta.Y * UserGameSettings:GetCameraYInvertValue())
                    if self.panEnabled then
                        local adjustedTouchSensitivity = TOUCH_SENSITIVTY
                        self:AdjustTouchSensitivity(delta, TOUCH_SENSITIVTY)
        
                        local desiredXYVector = self:InputTranslationToCameraAngleChange(delta, adjustedTouchSensitivity)
                        self.rotateInput = self.rotateInput + desiredXYVector
                    end
                    self.lastPos = input.Position
                end
            else
                self.panBeginLook = nil
                self.startPos = nil
                self.lastPos = nil
                self.userPanningTheCamera = false
            end
            if self.numUnsunkTouches == 2 then
                local unsunkTouches = {}
                for touch, wasSunk in pairs(self.fingerTouches) do
                    if not wasSunk then
                        table.insert(unsunkTouches, touch)
                    end
                end
                if #unsunkTouches == 2 then
                    local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
                    if self.startingDiff and self.pinchBeginZoom then
                        local scale = difference / math.max(0.01, self.startingDiff)
                        local clampedScale = math.clamp(scale, 0.1, 10)
                        if self.distanceChangeEnabled then
                            self:SetCameraToSubjectDistance(self.pinchBeginZoom / clampedScale)
                        end
                    else
                        self.startingDiff = difference
                        self.pinchBeginZoom = self:GetCameraToSubjectDistance()
                    end
                end
            else
                self.startingDiff = nil
                self.pinchBeginZoom = nil
            end
        end
        
        function BaseCamera:OnTouchEnded(input, processed)
            if input == self.dynamicTouchInput then
                self.dynamicTouchInput = nil
                return
            end
        
            if self.fingerTouches[input] == false then
                if self.numUnsunkTouches == 1 then
                    self.panBeginLook = nil
                    self.startPos = nil
                    self.lastPos = nil
                    self.userPanningTheCamera = false
                elseif self.numUnsunkTouches == 2 then
                    self.startingDiff = nil
                    self.pinchBeginZoom = nil
                end
            end
        
            if self.fingerTouches[input] ~= nil and self.fingerTouches[input] == false then
                self.numUnsunkTouches = self.numUnsunkTouches - 1
            end
            self.fingerTouches[input] = nil
            self.inputStartPositions[input] = nil
            self.inputStartTimes[input] = nil
        end
        
        function BaseCamera:OnMouse2Down(input, processed)
            if processed then return end
        
            self.isRightMouseDown = true
            self:OnMousePanButtonPressed(input, processed)
        end
        
        function BaseCamera:OnMouse2Up(input, processed)
            self.isRightMouseDown = false
            self:OnMousePanButtonReleased(input, processed)
        end
        
        function BaseCamera:OnMouse3Down(input, processed)
            if processed then return end
        
            self.isMiddleMouseDown = true
            self:OnMousePanButtonPressed(input, processed)
        end
        
        function BaseCamera:OnMouse3Up(input, processed)
            self.isMiddleMouseDown = false
            self:OnMousePanButtonReleased(input, processed)
        end
        
        function BaseCamera:OnMouseMoved(input, processed)
            if not self.hasGameLoaded and VRService.VREnabled then
                return
            end
        
            local inputDelta = input.Delta
            inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * UserGameSettings:GetCameraYInvertValue())
        
            local isInputPanning = FFlagUserCameraToggle and CameraInput.getPanning()
            local isBeginLook = self.startPos and self.lastPos and self.panBeginLook
            local isPanning = isBeginLook or self.inFirstPerson or self.inMouseLockedMode or isInputPanning
        
            if self.panEnabled and isPanning then
                local desiredXYVector = self:InputTranslationToCameraAngleChange(inputDelta, MOUSE_SENSITIVITY)
                self.rotateInput = self.rotateInput + desiredXYVector
            end
        
            if self.startPos and self.lastPos and self.panBeginLook then
                self.lastPos = self.lastPos + input.Delta
            end
        end
        
        function BaseCamera:OnMousePanButtonPressed(input, processed)
            if processed then return end
            if not FFlagUserCameraToggle then
                self:UpdateMouseBehavior()
            end
            self.panBeginLook = self.panBeginLook or self:GetCameraLookVector()
            self.startPos = self.startPos or input.Position
            self.lastPos = self.lastPos or self.startPos
            self.userPanningTheCamera = true
        end
        
        function BaseCamera:OnMousePanButtonReleased(input, processed)
            if not FFlagUserCameraToggle then
                self:UpdateMouseBehavior()
            end
            if not (self.isRightMouseDown or self.isMiddleMouseDown) then
                self.panBeginLook = nil
                self.startPos = nil
                self.lastPos = nil
                self.userPanningTheCamera = false
            end
        end
        
        function BaseCamera:UpdateMouseBehavior()
            if FFlagUserCameraToggle and self.isCameraToggle then
                CameraUI.setCameraModeToastEnabled(true)
                CameraInput.enableCameraToggleInput()
                CameraToggleStateController(self.inFirstPerson)
            else
                if FFlagUserCameraToggle then
                    CameraUI.setCameraModeToastEnabled(false)
                    CameraInput.disableCameraToggleInput()
                end
                -- first time transition to first person mode or mouse-locked third person
                if self.inFirstPerson or self.inMouseLockedMode then
                    --UserGameSettings.RotationType = Enum.RotationType.CameraRelative
                    UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
                else
                    UserGameSettings.RotationType = Enum.RotationType.MovementRelative
                    if self.isRightMouseDown or self.isMiddleMouseDown then
                        UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
                    else
                        UserInputService.MouseBehavior = Enum.MouseBehavior.Default
                    end
                end
            end
        end
        
        function BaseCamera:UpdateForDistancePropertyChange()
            -- Calling this setter with the current value will force checking that it is still
            -- in range after a change to the min/max distance limits
            self:SetCameraToSubjectDistance(self.currentSubjectDistance)
        end
        
        function BaseCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
            local lastSubjectDistance = self.currentSubjectDistance
        
            -- By default, camera modules will respect LockFirstPerson and override the currentSubjectDistance with 0
            -- regardless of what Player.CameraMinZoomDistance is set to, so that first person can be made
            -- available by the developer without needing to allow players to mousewheel dolly into first person.
            -- Some modules will override this function to remove or change first-person capability.
            if player.CameraMode == Enum.CameraMode.LockFirstPerson then
                self.currentSubjectDistance = 0.5
                if not self.inFirstPerson then
                    self:EnterFirstPerson()
                end
            else
                local newSubjectDistance = math.clamp(desiredSubjectDistance, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
                if newSubjectDistance < FIRST_PERSON_DISTANCE_THRESHOLD then
                    self.currentSubjectDistance = 0.5
                    if not self.inFirstPerson then
                        self:EnterFirstPerson()
                    end
                else
                    self.currentSubjectDistance = newSubjectDistance
                    if self.inFirstPerson then
                        self:LeaveFirstPerson()
                    end
                end
            end
        
            -- Pass target distance and zoom direction to the zoom controller
            ZoomController.SetZoomParameters(self.currentSubjectDistance, math.sign(desiredSubjectDistance - lastSubjectDistance))
        
            -- Returned only for convenience to the caller to know the outcome
            return self.currentSubjectDistance
        end
        
        function BaseCamera:SetCameraType( cameraType )
            --Used by derived classes
            self.cameraType = cameraType
        end
        
        function BaseCamera:GetCameraType()
            return self.cameraType
        end
        
        -- Movement mode standardized to Enum.ComputerCameraMovementMode values
        function BaseCamera:SetCameraMovementMode( cameraMovementMode )
            self.cameraMovementMode = cameraMovementMode
        end
        
        function BaseCamera:GetCameraMovementMode()
            return self.cameraMovementMode
        end
        
        function BaseCamera:SetIsMouseLocked(mouseLocked)
            self.inMouseLockedMode = mouseLocked
            if not FFlagUserCameraToggle then
                self:UpdateMouseBehavior()
            end
        end
        
        function BaseCamera:GetIsMouseLocked()
            return self.inMouseLockedMode
        end
        
        function BaseCamera:SetMouseLockOffset(offsetVector)
            self.mouseLockOffset = offsetVector
        end
        
        function BaseCamera:GetMouseLockOffset()
            return self.mouseLockOffset
        end
        
        function BaseCamera:InFirstPerson()
            return self.inFirstPerson
        end
        
        function BaseCamera:EnterFirstPerson()
            -- Overridden in ClassicCamera, the only module which supports FirstPerson
        end
        
        function BaseCamera:LeaveFirstPerson()
            -- Overridden in ClassicCamera, the only module which supports FirstPerson
        end
        
        -- Nominal distance, set by dollying in and out with the mouse wheel or equivalent, not measured distance
        function BaseCamera:GetCameraToSubjectDistance()
            return self.currentSubjectDistance
        end
        
        -- Actual measured distance to the camera Focus point, which may be needed in special circumstances, but should
        -- never be used as the starting point for updating the nominal camera-to-subject distance (self.currentSubjectDistance)
        -- since that is a desired target value set only by mouse wheel (or equivalent) input, PopperCam, and clamped to min max camera distance
        function BaseCamera:GetMeasuredDistanceToFocus()
            local camera = game.Workspace.CurrentCamera
            if camera then
                return (camera.CoordinateFrame.p - camera.Focus.p).magnitude
            end
            return nil
        end
        
        function BaseCamera:GetCameraLookVector()
            return game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame.lookVector or UNIT_Z
        end
        
        -- Replacements for RootCamera:RotateCamera() which did not actually rotate the camera
        -- suppliedLookVector is not normally passed in, it's used only by Watch camera
        function BaseCamera:CalculateNewLookCFrame(suppliedLookVector)
            local currLookVector = suppliedLookVector or self:GetCameraLookVector()
            local currPitchAngle = math.asin(currLookVector.y)
            local yTheta = math.clamp(self.rotateInput.y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
            local constrainedRotateInput = Vector2.new(self.rotateInput.x, yTheta)
            local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
            local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)
            return newLookCFrame
        end
        function BaseCamera:CalculateNewLookVector(suppliedLookVector)
            local newLookCFrame = self:CalculateNewLookCFrame(suppliedLookVector)
            return newLookCFrame.lookVector
        end
        
        function BaseCamera:CalculateNewLookVectorVR()
            local subjectPosition = self:GetSubjectPosition()
            local vecToSubject = (subjectPosition - game.Workspace.CurrentCamera.CFrame.p)
            local currLookVector = (vecToSubject * X1_Y0_Z1).unit
            local vrRotateInput = Vector2.new(self.rotateInput.x, 0)
            local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
            local yawRotatedVector = (CFrame.Angles(0, -vrRotateInput.x, 0) * startCFrame * CFrame.Angles(-vrRotateInput.y,0,0)).lookVector
            return (yawRotatedVector * X1_Y0_Z1).unit
        end
        
        function BaseCamera:GetHumanoid()
            local character = player and player.Character
            if character then
                local resultHumanoid = self.humanoidCache[player]
                if resultHumanoid and resultHumanoid.Parent == character then
                    return resultHumanoid
                else
                    self.humanoidCache[player] = nil -- Bust Old Cache
                    local humanoid = character:FindFirstChildOfClass("Humanoid")
                    if humanoid then
                        self.humanoidCache[player] = humanoid
                    end
                    return humanoid
                end
            end
            return nil
        end
        
        function BaseCamera:GetHumanoidPartToFollow(humanoid, humanoidStateType)
            if humanoidStateType == Enum.HumanoidStateType.Dead then
                local character = humanoid.Parent
                if character then
                    return character:FindFirstChild("Head") or humanoid.Torso
                else
                    return humanoid.Torso
                end
            else
                return humanoid.Torso
            end
        end
        
        function BaseCamera:UpdateGamepad()
            local gamepadPan = self.gamepadPanningCamera
            if gamepadPan and (self.hasGameLoaded or not VRService.VREnabled) then
                gamepadPan = Util.GamepadLinearToCurve(gamepadPan)
                local currentTime = tick()
                if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
                    self.userPanningTheCamera = true
                elseif gamepadPan == ZERO_VECTOR2 then
                    self.lastThumbstickRotate = nil
                    if self.lastThumbstickPos == ZERO_VECTOR2 then
                        self.currentSpeed = 0
                    end
                end
        
                local finalConstant = 0
        
                if self.lastThumbstickRotate then
                    if VRService.VREnabled then
                        self.currentSpeed = self.vrMaxSpeed
                    else
                        local elapsedTime = (currentTime - self.lastThumbstickRotate) * 10
                        self.currentSpeed = self.currentSpeed + (self.maxSpeed * ((elapsedTime*elapsedTime)/self.numOfSeconds))
        
                        if self.currentSpeed > self.maxSpeed then self.currentSpeed = self.maxSpeed end
        
                        if self.lastVelocity then
                            local velocity = (gamepadPan - self.lastThumbstickPos)/(currentTime - self.lastThumbstickRotate)
                            local velocityDeltaMag = (velocity - self.lastVelocity).magnitude
        
                            if velocityDeltaMag > 12 then
                                self.currentSpeed = self.currentSpeed * (20/velocityDeltaMag)
                                if self.currentSpeed > self.maxSpeed then self.currentSpeed = self.maxSpeed end
                            end
                        end
                    end
        
                    finalConstant = UserGameSettings.GamepadCameraSensitivity * self.currentSpeed
                    self.lastVelocity = (gamepadPan - self.lastThumbstickPos)/(currentTime - self.lastThumbstickRotate)
                end
        
                self.lastThumbstickPos = gamepadPan
                self.lastThumbstickRotate = currentTime
        
                return Vector2.new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * self.ySensitivity * UserGameSettings:GetCameraYInvertValue())
            end
        
            return ZERO_VECTOR2
        end
        
        -- [[ VR Support Section ]] --
        
        function BaseCamera:ApplyVRTransform()
            if not VRService.VREnabled then
                return
            end
        
            --we only want this to happen in first person VR
            local rootJoint = self.humanoidRootPart and self.humanoidRootPart:FindFirstChild("RootJoint")
            if not rootJoint then
                return
            end
        
            local cameraSubject = game.Workspace.CurrentCamera.CameraSubject
            local isInVehicle = cameraSubject and cameraSubject:IsA("VehicleSeat")
        
            if self.inFirstPerson and not isInVehicle then
                local vrFrame = VRService:GetUserCFrame(Enum.UserCFrame.Head)
                local vrRotation = vrFrame - vrFrame.p
                rootJoint.C0 = CFrame.new(vrRotation:vectorToObjectSpace(vrFrame.p)) * CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
            else
                rootJoint.C0 = CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
            end
        end
        
        function BaseCamera:IsInFirstPerson()
            return self.inFirstPerson
        end
        
        function BaseCamera:ShouldUseVRRotation()
            if not VRService.VREnabled then
                return false
            end
        
            if not self.VRRotationIntensityAvailable and tick() - self.lastVRRotationIntensityCheckTime < 1 then
                return false
            end
        
            local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
            self.VRRotationIntensityAvailable = success and vrRotationIntensity ~= nil
            self.lastVRRotationIntensityCheckTime = tick()
        
            self.shouldUseVRRotation = success and vrRotationIntensity ~= nil and vrRotationIntensity ~= "Smooth"
        
            return self.shouldUseVRRotation
        end
        
        function BaseCamera:GetVRRotationInput()
            local vrRotateSum = ZERO_VECTOR2
            local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
        
            if not success then
                return
            end
        
            local vrGamepadRotation = self.GamepadPanningCamera or ZERO_VECTOR2
            local delayExpired = (tick() - self.lastVRRotationTime) >= self:GetRepeatDelayValue(vrRotationIntensity)
        
            if math.abs(vrGamepadRotation.x) >= self:GetActivateValue() then
                if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]) then
                    local sign = 1
                    if vrGamepadRotation.x < 0 then
                        sign = -1
                    end
                    vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity) * sign
                    self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = true
                end
            elseif math.abs(vrGamepadRotation.x) < self:GetActivateValue() - 0.1 then
                self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = nil
            end
            if self.turningLeft then
                if delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Left] then
                    vrRotateSum = vrRotateSum - self:GetRotateAmountValue(vrRotationIntensity)
                    self.vrRotateKeyCooldown[Enum.KeyCode.Left] = true
                end
            else
                self.vrRotateKeyCooldown[Enum.KeyCode.Left] = nil
            end
            if self.turningRight then
                if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Right]) then
                    vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity)
                    self.vrRotateKeyCooldown[Enum.KeyCode.Right] = true
                end
            else
                self.vrRotateKeyCooldown[Enum.KeyCode.Right] = nil
            end
        
            if vrRotateSum ~= ZERO_VECTOR2 then
                self.lastVRRotationTime = tick()
            end
        
            return vrRotateSum
        end
        
        function BaseCamera:CancelCameraFreeze(keepConstraints)
            if not keepConstraints then
                self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 1, self.cameraTranslationConstraints.z)
            end
            if self.cameraFrozen then
                self.trackingHumanoid = nil
                self.cameraFrozen = false
            end
        end
        
        function BaseCamera:StartCameraFreeze(subjectPosition, humanoidToTrack)
            if not self.cameraFrozen then
                self.humanoidJumpOrigin = subjectPosition
                self.trackingHumanoid = humanoidToTrack
                self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 0, self.cameraTranslationConstraints.z)
                self.cameraFrozen = true
            end
        end
        
        function BaseCamera:OnNewCameraSubject()
            if self.subjectStateChangedConn then
                self.subjectStateChangedConn:Disconnect()
                self.subjectStateChangedConn = nil
            end
        
            local humanoid = workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject
            if self.trackingHumanoid ~= humanoid then
                self:CancelCameraFreeze()
            end
            if humanoid and humanoid:IsA("Humanoid") then
                self.subjectStateChangedConn = humanoid.StateChanged:Connect(function(oldState, newState)
                    if VRService.VREnabled and newState == Enum.HumanoidStateType.Jumping and not self.inFirstPerson then
                        self:StartCameraFreeze(self:GetSubjectPosition(), humanoid)
                    elseif newState ~= Enum.HumanoidStateType.Jumping and newState ~= Enum.HumanoidStateType.Freefall then
                        self:CancelCameraFreeze(true)
                    end
                end)
            end
        end
        
        function BaseCamera:GetVRFocus(subjectPosition, timeDelta)
            local lastFocus = self.LastCameraFocus or subjectPosition
            if not self.cameraFrozen then
                self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, math.min(1, self.cameraTranslationConstraints.y + 0.42 * timeDelta), self.cameraTranslationConstraints.z)
            end
        
            local newFocus
            if self.cameraFrozen and self.humanoidJumpOrigin and self.humanoidJumpOrigin.y > lastFocus.y then
                newFocus = CFrame.new(Vector3.new(subjectPosition.x, math.min(self.humanoidJumpOrigin.y, lastFocus.y + 5 * timeDelta), subjectPosition.z))
            else
                newFocus = CFrame.new(Vector3.new(subjectPosition.x, lastFocus.y, subjectPosition.z):lerp(subjectPosition, self.cameraTranslationConstraints.y))
            end
        
            if self.cameraFrozen then
                -- No longer in 3rd person
                if self.inFirstPerson then -- not VRService.VREnabled
                    self:CancelCameraFreeze()
                end
                -- This case you jumped off a cliff and want to keep your character in view
                -- 0.5 is to fix floating point error when not jumping off cliffs
                if self.humanoidJumpOrigin and subjectPosition.y < (self.humanoidJumpOrigin.y - 0.5) then
                    self:CancelCameraFreeze()
                end
            end
        
            return newFocus
        end
        
        function BaseCamera:GetRotateAmountValue(vrRotationIntensity)
            vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
            if vrRotationIntensity then
                if vrRotationIntensity == "Low" then
                    return VR_LOW_INTENSITY_ROTATION
                elseif vrRotationIntensity == "High" then
                    return VR_HIGH_INTENSITY_ROTATION
                end
            end
            return ZERO_VECTOR2
        end
        
        function BaseCamera:GetRepeatDelayValue(vrRotationIntensity)
            vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
            if vrRotationIntensity then
                if vrRotationIntensity == "Low" then
                    return VR_LOW_INTENSITY_REPEAT
                elseif vrRotationIntensity == "High" then
                    return VR_HIGH_INTENSITY_REPEAT
                end
            end
            return 0
        end
        
        function BaseCamera:Update(dt)
            error("BaseCamera:Update() This is a virtual function that should never be getting called.", 2)
        end
        
        BaseCamera.UpCFrame = CFrame.new()
        
        function BaseCamera:UpdateUpCFrame(cf)
            self.UpCFrame = cf
        end
        local ZERO = Vector3.new(0, 0, 0)
        function BaseCamera:CalculateNewLookCFrame(suppliedLookVector)
            local currLookVector = suppliedLookVector or self:GetCameraLookVector()
            currLookVector = self.UpCFrame:VectorToObjectSpace(currLookVector)
            
            local currPitchAngle = math.asin(currLookVector.y)
            local yTheta = math.clamp(self.rotateInput.y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
            local constrainedRotateInput = Vector2.new(self.rotateInput.x, yTheta)
            local startCFrame = CFrame.new(ZERO, currLookVector)
            local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)
            
            return newLookCFrame
        end
        
        return BaseCamera
    end
    
    function _BaseOcclusion()
        --[[ The Module ]]--
        local BaseOcclusion = {}
        BaseOcclusion.__index = BaseOcclusion
        setmetatable(BaseOcclusion, {
            __call = function(_, ...)
                return BaseOcclusion.new(...)
            end
        })
        
        function BaseOcclusion.new()
            local self = setmetatable({}, BaseOcclusion)
            return self
        end
        
        -- Called when character is added
        function BaseOcclusion:CharacterAdded(char, player)
        end
        
        -- Called when character is about to be removed
        function BaseOcclusion:CharacterRemoving(char, player)
        end
        
        function BaseOcclusion:OnCameraSubjectChanged(newSubject)
        end
        
        --[[ Derived classes are required to override and implement all of the following functions ]]--
        function BaseOcclusion:GetOcclusionMode()
            -- Must be overridden in derived classes to return an Enum.DevCameraOcclusionMode value
            warn("BaseOcclusion GetOcclusionMode must be overridden by derived classes")
            return nil
        end
        
        function BaseOcclusion:Enable(enabled)
            warn("BaseOcclusion Enable must be overridden by derived classes")
        end
        
        function BaseOcclusion:Update(dt, desiredCameraCFrame, desiredCameraFocus)
            warn("BaseOcclusion Update must be overridden by derived classes")
            return desiredCameraCFrame, desiredCameraFocus
        end
        
        return BaseOcclusion
    end
    
    function _Popper()
        
        local Players = game:GetService("Players")
        
        local camera = game.Workspace.CurrentCamera
        
        local min = math.min
        local tan = math.tan
        local rad = math.rad
        local inf = math.huge
        local ray = Ray.new
        
        local function getTotalTransparency(part)
            return 1 - (1 - part.Transparency)*(1 - part.LocalTransparencyModifier)
        end
        
        local function eraseFromEnd(t, toSize)
            for i = #t, toSize + 1, -1 do
                t[i] = nil
            end
        end
        
        local nearPlaneZ, projX, projY do
            local function updateProjection()
                local fov = rad(camera.FieldOfView)
                local view = camera.ViewportSize
                local ar = view.X/view.Y
        
                projY = 2*tan(fov/2)
                projX = ar*projY
            end
        
            camera:GetPropertyChangedSignal("FieldOfView"):Connect(updateProjection)
            camera:GetPropertyChangedSignal("ViewportSize"):Connect(updateProjection)
        
            updateProjection()
        
            nearPlaneZ = camera.NearPlaneZ
            camera:GetPropertyChangedSignal("NearPlaneZ"):Connect(function()
                nearPlaneZ = camera.NearPlaneZ
            end)
        end
        
        local blacklist = {} do
            local charMap = {}
        
            local function refreshIgnoreList()
                local n = 1
                blacklist = {}
                for _, character in pairs(charMap) do
                    blacklist[n] = character
                    n = n + 1
                end
            end
        
            local function playerAdded(player)
                local function characterAdded(character)
                    charMap[player] = character
                    refreshIgnoreList()
                end
                local function characterRemoving()
                    charMap[player] = nil
                    refreshIgnoreList()
                end
        
                player.CharacterAdded:Connect(characterAdded)
                player.CharacterRemoving:Connect(characterRemoving)
                if player.Character then
                    characterAdded(player.Character)
                end
            end
        
            local function playerRemoving(player)
                charMap[player] = nil
                refreshIgnoreList()
            end
        
            Players.PlayerAdded:Connect(playerAdded)
            Players.PlayerRemoving:Connect(playerRemoving)
        
            for _, player in ipairs(Players:GetPlayers()) do
                playerAdded(player)
            end
            refreshIgnoreList()
        end
        
        --------------------------------------------------------------------------------------------
        -- Popper uses the level geometry find an upper bound on subject-to-camera distance.
        --
        -- Hard limits are applied immediately and unconditionally. They are generally caused
        -- when level geometry intersects with the near plane (with exceptions, see below).
        --
        -- Soft limits are only applied under certain conditions.
        -- They are caused when level geometry occludes the subject without actually intersecting
        -- with the near plane at the target distance.
        --
        -- Soft limits can be promoted to hard limits and hard limits can be demoted to soft limits.
        -- We usually don"t want the latter to happen.
        --
        -- A soft limit will be promoted to a hard limit if an obstruction
        -- lies between the current and target camera positions.
        --------------------------------------------------------------------------------------------
        
        local subjectRoot
        local subjectPart
        
        camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
            local subject = camera.CameraSubject
            if subject:IsA("Humanoid") then
                subjectPart = subject.RootPart
            elseif subject:IsA("BasePart") then
                subjectPart = subject
            else
                subjectPart = nil
            end
        end)
        
        local function canOcclude(part)
            -- Occluders must be:
            -- 1. Opaque
            -- 2. Interactable
            -- 3. Not in the same assembly as the subject
        
            return
                getTotalTransparency(part) < 0.25 and
                part.CanCollide and
                subjectRoot ~= (part:GetRootPart() or part) and
                not part:IsA("TrussPart")
        end
        
        -- Offsets for the volume visibility test
        local SCAN_SAMPLE_OFFSETS = {
            Vector2.new( 0.4, 0.0),
            Vector2.new(-0.4, 0.0),
            Vector2.new( 0.0,-0.4),
            Vector2.new( 0.0, 0.4),
            Vector2.new( 0.0, 0.2),
        }
        
        --------------------------------------------------------------------------------
        -- Piercing raycasts
        
        local function getCollisionPoint(origin, dir)
            local originalSize = #blacklist
            repeat
                local hitPart, hitPoint = workspace:FindPartOnRayWithIgnoreList(
                    ray(origin, dir), blacklist, false, true
                )
        
                if hitPart then
                    if hitPart.CanCollide then
                        eraseFromEnd(blacklist, originalSize)
                        return hitPoint, true
                    end
                    blacklist[#blacklist + 1] = hitPart
                end
            until not hitPart
        
            eraseFromEnd(blacklist, originalSize)
            return origin + dir, false
        end
        
        --------------------------------------------------------------------------------
        
        local function queryPoint(origin, unitDir, dist, lastPos)
            debug.profilebegin("queryPoint")
        
            local originalSize = #blacklist
        
            dist = dist + nearPlaneZ
            local target = origin + unitDir*dist
        
            local softLimit = inf
            local hardLimit = inf
            local movingOrigin = origin
        
            repeat
                local entryPart, entryPos = workspace:FindPartOnRayWithIgnoreList(ray(movingOrigin, target - movingOrigin), blacklist, false, true)
        
                if entryPart then
                    if canOcclude(entryPart) then
                        local wl = {entryPart}
                        local exitPart = workspace:FindPartOnRayWithWhitelist(ray(target, entryPos - target), wl, true)
        
                        local lim = (entryPos - origin).Magnitude
        
                        if exitPart then
                            local promote = false
                            if lastPos then
                                promote =
                                    workspace:FindPartOnRayWithWhitelist(ray(lastPos, target - lastPos), wl, true) or
                                    workspace:FindPartOnRayWithWhitelist(ray(target, lastPos - target), wl, true)
                            end
        
                            if promote then
                                -- Ostensibly a soft limit, but the camera has passed through it in the last frame, so promote to a hard limit.
                                hardLimit = lim
                            elseif dist < softLimit then
                                -- Trivial soft limit
                                softLimit = lim
                            end
                        else
                            -- Trivial hard limit
                            hardLimit = lim
                        end
                    end
        
                    blacklist[#blacklist + 1] = entryPart
                    movingOrigin = entryPos - unitDir*1e-3
                end
            until hardLimit < inf or not entryPart
        
            eraseFromEnd(blacklist, originalSize)
        
            debug.profileend()
            return softLimit - nearPlaneZ, hardLimit - nearPlaneZ
        end
        
        local function queryViewport(focus, dist)
            debug.profilebegin("queryViewport")
        
            local fP =  focus.p
            local fX =  focus.rightVector
            local fY =  focus.upVector
            local fZ = -focus.lookVector
        
            local viewport = camera.ViewportSize
        
            local hardBoxLimit = inf
            local softBoxLimit = inf
        
            -- Center the viewport on the PoI, sweep points on the edge towards the target, and take the minimum limits
            for viewX = 0, 1 do
                local worldX = fX*((viewX - 0.5)*projX)
        
                for viewY = 0, 1 do
                    local worldY = fY*((viewY - 0.5)*projY)
        
                    local origin = fP + nearPlaneZ*(worldX + worldY)
                    local lastPos = camera:ViewportPointToRay(
                        viewport.x*viewX,
                        viewport.y*viewY
                    ).Origin
        
                    local softPointLimit, hardPointLimit = queryPoint(origin, fZ, dist, lastPos)
        
                    if hardPointLimit < hardBoxLimit then
                        hardBoxLimit = hardPointLimit
                    end
                    if softPointLimit < softBoxLimit then
                        softBoxLimit = softPointLimit
                    end
                end
            end
            debug.profileend()
        
            return softBoxLimit, hardBoxLimit
        end
        
        local function testPromotion(focus, dist, focusExtrapolation)
            debug.profilebegin("testPromotion")
        
            local fP = focus.p
            local fX = focus.rightVector
            local fY = focus.upVector
            local fZ = -focus.lookVector
        
            do
                -- Dead reckoning the camera rotation and focus
                debug.profilebegin("extrapolate")
        
                local SAMPLE_DT = 0.0625
                local SAMPLE_MAX_T = 1.25
        
                local maxDist = (getCollisionPoint(fP, focusExtrapolation.posVelocity*SAMPLE_MAX_T) - fP).Magnitude
                -- Metric that decides how many samples to take
                local combinedSpeed = focusExtrapolation.posVelocity.magnitude
        
                for dt = 0, min(SAMPLE_MAX_T, focusExtrapolation.rotVelocity.magnitude + maxDist/combinedSpeed), SAMPLE_DT do
                    local cfDt = focusExtrapolation.extrapolate(dt) -- Extrapolated CFrame at time dt
        
                    if queryPoint(cfDt.p, -cfDt.lookVector, dist) >= dist then
                        return false
                    end
                end
        
                debug.profileend()
            end
        
            do
                -- Test screen-space offsets from the focus for the presence of soft limits
                debug.profilebegin("testOffsets")
        
                for _, offset in ipairs(SCAN_SAMPLE_OFFSETS) do
                    local scaledOffset = offset
                    local pos = getCollisionPoint(fP, fX*scaledOffset.x + fY*scaledOffset.y)
                    if queryPoint(pos, (fP + fZ*dist - pos).Unit, dist) == inf then
                        return false
                    end
                end
        
                debug.profileend()
            end
        
            debug.profileend()
            return true
        end
        
        local function Popper(focus, targetDist, focusExtrapolation)
            debug.profilebegin("popper")
        
            subjectRoot = subjectPart and subjectPart:GetRootPart() or subjectPart
        
            local dist = targetDist
            local soft, hard = queryViewport(focus, targetDist)
            if hard < dist then
                dist = hard
            end
            if soft < dist and testPromotion(focus, targetDist, focusExtrapolation) then
                dist = soft
            end
        
            subjectRoot = nil
        
            debug.profileend()
            return dist
        end
        
        return Popper
    end
    
    function _ZoomController()
        local ZOOM_STIFFNESS = 4.5
        local ZOOM_DEFAULT = 12.5
        local ZOOM_ACCELERATION = 0.0375
        
        local MIN_FOCUS_DIST = 0.5
        local DIST_OPAQUE = 1
        
        local Popper = _Popper()
        
        local clamp = math.clamp
        local exp = math.exp
        local min = math.min
        local max = math.max
        local pi = math.pi
        
        local cameraMinZoomDistance, cameraMaxZoomDistance do
            local Player = game:GetService("Players").LocalPlayer
        
            local function updateBounds()
                cameraMinZoomDistance = Player.CameraMinZoomDistance
                cameraMaxZoomDistance = Player.CameraMaxZoomDistance
            end
        
            updateBounds()
        
            Player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(updateBounds)
            Player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(updateBounds)
        end
        
        local ConstrainedSpring = {} do
            ConstrainedSpring.__index = ConstrainedSpring
        
            function ConstrainedSpring.new(freq, x, minValue, maxValue)
                x = clamp(x, minValue, maxValue)
                return setmetatable({
                    freq = freq, -- Undamped frequency (Hz)
                    x = x, -- Current position
                    v = 0, -- Current velocity
                    minValue = minValue, -- Minimum bound
                    maxValue = maxValue, -- Maximum bound
                    goal = x, -- Goal position
                }, ConstrainedSpring)
            end
        
            function ConstrainedSpring:Step(dt)
                local freq = self.freq*2*pi -- Convert from Hz to rad/s
                local x = self.x
                local v = self.v
                local minValue = self.minValue
                local maxValue = self.maxValue
                local goal = self.goal
        
                -- Solve the spring ODE for position and velocity after time t, assuming critical damping:
                --   2*f*x'[t] + x''[t] = f^2*(g - x[t])
                -- Knowns are x[0] and x'[0].
                -- Solve for x[t] and x'[t].
        
                local offset = goal - x
                local step = freq*dt
                local decay = exp(-step)
        
                local x1 = goal + (v*dt - offset*(step + 1))*decay
                local v1 = ((offset*freq - v)*step + v)*decay
        
                -- Constrain
                if x1 < minValue then
                    x1 = minValue
                    v1 = 0
                elseif x1 > maxValue then
                    x1 = maxValue
                    v1 = 0
                end
        
                self.x = x1
                self.v = v1
        
                return x1
            end
        end
        
        local zoomSpring = ConstrainedSpring.new(ZOOM_STIFFNESS, ZOOM_DEFAULT, MIN_FOCUS_DIST, cameraMaxZoomDistance)
        
        local function stepTargetZoom(z, dz, zoomMin, zoomMax)
            z = clamp(z + dz*(1 + z*ZOOM_ACCELERATION), zoomMin, zoomMax)
            if z < DIST_OPAQUE then
                z = dz <= 0 and zoomMin or DIST_OPAQUE
            end
            return z
        end
        
        local zoomDelta = 0
        
        local Zoom = {} do
            function Zoom.Update(renderDt, focus, extrapolation)
                local poppedZoom = math.huge
        
                if zoomSpring.goal > DIST_OPAQUE then
                    -- Make a pessimistic estimate of zoom distance for this step without accounting for poppercam
                    local maxPossibleZoom = max(
                        zoomSpring.x,
                        stepTargetZoom(zoomSpring.goal, zoomDelta, cameraMinZoomDistance, cameraMaxZoomDistance)
                    )
        
                    -- Run the Popper algorithm on the feasible zoom range, [MIN_FOCUS_DIST, maxPossibleZoom]
                    poppedZoom = Popper(
                        focus*CFrame.new(0, 0, MIN_FOCUS_DIST),
                        maxPossibleZoom - MIN_FOCUS_DIST,
                        extrapolation
                    ) + MIN_FOCUS_DIST
                end
        
                zoomSpring.minValue = MIN_FOCUS_DIST
                zoomSpring.maxValue = min(cameraMaxZoomDistance, poppedZoom)
        
                return zoomSpring:Step(renderDt)
            end
        
            function Zoom.SetZoomParameters(targetZoom, newZoomDelta)
                zoomSpring.goal = targetZoom
                zoomDelta = newZoomDelta
            end
        end
        
        return Zoom
    end
    
    function _MouseLockController()
        --[[ Constants ]]--
        local DEFAULT_MOUSE_LOCK_CURSOR = "rbxasset://textures/MouseLockedCursor.png"
        
        local CONTEXT_ACTION_NAME = "MouseLockSwitchAction"
        local MOUSELOCK_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
        
        --[[ Services ]]--
        local PlayersService = game:GetService("Players")
        local ContextActionService = game:GetService("ContextActionService")
        local Settings = UserSettings()	-- ignore warning
        local GameSettings = Settings.GameSettings
        local Mouse = PlayersService.LocalPlayer:GetMouse()
        
        --[[ The Module ]]--
        local MouseLockController = {}
        MouseLockController.__index = MouseLockController
        
        function MouseLockController.new()
            local self = setmetatable({}, MouseLockController)
        
            self.isMouseLocked = false
            self.savedMouseCursor = nil
            self.boundKeys = {Enum.KeyCode.LeftShift, Enum.KeyCode.RightShift} -- defaults
        
            self.mouseLockToggledEvent = Instance.new("BindableEvent")
        
            local boundKeysObj = script:FindFirstChild("BoundKeys")
            if (not boundKeysObj) or (not boundKeysObj:IsA("StringValue")) then
                -- If object with correct name was found, but it's not a StringValue, destroy and replace
                if boundKeysObj then
                    boundKeysObj:Destroy()
                end
        
                boundKeysObj = Instance.new("StringValue")
                boundKeysObj.Name = "BoundKeys"
                boundKeysObj.Value = "LeftShift,RightShift"
                boundKeysObj.Parent = script
            end
        
            if boundKeysObj then
                boundKeysObj.Changed:Connect(function(value)
                    self:OnBoundKeysObjectChanged(value)
                end)
                self:OnBoundKeysObjectChanged(boundKeysObj.Value) -- Initial setup call
            end
        
            -- Watch for changes to user's ControlMode and ComputerMovementMode settings and update the feature availability accordingly
            GameSettings.Changed:Connect(function(property)
                if property == "ControlMode" or property == "ComputerMovementMode" then
                    self:UpdateMouseLockAvailability()
                end
            end)
        
            -- Watch for changes to DevEnableMouseLock and update the feature availability accordingly
            PlayersService.LocalPlayer:GetPropertyChangedSignal("DevEnableMouseLock"):Connect(function()
                self:UpdateMouseLockAvailability()
            end)
        
            -- Watch for changes to DevEnableMouseLock and update the feature availability accordingly
            PlayersService.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(function()
                self:UpdateMouseLockAvailability()
            end)
        
            self:UpdateMouseLockAvailability()
        
            return self
        end
        
        function MouseLockController:GetIsMouseLocked()
            return self.isMouseLocked
        end
        
        function MouseLockController:GetBindableToggleEvent()
            return self.mouseLockToggledEvent.Event
        end
        
        function MouseLockController:GetMouseLockOffset()
            local offsetValueObj = script:FindFirstChild("CameraOffset")
            if offsetValueObj and offsetValueObj:IsA("Vector3Value") then
                return offsetValueObj.Value
            else
                -- If CameraOffset object was found but not correct type, destroy
                if offsetValueObj then
                    offsetValueObj:Destroy()
                end
                offsetValueObj = Instance.new("Vector3Value")
                offsetValueObj.Name = "CameraOffset"
                offsetValueObj.Value = Vector3.new(1.75,0,0) -- Legacy Default Value
                offsetValueObj.Parent = script
            end
        
            if offsetValueObj and offsetValueObj.Value then
                return offsetValueObj.Value
            end
        
            return Vector3.new(1.75,0,0)
        end
        
        function MouseLockController:UpdateMouseLockAvailability()
            local devAllowsMouseLock = PlayersService.LocalPlayer.DevEnableMouseLock
            local devMovementModeIsScriptable = PlayersService.LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.Scriptable
            local userHasMouseLockModeEnabled = GameSettings.ControlMode == Enum.ControlMode.MouseLockSwitch
            local userHasClickToMoveEnabled =  GameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove
            local MouseLockAvailable = devAllowsMouseLock and userHasMouseLockModeEnabled and not userHasClickToMoveEnabled and not devMovementModeIsScriptable
        
            if MouseLockAvailable~=self.enabled then
                self:EnableMouseLock(MouseLockAvailable)
            end
        end
        
        function MouseLockController:OnBoundKeysObjectChanged(newValue)
            self.boundKeys = {} -- Overriding defaults, note: possibly with nothing at all if boundKeysObj.Value is "" or contains invalid values
            for token in string.gmatch(newValue,"[^%s,]+") do
                for _, keyEnum in pairs(Enum.KeyCode:GetEnumItems()) do
                    if token == keyEnum.Name then
                        self.boundKeys[#self.boundKeys+1] = keyEnum
                        break
                    end
                end
            end
            self:UnbindContextActions()
            self:BindContextActions()
        end
        
        --[[ Local Functions ]]--
        function MouseLockController:OnMouseLockToggled()
            self.isMouseLocked = not self.isMouseLocked
        
            if self.isMouseLocked then
                local cursorImageValueObj = script:FindFirstChild("CursorImage")
                if cursorImageValueObj and cursorImageValueObj:IsA("StringValue") and cursorImageValueObj.Value then
                    self.savedMouseCursor = Mouse.Icon
                    Mouse.Icon = cursorImageValueObj.Value
                else
                    if cursorImageValueObj then
                        cursorImageValueObj:Destroy()
                    end
                    cursorImageValueObj = Instance.new("StringValue")
                    cursorImageValueObj.Name = "CursorImage"
                    cursorImageValueObj.Value = DEFAULT_MOUSE_LOCK_CURSOR
                    cursorImageValueObj.Parent = script
                    self.savedMouseCursor = Mouse.Icon
                    Mouse.Icon = DEFAULT_MOUSE_LOCK_CURSOR
                end
            else
                if self.savedMouseCursor then
                    Mouse.Icon = self.savedMouseCursor
                    self.savedMouseCursor = nil
                end
            end
        
            self.mouseLockToggledEvent:Fire()
        end
        
        function MouseLockController:DoMouseLockSwitch(name, state, input)
            if state == Enum.UserInputState.Begin then
                self:OnMouseLockToggled()
                return Enum.ContextActionResult.Sink
            end
            return Enum.ContextActionResult.Pass
        end
        
        function MouseLockController:BindContextActions()
            ContextActionService:BindActionAtPriority(CONTEXT_ACTION_NAME, function(name, state, input)
                return self:DoMouseLockSwitch(name, state, input)
            end, false, MOUSELOCK_ACTION_PRIORITY, unpack(self.boundKeys))
        end
        
        function MouseLockController:UnbindContextActions()
            ContextActionService:UnbindAction(CONTEXT_ACTION_NAME)
        end
        
        function MouseLockController:IsMouseLocked()
            return self.enabled and self.isMouseLocked
        end
        
        function MouseLockController:EnableMouseLock(enable)
            if enable ~= self.enabled then
        
                self.enabled = enable
        
                if self.enabled then
                    -- Enabling the mode
                    self:BindContextActions()
                else
                    -- Disabling
                    -- Restore mouse cursor
                    if Mouse.Icon~="" then
                        Mouse.Icon = ""
                    end
        
                    self:UnbindContextActions()
        
                    -- If the mode is disabled while being used, fire the event to toggle it off
                    if self.isMouseLocked then
                        self.mouseLockToggledEvent:Fire()
                    end
        
                    self.isMouseLocked = false
                end
        
            end
        end
        
        return MouseLockController
    end
    
    function _TransparencyController()
        
        local MAX_TWEEN_RATE = 2.8 -- per second
        
        local Util = _CameraUtils()
        
        --[[ The Module ]]--
        local TransparencyController = {}
        TransparencyController.__index = TransparencyController
        
        function TransparencyController.new()
            local self = setmetatable({}, TransparencyController)
        
            self.lastUpdate = tick()
            self.transparencyDirty = false
            self.enabled = false
            self.lastTransparency = nil
        
            self.descendantAddedConn, self.descendantRemovingConn = nil, nil
            self.toolDescendantAddedConns = {}
            self.toolDescendantRemovingConns = {}
            self.cachedParts = {}
        
            return self
        end
        
        
        function TransparencyController:HasToolAncestor(object)
            if object.Parent == nil then return false end
            return object.Parent:IsA('Tool') or self:HasToolAncestor(object.Parent)
        end
        
        function TransparencyController:IsValidPartToModify(part)
            if part:IsA('BasePart') or part:IsA('Decal') then
                return not self:HasToolAncestor(part)
            end
            return false
        end
        
        function TransparencyController:CachePartsRecursive(object)
            if object then
                if self:IsValidPartToModify(object) then
                    self.cachedParts[object] = true
                    self.transparencyDirty = true
                end
                for _, child in pairs(object:GetChildren()) do
                    self:CachePartsRecursive(child)
                end
            end
        end
        
        function TransparencyController:TeardownTransparency()
            for child, _ in pairs(self.cachedParts) do
                child.LocalTransparencyModifier = 0
            end
            self.cachedParts = {}
            self.transparencyDirty = true
            self.lastTransparency = nil
        
            if self.descendantAddedConn then
                self.descendantAddedConn:disconnect()
                self.descendantAddedConn = nil
            end
            if self.descendantRemovingConn then
                self.descendantRemovingConn:disconnect()
                self.descendantRemovingConn = nil
            end
            for object, conn in pairs(self.toolDescendantAddedConns) do
                conn:Disconnect()
                self.toolDescendantAddedConns[object] = nil
            end
            for object, conn in pairs(self.toolDescendantRemovingConns) do
                conn:Disconnect()
                self.toolDescendantRemovingConns[object] = nil
            end
        end
        
        function TransparencyController:SetupTransparency(character)
            self:TeardownTransparency()
        
            if self.descendantAddedConn then self.descendantAddedConn:disconnect() end
            self.descendantAddedConn = character.DescendantAdded:Connect(function(object)
                -- This is a part we want to invisify
                if self:IsValidPartToModify(object) then
                    self.cachedParts[object] = true
                    self.transparencyDirty = true
                -- There is now a tool under the character
                elseif object:IsA('Tool') then
                    if self.toolDescendantAddedConns[object] then self.toolDescendantAddedConns[object]:Disconnect() end
                    self.toolDescendantAddedConns[object] = object.DescendantAdded:Connect(function(toolChild)
                        self.cachedParts[toolChild] = nil
                        if toolChild:IsA('BasePart') or toolChild:IsA('Decal') then
                            -- Reset the transparency
                            toolChild.LocalTransparencyModifier = 0
                        end
                    end)
                    if self.toolDescendantRemovingConns[object] then self.toolDescendantRemovingConns[object]:disconnect() end
                    self.toolDescendantRemovingConns[object] = object.DescendantRemoving:Connect(function(formerToolChild)
                        wait() -- wait for new parent
                        if character and formerToolChild and formerToolChild:IsDescendantOf(character) then
                            if self:IsValidPartToModify(formerToolChild) then
                                self.cachedParts[formerToolChild] = true
                                self.transparencyDirty = true
                            end
                        end
                    end)
                end
            end)
            if self.descendantRemovingConn then self.descendantRemovingConn:disconnect() end
            self.descendantRemovingConn = character.DescendantRemoving:connect(function(object)
                if self.cachedParts[object] then
                    self.cachedParts[object] = nil
                    -- Reset the transparency
                    object.LocalTransparencyModifier = 0
                end
            end)
            self:CachePartsRecursive(character)
        end
        
        
        function TransparencyController:Enable(enable)
            if self.enabled ~= enable then
                self.enabled = enable
                self:Update()
            end
        end
        
        function TransparencyController:SetSubject(subject)
            local character = nil
            if subject and subject:IsA("Humanoid") then
                character = subject.Parent
            end
            if subject and subject:IsA("VehicleSeat") and subject.Occupant then
                character = subject.Occupant.Parent
            end
            if character then
                self:SetupTransparency(character)
            else
                self:TeardownTransparency()
            end
        end
        
        function TransparencyController:Update()
            local instant = false
            local now = tick()
            local currentCamera = workspace.CurrentCamera
        
            if currentCamera then
                local transparency = 0
                if not self.enabled then
                    instant = true
                else
                    local distance = (currentCamera.Focus.p - currentCamera.CoordinateFrame.p).magnitude
                    transparency = (distance<2) and (1.0-(distance-0.5)/1.5) or 0 --(7 - distance) / 5
                    if transparency < 0.5 then
                        transparency = 0
                    end
        
                    if self.lastTransparency then
                        local deltaTransparency = transparency - self.lastTransparency
        
                        -- Don't tween transparency if it is instant or your character was fully invisible last frame
                        if not instant and transparency < 1 and self.lastTransparency < 0.95 then
                            local maxDelta = MAX_TWEEN_RATE * (now - self.lastUpdate)
                            deltaTransparency = math.clamp(deltaTransparency, -maxDelta, maxDelta)
                        end
                        transparency = self.lastTransparency + deltaTransparency
                    else
                        self.transparencyDirty = true
                    end
        
                    transparency = math.clamp(Util.Round(transparency, 2), 0, 1)
                end
        
                if self.transparencyDirty or self.lastTransparency ~= transparency then
                    for child, _ in pairs(self.cachedParts) do
                        child.LocalTransparencyModifier = transparency
                    end
                    self.transparencyDirty = false
                    self.lastTransparency = transparency
                end
            end
            self.lastUpdate = now
        end
        
        return TransparencyController
    end
    
    function _Poppercam()
        local ZoomController =  _ZoomController()
        
        local TransformExtrapolator = {} do
            TransformExtrapolator.__index = TransformExtrapolator
        
            local CF_IDENTITY = CFrame.new()
        
            local function cframeToAxis(cframe)
                local axis, angle = cframe:toAxisAngle()
                return axis*angle
            end
        
            local function axisToCFrame(axis)
                local angle = axis.magnitude
                if angle > 1e-5 then
                    return CFrame.fromAxisAngle(axis, angle)
                end
                return CF_IDENTITY
            end
        
            local function extractRotation(cf)
                local _, _, _, xx, yx, zx, xy, yy, zy, xz, yz, zz = cf:components()
                return CFrame.new(0, 0, 0, xx, yx, zx, xy, yy, zy, xz, yz, zz)
            end
        
            function TransformExtrapolator.new()
                return setmetatable({
                    lastCFrame = nil,
                }, TransformExtrapolator)
            end
        
            function TransformExtrapolator:Step(dt, currentCFrame)
                local lastCFrame = self.lastCFrame or currentCFrame
                self.lastCFrame = currentCFrame
        
                local currentPos = currentCFrame.p
                local currentRot = extractRotation(currentCFrame)
        
                local lastPos = lastCFrame.p
                local lastRot = extractRotation(lastCFrame)
        
                -- Estimate velocities from the delta between now and the last frame
                -- This estimation can be a little noisy.
                local dp = (currentPos - lastPos)/dt
                local dr = cframeToAxis(currentRot*lastRot:inverse())/dt
        
                local function extrapolate(t)
                    local p = dp*t + currentPos
                    local r = axisToCFrame(dr*t)*currentRot
                    return r + p
                end
        
                return {
                    extrapolate = extrapolate,
                    posVelocity = dp,
                    rotVelocity = dr,
                }
            end
        
            function TransformExtrapolator:Reset()
                self.lastCFrame = nil
            end
        end
        
        --[[ The Module ]]--
        local BaseOcclusion = _BaseOcclusion()
        local Poppercam = setmetatable({}, BaseOcclusion)
        Poppercam.__index = Poppercam
        
        function Poppercam.new()
            local self = setmetatable(BaseOcclusion.new(), Poppercam)
            self.focusExtrapolator = TransformExtrapolator.new()
            return self
        end
        
        function Poppercam:GetOcclusionMode()
            return Enum.DevCameraOcclusionMode.Zoom
        end
        
        function Poppercam:Enable(enable)
            self.focusExtrapolator:Reset()
        end
        
        function Poppercam:Update(renderDt, desiredCameraCFrame, desiredCameraFocus, cameraController)
            local rotatedFocus = CFrame.new(desiredCameraFocus.p, desiredCameraCFrame.p)*CFrame.new(
                0, 0, 0,
                -1, 0, 0,
                0, 1, 0,
                0, 0, -1
            )
            local extrapolation = self.focusExtrapolator:Step(renderDt, rotatedFocus)
            local zoom = ZoomController.Update(renderDt, rotatedFocus, extrapolation)
            return rotatedFocus*CFrame.new(0, 0, zoom), desiredCameraFocus
        end
        
        -- Called when character is added
        function Poppercam:CharacterAdded(character, player)
        end
        
        -- Called when character is about to be removed
        function Poppercam:CharacterRemoving(character, player)
        end
        
        function Poppercam:OnCameraSubjectChanged(newSubject)
        end
        
        local ZoomController = _ZoomController()
        
        function Poppercam:Update(renderDt, desiredCameraCFrame, desiredCameraFocus, cameraController)
            local rotatedFocus = desiredCameraFocus * (desiredCameraCFrame - desiredCameraCFrame.p)
            local extrapolation = self.focusExtrapolator:Step(renderDt, rotatedFocus)
            local zoom = ZoomController.Update(renderDt, rotatedFocus, extrapolation)
            return rotatedFocus*CFrame.new(0, 0, zoom), desiredCameraFocus
        end
        
        return Poppercam
    end
    
    function _Invisicam()
        
        --[[ Top Level Roblox Services ]]--
        local PlayersService = game:GetService("Players")
        
        --[[ Constants ]]--
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        local USE_STACKING_TRANSPARENCY = true	-- Multiple items between the subject and camera get transparency values that add up to TARGET_TRANSPARENCY
        local TARGET_TRANSPARENCY = 0.75 -- Classic Invisicam's Value, also used by new invisicam for parts hit by head and torso rays
        local TARGET_TRANSPARENCY_PERIPHERAL = 0.5 -- Used by new SMART_CIRCLE mode for items not hit by head and torso rays
        
        local MODE = {
            --CUSTOM = 1, 		-- Retired, unused
            LIMBS = 2, 			-- Track limbs
            MOVEMENT = 3, 		-- Track movement
            CORNERS = 4, 		-- Char model corners
            CIRCLE1 = 5, 		-- Circle of casts around character
            CIRCLE2 = 6, 		-- Circle of casts around character, camera relative
            LIMBMOVE = 7, 		-- LIMBS mode + MOVEMENT mode
            SMART_CIRCLE = 8, 	-- More sample points on and around character
            CHAR_OUTLINE = 9,	-- Dynamic outline around the character
        }
        
        local LIMB_TRACKING_SET = {
            -- Body parts common to R15 and R6
            ['Head'] = true,
        
            -- Body parts unique to R6
            ['Left Arm'] = true,
            ['Right Arm'] = true,
            ['Left Leg'] = true,
            ['Right Leg'] = true,
        
            -- Body parts unique to R15
            ['LeftLowerArm'] = true,
            ['RightLowerArm'] = true,
            ['LeftUpperLeg'] = true,
            ['RightUpperLeg'] = true
        }
        
        local CORNER_FACTORS = {
            Vector3.new(1,1,-1),
            Vector3.new(1,-1,-1),
            Vector3.new(-1,-1,-1),
            Vector3.new(-1,1,-1)
        }
        
        local CIRCLE_CASTS = 10
        local MOVE_CASTS = 3
        local SMART_CIRCLE_CASTS = 24
        local SMART_CIRCLE_INCREMENT = 2.0 * math.pi / SMART_CIRCLE_CASTS
        local CHAR_OUTLINE_CASTS = 24
        
        -- Used to sanitize user-supplied functions
        local function AssertTypes(param, ...)
            local allowedTypes = {}
            local typeString = ''
            for _, typeName in pairs({...}) do
                allowedTypes[typeName] = true
                typeString = typeString .. (typeString == '' and '' or ' or ') .. typeName
            end
            local theType = type(param)
            assert(allowedTypes[theType], typeString .. " type expected, got: " .. theType)
        end
        
        -- Helper function for Determinant of 3x3, not in CameraUtils for performance reasons
        local function Det3x3(a,b,c,d,e,f,g,h,i)
            return (a*(e*i-f*h)-b*(d*i-f*g)+c*(d*h-e*g))
        end
        
        -- Smart Circle mode needs the intersection of 2 rays that are known to be in the same plane
        -- because they are generated from cross products with a common vector. This function is computing
        -- that intersection, but it's actually the general solution for the point halfway between where
        -- two skew lines come nearest to each other, which is more forgiving.
        local function RayIntersection(p0, v0, p1, v1)
            local v2 = v0:Cross(v1)
            local d1 = p1.x - p0.x
            local d2 = p1.y - p0.y
            local d3 = p1.z - p0.z
            local denom = Det3x3(v0.x,-v1.x,v2.x,v0.y,-v1.y,v2.y,v0.z,-v1.z,v2.z)
        
            if (denom == 0) then
                return ZERO_VECTOR3 -- No solution (rays are parallel)
            end
        
            local t0 = Det3x3(d1,-v1.x,v2.x,d2,-v1.y,v2.y,d3,-v1.z,v2.z) / denom
            local t1 = Det3x3(v0.x,d1,v2.x,v0.y,d2,v2.y,v0.z,d3,v2.z) / denom
            local s0 = p0 + t0 * v0
            local s1 = p1 + t1 * v1
            local s = s0 + 0.5 * ( s1 - s0 )
        
            -- 0.25 studs is a threshold for deciding if the rays are
            -- close enough to be considered intersecting, found through testing 
            if (s1-s0).Magnitude < 0.25 then
                return s
            else
                return ZERO_VECTOR3
            end
        end
        
        
        
        --[[ The Module ]]--
        local BaseOcclusion = _BaseOcclusion()
        local Invisicam = setmetatable({}, BaseOcclusion)
        Invisicam.__index = Invisicam
        
        function Invisicam.new()
            local self = setmetatable(BaseOcclusion.new(), Invisicam)
        
            self.char = nil
            self.humanoidRootPart = nil
            self.torsoPart = nil
            self.headPart = nil
        
            self.childAddedConn = nil
            self.childRemovedConn = nil
        
            self.behaviors = {} 	-- Map of modes to behavior fns
            self.behaviors[MODE.LIMBS] = self.LimbBehavior
            self.behaviors[MODE.MOVEMENT] = self.MoveBehavior
            self.behaviors[MODE.CORNERS] = self.CornerBehavior
            self.behaviors[MODE.CIRCLE1] = self.CircleBehavior
            self.behaviors[MODE.CIRCLE2] = self.CircleBehavior
            self.behaviors[MODE.LIMBMOVE] = self.LimbMoveBehavior
            self.behaviors[MODE.SMART_CIRCLE] = self.SmartCircleBehavior
            self.behaviors[MODE.CHAR_OUTLINE] = self.CharacterOutlineBehavior
        
            self.mode = MODE.SMART_CIRCLE
            self.behaviorFunction = self.SmartCircleBehavior
        
            self.savedHits = {} 	-- Objects currently being faded in/out
            self.trackedLimbs = {}	-- Used in limb-tracking casting modes
        
            self.camera = game.Workspace.CurrentCamera
        
            self.enabled = false
            return self
        end
        
        function Invisicam:Enable(enable)
            self.enabled = enable
        
            if not enable then
                self:Cleanup()
            end
        end
        
        function Invisicam:GetOcclusionMode()
            return Enum.DevCameraOcclusionMode.Invisicam
        end
        
        --[[ Module functions ]]--
        function Invisicam:LimbBehavior(castPoints)
            for limb, _ in pairs(self.trackedLimbs) do
                castPoints[#castPoints + 1] = limb.Position
            end
        end
        
        function Invisicam:MoveBehavior(castPoints)
            for i = 1, MOVE_CASTS do
                local position, velocity = self.humanoidRootPart.Position, self.humanoidRootPart.Velocity
                local horizontalSpeed = Vector3.new(velocity.X, 0, velocity.Z).Magnitude / 2
                local offsetVector = (i - 1) * self.humanoidRootPart.CFrame.lookVector * horizontalSpeed
                castPoints[#castPoints + 1] = position + offsetVector
            end
        end
        
        function Invisicam:CornerBehavior(castPoints)
            local cframe = self.humanoidRootPart.CFrame
            local centerPoint = cframe.p
            local rotation = cframe - centerPoint
            local halfSize = self.char:GetExtentsSize() / 2 --NOTE: Doesn't update w/ limb animations
            castPoints[#castPoints + 1] = centerPoint
            for i = 1, #CORNER_FACTORS do
                castPoints[#castPoints + 1] = centerPoint + (rotation * (halfSize * CORNER_FACTORS[i]))
            end
        end
        
        function Invisicam:CircleBehavior(castPoints)
            local cframe
            if self.mode == MODE.CIRCLE1 then
                cframe = self.humanoidRootPart.CFrame
            else
                local camCFrame = self.camera.CoordinateFrame
                cframe = camCFrame - camCFrame.p + self.humanoidRootPart.Position
            end
            castPoints[#castPoints + 1] = cframe.p
            for i = 0, CIRCLE_CASTS - 1 do
                local angle = (2 * math.pi / CIRCLE_CASTS) * i
                local offset = 3 * Vector3.new(math.cos(angle), math.sin(angle), 0)
                castPoints[#castPoints + 1] = cframe * offset
            end
        end
        
        function Invisicam:LimbMoveBehavior(castPoints)
            self:LimbBehavior(castPoints)
            self:MoveBehavior(castPoints)
        end
        
        function Invisicam:CharacterOutlineBehavior(castPoints)
            local torsoUp = self.torsoPart.CFrame.upVector.unit
            local torsoRight = self.torsoPart.CFrame.rightVector.unit
        
            -- Torso cross of points for interior coverage
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoUp
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoUp
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoRight
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoRight
            if self.headPart then
                castPoints[#castPoints + 1] = self.headPart.CFrame.p
            end
        
            local cframe = CFrame.new(ZERO_VECTOR3,Vector3.new(self.camera.CoordinateFrame.lookVector.X,0,self.camera.CoordinateFrame.lookVector.Z))
            local centerPoint = (self.torsoPart and self.torsoPart.Position or self.humanoidRootPart.Position)
        
            local partsWhitelist = {self.torsoPart}
            if self.headPart then
                partsWhitelist[#partsWhitelist + 1] = self.headPart
            end
        
            for i = 1, CHAR_OUTLINE_CASTS do
                local angle = (2 * math.pi * i / CHAR_OUTLINE_CASTS)
                local offset = cframe * (3 * Vector3.new(math.cos(angle), math.sin(angle), 0))
        
                offset = Vector3.new(offset.X, math.max(offset.Y, -2.25), offset.Z)	
        
                local ray = Ray.new(centerPoint + offset, -3 * offset)
                local hit, hitPoint = game.Workspace:FindPartOnRayWithWhitelist(ray, partsWhitelist, false, false)
        
                if hit then
                    -- Use hit point as the cast point, but nudge it slightly inside the character so that bumping up against
                    -- walls is less likely to cause a transparency glitch
                    castPoints[#castPoints + 1] = hitPoint + 0.2 * (centerPoint - hitPoint).unit
                end
            end
        end
        
        function Invisicam:SmartCircleBehavior(castPoints)
            local torsoUp = self.torsoPart.CFrame.upVector.unit
            local torsoRight = self.torsoPart.CFrame.rightVector.unit
        
            -- SMART_CIRCLE mode includes rays to head and 5 to the torso.
            -- Hands, arms, legs and feet are not included since they
            -- are not canCollide and can therefore go inside of parts
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoUp
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoUp
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoRight
            castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoRight
            if self.headPart then
                castPoints[#castPoints + 1] = self.headPart.CFrame.p
            end
        
            local cameraOrientation = self.camera.CFrame - self.camera.CFrame.p
            local torsoPoint = Vector3.new(0,0.5,0) + (self.torsoPart and self.torsoPart.Position or self.humanoidRootPart.Position)
            local radius = 2.5
        
            -- This loop first calculates points in a circle of radius 2.5 around the torso of the character, in the
            -- plane orthogonal to the camera's lookVector. Each point is then raycast to, to determine if it is within
            -- the free space surrounding the player (not inside anything). Two iterations are done to adjust points that
            -- are inside parts, to try to move them to valid locations that are still on their camera ray, so that the
            -- circle remains circular from the camera's perspective, but does not cast rays into walls or parts that are
            -- behind, below or beside the character and not really obstructing view of the character. This minimizes
            -- the undesirable situation where the character walks up to an exterior wall and it is made invisible even
            -- though it is behind the character.
            for i = 1, SMART_CIRCLE_CASTS do
                local angle = SMART_CIRCLE_INCREMENT * i - 0.5 * math.pi
                local offset = radius * Vector3.new(math.cos(angle), math.sin(angle), 0)
                local circlePoint = torsoPoint + cameraOrientation * offset
        
                -- Vector from camera to point on the circle being tested
                local vp = circlePoint - self.camera.CFrame.p
        
                local ray = Ray.new(torsoPoint, circlePoint - torsoPoint)
                local hit, hp, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
                local castPoint = circlePoint
        
                if hit then
                    local hprime = hp + 0.1 * hitNormal.unit -- Slightly offset hit point from the hit surface
                    local v0 = hprime - torsoPoint -- Vector from torso to offset hit point
        
                    local perp = (v0:Cross(vp)).unit
        
                    -- Vector from the offset hit point, along the hit surface
                    local v1 = (perp:Cross(hitNormal)).unit
        
                    -- Vector from camera to offset hit
                    local vprime = (hprime - self.camera.CFrame.p).unit
        
                    -- This dot product checks to see if the vector along the hit surface would hit the correct
                    -- side of the invisicam cone, or if it would cross the camera look vector and hit the wrong side
                    if ( v0.unit:Dot(-v1) < v0.unit:Dot(vprime)) then
                        castPoint = RayIntersection(hprime, v1, circlePoint, vp)
        
                        if castPoint.Magnitude > 0 then
                            local ray = Ray.new(hprime, castPoint - hprime)
                            local hit, hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
        
                            if hit then
                                local hprime2 = hitPoint + 0.1 * hitNormal.unit
                                castPoint = hprime2
                            end
                        else
                            castPoint = hprime
                        end
                    else
                        castPoint = hprime
                    end
        
                    local ray = Ray.new(torsoPoint, (castPoint - torsoPoint))
                    local hit, hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
        
                    if hit then
                        local castPoint2 = hitPoint - 0.1 * (castPoint - torsoPoint).unit
                        castPoint = castPoint2
                    end
                end
        
                castPoints[#castPoints + 1] = castPoint
            end
        end
        
        function Invisicam:CheckTorsoReference()
            if self.char then
                self.torsoPart = self.char:FindFirstChild("Torso")
                if not self.torsoPart then
                    self.torsoPart = self.char:FindFirstChild("UpperTorso")
                    if not self.torsoPart then
                        self.torsoPart = self.char:FindFirstChild("HumanoidRootPart")
                    end
                end
        
                self.headPart = self.char:FindFirstChild("Head")
            end
        end
        
        function Invisicam:CharacterAdded(char, player)
            -- We only want the LocalPlayer's character
            if player~=PlayersService.LocalPlayer then return end
        
            if self.childAddedConn then
                self.childAddedConn:Disconnect()
                self.childAddedConn = nil
            end
            if self.childRemovedConn then
                self.childRemovedConn:Disconnect()
                self.childRemovedConn = nil
            end
        
            self.char = char
        
            self.trackedLimbs = {}
            local function childAdded(child)
                if child:IsA("BasePart") then
                    if LIMB_TRACKING_SET[child.Name] then
                        self.trackedLimbs[child] = true
                    end
        
                    if child.Name == "Torso" or child.Name == "UpperTorso" then
                        self.torsoPart = child
                    end
        
                    if child.Name == "Head" then
                        self.headPart = child
                    end
                end
            end
        
            local function childRemoved(child)
                self.trackedLimbs[child] = nil
        
                -- If removed/replaced part is 'Torso' or 'UpperTorso' double check that we still have a TorsoPart to use
                self:CheckTorsoReference()
            end
        
            self.childAddedConn = char.ChildAdded:Connect(childAdded)
            self.childRemovedConn = char.ChildRemoved:Connect(childRemoved)
            for _, child in pairs(self.char:GetChildren()) do
                childAdded(child)
            end
        end
        
        function Invisicam:SetMode(newMode)
            AssertTypes(newMode, 'number')
            for _, modeNum in pairs(MODE) do
                if modeNum == newMode then
                    self.mode = newMode
                    self.behaviorFunction = self.behaviors[self.mode]
                    return
                end
            end
            error("Invalid mode number")
        end
        
        function Invisicam:GetObscuredParts()
            return self.savedHits
        end
        
        -- Want to turn off Invisicam? Be sure to call this after.
        function Invisicam:Cleanup()
            for hit, originalFade in pairs(self.savedHits) do
                hit.LocalTransparencyModifier = originalFade
            end
        end
        
        function Invisicam:Update(dt, desiredCameraCFrame, desiredCameraFocus)
            -- Bail if there is no Character
            if not self.enabled or not self.char then
                return desiredCameraCFrame, desiredCameraFocus
            end
        
            self.camera = game.Workspace.CurrentCamera
        
            -- TODO: Move this to a GetHumanoidRootPart helper, probably combine with CheckTorsoReference
            -- Make sure we still have a HumanoidRootPart
            if not self.humanoidRootPart then
                local humanoid = self.char:FindFirstChildOfClass("Humanoid")
                if humanoid and humanoid.RootPart then
                    self.humanoidRootPart = humanoid.RootPart
                else
                    -- Not set up with Humanoid? Try and see if there's one in the Character at all:
                    self.humanoidRootPart = self.char:FindFirstChild("HumanoidRootPart")
                    if not self.humanoidRootPart then
                        -- Bail out, since we're relying on HumanoidRootPart existing
                        return desiredCameraCFrame, desiredCameraFocus
                    end
                end
        
                -- TODO: Replace this with something more sensible
                local ancestryChangedConn
                ancestryChangedConn = self.humanoidRootPart.AncestryChanged:Connect(function(child, parent)
                    if child == self.humanoidRootPart and not parent then 
                        self.humanoidRootPart = nil
                        if ancestryChangedConn and ancestryChangedConn.Connected then
                            ancestryChangedConn:Disconnect()
                            ancestryChangedConn = nil
                        end
                    end
                end)
            end
        
            if not self.torsoPart then
                self:CheckTorsoReference()
                if not self.torsoPart then
                    -- Bail out, since we're relying on Torso existing, should never happen since we fall back to using HumanoidRootPart as torso
                    return desiredCameraCFrame, desiredCameraFocus
                end
            end
        
            -- Make a list of world points to raycast to
            local castPoints = {}
            self.behaviorFunction(self, castPoints)
        
            -- Cast to get a list of objects between the camera and the cast points
            local currentHits = {}
            local ignoreList = {self.char}
            local function add(hit)
                currentHits[hit] = true
                if not self.savedHits[hit] then
                    self.savedHits[hit] = hit.LocalTransparencyModifier
                end
            end
        
            local hitParts
            local hitPartCount = 0
        
            -- Hash table to treat head-ray-hit parts differently than the rest of the hit parts hit by other rays
            -- head/torso ray hit parts will be more transparent than peripheral parts when USE_STACKING_TRANSPARENCY is enabled
            local headTorsoRayHitParts = {}
        
            local perPartTransparencyHeadTorsoHits = TARGET_TRANSPARENCY
            local perPartTransparencyOtherHits = TARGET_TRANSPARENCY
        
            if USE_STACKING_TRANSPARENCY then
        
                -- This first call uses head and torso rays to find out how many parts are stacked up
                -- for the purpose of calculating required per-part transparency
                local headPoint = self.headPart and self.headPart.CFrame.p or castPoints[1]
                local torsoPoint = self.torsoPart and self.torsoPart.CFrame.p or castPoints[2]
                hitParts = self.camera:GetPartsObscuringTarget({headPoint, torsoPoint}, ignoreList)
        
                -- Count how many things the sample rays passed through, including decals. This should only
                -- count decals facing the camera, but GetPartsObscuringTarget does not return surface normals,
                -- so my compromise for now is to just let any decal increase the part count by 1. Only one
                -- decal per part will be considered.
                for i = 1, #hitParts do
                    local hitPart = hitParts[i]
                    hitPartCount = hitPartCount + 1 -- count the part itself
                    headTorsoRayHitParts[hitPart] = true
                    for _, child in pairs(hitPart:GetChildren()) do
                        if child:IsA('Decal') or child:IsA('Texture') then
                            hitPartCount = hitPartCount + 1 -- count first decal hit, then break
                            break
                        end
                    end
                end
        
                if (hitPartCount > 0) then
                    perPartTransparencyHeadTorsoHits = math.pow( ((0.5 * TARGET_TRANSPARENCY) + (0.5 * TARGET_TRANSPARENCY / hitPartCount)), 1 / hitPartCount )
                    perPartTransparencyOtherHits = math.pow( ((0.5 * TARGET_TRANSPARENCY_PERIPHERAL) + (0.5 * TARGET_TRANSPARENCY_PERIPHERAL / hitPartCount)), 1 / hitPartCount )
                end
            end
        
            -- Now get all the parts hit by all the rays
            hitParts = self.camera:GetPartsObscuringTarget(castPoints, ignoreList)
        
            local partTargetTransparency = {}
        
            -- Include decals and textures
            for i = 1, #hitParts do
                local hitPart = hitParts[i]
        
                partTargetTransparency[hitPart] =headTorsoRayHitParts[hitPart] and perPartTransparencyHeadTorsoHits or perPartTransparencyOtherHits
        
                -- If the part is not already as transparent or more transparent than what invisicam requires, add it to the list of
                -- parts to be modified by invisicam
                if hitPart.Transparency < partTargetTransparency[hitPart] then
                    add(hitPart)
                end
        
                -- Check all decals and textures on the part
                for _, child in pairs(hitPart:GetChildren()) do
                    if child:IsA('Decal') or child:IsA('Texture') then
                        if (child.Transparency < partTargetTransparency[hitPart]) then
                            partTargetTransparency[child] = partTargetTransparency[hitPart]
                            add(child)
                        end
                    end
                end
            end
        
            -- Invisibilize objects that are in the way, restore those that aren't anymore
            for hitPart, originalLTM in pairs(self.savedHits) do
                if currentHits[hitPart] then
                    -- LocalTransparencyModifier gets whatever value is required to print the part's total transparency to equal perPartTransparency
                    hitPart.LocalTransparencyModifier = (hitPart.Transparency < 1) and ((partTargetTransparency[hitPart] - hitPart.Transparency) / (1.0 - hitPart.Transparency)) or 0
                else -- Restore original pre-invisicam value of LTM
                    hitPart.LocalTransparencyModifier = originalLTM
                    self.savedHits[hitPart] = nil
                end
            end
        
            -- Invisicam does not change the camera values
            return desiredCameraCFrame, desiredCameraFocus
        end
        
        return Invisicam
    end
    
    function _LegacyCamera()
        
        local ZERO_VECTOR2 = Vector2.new(0,0)
        
        local Util = _CameraUtils()
        
        --[[ Services ]]--
        local PlayersService = game:GetService('Players')
        
        --[[ The Module ]]--
        local BaseCamera = _BaseCamera()
        local LegacyCamera = setmetatable({}, BaseCamera)
        LegacyCamera.__index = LegacyCamera
        
        function LegacyCamera.new()
            local self = setmetatable(BaseCamera.new(), LegacyCamera)
        
            self.cameraType = Enum.CameraType.Fixed
            self.lastUpdate = tick()
            self.lastDistanceToSubject = nil
        
            return self
        end
        
        function LegacyCamera:GetModuleName()
            return "LegacyCamera"
        end
        
        --[[ Functions overridden from BaseCamera ]]--
        function LegacyCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
            return BaseCamera.SetCameraToSubjectDistance(self,desiredSubjectDistance)
        end
        
        function LegacyCamera:Update(dt)
        
            -- Cannot update until cameraType has been set
            if not self.cameraType then return end
        
            local now = tick()
            local timeDelta = (now - self.lastUpdate)
            local camera = 	workspace.CurrentCamera
            local newCameraCFrame = camera.CFrame
            local newCameraFocus = camera.Focus
            local player = PlayersService.LocalPlayer
        
            if self.lastUpdate == nil or timeDelta > 1 then
                self.lastDistanceToSubject = nil
            end
            local subjectPosition = self:GetSubjectPosition()
        
            if self.cameraType == Enum.CameraType.Fixed then
                if self.lastUpdate then
                    -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
                    local delta = math.min(0.1, now - self.lastUpdate)
                    local gamepadRotation = self:UpdateGamepad()
                    self.rotateInput = self.rotateInput + (gamepadRotation * delta)
                end
        
                if subjectPosition and player and camera then
                    local distanceToSubject = self:GetCameraToSubjectDistance()
                    local newLookVector = self:CalculateNewLookVector()
                    self.rotateInput = ZERO_VECTOR2
        
                    newCameraFocus = camera.Focus -- Fixed camera does not change focus
                    newCameraCFrame = CFrame.new(camera.CFrame.p, camera.CFrame.p + (distanceToSubject * newLookVector))
                end
            elseif self.cameraType == Enum.CameraType.Attach then
                if subjectPosition and camera then
                    local distanceToSubject = self:GetCameraToSubjectDistance()
                    local humanoid = self:GetHumanoid()
                    if self.lastUpdate and humanoid and humanoid.RootPart then
        
                        -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
                        local delta = math.min(0.1, now - self.lastUpdate)
                        local gamepadRotation = self:UpdateGamepad()
                        self.rotateInput = self.rotateInput + (gamepadRotation * delta)
        
                        local forwardVector = humanoid.RootPart.CFrame.lookVector
        
                        local y = Util.GetAngleBetweenXZVectors(forwardVector, self:GetCameraLookVector())
                        if Util.IsFinite(y) then
                            -- Preserve vertical rotation from user input
                            self.rotateInput = Vector2.new(y, self.rotateInput.Y)
                        end
                    end
        
                    local newLookVector = self:CalculateNewLookVector()
                    self.rotateInput = ZERO_VECTOR2
        
                    newCameraFocus = CFrame.new(subjectPosition)
                    newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)
                end
            elseif self.cameraType == Enum.CameraType.Watch then
                if subjectPosition and player and camera then
                    local cameraLook = nil
        
                    local humanoid = self:GetHumanoid()
                    if humanoid and humanoid.RootPart then
                        local diffVector = subjectPosition - camera.CFrame.p
                        cameraLook = diffVector.unit
        
                        if self.lastDistanceToSubject and self.lastDistanceToSubject == self:GetCameraToSubjectDistance() then
                            -- Don't clobber the zoom if they zoomed the camera
                            local newDistanceToSubject = diffVector.magnitude
                            self:SetCameraToSubjectDistance(newDistanceToSubject)
                        end
                    end
        
                    local distanceToSubject = self:GetCameraToSubjectDistance()
                    local newLookVector = self:CalculateNewLookVector(cameraLook)
                    self.rotateInput = ZERO_VECTOR2
        
                    newCameraFocus = CFrame.new(subjectPosition)
                    newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)
        
                    self.lastDistanceToSubject = distanceToSubject
                end
            else
                -- Unsupported type, return current values unchanged
                return camera.CFrame, camera.Focus
            end
        
            self.lastUpdate = now
            return newCameraCFrame, newCameraFocus
        end
        
        return LegacyCamera
    end
    
    function _OrbitalCamera()
        
        -- Local private variables and constants
        local UNIT_Z = Vector3.new(0,0,1)
        local X1_Y0_Z1 = Vector3.new(1,0,1)	--Note: not a unit vector, used for projecting onto XZ plane
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        local ZERO_VECTOR2 = Vector2.new(0,0)
        local TAU = 2 * math.pi
        
        --[[ Gamepad Support ]]--
        local THUMBSTICK_DEADZONE = 0.2
        
        -- Do not edit these values, they are not the developer-set limits, they are limits
        -- to the values the camera system equations can correctly handle
        local MIN_ALLOWED_ELEVATION_DEG = -80
        local MAX_ALLOWED_ELEVATION_DEG = 80
        
        local externalProperties = {}
        externalProperties["InitialDistance"]  = 25
        externalProperties["MinDistance"]      = 10
        externalProperties["MaxDistance"]      = 100
        externalProperties["InitialElevation"] = 35
        externalProperties["MinElevation"]     = 35
        externalProperties["MaxElevation"]     = 35
        externalProperties["ReferenceAzimuth"] = -45	-- Angle around the Y axis where the camera starts. -45 offsets the camera in the -X and +Z directions equally
        externalProperties["CWAzimuthTravel"]  = 90	-- How many degrees the camera is allowed to rotate from the reference position, CW as seen from above
        externalProperties["CCWAzimuthTravel"] = 90	-- How many degrees the camera is allowed to rotate from the reference position, CCW as seen from above
        externalProperties["UseAzimuthLimits"] = false -- Full rotation around Y axis available by default
        
        local Util = _CameraUtils()
        
        --[[ Services ]]--
        local PlayersService = game:GetService('Players')
        local VRService = game:GetService("VRService")
        
        --[[ The Module ]]--
        local BaseCamera = _BaseCamera()
        local OrbitalCamera = setmetatable({}, BaseCamera)
        OrbitalCamera.__index = OrbitalCamera
        
        
        function OrbitalCamera.new()
            local self = setmetatable(BaseCamera.new(), OrbitalCamera)
        
            self.lastUpdate = tick()
        
            -- OrbitalCamera-specific members
            self.changedSignalConnections = {}
            self.refAzimuthRad = nil
            self.curAzimuthRad = nil
            self.minAzimuthAbsoluteRad = nil
            self.maxAzimuthAbsoluteRad = nil
            self.useAzimuthLimits = nil
            self.curElevationRad = nil
            self.minElevationRad = nil
            self.maxElevationRad = nil
            self.curDistance = nil
            self.minDistance = nil
            self.maxDistance = nil
        
            -- Gamepad
            self.r3ButtonDown = false
            self.l3ButtonDown = false
            self.gamepadDollySpeedMultiplier = 1
        
            self.lastUserPanCamera = tick()
        
            self.externalProperties = {}
            self.externalProperties["InitialDistance"] 	= 25
            self.externalProperties["MinDistance"] 		= 10
            self.externalProperties["MaxDistance"] 		= 100
            self.externalProperties["InitialElevation"] 	= 35
            self.externalProperties["MinElevation"] 		= 35
            self.externalProperties["MaxElevation"] 		= 35
            self.externalProperties["ReferenceAzimuth"] 	= -45	-- Angle around the Y axis where the camera starts. -45 offsets the camera in the -X and +Z directions equally
            self.externalProperties["CWAzimuthTravel"] 	= 90	-- How many degrees the camera is allowed to rotate from the reference position, CW as seen from above
            self.externalProperties["CCWAzimuthTravel"] 	= 90	-- How many degrees the camera is allowed to rotate from the reference position, CCW as seen from above
            self.externalProperties["UseAzimuthLimits"] 	= false -- Full rotation around Y axis available by default
            self:LoadNumberValueParameters()
        
            return self
        end
        
        function OrbitalCamera:LoadOrCreateNumberValueParameter(name, valueType, updateFunction)
            local valueObj = script:FindFirstChild(name)
        
            if valueObj and valueObj:isA(valueType) then
                -- Value object exists and is the correct type, use its value
                self.externalProperties[name] = valueObj.Value
            elseif self.externalProperties[name] ~= nil then
                -- Create missing (or replace incorrectly-typed) valueObject with default value
                valueObj = Instance.new(valueType)
                valueObj.Name = name
                valueObj.Parent = script
                valueObj.Value = self.externalProperties[name]
            else
                print("externalProperties table has no entry for ",name)
                return
            end
        
            if updateFunction then
                if self.changedSignalConnections[name] then
                    self.changedSignalConnections[name]:Disconnect()
                end
                self.changedSignalConnections[name] = valueObj.Changed:Connect(function(newValue)
                    self.externalProperties[name] = newValue
                    updateFunction(self)
                end)
            end
        end
        
        function OrbitalCamera:SetAndBoundsCheckAzimuthValues()
            self.minAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) - math.abs(math.rad(self.externalProperties["CWAzimuthTravel"]))
            self.maxAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) + math.abs(math.rad(self.externalProperties["CCWAzimuthTravel"]))
            self.useAzimuthLimits = self.externalProperties["UseAzimuthLimits"]
            if self.useAzimuthLimits then
                self.curAzimuthRad = math.max(self.curAzimuthRad, self.minAzimuthAbsoluteRad)
                self.curAzimuthRad = math.min(self.curAzimuthRad, self.maxAzimuthAbsoluteRad)
            end
        end
        
        function OrbitalCamera:SetAndBoundsCheckElevationValues()
            -- These degree values are the direct user input values. It is deliberate that they are
            -- ranged checked only against the extremes, and not against each other. Any time one
            -- is changed, both of the internal values in radians are recalculated. This allows for
            -- A developer to change the values in any order and for the end results to be that the
            -- internal values adjust to match intent as best as possible.
            local minElevationDeg = math.max(self.externalProperties["MinElevation"], MIN_ALLOWED_ELEVATION_DEG)
            local maxElevationDeg = math.min(self.externalProperties["MaxElevation"], MAX_ALLOWED_ELEVATION_DEG)
        
            -- Set internal values in radians
            self.minElevationRad = math.rad(math.min(minElevationDeg, maxElevationDeg))
            self.maxElevationRad = math.rad(math.max(minElevationDeg, maxElevationDeg))
            self.curElevationRad = math.max(self.curElevationRad, self.minElevationRad)
            self.curElevationRad = math.min(self.curElevationRad, self.maxElevationRad)
        end
        
        function OrbitalCamera:SetAndBoundsCheckDistanceValues()
            self.minDistance = self.externalProperties["MinDistance"]
            self.maxDistance = self.externalProperties["MaxDistance"]
            self.curDistance = math.max(self.curDistance, self.minDistance)
            self.curDistance = math.min(self.curDistance, self.maxDistance)
        end
        
        -- This loads from, or lazily creates, NumberValue objects for exposed parameters
        function OrbitalCamera:LoadNumberValueParameters()
            -- These initial values do not require change listeners since they are read only once
            self:LoadOrCreateNumberValueParameter("InitialElevation", "NumberValue", nil)
            self:LoadOrCreateNumberValueParameter("InitialDistance", "NumberValue", nil)
        
            -- Note: ReferenceAzimuth is also used as an initial value, but needs a change listener because it is used in the calculation of the limits
            self:LoadOrCreateNumberValueParameter("ReferenceAzimuth", "NumberValue", self.SetAndBoundsCheckAzimuthValue)
            self:LoadOrCreateNumberValueParameter("CWAzimuthTravel", "NumberValue", self.SetAndBoundsCheckAzimuthValues)
            self:LoadOrCreateNumberValueParameter("CCWAzimuthTravel", "NumberValue", self.SetAndBoundsCheckAzimuthValues)
            self:LoadOrCreateNumberValueParameter("MinElevation", "NumberValue", self.SetAndBoundsCheckElevationValues)
            self:LoadOrCreateNumberValueParameter("MaxElevation", "NumberValue", self.SetAndBoundsCheckElevationValues)
            self:LoadOrCreateNumberValueParameter("MinDistance", "NumberValue", self.SetAndBoundsCheckDistanceValues)
            self:LoadOrCreateNumberValueParameter("MaxDistance", "NumberValue", self.SetAndBoundsCheckDistanceValues)
            self:LoadOrCreateNumberValueParameter("UseAzimuthLimits", "BoolValue", self.SetAndBoundsCheckAzimuthValues)
        
            -- Internal values set (in radians, from degrees), plus sanitization
            self.curAzimuthRad = math.rad(self.externalProperties["ReferenceAzimuth"])
            self.curElevationRad = math.rad(self.externalProperties["InitialElevation"])
            self.curDistance = self.externalProperties["InitialDistance"]
        
            self:SetAndBoundsCheckAzimuthValues()
            self:SetAndBoundsCheckElevationValues()
            self:SetAndBoundsCheckDistanceValues()
        end
        
        function OrbitalCamera:GetModuleName()
            return "OrbitalCamera"
        end
        
        function OrbitalCamera:SetInitialOrientation(humanoid)
            if not humanoid or not humanoid.RootPart then
                warn("OrbitalCamera could not set initial orientation due to missing humanoid")
                return
            end
            local newDesiredLook = (humanoid.RootPart.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
            local horizontalShift = Util.GetAngleBetweenXZVectors(newDesiredLook, self:GetCameraLookVector())
            local vertShift = math.asin(self:GetCameraLookVector().y) - math.asin(newDesiredLook.y)
            if not Util.IsFinite(horizontalShift) then
                horizontalShift = 0
            end
            if not Util.IsFinite(vertShift) then
                vertShift = 0
            end
            self.rotateInput = Vector2.new(horizontalShift, vertShift)
        end
        
        --[[ Functions of BaseCamera that are overridden by OrbitalCamera ]]--
        function OrbitalCamera:GetCameraToSubjectDistance()
            return self.curDistance
        end
        
        function OrbitalCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
            print("OrbitalCamera SetCameraToSubjectDistance ",desiredSubjectDistance)
            local player = PlayersService.LocalPlayer
            if player then
                self.currentSubjectDistance = math.clamp(desiredSubjectDistance, self.minDistance, self.maxDistance)
        
                -- OrbitalCamera is not allowed to go into the first-person range
                self.currentSubjectDistance = math.max(self.currentSubjectDistance, self.FIRST_PERSON_DISTANCE_THRESHOLD)
            end
            self.inFirstPerson = false
            self:UpdateMouseBehavior()
            return self.currentSubjectDistance
        end
        
        function OrbitalCamera:CalculateNewLookVector(suppliedLookVector, xyRotateVector)
            local currLookVector = suppliedLookVector or self:GetCameraLookVector()
            local currPitchAngle = math.asin(currLookVector.y)
            local yTheta = math.clamp(xyRotateVector.y, currPitchAngle - math.rad(MAX_ALLOWED_ELEVATION_DEG), currPitchAngle - math.rad(MIN_ALLOWED_ELEVATION_DEG))
            local constrainedRotateInput = Vector2.new(xyRotateVector.x, yTheta)
            local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
            local newLookVector = (CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)).lookVector
            return newLookVector
        end
        
        function OrbitalCamera:GetGamepadPan(name, state, input)
            if input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
                if self.r3ButtonDown or self.l3ButtonDown then
                -- R3 or L3 Thumbstick is depressed, right stick controls dolly in/out
                    if (input.Position.Y > THUMBSTICK_DEADZONE) then
                        self.gamepadDollySpeedMultiplier = 0.96
                    elseif (input.Position.Y < -THUMBSTICK_DEADZONE) then
                        self.gamepadDollySpeedMultiplier = 1.04
                    else
                        self.gamepadDollySpeedMultiplier = 1.00
                    end
                else
                    if state == Enum.UserInputState.Cancel then
                        self.gamepadPanningCamera = ZERO_VECTOR2
                        return
                    end
        
                    local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
                    if inputVector.magnitude > THUMBSTICK_DEADZONE then
                        self.gamepadPanningCamera = Vector2.new(input.Position.X, -input.Position.Y)
                    else
                        self.gamepadPanningCamera = ZERO_VECTOR2
                    end
                end
                return Enum.ContextActionResult.Sink
            end
            return Enum.ContextActionResult.Pass
        end
        
        function OrbitalCamera:DoGamepadZoom(name, state, input)
            if input.UserInputType == self.activeGamepad and (input.KeyCode == Enum.KeyCode.ButtonR3 or input.KeyCode == Enum.KeyCode.ButtonL3) then
                if (state == Enum.UserInputState.Begin) then
                    self.r3ButtonDown = input.KeyCode == Enum.KeyCode.ButtonR3
                    self.l3ButtonDown = input.KeyCode == Enum.KeyCode.ButtonL3
                elseif (state == Enum.UserInputState.End) then
                    if (input.KeyCode == Enum.KeyCode.ButtonR3) then
                        self.r3ButtonDown = false
                    elseif (input.KeyCode == Enum.KeyCode.ButtonL3) then
                        self.l3ButtonDown = false
                    end
                    if (not self.r3ButtonDown) and (not self.l3ButtonDown) then
                        self.gamepadDollySpeedMultiplier = 1.00
                    end
                end
                return Enum.ContextActionResult.Sink
            end
            return Enum.ContextActionResult.Pass
        end
        
        function OrbitalCamera:BindGamepadInputActions()
            self:BindAction("OrbitalCamGamepadPan", function(name, state, input) return self:GetGamepadPan(name, state, input) end,
                false, Enum.KeyCode.Thumbstick2)
            self:BindAction("OrbitalCamGamepadZoom", function(name, state, input) return self:DoGamepadZoom(name, state, input) end,
                false, Enum.KeyCode.ButtonR3, Enum.KeyCode.ButtonL3)
        end
        
        
        -- [[ Update ]]--
        function OrbitalCamera:Update(dt)
            local now = tick()
            local timeDelta = (now - self.lastUpdate)
            local userPanningTheCamera = (self.UserPanningTheCamera == true)
            local camera = 	workspace.CurrentCamera
            local newCameraCFrame = camera.CFrame
            local newCameraFocus = camera.Focus
            local player = PlayersService.LocalPlayer
            local cameraSubject = camera and camera.CameraSubject
            local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
            local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
        
            if self.lastUpdate == nil or timeDelta > 1 then
                self.lastCameraTransform = nil
            end
        
            if self.lastUpdate then
                local gamepadRotation = self:UpdateGamepad()
        
                if self:ShouldUseVRRotation() then
                    self.RotateInput = self.RotateInput + self:GetVRRotationInput()
                else
                    -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
                    local delta = math.min(0.1, timeDelta)
        
                    if gamepadRotation ~= ZERO_VECTOR2 then
                        userPanningTheCamera = true
                        self.rotateInput = self.rotateInput + (gamepadRotation * delta)
                    end
        
                    local angle = 0
                    if not (isInVehicle or isOnASkateboard) then
                        angle = angle + (self.TurningLeft and -120 or 0)
                        angle = angle + (self.TurningRight and 120 or 0)
                    end
        
                    if angle ~= 0 then
                        self.rotateInput = self.rotateInput +  Vector2.new(math.rad(angle * delta), 0)
                        userPanningTheCamera = true
                    end
                end
            end
        
            -- Reset tween speed if user is panning
            if userPanningTheCamera then
                self.lastUserPanCamera = tick()
            end
        
            local subjectPosition = self:GetSubjectPosition()
        
            if subjectPosition and player and camera then
        
                -- Process any dollying being done by gamepad
                -- TODO: Move this
                if self.gamepadDollySpeedMultiplier ~= 1 then
                    self:SetCameraToSubjectDistance(self.currentSubjectDistance * self.gamepadDollySpeedMultiplier)
                end
        
                local VREnabled = VRService.VREnabled
                newCameraFocus = VREnabled and self:GetVRFocus(subjectPosition, timeDelta) or CFrame.new(subjectPosition)
        
                local cameraFocusP = newCameraFocus.p
                if VREnabled and not self:IsInFirstPerson() then
                    local cameraHeight = self:GetCameraHeight()
                    local vecToSubject = (subjectPosition - camera.CFrame.p)
                    local distToSubject = vecToSubject.magnitude
        
                    -- Only move the camera if it exceeded a maximum distance to the subject in VR
                    if distToSubject > self.currentSubjectDistance or self.rotateInput.x ~= 0 then
                        local desiredDist = math.min(distToSubject, self.currentSubjectDistance)
        
                        -- Note that CalculateNewLookVector is overridden from BaseCamera
                        vecToSubject = self:CalculateNewLookVector(vecToSubject.unit * X1_Y0_Z1, Vector2.new(self.rotateInput.x, 0)) * desiredDist
        
                        local newPos = cameraFocusP - vecToSubject
                        local desiredLookDir = camera.CFrame.lookVector
                        if self.rotateInput.x ~= 0 then
                            desiredLookDir = vecToSubject
                        end
                        local lookAt = Vector3.new(newPos.x + desiredLookDir.x, newPos.y, newPos.z + desiredLookDir.z)
                        self.RotateInput = ZERO_VECTOR2
        
                        newCameraCFrame = CFrame.new(newPos, lookAt) + Vector3.new(0, cameraHeight, 0)
                    end
                else
                    -- self.RotateInput is a Vector2 of mouse movement deltas since last update
                    self.curAzimuthRad = self.curAzimuthRad - self.rotateInput.x
        
                    if self.useAzimuthLimits then
                        self.curAzimuthRad = math.clamp(self.curAzimuthRad, self.minAzimuthAbsoluteRad, self.maxAzimuthAbsoluteRad)
                    else
                        self.curAzimuthRad = (self.curAzimuthRad ~= 0) and (math.sign(self.curAzimuthRad) * (math.abs(self.curAzimuthRad) % TAU)) or 0
                    end
        
                    self.curElevationRad = math.clamp(self.curElevationRad + self.rotateInput.y, self.minElevationRad, self.maxElevationRad)
        
                    local cameraPosVector = self.currentSubjectDistance * ( CFrame.fromEulerAnglesYXZ( -self.curElevationRad, self.curAzimuthRad, 0 ) * UNIT_Z )
                    local camPos = subjectPosition + cameraPosVector
        
                    newCameraCFrame = CFrame.new(camPos, subjectPosition)
        
                    self.rotateInput = ZERO_VECTOR2
                end
        
                self.lastCameraTransform = newCameraCFrame
                self.lastCameraFocus = newCameraFocus
                if (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
                    self.lastSubjectCFrame = cameraSubject.CFrame
                else
                    self.lastSubjectCFrame = nil
                end
            end
        
            self.lastUpdate = now
            return newCameraCFrame, newCameraFocus
        end
        
        return OrbitalCamera
    end
    
    function _ClassicCamera()
        
        -- Local private variables and constants
        local ZERO_VECTOR2 = Vector2.new(0,0)
        
        local tweenAcceleration = math.rad(220)		--Radians/Second^2
        local tweenSpeed = math.rad(0)				--Radians/Second
        local tweenMaxSpeed = math.rad(250)			--Radians/Second
        local TIME_BEFORE_AUTO_ROTATE = 2.0 		--Seconds, used when auto-aligning camera with vehicles
        
        local INITIAL_CAMERA_ANGLE = CFrame.fromOrientation(math.rad(-15), 0, 0)
        
        local FFlagUserCameraToggle do
            local success, result = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
            end)
            FFlagUserCameraToggle = success and result
        end
        
        --[[ Services ]]--
        local PlayersService = game:GetService('Players')
        local VRService = game:GetService("VRService")
        
        local CameraInput = _CameraInput()
        local Util = _CameraUtils()
        
        --[[ The Module ]]--
        local BaseCamera = _BaseCamera()
        local ClassicCamera = setmetatable({}, BaseCamera)
        ClassicCamera.__index = ClassicCamera
        
        function ClassicCamera.new()
            local self = setmetatable(BaseCamera.new(), ClassicCamera)
        
            self.isFollowCamera = false
            self.isCameraToggle = false
            self.lastUpdate = tick()
            self.cameraToggleSpring = Util.Spring.new(5, 0)
        
            return self
        end
        
        function ClassicCamera:GetCameraToggleOffset(dt)
            assert(FFlagUserCameraToggle)
        
            if self.isCameraToggle then
                local zoom = self.currentSubjectDistance
        
                if CameraInput.getTogglePan() then
                    self.cameraToggleSpring.goal = math.clamp(Util.map(zoom, 0.5, self.FIRST_PERSON_DISTANCE_THRESHOLD, 0, 1), 0, 1)
                else
                    self.cameraToggleSpring.goal = 0
                end
        
                local distanceOffset = math.clamp(Util.map(zoom, 0.5, 64, 0, 1), 0, 1) + 1
                return Vector3.new(0, self.cameraToggleSpring:step(dt)*distanceOffset, 0)
            end
        
            return Vector3.new()
        end
        
        -- Movement mode standardized to Enum.ComputerCameraMovementMode values
        function ClassicCamera:SetCameraMovementMode(cameraMovementMode)
            BaseCamera.SetCameraMovementMode(self, cameraMovementMode)
        
            self.isFollowCamera = cameraMovementMode == Enum.ComputerCameraMovementMode.Follow
            self.isCameraToggle = cameraMovementMode == Enum.ComputerCameraMovementMode.CameraToggle
        end
        
        function ClassicCamera:Update()
            local now = tick()
            local timeDelta = now - self.lastUpdate
        
            local camera = workspace.CurrentCamera
            local newCameraCFrame = camera.CFrame
            local newCameraFocus = camera.Focus
        
            local overrideCameraLookVector = nil
            if self.resetCameraAngle then
                local rootPart = self:GetHumanoidRootPart()
                if rootPart then
                    overrideCameraLookVector = (rootPart.CFrame * INITIAL_CAMERA_ANGLE).lookVector
                else
                    overrideCameraLookVector = INITIAL_CAMERA_ANGLE.lookVector
                end
                self.resetCameraAngle = false
            end
        
            local player = PlayersService.LocalPlayer
            local humanoid = self:GetHumanoid()
            local cameraSubject = camera.CameraSubject
            local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
            local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
            local isClimbing = humanoid and humanoid:GetState() == Enum.HumanoidStateType.Climbing
        
            if self.lastUpdate == nil or timeDelta > 1 then
                self.lastCameraTransform = nil
            end
        
            if self.lastUpdate then
                local gamepadRotation = self:UpdateGamepad()
        
                if self:ShouldUseVRRotation() then
                    self.rotateInput = self.rotateInput + self:GetVRRotationInput()
                else
                    -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
                    local delta = math.min(0.1, timeDelta)
        
                    if gamepadRotation ~= ZERO_VECTOR2 then
                        self.rotateInput = self.rotateInput + (gamepadRotation * delta)
                    end
        
                    local angle = 0
                    if not (isInVehicle or isOnASkateboard) then
                        angle = angle + (self.turningLeft and -120 or 0)
                        angle = angle + (self.turningRight and 120 or 0)
                    end
        
                    if angle ~= 0 then
                        self.rotateInput = self.rotateInput +  Vector2.new(math.rad(angle * delta), 0)
                    end
                end
            end
        
            local cameraHeight = self:GetCameraHeight()
        
            -- Reset tween speed if user is panning
            if self.userPanningTheCamera then
                tweenSpeed = 0
                self.lastUserPanCamera = tick()
            end
        
            local userRecentlyPannedCamera = now - self.lastUserPanCamera < TIME_BEFORE_AUTO_ROTATE
            local subjectPosition = self:GetSubjectPosition()
        
            if subjectPosition and player and camera then
                local zoom = self:GetCameraToSubjectDistance()
                if zoom < 0.5 then
                    zoom = 0.5
                end
        
                if self:GetIsMouseLocked() and not self:IsInFirstPerson() then
                    -- We need to use the right vector of the camera after rotation, not before
                    local newLookCFrame = self:CalculateNewLookCFrame(overrideCameraLookVector)
        
                    local offset = self:GetMouseLockOffset()
                    local cameraRelativeOffset = offset.X * newLookCFrame.rightVector + offset.Y * newLookCFrame.upVector + offset.Z * newLookCFrame.lookVector
        
                    --offset can be NAN, NAN, NAN if newLookVector has only y component
                    if Util.IsFiniteVector3(cameraRelativeOffset) then
                        subjectPosition = subjectPosition + cameraRelativeOffset
                    end
                else
                    if not self.userPanningTheCamera and self.lastCameraTransform then
        
                        local isInFirstPerson = self:IsInFirstPerson()
        
                        if (isInVehicle or isOnASkateboard or (self.isFollowCamera and isClimbing)) and self.lastUpdate and humanoid and humanoid.Torso then
                            if isInFirstPerson then
                                if self.lastSubjectCFrame and (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
                                    local y = -Util.GetAngleBetweenXZVectors(self.lastSubjectCFrame.lookVector, cameraSubject.CFrame.lookVector)
                                    if Util.IsFinite(y) then
                                        self.rotateInput = self.rotateInput + Vector2.new(y, 0)
                                    end
                                    tweenSpeed = 0
                                end
                            elseif not userRecentlyPannedCamera then
                                local forwardVector = humanoid.Torso.CFrame.lookVector
                                if isOnASkateboard then
                                    forwardVector = cameraSubject.CFrame.lookVector
                                end
        
                                tweenSpeed = math.clamp(tweenSpeed + tweenAcceleration * timeDelta, 0, tweenMaxSpeed)
        
                                local percent = math.clamp(tweenSpeed * timeDelta, 0, 1)
                                if self:IsInFirstPerson() and not (self.isFollowCamera and self.isClimbing) then
                                    percent = 1
                                end
        
                                local y = Util.GetAngleBetweenXZVectors(forwardVector, self:GetCameraLookVector())
                                if Util.IsFinite(y) and math.abs(y) > 0.0001 then
                                    self.rotateInput = self.rotateInput + Vector2.new(y * percent, 0)
                                end
                            end
        
                        elseif self.isFollowCamera and (not (isInFirstPerson or userRecentlyPannedCamera) and not VRService.VREnabled) then
                            -- Logic that was unique to the old FollowCamera module
                            local lastVec = -(self.lastCameraTransform.p - subjectPosition)
        
                            local y = Util.GetAngleBetweenXZVectors(lastVec, self:GetCameraLookVector())
        
                            -- This cutoff is to decide if the humanoid's angle of movement,
                            -- relative to the camera's look vector, is enough that
                            -- we want the camera to be following them. The point is to provide
                            -- a sizable dead zone to allow more precise forward movements.
                            local thetaCutoff = 0.4
        
                            -- Check for NaNs
                            if Util.IsFinite(y) and math.abs(y) > 0.0001 and math.abs(y) > thetaCutoff * timeDelta then
                                self.rotateInput = self.rotateInput + Vector2.new(y, 0)
                            end
                        end
                    end
                end
        
                if not self.isFollowCamera then
                    local VREnabled = VRService.VREnabled
        
                    if VREnabled then
                        newCameraFocus = self:GetVRFocus(subjectPosition, timeDelta)
                    else
                        newCameraFocus = CFrame.new(subjectPosition)
                    end
        
                    local cameraFocusP = newCameraFocus.p
                    if VREnabled and not self:IsInFirstPerson() then
                        local vecToSubject = (subjectPosition - camera.CFrame.p)
                        local distToSubject = vecToSubject.magnitude
        
                        -- Only move the camera if it exceeded a maximum distance to the subject in VR
                        if distToSubject > zoom or self.rotateInput.x ~= 0 then
                            local desiredDist = math.min(distToSubject, zoom)
                            vecToSubject = self:CalculateNewLookVectorVR() * desiredDist
                            local newPos = cameraFocusP - vecToSubject
                            local desiredLookDir = camera.CFrame.lookVector
                            if self.rotateInput.x ~= 0 then
                                desiredLookDir = vecToSubject
                            end
                            local lookAt = Vector3.new(newPos.x + desiredLookDir.x, newPos.y, newPos.z + desiredLookDir.z)
                            self.rotateInput = ZERO_VECTOR2
        
                            newCameraCFrame = CFrame.new(newPos, lookAt) + Vector3.new(0, cameraHeight, 0)
                        end
                    else
                        local newLookVector = self:CalculateNewLookVector(overrideCameraLookVector)
                        self.rotateInput = ZERO_VECTOR2
                        newCameraCFrame = CFrame.new(cameraFocusP - (zoom * newLookVector), cameraFocusP)
                    end
                else -- is FollowCamera
                    local newLookVector = self:CalculateNewLookVector(overrideCameraLookVector)
                    self.rotateInput = ZERO_VECTOR2
        
                    if VRService.VREnabled then
                        newCameraFocus = self:GetVRFocus(subjectPosition, timeDelta)
                    else
                        newCameraFocus = CFrame.new(subjectPosition)
                    end
                    newCameraCFrame = CFrame.new(newCameraFocus.p - (zoom * newLookVector), newCameraFocus.p) + Vector3.new(0, cameraHeight, 0)
                end
        
                if FFlagUserCameraToggle then
                    local toggleOffset = self:GetCameraToggleOffset(timeDelta)
                    newCameraFocus = newCameraFocus + toggleOffset
                    newCameraCFrame = newCameraCFrame + toggleOffset
                end
        
                self.lastCameraTransform = newCameraCFrame
                self.lastCameraFocus = newCameraFocus
                if (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
                    self.lastSubjectCFrame = cameraSubject.CFrame
                else
                    self.lastSubjectCFrame = nil
                end
            end
        
            self.lastUpdate = now
            return newCameraCFrame, newCameraFocus
        end
        
        function ClassicCamera:EnterFirstPerson()
            self.inFirstPerson = true
            self:UpdateMouseBehavior()
        end
        
        function ClassicCamera:LeaveFirstPerson()
            self.inFirstPerson = false
            self:UpdateMouseBehavior()
        end
        
        return ClassicCamera
    end
    
    function _CameraUtils()
    
        local CameraUtils = {}
        
        local FFlagUserCameraToggle do
            local success, result = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
            end)
            FFlagUserCameraToggle = success and result
        end
        
        local function round(num)
            return math.floor(num + 0.5)
        end
        
        -- Critically damped spring class for fluid motion effects
        local Spring = {} do
            Spring.__index = Spring
        
            -- Initialize to a given undamped frequency and default position
            function Spring.new(freq, pos)
                return setmetatable({
                    freq = freq,
                    goal = pos,
                    pos = pos,
                    vel = 0,
                }, Spring)
            end
        
            -- Advance the spring simulation by `dt` seconds
            function Spring:step(dt)
                local f = self.freq*2*math.pi
                local g = self.goal
                local p0 = self.pos
                local v0 = self.vel
        
                local offset = p0 - g
                local decay = math.exp(-f*dt)
        
                local p1 = (offset*(1 + f*dt) + v0*dt)*decay + g
                local v1 = (v0*(1 - f*dt) - offset*(f*f*dt))*decay
        
                self.pos = p1
                self.vel = v1
        
                return p1
            end
        end
        
        CameraUtils.Spring = Spring
        
        -- map a value from one range to another
        function CameraUtils.map(x, inMin, inMax, outMin, outMax)
            return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
        end
        
        -- From TransparencyController
        function CameraUtils.Round(num, places)
            local decimalPivot = 10^places
            return math.floor(num * decimalPivot + 0.5) / decimalPivot
        end
        
        function CameraUtils.IsFinite(val)
            return val == val and val ~= math.huge and val ~= -math.huge
        end
        
        function CameraUtils.IsFiniteVector3(vec3)
            return CameraUtils.IsFinite(vec3.X) and CameraUtils.IsFinite(vec3.Y) and CameraUtils.IsFinite(vec3.Z)
        end
        
        -- Legacy implementation renamed
        function CameraUtils.GetAngleBetweenXZVectors(v1, v2)
            return math.atan2(v2.X*v1.Z-v2.Z*v1.X, v2.X*v1.X+v2.Z*v1.Z)
        end
        
        function  CameraUtils.RotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
            if camLook.Magnitude > 0 then
                camLook = camLook.unit
                local currAngle = math.atan2(camLook.z, camLook.x)
                local newAngle = round((math.atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
                return newAngle - currAngle
            end
            return 0
        end
        
        -- K is a tunable parameter that changes the shape of the S-curve
        -- the larger K is the more straight/linear the curve gets
        local k = 0.35
        local lowerK = 0.8
        local function SCurveTranform(t)
            t = math.clamp(t, -1, 1)
            if t >= 0 then
                return (k*t) / (k - t + 1)
            end
            return -((lowerK*-t) / (lowerK + t + 1))
        end
        
        local DEADZONE = 0.1
        local function toSCurveSpace(t)
            return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
        end
        
        local function fromSCurveSpace(t)
            return t/2 + 0.5
        end
        
        function CameraUtils.GamepadLinearToCurve(thumbstickPosition)
            local function onAxis(axisValue)
                local sign = 1
                if axisValue < 0 then
                    sign = -1
                end
                local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math.abs(axisValue))))
                point = point * sign
                return math.clamp(point, -1, 1)
            end
            return Vector2.new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
        end
        
        -- This function converts 4 different, redundant enumeration types to one standard so the values can be compared
        function CameraUtils.ConvertCameraModeEnumToStandard(enumValue)
            if enumValue == Enum.TouchCameraMovementMode.Default then
                return Enum.ComputerCameraMovementMode.Follow
            end
        
            if enumValue == Enum.ComputerCameraMovementMode.Default then
                return Enum.ComputerCameraMovementMode.Classic
            end
        
            if enumValue == Enum.TouchCameraMovementMode.Classic or
                enumValue == Enum.DevTouchCameraMovementMode.Classic or
                enumValue == Enum.DevComputerCameraMovementMode.Classic or
                enumValue == Enum.ComputerCameraMovementMode.Classic then
                return Enum.ComputerCameraMovementMode.Classic
            end
        
            if enumValue == Enum.TouchCameraMovementMode.Follow or
                enumValue == Enum.DevTouchCameraMovementMode.Follow or
                enumValue == Enum.DevComputerCameraMovementMode.Follow or
                enumValue == Enum.ComputerCameraMovementMode.Follow then
                return Enum.ComputerCameraMovementMode.Follow
            end
        
            if enumValue == Enum.TouchCameraMovementMode.Orbital or
                enumValue == Enum.DevTouchCameraMovementMode.Orbital or
                enumValue == Enum.DevComputerCameraMovementMode.Orbital or
                enumValue == Enum.ComputerCameraMovementMode.Orbital then
                return Enum.ComputerCameraMovementMode.Orbital
            end
        
            if FFlagUserCameraToggle then
                if enumValue == Enum.ComputerCameraMovementMode.CameraToggle or
                    enumValue == Enum.DevComputerCameraMovementMode.CameraToggle then
                    return Enum.ComputerCameraMovementMode.CameraToggle
                end
            end
        
            -- Note: Only the Dev versions of the Enums have UserChoice as an option
            if enumValue == Enum.DevTouchCameraMovementMode.UserChoice or
                enumValue == Enum.DevComputerCameraMovementMode.UserChoice then
                return Enum.DevComputerCameraMovementMode.UserChoice
            end
        
            -- For any unmapped options return Classic camera
            return Enum.ComputerCameraMovementMode.Classic
        end
        
        return CameraUtils
    end
    
    function _CameraModule()
        local CameraModule = {}
        CameraModule.__index = CameraModule
        
        local FFlagUserCameraToggle do
            local success, result = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
            end)
            FFlagUserCameraToggle = success and result
        end
        
        local FFlagUserRemoveTheCameraApi do
            local success, result = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserRemoveTheCameraApi")
            end)
            FFlagUserRemoveTheCameraApi = success and result
        end
        
        -- NOTICE: Player property names do not all match their StarterPlayer equivalents,
        -- with the differences noted in the comments on the right
        local PLAYER_CAMERA_PROPERTIES =
        {
            "CameraMinZoomDistance",
            "CameraMaxZoomDistance",
            "CameraMode",
            "DevCameraOcclusionMode",
            "DevComputerCameraMode",			-- Corresponds to StarterPlayer.DevComputerCameraMovementMode
            "DevTouchCameraMode",				-- Corresponds to StarterPlayer.DevTouchCameraMovementMode
        
            -- Character movement mode
            "DevComputerMovementMode",
            "DevTouchMovementMode",
            "DevEnableMouseLock",				-- Corresponds to StarterPlayer.EnableMouseLockOption
        }
        
        local USER_GAME_SETTINGS_PROPERTIES =
        {
            "ComputerCameraMovementMode",
            "ComputerMovementMode",
            "ControlMode",
            "GamepadCameraSensitivity",
            "MouseSensitivity",
            "RotationType",
            "TouchCameraMovementMode",
            "TouchMovementMode",
        }
        
        --[[ Roblox Services ]]--
        local Players = game:GetService("Players")
        local RunService = game:GetService("RunService")
        local UserInputService = game:GetService("UserInputService")
        local UserGameSettings = UserSettings():GetService("UserGameSettings")
        
        -- Camera math utility library
        local CameraUtils = _CameraUtils()
        
        -- Load Roblox Camera Controller Modules
        local ClassicCamera = _ClassicCamera()
        local OrbitalCamera = _OrbitalCamera()
        local LegacyCamera = _LegacyCamera()
        
        -- Load Roblox Occlusion Modules
        local Invisicam = _Invisicam()
        local Poppercam = _Poppercam()
        
        -- Load the near-field character transparency controller and the mouse lock "shift lock" controller
        local TransparencyController = _TransparencyController()
        local MouseLockController = _MouseLockController()
        
        -- Table of camera controllers that have been instantiated. They are instantiated as they are used.
        local instantiatedCameraControllers = {}
        local instantiatedOcclusionModules = {}
        
        -- Management of which options appear on the Roblox User Settings screen
        do
            local PlayerScripts = Players.LocalPlayer:WaitForChild("PlayerScripts")
        
            PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Default)
            PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Follow)
            PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Classic)
        
            PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Default)
            PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Follow)
            PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Classic)
            if FFlagUserCameraToggle then
                PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.CameraToggle)
            end
        end
        
        CameraModule.FFlagUserCameraToggle = FFlagUserCameraToggle
        
        
        function CameraModule.new()
            local self = setmetatable({},CameraModule)
        
            -- Current active controller instances
            self.activeCameraController = nil
            self.activeOcclusionModule = nil
            self.activeTransparencyController = nil
            self.activeMouseLockController = nil
        
            self.currentComputerCameraMovementMode = nil
        
            -- Connections to events
            self.cameraSubjectChangedConn = nil
            self.cameraTypeChangedConn = nil
        
            -- Adds CharacterAdded and CharacterRemoving event handlers for all current players
            for _,player in pairs(Players:GetPlayers()) do
                self:OnPlayerAdded(player)
            end
        
            -- Adds CharacterAdded and CharacterRemoving event handlers for all players who join in the future
            Players.PlayerAdded:Connect(function(player)
                self:OnPlayerAdded(player)
            end)
        
            self.activeTransparencyController = TransparencyController.new()
            self.activeTransparencyController:Enable(true)
        
            if not UserInputService.TouchEnabled then
                self.activeMouseLockController = MouseLockController.new()
                local toggleEvent = self.activeMouseLockController:GetBindableToggleEvent()
                if toggleEvent then
                    toggleEvent:Connect(function()
                        self:OnMouseLockToggled()
                    end)
                end
            end
        
            self:ActivateCameraController(self:GetCameraControlChoice())
            self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
            self:OnCurrentCameraChanged() -- Does initializations and makes first camera controller
            RunService:BindToRenderStep("cameraRenderUpdate", Enum.RenderPriority.Camera.Value, function(dt) self:Update(dt) end)
        
            -- Connect listeners to camera-related properties
            for _, propertyName in pairs(PLAYER_CAMERA_PROPERTIES) do
                Players.LocalPlayer:GetPropertyChangedSignal(propertyName):Connect(function()
                    self:OnLocalPlayerCameraPropertyChanged(propertyName)
                end)
            end
        
            for _, propertyName in pairs(USER_GAME_SETTINGS_PROPERTIES) do
                UserGameSettings:GetPropertyChangedSignal(propertyName):Connect(function()
                    self:OnUserGameSettingsPropertyChanged(propertyName)
                end)
            end
            game.Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
                self:OnCurrentCameraChanged()
            end)
        
            self.lastInputType = UserInputService:GetLastInputType()
            UserInputService.LastInputTypeChanged:Connect(function(newLastInputType)
                self.lastInputType = newLastInputType
            end)
        
            return self
        end
        
        function CameraModule:GetCameraMovementModeFromSettings()
            local cameraMode = Players.LocalPlayer.CameraMode
        
            -- Lock First Person trumps all other settings and forces ClassicCamera
            if cameraMode == Enum.CameraMode.LockFirstPerson then
                return CameraUtils.ConvertCameraModeEnumToStandard(Enum.ComputerCameraMovementMode.Classic)
            end
        
            local devMode, userMode
            if UserInputService.TouchEnabled then
                devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevTouchCameraMode)
                userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.TouchCameraMovementMode)
            else
                devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevComputerCameraMode)
                userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
            end
        
            if devMode == Enum.DevComputerCameraMovementMode.UserChoice then
                -- Developer is allowing user choice, so user setting is respected
                return userMode
            end
        
            return devMode
        end
        
        function CameraModule:ActivateOcclusionModule( occlusionMode )
            local newModuleCreator
            if occlusionMode == Enum.DevCameraOcclusionMode.Zoom then
                newModuleCreator = Poppercam
            elseif occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
                newModuleCreator = Invisicam
            else
                warn("CameraScript ActivateOcclusionModule called with unsupported mode")
                return
            end
        
            -- First check to see if there is actually a change. If the module being requested is already
            -- the currently-active solution then just make sure it's enabled and exit early
            if self.activeOcclusionModule and self.activeOcclusionModule:GetOcclusionMode() == occlusionMode then
                if not self.activeOcclusionModule:GetEnabled() then
                    self.activeOcclusionModule:Enable(true)
                end
                return
            end
        
            -- Save a reference to the current active module (may be nil) so that we can disable it if
            -- we are successful in activating its replacement
            local prevOcclusionModule = self.activeOcclusionModule
        
            -- If there is no active module, see if the one we need has already been instantiated
            self.activeOcclusionModule = instantiatedOcclusionModules[newModuleCreator]
        
            -- If the module was not already instantiated and selected above, instantiate it
            if not self.activeOcclusionModule then
                self.activeOcclusionModule = newModuleCreator.new()
                if self.activeOcclusionModule then
                    instantiatedOcclusionModules[newModuleCreator] = self.activeOcclusionModule
                end
            end
        
            -- If we were successful in either selecting or instantiating the module,
            -- enable it if it's not already the currently-active enabled module
            if self.activeOcclusionModule then
                local newModuleOcclusionMode = self.activeOcclusionModule:GetOcclusionMode()
                -- Sanity check that the module we selected or instantiated actually supports the desired occlusionMode
                if newModuleOcclusionMode ~= occlusionMode then
                    warn("CameraScript ActivateOcclusionModule mismatch: ",self.activeOcclusionModule:GetOcclusionMode(),"~=",occlusionMode)
                end
        
                -- Deactivate current module if there is one
                if prevOcclusionModule then
                    -- Sanity check that current module is not being replaced by itself (that should have been handled above)
                    if prevOcclusionModule ~= self.activeOcclusionModule then
                        prevOcclusionModule:Enable(false)
                    else
                        warn("CameraScript ActivateOcclusionModule failure to detect already running correct module")
                    end
                end
        
                -- Occlusion modules need to be initialized with information about characters and cameraSubject
                -- Invisicam needs the LocalPlayer's character
                -- Poppercam needs all player characters and the camera subject
                if occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
                    -- Optimization to only send Invisicam what we know it needs
                    if Players.LocalPlayer.Character then
                        self.activeOcclusionModule:CharacterAdded(Players.LocalPlayer.Character, Players.LocalPlayer )
                    end
                else
                    -- When Poppercam is enabled, we send it all existing player characters for its raycast ignore list
                    for _, player in pairs(Players:GetPlayers()) do
                        if player and player.Character then
                            self.activeOcclusionModule:CharacterAdded(player.Character, player)
                        end
                    end
                    self.activeOcclusionModule:OnCameraSubjectChanged(game.Workspace.CurrentCamera.CameraSubject)
                end
        
                -- Activate new choice
                self.activeOcclusionModule:Enable(true)
            end
        end
        
        -- When supplied, legacyCameraType is used and cameraMovementMode is ignored (should be nil anyways)
        -- Next, if userCameraCreator is passed in, that is used as the cameraCreator
        function CameraModule:ActivateCameraController(cameraMovementMode, legacyCameraType)
            local newCameraCreator = nil
        
            if legacyCameraType~=nil then
                --[[
                    This function has been passed a CameraType enum value. Some of these map to the use of
                    the LegacyCamera module, the value "Custom" will be translated to a movementMode enum
                    value based on Dev and User settings, and "Scriptable" will disable the camera controller.
                --]]
        
                if legacyCameraType == Enum.CameraType.Scriptable then
                    if self.activeCameraController then
                        self.activeCameraController:Enable(false)
                        self.activeCameraController = nil
                        return
                    end
                elseif legacyCameraType == Enum.CameraType.Custom then
                    cameraMovementMode = self:GetCameraMovementModeFromSettings()
        
                elseif legacyCameraType == Enum.CameraType.Track then
                    -- Note: The TrackCamera module was basically an older, less fully-featured
                    -- version of ClassicCamera, no longer actively maintained, but it is re-implemented in
                    -- case a game was dependent on its lack of ClassicCamera's extra functionality.
                    cameraMovementMode = Enum.ComputerCameraMovementMode.Classic
        
                elseif legacyCameraType == Enum.CameraType.Follow then
                    cameraMovementMode = Enum.ComputerCameraMovementMode.Follow
        
                elseif legacyCameraType == Enum.CameraType.Orbital then
                    cameraMovementMode = Enum.ComputerCameraMovementMode.Orbital
        
                elseif legacyCameraType == Enum.CameraType.Attach or
                       legacyCameraType == Enum.CameraType.Watch or
                       legacyCameraType == Enum.CameraType.Fixed then
                    newCameraCreator = LegacyCamera
                else
                    warn("CameraScript encountered an unhandled Camera.CameraType value: ",legacyCameraType)
                end
            end
        
            if not newCameraCreator then
                if cameraMovementMode == Enum.ComputerCameraMovementMode.Classic or
                    cameraMovementMode == Enum.ComputerCameraMovementMode.Follow or
                    cameraMovementMode == Enum.ComputerCameraMovementMode.Default or
                    (FFlagUserCameraToggle and cameraMovementMode == Enum.ComputerCameraMovementMode.CameraToggle) then
                    newCameraCreator = ClassicCamera
                elseif cameraMovementMode == Enum.ComputerCameraMovementMode.Orbital then
                    newCameraCreator = OrbitalCamera
                else
                    warn("ActivateCameraController did not select a module.")
                    return
                end
            end
        
            -- Create the camera control module we need if it does not already exist in instantiatedCameraControllers
            local newCameraController
            if not instantiatedCameraControllers[newCameraCreator] then
                newCameraController = newCameraCreator.new()
                instantiatedCameraControllers[newCameraCreator] = newCameraController
            else
                newCameraController = instantiatedCameraControllers[newCameraCreator]
            end
        
            -- If there is a controller active and it's not the one we need, disable it,
            -- if it is the one we need, make sure it's enabled
            if self.activeCameraController then
                if self.activeCameraController ~= newCameraController then
                    self.activeCameraController:Enable(false)
                    self.activeCameraController = newCameraController
                    self.activeCameraController:Enable(true)
                elseif not self.activeCameraController:GetEnabled() then
                    self.activeCameraController:Enable(true)
                end
            elseif newCameraController ~= nil then
                self.activeCameraController = newCameraController
                self.activeCameraController:Enable(true)
            end
        
            if self.activeCameraController then
                if cameraMovementMode~=nil then
                    self.activeCameraController:SetCameraMovementMode(cameraMovementMode)
                elseif legacyCameraType~=nil then
                    -- Note that this is only called when legacyCameraType is not a type that
                    -- was convertible to a ComputerCameraMovementMode value, i.e. really only applies to LegacyCamera
                    self.activeCameraController:SetCameraType(legacyCameraType)
                end
            end
        end
        
        -- Note: The active transparency controller could be made to listen for this event itself.
        function CameraModule:OnCameraSubjectChanged()
            if self.activeTransparencyController then
                self.activeTransparencyController:SetSubject(game.Workspace.CurrentCamera.CameraSubject)
            end
        
            if self.activeOcclusionModule then
                self.activeOcclusionModule:OnCameraSubjectChanged(game.Workspace.CurrentCamera.CameraSubject)
            end
        end
        
        function CameraModule:OnCameraTypeChanged(newCameraType)
            if newCameraType == Enum.CameraType.Scriptable then
                if UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then
                    UserInputService.MouseBehavior = Enum.MouseBehavior.Default
                end
            end
        
            -- Forward the change to ActivateCameraController to handle
            self:ActivateCameraController(nil, newCameraType)
        end
        
        -- Note: Called whenever workspace.CurrentCamera changes, but also on initialization of this script
        function CameraModule:OnCurrentCameraChanged()
            local currentCamera = game.Workspace.CurrentCamera
            if not currentCamera then return end
        
            if self.cameraSubjectChangedConn then
                self.cameraSubjectChangedConn:Disconnect()
            end
        
            if self.cameraTypeChangedConn then
                self.cameraTypeChangedConn:Disconnect()
            end
        
            self.cameraSubjectChangedConn = currentCamera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
                self:OnCameraSubjectChanged(currentCamera.CameraSubject)
            end)
        
            self.cameraTypeChangedConn = currentCamera:GetPropertyChangedSignal("CameraType"):Connect(function()
                self:OnCameraTypeChanged(currentCamera.CameraType)
            end)
        
            self:OnCameraSubjectChanged(currentCamera.CameraSubject)
            self:OnCameraTypeChanged(currentCamera.CameraType)
        end
        
        function CameraModule:OnLocalPlayerCameraPropertyChanged(propertyName)
            if propertyName == "CameraMode" then
                -- CameraMode is only used to turn on/off forcing the player into first person view. The
                -- Note: The case "Classic" is used for all other views and does not correspond only to the ClassicCamera module
                if Players.LocalPlayer.CameraMode == Enum.CameraMode.LockFirstPerson then
                    -- Locked in first person, use ClassicCamera which supports this
                    if not self.activeCameraController or self.activeCameraController:GetModuleName() ~= "ClassicCamera" then
                        self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(Enum.DevComputerCameraMovementMode.Classic))
                    end
        
                    if self.activeCameraController then
                        self.activeCameraController:UpdateForDistancePropertyChange()
                    end
                elseif Players.LocalPlayer.CameraMode == Enum.CameraMode.Classic then
                    -- Not locked in first person view
                    local cameraMovementMode =self: GetCameraMovementModeFromSettings()
                    self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
                else
                    warn("Unhandled value for property player.CameraMode: ",Players.LocalPlayer.CameraMode)
                end
        
            elseif propertyName == "DevComputerCameraMode" or 
                   propertyName == "DevTouchCameraMode" then
                local cameraMovementMode = self:GetCameraMovementModeFromSettings()
                self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
        
            elseif propertyName == "DevCameraOcclusionMode" then
                self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
        
            elseif propertyName == "CameraMinZoomDistance" or propertyName == "CameraMaxZoomDistance" then
                if self.activeCameraController then
                    self.activeCameraController:UpdateForDistancePropertyChange()
                end
            elseif propertyName == "DevTouchMovementMode" then
            elseif propertyName == "DevComputerMovementMode" then
            elseif propertyName == "DevEnableMouseLock" then
                -- This is the enabling/disabling of "Shift Lock" mode, not LockFirstPerson (which is a CameraMode)
                -- Note: Enabling and disabling of MouseLock mode is normally only a publish-time choice made via
                -- the corresponding EnableMouseLockOption checkbox of StarterPlayer, and this script does not have
                -- support for changing the availability of MouseLock at runtime (this would require listening to
                -- Player.DevEnableMouseLock changes)
            end
        end
        
        function CameraModule:OnUserGameSettingsPropertyChanged(propertyName)
            if propertyName == 	"ComputerCameraMovementMode" then
                local cameraMovementMode = self:GetCameraMovementModeFromSettings()
                self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
            end
        end
        
        --[[
            Main RenderStep Update. The camera controller and occlusion module both have opportunities
            to set and modify (respectively) the CFrame and Focus before it is set once on CurrentCamera.
            The camera and occlusion modules should only return CFrames, not set the CFrame property of
            CurrentCamera directly.
        --]]
        function CameraModule:Update(dt)
            if self.activeCameraController then
                if FFlagUserCameraToggle then
                    self.activeCameraController:UpdateMouseBehavior()
                end
        
                local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
                self.activeCameraController:ApplyVRTransform()
                if self.activeOcclusionModule then
                    newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
                end
        
                -- Here is where the new CFrame and Focus are set for this render frame
                game.Workspace.CurrentCamera.CFrame = newCameraCFrame
                game.Workspace.CurrentCamera.Focus = newCameraFocus
        
                -- Update to character local transparency as needed based on camera-to-subject distance
                if self.activeTransparencyController then
                    self.activeTransparencyController:Update()
                end
            end
        end
        
        -- Formerly getCurrentCameraMode, this function resolves developer and user camera control settings to
        -- decide which camera control module should be instantiated. The old method of converting redundant enum types
        function CameraModule:GetCameraControlChoice()
            local player = Players.LocalPlayer
        
            if player then
                if self.lastInputType == Enum.UserInputType.Touch or UserInputService.TouchEnabled then
                    -- Touch
                    if player.DevTouchCameraMode == Enum.DevTouchCameraMovementMode.UserChoice then
                        return CameraUtils.ConvertCameraModeEnumToStandard( UserGameSettings.TouchCameraMovementMode )
                    else
                        return CameraUtils.ConvertCameraModeEnumToStandard( player.DevTouchCameraMode )
                    end
                else
                    -- Computer
                    if player.DevComputerCameraMode == Enum.DevComputerCameraMovementMode.UserChoice then
                        local computerMovementMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
                        return CameraUtils.ConvertCameraModeEnumToStandard(computerMovementMode)
                    else
                        return CameraUtils.ConvertCameraModeEnumToStandard(player.DevComputerCameraMode)
                    end
                end
            end
        end
        
        function CameraModule:OnCharacterAdded(char, player)
            if self.activeOcclusionModule then
                self.activeOcclusionModule:CharacterAdded(char, player)
            end
        end
        
        function CameraModule:OnCharacterRemoving(char, player)
            if self.activeOcclusionModule then
                self.activeOcclusionModule:CharacterRemoving(char, player)
            end
        end
        
        function CameraModule:OnPlayerAdded(player)
            player.CharacterAdded:Connect(function(char)
                self:OnCharacterAdded(char, player)
            end)
            player.CharacterRemoving:Connect(function(char)
                self:OnCharacterRemoving(char, player)
            end)
        end
        
        function CameraModule:OnMouseLockToggled()
            if self.activeMouseLockController then
                local mouseLocked = self.activeMouseLockController:GetIsMouseLocked()
                local mouseLockOffset = self.activeMouseLockController:GetMouseLockOffset()
                if self.activeCameraController then
                    self.activeCameraController:SetIsMouseLocked(mouseLocked)
                    self.activeCameraController:SetMouseLockOffset(mouseLockOffset)
                end
            end
        end
        --begin edit
        local Camera = CameraModule
        local IDENTITYCF = CFrame.new()
        local lastUpCFrame = IDENTITYCF
        
        Camera.UpVector = Vector3.new(0, 1, 0)
        Camera.TransitionRate = 0.15
        Camera.UpCFrame = IDENTITYCF
        
        function Camera:GetUpVector(oldUpVector)
            return oldUpVector
        end
        local function getRotationBetween(u, v, axis)
            local dot, uxv = u:Dot(v), u:Cross(v)
            if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
            return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
        end
        function Camera:CalculateUpCFrame()
            local oldUpVector = self.UpVector
            local newUpVector = self:GetUpVector(oldUpVector)
            
            local backup = game.Workspace.CurrentCamera.CFrame.RightVector
            local transitionCF = getRotationBetween(oldUpVector, newUpVector, backup)
            local vecSlerpCF = IDENTITYCF:Lerp(transitionCF, self.TransitionRate)
            
            self.UpVector = vecSlerpCF * oldUpVector
            self.UpCFrame = vecSlerpCF * self.UpCFrame
            
            lastUpCFrame = self.UpCFrame
        end
        
        function Camera:Update(dt)
            if self.activeCameraController then
                if Camera.FFlagUserCameraToggle then
                    self.activeCameraController:UpdateMouseBehavior()
                end
                
                local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
                self.activeCameraController:ApplyVRTransform()
                
                self:CalculateUpCFrame()
                self.activeCameraController:UpdateUpCFrame(self.UpCFrame)
                
                -- undo shift-lock offset
        
                local lockOffset = Vector3.new(0, 0, 0)
                if (self.activeMouseLockController and self.activeMouseLockController:GetIsMouseLocked()) then
                    lockOffset = self.activeMouseLockController:GetMouseLockOffset()
                end
                
                local offset = newCameraFocus:ToObjectSpace(newCameraCFrame)
                local camRotation = self.UpCFrame * offset
                newCameraFocus = newCameraFocus - newCameraCFrame:VectorToWorldSpace(lockOffset) + camRotation:VectorToWorldSpace(lockOffset)
                newCameraCFrame = newCameraFocus * camRotation
                
                --local offset = newCameraFocus:Inverse() * newCameraCFrame
                --newCameraCFrame = newCameraFocus * self.UpCFrame * offset
                
                if (self.activeCameraController.lastCameraTransform) then
                    self.activeCameraController.lastCameraTransform = newCameraCFrame
                    self.activeCameraController.lastCameraFocus = newCameraFocus
                end
                
                if self.activeOcclusionModule then
                    newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
                end
        
                game.Workspace.CurrentCamera.CFrame = newCameraCFrame
                game.Workspace.CurrentCamera.Focus = newCameraFocus
        
                if self.activeTransparencyController then
                    self.activeTransparencyController:Update()
                end
            end
        end
        
        function Camera:IsFirstPerson()
            if self.activeCameraController then
                return self.activeCameraController:InFirstPerson()
            end
            return false
        end
        
        function Camera:IsMouseLocked()
            if self.activeCameraController then
                return self.activeCameraController:GetIsMouseLocked()
            end
            return false
        end
        function Camera:IsToggleMode()
            if self.activeCameraController then
                return self.activeCameraController.isCameraToggle
            end
            return false
        end
        function Camera:IsCamRelative()
            return self:IsMouseLocked() or self:IsFirstPerson()
            --return self:IsToggleMode(), self:IsMouseLocked(), self:IsFirstPerson()
        end
        --
        local Utils = _CameraUtils()
        function Utils.GetAngleBetweenXZVectors(v1, v2)
            local upCFrame = lastUpCFrame
            v1 = upCFrame:VectorToObjectSpace(v1)
            v2 = upCFrame:VectorToObjectSpace(v2)
            return math.atan2(v2.X*v1.Z-v2.Z*v1.X, v2.X*v1.X+v2.Z*v1.Z)
        end
        --end edit
        local cameraModuleObject = CameraModule.new()
        local cameraApi = {}
        return cameraModuleObject
    end
    
    function _ClickToMoveDisplay()
        local ClickToMoveDisplay = {}
        
        local FAILURE_ANIMATION_ID = "rbxassetid://2874840706"
        
        local TrailDotIcon = "rbxasset://textures/ui/traildot.png"
        local EndWaypointIcon = "rbxasset://textures/ui/waypoint.png"
        
        local WaypointsAlwaysOnTop = false
        
        local WAYPOINT_INCLUDE_FACTOR = 2
        local LAST_DOT_DISTANCE = 3
        
        local WAYPOINT_BILLBOARD_SIZE = UDim2.new(0, 1.68 * 25, 0, 2 * 25)
        
        local ENDWAYPOINT_SIZE_OFFSET_MIN = Vector2.new(0, 0.5)
        local ENDWAYPOINT_SIZE_OFFSET_MAX = Vector2.new(0, 1)
        
        local FAIL_WAYPOINT_SIZE_OFFSET_CENTER = Vector2.new(0, 0.5)
        local FAIL_WAYPOINT_SIZE_OFFSET_LEFT = Vector2.new(0.1, 0.5)
        local FAIL_WAYPOINT_SIZE_OFFSET_RIGHT = Vector2.new(-0.1, 0.5)
        
        local FAILURE_TWEEN_LENGTH = 0.125
        local FAILURE_TWEEN_COUNT = 4
        
        local TWEEN_WAYPOINT_THRESHOLD = 5
        
        local TRAIL_DOT_PARENT_NAME = "ClickToMoveDisplay"
        
        local TrailDotSize = Vector2.new(1.5, 1.5)
        
        local TRAIL_DOT_MIN_SCALE = 1
        local TRAIL_DOT_MIN_DISTANCE = 10
        local TRAIL_DOT_MAX_SCALE = 2.5
        local TRAIL_DOT_MAX_DISTANCE = 100
        
        local PlayersService = game:GetService("Players")
        local TweenService = game:GetService("TweenService")
        local RunService = game:GetService("RunService")
        local Workspace = game:GetService("Workspace")
        
        local LocalPlayer = PlayersService.LocalPlayer
        
        local function CreateWaypointTemplates()
            local TrailDotTemplate = Instance.new("Part")
            TrailDotTemplate.Size = Vector3.new(1, 1, 1)
            TrailDotTemplate.Anchored = true
            TrailDotTemplate.CanCollide = false
            TrailDotTemplate.Name = "TrailDot"
            TrailDotTemplate.Transparency = 1
            local TrailDotImage = Instance.new("ImageHandleAdornment")
            TrailDotImage.Name = "TrailDotImage"
            TrailDotImage.Size = TrailDotSize
            TrailDotImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
            TrailDotImage.AlwaysOnTop = WaypointsAlwaysOnTop
            TrailDotImage.Image = TrailDotIcon
            TrailDotImage.Adornee = TrailDotTemplate
            TrailDotImage.Parent = TrailDotTemplate
        
            local EndWaypointTemplate = Instance.new("Part")
            EndWaypointTemplate.Size = Vector3.new(2, 2, 2)
            EndWaypointTemplate.Anchored = true
            EndWaypointTemplate.CanCollide = false
            EndWaypointTemplate.Name = "EndWaypoint"
            EndWaypointTemplate.Transparency = 1
            local EndWaypointImage = Instance.new("ImageHandleAdornment")
            EndWaypointImage.Name = "TrailDotImage"
            EndWaypointImage.Size = TrailDotSize
            EndWaypointImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
            EndWaypointImage.AlwaysOnTop = WaypointsAlwaysOnTop
            EndWaypointImage.Image = TrailDotIcon
            EndWaypointImage.Adornee = EndWaypointTemplate
            EndWaypointImage.Parent = EndWaypointTemplate
            local EndWaypointBillboard = Instance.new("BillboardGui")
            EndWaypointBillboard.Name = "EndWaypointBillboard"
            EndWaypointBillboard.Size = WAYPOINT_BILLBOARD_SIZE
            EndWaypointBillboard.LightInfluence = 0
            EndWaypointBillboard.SizeOffset = ENDWAYPOINT_SIZE_OFFSET_MIN
            EndWaypointBillboard.AlwaysOnTop = true
            EndWaypointBillboard.Adornee = EndWaypointTemplate
            EndWaypointBillboard.Parent = EndWaypointTemplate
            local EndWaypointImageLabel = Instance.new("ImageLabel")
            EndWaypointImageLabel.Image = EndWaypointIcon
            EndWaypointImageLabel.BackgroundTransparency = 1
            EndWaypointImageLabel.Size = UDim2.new(1, 0, 1, 0)
            EndWaypointImageLabel.Parent = EndWaypointBillboard
        
        
            local FailureWaypointTemplate = Instance.new("Part")
            FailureWaypointTemplate.Size = Vector3.new(2, 2, 2)
            FailureWaypointTemplate.Anchored = true
            FailureWaypointTemplate.CanCollide = false
            FailureWaypointTemplate.Name = "FailureWaypoint"
            FailureWaypointTemplate.Transparency = 1
            local FailureWaypointImage = Instance.new("ImageHandleAdornment")
            FailureWaypointImage.Name = "TrailDotImage"
            FailureWaypointImage.Size = TrailDotSize
            FailureWaypointImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
            FailureWaypointImage.AlwaysOnTop = WaypointsAlwaysOnTop
            FailureWaypointImage.Image = TrailDotIcon
            FailureWaypointImage.Adornee = FailureWaypointTemplate
            FailureWaypointImage.Parent = FailureWaypointTemplate
            local FailureWaypointBillboard = Instance.new("BillboardGui")
            FailureWaypointBillboard.Name = "FailureWaypointBillboard"
            FailureWaypointBillboard.Size = WAYPOINT_BILLBOARD_SIZE
            FailureWaypointBillboard.LightInfluence = 0
            FailureWaypointBillboard.SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_CENTER
            FailureWaypointBillboard.AlwaysOnTop = true
            FailureWaypointBillboard.Adornee = FailureWaypointTemplate
            FailureWaypointBillboard.Parent = FailureWaypointTemplate
            local FailureWaypointFrame = Instance.new("Frame")
            FailureWaypointFrame.BackgroundTransparency = 1
            FailureWaypointFrame.Size = UDim2.new(0, 0, 0, 0)
            FailureWaypointFrame.Position = UDim2.new(0.5, 0, 1, 0)
            FailureWaypointFrame.Parent = FailureWaypointBillboard
            local FailureWaypointImageLabel = Instance.new("ImageLabel")
            FailureWaypointImageLabel.Image = EndWaypointIcon
            FailureWaypointImageLabel.BackgroundTransparency = 1
            FailureWaypointImageLabel.Position = UDim2.new(
                0, -WAYPOINT_BILLBOARD_SIZE.X.Offset/2, 0, -WAYPOINT_BILLBOARD_SIZE.Y.Offset
            )
            FailureWaypointImageLabel.Size = WAYPOINT_BILLBOARD_SIZE
            FailureWaypointImageLabel.Parent = FailureWaypointFrame
        
            return TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate
        end
        
        local TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
        
        local function getTrailDotParent()
            local camera = Workspace.CurrentCamera
            local trailParent = camera:FindFirstChild(TRAIL_DOT_PARENT_NAME)
            if not trailParent then
                trailParent = Instance.new("Model")
                trailParent.Name = TRAIL_DOT_PARENT_NAME
                trailParent.Parent = camera
            end
            return trailParent
        end
        
        local function placePathWaypoint(waypointModel, position)
            local ray = Ray.new(position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
            local hitPart, hitPoint, hitNormal = Workspace:FindPartOnRayWithIgnoreList(
                ray,
                { Workspace.CurrentCamera, LocalPlayer.Character }
            )
            if hitPart then
                waypointModel.CFrame = CFrame.new(hitPoint, hitPoint + hitNormal)
                waypointModel.Parent = getTrailDotParent()
            end
        end
        
        local TrailDot = {}
        TrailDot.__index = TrailDot
        
        function TrailDot:Destroy()
            self.DisplayModel:Destroy()
        end
        
        function TrailDot:NewDisplayModel(position)
            local newDisplayModel = TrailDotTemplate:Clone()
            placePathWaypoint(newDisplayModel, position)
            return newDisplayModel
        end
        
        function TrailDot.new(position, closestWaypoint)
            local self = setmetatable({}, TrailDot)
        
            self.DisplayModel = self:NewDisplayModel(position)
            self.ClosestWayPoint = closestWaypoint
        
            return self
        end
        
        local EndWaypoint = {}
        EndWaypoint.__index = EndWaypoint
        
        function EndWaypoint:Destroy()
            self.Destroyed = true
            self.Tween:Cancel()
            self.DisplayModel:Destroy()
        end
        
        function EndWaypoint:NewDisplayModel(position)
            local newDisplayModel = EndWaypointTemplate:Clone()
            placePathWaypoint(newDisplayModel, position)
            return newDisplayModel
        end
        
        function EndWaypoint:CreateTween()
            local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, -1, true)
            local tween = TweenService:Create(
                self.DisplayModel.EndWaypointBillboard,
                tweenInfo,
                { SizeOffset = ENDWAYPOINT_SIZE_OFFSET_MAX }
            )
            tween:Play()
            return tween
        end
        
        function EndWaypoint:TweenInFrom(originalPosition)
            local currentPositon = self.DisplayModel.Position
            local studsOffset = originalPosition - currentPositon
            self.DisplayModel.EndWaypointBillboard.StudsOffset = Vector3.new(0, studsOffset.Y, 0)
            local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
            local tween = TweenService:Create(
                self.DisplayModel.EndWaypointBillboard,
                tweenInfo,
                { StudsOffset = Vector3.new(0, 0, 0) }
            )
            tween:Play()
            return tween
        end
        
        function EndWaypoint.new(position, closestWaypoint, originalPosition)
            local self = setmetatable({}, EndWaypoint)
        
            self.DisplayModel = self:NewDisplayModel(position)
            self.Destroyed = false
            if originalPosition and (originalPosition - position).magnitude > TWEEN_WAYPOINT_THRESHOLD then
                self.Tween = self:TweenInFrom(originalPosition)
                coroutine.wrap(function()
                    self.Tween.Completed:Wait()
                    if not self.Destroyed then
                        self.Tween = self:CreateTween()
                    end
                end)()
            else
                self.Tween = self:CreateTween()
            end
            self.ClosestWayPoint = closestWaypoint
        
            return self
        end
        
        local FailureWaypoint = {}
        FailureWaypoint.__index = FailureWaypoint
        
        function FailureWaypoint:Hide()
            self.DisplayModel.Parent = nil
        end
        
        function FailureWaypoint:Destroy()
            self.DisplayModel:Destroy()
        end
        
        function FailureWaypoint:NewDisplayModel(position)
            local newDisplayModel = FailureWaypointTemplate:Clone()
            placePathWaypoint(newDisplayModel, position)
            local ray = Ray.new(position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
            local hitPart, hitPoint, hitNormal = Workspace:FindPartOnRayWithIgnoreList(
                ray, { Workspace.CurrentCamera, LocalPlayer.Character }
            )
            if hitPart then
                newDisplayModel.CFrame = CFrame.new(hitPoint, hitPoint + hitNormal)
                newDisplayModel.Parent = getTrailDotParent()
            end
            return newDisplayModel
        end
        
        function FailureWaypoint:RunFailureTween()
            wait(FAILURE_TWEEN_LENGTH) -- Delay one tween length betfore starting tweening
            -- Tween out from center
            local tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH/2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
            local tweenLeft = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
                { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_LEFT })
            tweenLeft:Play()
        
            local tweenLeftRoation = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
                { Rotation = 10 })
            tweenLeftRoation:Play()
        
            tweenLeft.Completed:wait()
        
            -- Tween back and forth
            tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH, Enum.EasingStyle.Sine, Enum.EasingDirection.Out,
                FAILURE_TWEEN_COUNT - 1, true)
            local tweenSideToSide = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
                { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_RIGHT})
            tweenSideToSide:Play()
        
            -- Tween flash dark and roate left and right
            tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH, Enum.EasingStyle.Sine, Enum.EasingDirection.Out,
                FAILURE_TWEEN_COUNT - 1, true)
            local tweenFlash = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame.ImageLabel, tweenInfo,
                { ImageColor3 = Color3.new(0.75, 0.75, 0.75)})
            tweenFlash:Play()
        
            local tweenRotate = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
                { Rotation = -10 })
            tweenRotate:Play()
        
            tweenSideToSide.Completed:wait()
        
            -- Tween back to center
            tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH/2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
            local tweenCenter = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
                { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_CENTER })
            tweenCenter:Play()
        
            local tweenRoation = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
                { Rotation = 0 })
            tweenRoation:Play()
        
            tweenCenter.Completed:wait()
        
            wait(FAILURE_TWEEN_LENGTH) -- Delay one tween length betfore removing
        end
        
        function FailureWaypoint.new(position)
            local self = setmetatable({}, FailureWaypoint)
        
            self.DisplayModel = self:NewDisplayModel(position)
        
            return self
        end
        
        local failureAnimation = Instance.new("Animation")
        failureAnimation.AnimationId = FAILURE_ANIMATION_ID
        
        local lastHumanoid = nil
        local lastFailureAnimationTrack = nil
        
        local function getFailureAnimationTrack(myHumanoid)
            if myHumanoid == lastHumanoid then
                return lastFailureAnimationTrack
            end
            lastFailureAnimationTrack = myHumanoid:LoadAnimation(failureAnimation)
            lastFailureAnimationTrack.Priority = Enum.AnimationPriority.Action
            lastFailureAnimationTrack.Looped = false
            return lastFailureAnimationTrack
        end
        
        local function findPlayerHumanoid()
            local character = LocalPlayer.Character
            if character then
                return character:FindFirstChildOfClass("Humanoid")
            end
        end
        
        local function createTrailDots(wayPoints, originalEndWaypoint)
            local newTrailDots = {}
            local count = 1
            for i = 1, #wayPoints - 1 do
                local closeToEnd = (wayPoints[i].Position - wayPoints[#wayPoints].Position).magnitude < LAST_DOT_DISTANCE
                local includeWaypoint = i % WAYPOINT_INCLUDE_FACTOR == 0 and not closeToEnd
                if includeWaypoint then
                    local trailDot = TrailDot.new(wayPoints[i].Position, i)
                    newTrailDots[count] = trailDot
                    count = count + 1
                end
            end
        
            local newEndWaypoint = EndWaypoint.new(wayPoints[#wayPoints].Position, #wayPoints, originalEndWaypoint)
            table.insert(newTrailDots, newEndWaypoint)
        
            local reversedTrailDots = {}
            count = 1
            for i = #newTrailDots, 1, -1 do
                reversedTrailDots[count] = newTrailDots[i]
                count = count + 1
            end
            return reversedTrailDots
        end
        
        local function getTrailDotScale(distanceToCamera, defaultSize)
            local rangeLength = TRAIL_DOT_MAX_DISTANCE - TRAIL_DOT_MIN_DISTANCE
            local inRangePoint = math.clamp(distanceToCamera - TRAIL_DOT_MIN_DISTANCE, 0, rangeLength)/rangeLength
            local scale = TRAIL_DOT_MIN_SCALE + (TRAIL_DOT_MAX_SCALE - TRAIL_DOT_MIN_SCALE)*inRangePoint
            return defaultSize * scale
        end
        
        local createPathCount = 0
        -- originalEndWaypoint is optional, causes the waypoint to tween from that position.
        function ClickToMoveDisplay.CreatePathDisplay(wayPoints, originalEndWaypoint)
            createPathCount = createPathCount + 1
            local trailDots = createTrailDots(wayPoints, originalEndWaypoint)
        
            local function removePathBeforePoint(wayPointNumber)
                -- kill all trailDots before and at wayPointNumber
                for i = #trailDots, 1, -1 do
                    local trailDot = trailDots[i]
                    if trailDot.ClosestWayPoint <= wayPointNumber then
                        trailDot:Destroy()
                        trailDots[i] = nil
                    else
                        break
                    end
                end
            end
        
            local reiszeTrailDotsUpdateName = "ClickToMoveResizeTrail" ..createPathCount
            local function resizeTrailDots()
                if #trailDots == 0 then
                    RunService:UnbindFromRenderStep(reiszeTrailDotsUpdateName)
                    return
                end
                local cameraPos = Workspace.CurrentCamera.CFrame.p
                for i = 1, #trailDots do
                    local trailDotImage = trailDots[i].DisplayModel:FindFirstChild("TrailDotImage")
                    if trailDotImage then
                        local distanceToCamera = (trailDots[i].DisplayModel.Position - cameraPos).magnitude
                        trailDotImage.Size = getTrailDotScale(distanceToCamera, TrailDotSize)
                    end
                end
            end
            RunService:BindToRenderStep(reiszeTrailDotsUpdateName, Enum.RenderPriority.Camera.Value - 1, resizeTrailDots)
        
            local function removePath()
                removePathBeforePoint(#wayPoints)
            end
        
            return removePath, removePathBeforePoint
        end
        
        local lastFailureWaypoint = nil
        function ClickToMoveDisplay.DisplayFailureWaypoint(position)
            if lastFailureWaypoint then
                lastFailureWaypoint:Hide()
            end
            local failureWaypoint = FailureWaypoint.new(position)
            lastFailureWaypoint = failureWaypoint
            coroutine.wrap(function()
                failureWaypoint:RunFailureTween()
                failureWaypoint:Destroy()
                failureWaypoint = nil
            end)()
        end
        
        function ClickToMoveDisplay.CreateEndWaypoint(position)
            return EndWaypoint.new(position)
        end
        
        function ClickToMoveDisplay.PlayFailureAnimation()
            local myHumanoid = findPlayerHumanoid()
            if myHumanoid then
                local animationTrack = getFailureAnimationTrack(myHumanoid)
                animationTrack:Play()
            end
        end
        
        function ClickToMoveDisplay.CancelFailureAnimation()
            if lastFailureAnimationTrack ~= nil and lastFailureAnimationTrack.IsPlaying then
                lastFailureAnimationTrack:Stop()
            end
        end
        
        function ClickToMoveDisplay.SetWaypointTexture(texture)
            TrailDotIcon = texture
            TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
        end
        
        function ClickToMoveDisplay.GetWaypointTexture()
            return TrailDotIcon
        end
        
        function ClickToMoveDisplay.SetWaypointRadius(radius)
            TrailDotSize = Vector2.new(radius, radius)
            TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
        end
        
        function ClickToMoveDisplay.GetWaypointRadius()
            return TrailDotSize.X
        end
        
        function ClickToMoveDisplay.SetEndWaypointTexture(texture)
            EndWaypointIcon = texture
            TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
        end
        
        function ClickToMoveDisplay.GetEndWaypointTexture()
            return EndWaypointIcon
        end
        
        function ClickToMoveDisplay.SetWaypointsAlwaysOnTop(alwaysOnTop)
            WaypointsAlwaysOnTop = alwaysOnTop
            TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
        end
        
        function ClickToMoveDisplay.GetWaypointsAlwaysOnTop()
            return WaypointsAlwaysOnTop
        end
        
        return ClickToMoveDisplay
    end
    
    function _BaseCharacterController()
    
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        
        --[[ The Module ]]--
        local BaseCharacterController = {}
        BaseCharacterController.__index = BaseCharacterController
        
        function BaseCharacterController.new()
            local self = setmetatable({}, BaseCharacterController)
            self.enabled = false
            self.moveVector = ZERO_VECTOR3
            self.moveVectorIsCameraRelative = true
            self.isJumping = false
            return self
        end
        
        function BaseCharacterController:OnRenderStepped(dt)
            -- By default, nothing to do
        end
        
        function BaseCharacterController:GetMoveVector()
            return self.moveVector
        end
        
        function BaseCharacterController:IsMoveVectorCameraRelative()
            return self.moveVectorIsCameraRelative
        end
        
        function BaseCharacterController:GetIsJumping()
            return self.isJumping
        end
        
        -- Override in derived classes to set self.enabled and return boolean indicating
        -- whether Enable/Disable was successful. Return true if controller is already in the requested state.
        function BaseCharacterController:Enable(enable)
            error("BaseCharacterController:Enable must be overridden in derived classes and should not be called.")
            return false
        end
        
        return BaseCharacterController
    end
    
    function _VehicleController()
        local ContextActionService = game:GetService("ContextActionService")
        
        --[[ Constants ]]--
        -- Set this to true if you want to instead use the triggers for the throttle
        local useTriggersForThrottle = true
        -- Also set this to true if you want the thumbstick to not affect throttle, only triggers when a gamepad is conected
        local onlyTriggersForThrottle = false
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        
        local AUTO_PILOT_DEFAULT_MAX_STEERING_ANGLE = 35
        
        
        -- Note that VehicleController does not derive from BaseCharacterController, it is a special case
        local VehicleController = {}
        VehicleController.__index = VehicleController
        
        function VehicleController.new(CONTROL_ACTION_PRIORITY)
            local self = setmetatable({}, VehicleController)
        
            self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
        
            self.enabled = false
            self.vehicleSeat = nil
            self.throttle = 0
            self.steer = 0
        
            self.acceleration = 0
            self.decceleration = 0
            self.turningRight = 0
            self.turningLeft = 0
        
            self.vehicleMoveVector = ZERO_VECTOR3
        
            self.autoPilot = {}
            self.autoPilot.MaxSpeed = 0
            self.autoPilot.MaxSteeringAngle = 0
        
            return self
        end
        
        function VehicleController:BindContextActions()
            if useTriggersForThrottle then
                ContextActionService:BindActionAtPriority("throttleAccel", (function(actionName, inputState, inputObject)
                    self:OnThrottleAccel(actionName, inputState, inputObject)
                    return Enum.ContextActionResult.Pass
                end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonR2)
                ContextActionService:BindActionAtPriority("throttleDeccel", (function(actionName, inputState, inputObject)
                    self:OnThrottleDeccel(actionName, inputState, inputObject)
                    return Enum.ContextActionResult.Pass
                end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonL2)
            end
            ContextActionService:BindActionAtPriority("arrowSteerRight", (function(actionName, inputState, inputObject)
                self:OnSteerRight(actionName, inputState, inputObject)
                return Enum.ContextActionResult.Pass
            end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Right)
            ContextActionService:BindActionAtPriority("arrowSteerLeft", (function(actionName, inputState, inputObject)
                self:OnSteerLeft(actionName, inputState, inputObject)
                return Enum.ContextActionResult.Pass
            end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Left)
        end
        
        function VehicleController:Enable(enable, vehicleSeat)
            if enable == self.enabled and vehicleSeat == self.vehicleSeat then
                return
            end
        
            self.enabled = enable
            self.vehicleMoveVector = ZERO_VECTOR3
        
            if enable then
                if vehicleSeat then
                    self.vehicleSeat = vehicleSeat
        
                    self:SetupAutoPilot()
                    self:BindContextActions()
                end
            else
                if useTriggersForThrottle then
                    ContextActionService:UnbindAction("throttleAccel")
                    ContextActionService:UnbindAction("throttleDeccel")
                end
                ContextActionService:UnbindAction("arrowSteerRight")
                ContextActionService:UnbindAction("arrowSteerLeft")
                self.vehicleSeat = nil
            end
        end
        
        function VehicleController:OnThrottleAccel(actionName, inputState, inputObject)
            if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
                self.acceleration = 0
            else
                self.acceleration = -1
            end
            self.throttle = self.acceleration + self.decceleration
        end
        
        function VehicleController:OnThrottleDeccel(actionName, inputState, inputObject)
            if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
                self.decceleration = 0
            else
                self.decceleration = 1
            end
            self.throttle = self.acceleration + self.decceleration
        end
        
        function VehicleController:OnSteerRight(actionName, inputState, inputObject)
            if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
                self.turningRight = 0
            else
                self.turningRight = 1
            end
            self.steer = self.turningRight + self.turningLeft
        end
        
        function VehicleController:OnSteerLeft(actionName, inputState, inputObject)
            if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
                self.turningLeft = 0
            else
                self.turningLeft = -1
            end
            self.steer = self.turningRight + self.turningLeft
        end
        
        -- Call this from a function bound to Renderstep with Input Priority
        function VehicleController:Update(moveVector, cameraRelative, usingGamepad)
            if self.vehicleSeat then
                if cameraRelative then
                    -- This is the default steering mode
                    moveVector = moveVector + Vector3.new(self.steer, 0, self.throttle)
                    if usingGamepad and onlyTriggersForThrottle and useTriggersForThrottle then
                        self.vehicleSeat.ThrottleFloat = -self.throttle
                    else
                        self.vehicleSeat.ThrottleFloat = -moveVector.Z
                    end
                    self.vehicleSeat.SteerFloat = moveVector.X
        
                    return moveVector, true
                else
                    -- This is the path following mode
                    local localMoveVector = self.vehicleSeat.Occupant.RootPart.CFrame:VectorToObjectSpace(moveVector)
        
                    self.vehicleSeat.ThrottleFloat = self:ComputeThrottle(localMoveVector)
                    self.vehicleSeat.SteerFloat = self:ComputeSteer(localMoveVector)
        
                    return ZERO_VECTOR3, true
                end
            end
            return moveVector, false
        end
        
        function VehicleController:ComputeThrottle(localMoveVector)
            if localMoveVector ~= ZERO_VECTOR3 then
                local throttle = -localMoveVector.Z
                return throttle
            else
                return 0.0
            end
        end
        
        function VehicleController:ComputeSteer(localMoveVector)
            if localMoveVector ~= ZERO_VECTOR3 then
                local steerAngle = -math.atan2(-localMoveVector.x, -localMoveVector.z) * (180 / math.pi)
                return steerAngle / self.autoPilot.MaxSteeringAngle
            else
                return 0.0
            end
        end
        
        function VehicleController:SetupAutoPilot()
            -- Setup default
            self.autoPilot.MaxSpeed = self.vehicleSeat.MaxSpeed
            self.autoPilot.MaxSteeringAngle = AUTO_PILOT_DEFAULT_MAX_STEERING_ANGLE
        
            -- VehicleSeat should have a MaxSteeringAngle as well.
            -- Or we could look for a child "AutoPilotConfigModule" to find these values
            -- Or allow developer to set them through the API as like the CLickToMove customization API
        end
        
        return VehicleController
    end
    
    function _TouchJump()
        
        local Players = game:GetService("Players")
        local GuiService = game:GetService("GuiService")
        
        --[[ Constants ]]--
        local TOUCH_CONTROL_SHEET = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
        
        --[[ The Module ]]--
        local BaseCharacterController = _BaseCharacterController()
        local TouchJump = setmetatable({}, BaseCharacterController)
        TouchJump.__index = TouchJump
        
        function TouchJump.new()
            local self = setmetatable(BaseCharacterController.new(), TouchJump)
        
            self.parentUIFrame = nil
            self.jumpButton = nil
            self.characterAddedConn = nil
            self.humanoidStateEnabledChangedConn = nil
            self.humanoidJumpPowerConn = nil
            self.humanoidParentConn = nil
            self.externallyEnabled = false
            self.jumpPower = 0
            self.jumpStateEnabled = true
            self.isJumping = false
            self.humanoid = nil -- saved reference because property change connections are made using it
        
            return self
        end
        
        function TouchJump:EnableButton(enable)
            if enable then
                if not self.jumpButton then
                    self:Create()
                end
                local humanoid = Players.LocalPlayer.Character and Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
                if humanoid and self.externallyEnabled then
                    if self.externallyEnabled then
                        if humanoid.JumpPower > 0 then
                            self.jumpButton.Visible = true
                        end
                    end
                end
            else
                self.jumpButton.Visible = false
                self.isJumping = false
                self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
            end
        end
        
        function TouchJump:UpdateEnabled()
            if self.jumpPower > 0 and self.jumpStateEnabled then
                self:EnableButton(true)
            else
                self:EnableButton(false)
            end
        end
        
        function TouchJump:HumanoidChanged(prop)
            local humanoid = Players.LocalPlayer.Character and Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
            if humanoid then
                if prop == "JumpPower" then
                    self.jumpPower =  humanoid.JumpPower
                    self:UpdateEnabled()
                elseif prop == "Parent" then
                    if not humanoid.Parent then
                        self.humanoidChangeConn:Disconnect()
                    end
                end
            end
        end
        
        function TouchJump:HumanoidStateEnabledChanged(state, isEnabled)
            if state == Enum.HumanoidStateType.Jumping then
                self.jumpStateEnabled = isEnabled
                self:UpdateEnabled()
            end
        end
        
        function TouchJump:CharacterAdded(char)
            if self.humanoidChangeConn then
                self.humanoidChangeConn:Disconnect()
                self.humanoidChangeConn = nil
            end
        
            self.humanoid = char:FindFirstChildOfClass("Humanoid")
            while not self.humanoid do
                char.ChildAdded:wait()
                self.humanoid = char:FindFirstChildOfClass("Humanoid")
            end
        
            self.humanoidJumpPowerConn = self.humanoid:GetPropertyChangedSignal("JumpPower"):Connect(function()
                self.jumpPower =  self.humanoid.JumpPower
                self:UpdateEnabled()
            end)
        
            self.humanoidParentConn = self.humanoid:GetPropertyChangedSignal("Parent"):Connect(function()
                if not self.humanoid.Parent then
                    self.humanoidJumpPowerConn:Disconnect()
                    self.humanoidJumpPowerConn = nil
                    self.humanoidParentConn:Disconnect()
                    self.humanoidParentConn = nil
                end
            end)
        
            self.humanoidStateEnabledChangedConn = self.humanoid.StateEnabledChanged:Connect(function(state, enabled)
                self:HumanoidStateEnabledChanged(state, enabled)
            end)
        
            self.jumpPower = self.humanoid.JumpPower
            self.jumpStateEnabled = self.humanoid:GetStateEnabled(Enum.HumanoidStateType.Jumping)
            self:UpdateEnabled()
        end
        
        function TouchJump:SetupCharacterAddedFunction()
            self.characterAddedConn = Players.LocalPlayer.CharacterAdded:Connect(function(char)
                self:CharacterAdded(char)
            end)
            if Players.LocalPlayer.Character then
                self:CharacterAdded(Players.LocalPlayer.Character)
            end
        end
        
        function TouchJump:Enable(enable, parentFrame)
            if parentFrame then
                self.parentUIFrame = parentFrame
            end
            self.externallyEnabled = enable
            self:EnableButton(enable)
        end
        
        function TouchJump:Create()
            if not self.parentUIFrame then
                return
            end
        
            if self.jumpButton then
                self.jumpButton:Destroy()
                self.jumpButton = nil
            end
        
            local minAxis = math.min(self.parentUIFrame.AbsoluteSize.x, self.parentUIFrame.AbsoluteSize.y)
            local isSmallScreen = minAxis <= 500
            local jumpButtonSize = isSmallScreen and 70 or 120
        
            self.jumpButton = Instance.new("ImageButton")
            self.jumpButton.Name = "JumpButton"
            self.jumpButton.Visible = false
            self.jumpButton.BackgroundTransparency = 1
            self.jumpButton.Image = TOUCH_CONTROL_SHEET
            self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
            self.jumpButton.ImageRectSize = Vector2.new(144, 144)
            self.jumpButton.Size = UDim2.new(0, jumpButtonSize, 0, jumpButtonSize)
        
            self.jumpButton.Position = isSmallScreen and UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize - 20) or
                UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize * 1.75)
        
            local touchObject = nil
            self.jumpButton.InputBegan:connect(function(inputObject)
                --A touch that starts elsewhere on the screen will be sent to a frame's InputBegan event
                --if it moves over the frame. So we check that this is actually a new touch (inputObject.UserInputState ~= Enum.UserInputState.Begin)
                if touchObject or inputObject.UserInputType ~= Enum.UserInputType.Touch
                    or inputObject.UserInputState ~= Enum.UserInputState.Begin then
                    return
                end
        
                touchObject = inputObject
                self.jumpButton.ImageRectOffset = Vector2.new(146, 146)
                self.isJumping = true
            end)
        
            local OnInputEnded = function()
                touchObject = nil
                self.isJumping = false
                self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
            end
        
            self.jumpButton.InputEnded:connect(function(inputObject)
                if inputObject == touchObject then
                    OnInputEnded()
                end
            end)
        
            GuiService.MenuOpened:connect(function()
                if touchObject then
                    OnInputEnded()
                end
            end)
        
            if not self.characterAddedConn then
                self:SetupCharacterAddedFunction()
            end
        
            self.jumpButton.Parent = self.parentUIFrame
        end
        
        return TouchJump
    end
    
    function _ClickToMoveController()
        --[[ Roblox Services ]]--
        local UserInputService = game:GetService("UserInputService")
        local PathfindingService = game:GetService("PathfindingService")
        local Players = game:GetService("Players")
        local DebrisService = game:GetService('Debris')
        local StarterGui = game:GetService("StarterGui")
        local Workspace = game:GetService("Workspace")
        local CollectionService = game:GetService("CollectionService")
        local GuiService = game:GetService("GuiService")
        
        --[[ Configuration ]]
        local ShowPath = true
        local PlayFailureAnimation = true
        local UseDirectPath = false
        local UseDirectPathForVehicle = true
        local AgentSizeIncreaseFactor = 1.0
        local UnreachableWaypointTimeout = 8
        
        --[[ Constants ]]--
        local movementKeys = {
            [Enum.KeyCode.W] = true;
            [Enum.KeyCode.A] = true;
            [Enum.KeyCode.S] = true;
            [Enum.KeyCode.D] = true;
            [Enum.KeyCode.Up] = true;
            [Enum.KeyCode.Down] = true;
        }
        
        local FFlagUserNavigationClickToMoveSkipPassedWaypointsSuccess, FFlagUserNavigationClickToMoveSkipPassedWaypointsResult = pcall(function() return UserSettings():IsUserFeatureEnabled("UserNavigationClickToMoveSkipPassedWaypoints") end)
        local FFlagUserNavigationClickToMoveSkipPassedWaypoints = FFlagUserNavigationClickToMoveSkipPassedWaypointsSuccess and FFlagUserNavigationClickToMoveSkipPassedWaypointsResult
        
        local Player = Players.LocalPlayer
        
        local ClickToMoveDisplay = _ClickToMoveDisplay()
        
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        local ALMOST_ZERO = 0.000001
        
        
        --------------------------UTIL LIBRARY-------------------------------
        local Utility = {}
        do
            local function FindCharacterAncestor(part)
                if part then
                    local humanoid = part:FindFirstChildOfClass("Humanoid")
                    if humanoid then
                        return part, humanoid
                    else
                        return FindCharacterAncestor(part.Parent)
                    end
                end
            end
            Utility.FindCharacterAncestor = FindCharacterAncestor
        
            local function Raycast(ray, ignoreNonCollidable, ignoreList)
                ignoreList = ignoreList or {}
                local hitPart, hitPos, hitNorm, hitMat = Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
                if hitPart then
                    if ignoreNonCollidable and hitPart.CanCollide == false then
                        -- We always include character parts so a user can click on another character
                        -- to walk to them.
                        local _, humanoid = FindCharacterAncestor(hitPart)
                        if humanoid == nil then
                            table.insert(ignoreList, hitPart)
                            return Raycast(ray, ignoreNonCollidable, ignoreList)
                        end
                    end
                    return hitPart, hitPos, hitNorm, hitMat
                end
                return nil, nil
            end
            Utility.Raycast = Raycast
        end
        
        local humanoidCache = {}
        local function findPlayerHumanoid(player)
            local character = player and player.Character
            if character then
                local resultHumanoid = humanoidCache[player]
                if resultHumanoid and resultHumanoid.Parent == character then
                    return resultHumanoid
                else
                    humanoidCache[player] = nil -- Bust Old Cache
                    local humanoid = character:FindFirstChildOfClass("Humanoid")
                    if humanoid then
                        humanoidCache[player] = humanoid
                    end
                    return humanoid
                end
            end
        end
        
        --------------------------CHARACTER CONTROL-------------------------------
        local CurrentIgnoreList
        local CurrentIgnoreTag = nil
        
        local TaggedInstanceAddedConnection = nil
        local TaggedInstanceRemovedConnection = nil
        
        local function GetCharacter()
            return Player and Player.Character
        end
        
        local function UpdateIgnoreTag(newIgnoreTag)
            if newIgnoreTag == CurrentIgnoreTag then
                return
            end
            if TaggedInstanceAddedConnection then
                TaggedInstanceAddedConnection:Disconnect()
                TaggedInstanceAddedConnection = nil
            end
            if TaggedInstanceRemovedConnection then
                TaggedInstanceRemovedConnection:Disconnect()
                TaggedInstanceRemovedConnection = nil
            end
            CurrentIgnoreTag = newIgnoreTag
            CurrentIgnoreList = {GetCharacter()}
            if CurrentIgnoreTag ~= nil then
                local ignoreParts = CollectionService:GetTagged(CurrentIgnoreTag)
                for _, ignorePart in ipairs(ignoreParts) do
                    table.insert(CurrentIgnoreList, ignorePart)
                end
                TaggedInstanceAddedConnection = CollectionService:GetInstanceAddedSignal(
                    CurrentIgnoreTag):Connect(function(ignorePart)
                    table.insert(CurrentIgnoreList, ignorePart)
                end)
                TaggedInstanceRemovedConnection = CollectionService:GetInstanceRemovedSignal(
                    CurrentIgnoreTag):Connect(function(ignorePart)
                    for i = 1, #CurrentIgnoreList do
                        if CurrentIgnoreList[i] == ignorePart then
                            CurrentIgnoreList[i] = CurrentIgnoreList[#CurrentIgnoreList]
                            table.remove(CurrentIgnoreList)
                            break
                        end
                    end
                end)
            end
        end
        
        local function getIgnoreList()
            if CurrentIgnoreList then
                return CurrentIgnoreList
            end
            CurrentIgnoreList = {}
            table.insert(CurrentIgnoreList, GetCharacter())
            return CurrentIgnoreList
        end
        
        -----------------------------------PATHER--------------------------------------
        
        local function Pather(endPoint, surfaceNormal, overrideUseDirectPath)
            local this = {}
        
            local directPathForHumanoid
            local directPathForVehicle
            if overrideUseDirectPath ~= nil then
                directPathForHumanoid = overrideUseDirectPath
                directPathForVehicle = overrideUseDirectPath
            else
                directPathForHumanoid = UseDirectPath
                directPathForVehicle = UseDirectPathForVehicle
            end
        
            this.Cancelled = false
            this.Started = false
        
            this.Finished = Instance.new("BindableEvent")
            this.PathFailed = Instance.new("BindableEvent")
        
            this.PathComputing = false
            this.PathComputed = false
        
            this.OriginalTargetPoint = endPoint
            this.TargetPoint = endPoint
            this.TargetSurfaceNormal = surfaceNormal
        
            this.DiedConn = nil
            this.SeatedConn = nil
            this.BlockedConn = nil
            this.TeleportedConn = nil
        
            this.CurrentPoint = 0
        
            this.HumanoidOffsetFromPath = ZERO_VECTOR3
        
            this.CurrentWaypointPosition = nil
            this.CurrentWaypointPlaneNormal = ZERO_VECTOR3
            this.CurrentWaypointPlaneDistance = 0
            this.CurrentWaypointNeedsJump = false;
        
            this.CurrentHumanoidPosition = ZERO_VECTOR3
            this.CurrentHumanoidVelocity = 0
        
            this.NextActionMoveDirection = ZERO_VECTOR3
            this.NextActionJump = false
        
            this.Timeout = 0
        
            this.Humanoid = findPlayerHumanoid(Player)
            this.OriginPoint = nil
            this.AgentCanFollowPath = false
            this.DirectPath = false
            this.DirectPathRiseFirst = false
        
            local rootPart = this.Humanoid and this.Humanoid.RootPart
            if rootPart then
                -- Setup origin
                this.OriginPoint = rootPart.CFrame.p
        
                -- Setup agent
                local agentRadius = 2
                local agentHeight = 5
                local agentCanJump = true
        
                local seat = this.Humanoid.SeatPart
                if seat and seat:IsA("VehicleSeat") then
                    -- Humanoid is seated on a vehicle
                    local vehicle = seat:FindFirstAncestorOfClass("Model")
                    if vehicle then
                        -- Make sure the PrimaryPart is set to the vehicle seat while we compute the extends.
                        local tempPrimaryPart = vehicle.PrimaryPart
                        vehicle.PrimaryPart = seat
        
                        -- For now, only direct path
                        if directPathForVehicle then
                            local extents = vehicle:GetExtentsSize()
                            agentRadius = AgentSizeIncreaseFactor * 0.5 * math.sqrt(extents.X * extents.X + extents.Z * extents.Z)
                            agentHeight = AgentSizeIncreaseFactor * extents.Y
                            agentCanJump = false
                            this.AgentCanFollowPath = true
                            this.DirectPath = directPathForVehicle
                        end
        
                        -- Reset PrimaryPart
                        vehicle.PrimaryPart = tempPrimaryPart
                    end
                else
                    local extents = GetCharacter():GetExtentsSize()
                    agentRadius = AgentSizeIncreaseFactor * 0.5 * math.sqrt(extents.X * extents.X + extents.Z * extents.Z)
                    agentHeight = AgentSizeIncreaseFactor * extents.Y
                    agentCanJump = (this.Humanoid.JumpPower > 0)
                    this.AgentCanFollowPath = true
                    this.DirectPath = directPathForHumanoid
                    this.DirectPathRiseFirst = this.Humanoid.Sit
                end
        
                -- Build path object
                this.pathResult = PathfindingService:CreatePath({AgentRadius = agentRadius, AgentHeight = agentHeight, AgentCanJump = agentCanJump})
            end
        
            function this:Cleanup()
                if this.stopTraverseFunc then
                    this.stopTraverseFunc()
                    this.stopTraverseFunc = nil
                end
        
                if this.MoveToConn then
                    this.MoveToConn:Disconnect()
                    this.MoveToConn = nil
                end
        
                if this.BlockedConn then
                    this.BlockedConn:Disconnect()
                    this.BlockedConn = nil
                end
        
                if this.DiedConn then
                    this.DiedConn:Disconnect()
                    this.DiedConn = nil
                end
        
                if this.SeatedConn then
                    this.SeatedConn:Disconnect()
                    this.SeatedConn = nil
                end
        
                if this.TeleportedConn then
                    this.TeleportedConn:Disconnect()
                    this.TeleportedConn = nil
                end
        
                this.Started = false
            end
        
            function this:Cancel()
                this.Cancelled = true
                this:Cleanup()
            end
        
            function this:IsActive()
                return this.AgentCanFollowPath and this.Started and not this.Cancelled
            end
        
            function this:OnPathInterrupted()
                -- Stop moving
                this.Cancelled = true
                this:OnPointReached(false)
            end
        
            function this:ComputePath()
                if this.OriginPoint then
                    if this.PathComputed or this.PathComputing then return end
                    this.PathComputing = true
                    if this.AgentCanFollowPath then
                        if this.DirectPath then
                            this.pointList = {
                                PathWaypoint.new(this.OriginPoint, Enum.PathWaypointAction.Walk),
                                PathWaypoint.new(this.TargetPoint, this.DirectPathRiseFirst and Enum.PathWaypointAction.Jump or Enum.PathWaypointAction.Walk)
                            }
                            this.PathComputed = true
                        else
                            this.pathResult:ComputeAsync(this.OriginPoint, this.TargetPoint)
                            this.pointList = this.pathResult:GetWaypoints()
                            this.BlockedConn = this.pathResult.Blocked:Connect(function(blockedIdx) this:OnPathBlocked(blockedIdx) end)
                            this.PathComputed = this.pathResult.Status == Enum.PathStatus.Success
                        end
                    end
                    this.PathComputing = false
                end
            end
        
            function this:IsValidPath()
                this:ComputePath()
                return this.PathComputed and this.AgentCanFollowPath
            end
        
            this.Recomputing = false
            function this:OnPathBlocked(blockedWaypointIdx)
                local pathBlocked = blockedWaypointIdx >= this.CurrentPoint
                if not pathBlocked or this.Recomputing then
                    return
                end
        
                this.Recomputing = true
        
                if this.stopTraverseFunc then
                    this.stopTraverseFunc()
                    this.stopTraverseFunc = nil
                end
        
                this.OriginPoint = this.Humanoid.RootPart.CFrame.p
        
                this.pathResult:ComputeAsync(this.OriginPoint, this.TargetPoint)
                this.pointList = this.pathResult:GetWaypoints()
                if #this.pointList > 0 then
                    this.HumanoidOffsetFromPath = this.pointList[1].Position - this.OriginPoint
                end
                this.PathComputed = this.pathResult.Status == Enum.PathStatus.Success
        
                if ShowPath then
                    this.stopTraverseFunc, this.setPointFunc = ClickToMoveDisplay.CreatePathDisplay(this.pointList)
                end
                if this.PathComputed then
                    this.CurrentPoint = 1 -- The first waypoint is always the start location. Skip it.
                    this:OnPointReached(true) -- Move to first point
                else
                    this.PathFailed:Fire()
                    this:Cleanup()
                end
        
                this.Recomputing = false
            end
        
            function this:OnRenderStepped(dt)
                if this.Started and not this.Cancelled then
                    -- Check for Timeout (if a waypoint is not reached within the delay, we fail)
                    this.Timeout = this.Timeout + dt
                    if this.Timeout > UnreachableWaypointTimeout then
                        this:OnPointReached(false)
                        return
                    end
        
                    -- Get Humanoid position and velocity
                    this.CurrentHumanoidPosition = this.Humanoid.RootPart.Position + this.HumanoidOffsetFromPath
                    this.CurrentHumanoidVelocity = this.Humanoid.RootPart.Velocity
        
                    -- Check if it has reached some waypoints
                    while this.Started and this:IsCurrentWaypointReached() do
                        this:OnPointReached(true)
                    end
        
                    -- If still started, update actions
                    if this.Started then
                        -- Move action
                        this.NextActionMoveDirection = this.CurrentWaypointPosition - this.CurrentHumanoidPosition
                        if this.NextActionMoveDirection.Magnitude > ALMOST_ZERO then
                            this.NextActionMoveDirection = this.NextActionMoveDirection.Unit
                        else
                            this.NextActionMoveDirection = ZERO_VECTOR3
                        end
                        -- Jump action
                        if this.CurrentWaypointNeedsJump then
                            this.NextActionJump = true
                            this.CurrentWaypointNeedsJump = false	-- Request jump only once
                        else
                            this.NextActionJump = false
                        end
                    end
                end
            end
        
            function this:IsCurrentWaypointReached()
                local reached = false
        
                -- Check we do have a plane, if not, we consider the waypoint reached
                if this.CurrentWaypointPlaneNormal ~= ZERO_VECTOR3 then
                    -- Compute distance of Humanoid from destination plane
                    local dist = this.CurrentWaypointPlaneNormal:Dot(this.CurrentHumanoidPosition) - this.CurrentWaypointPlaneDistance
                    -- Compute the component of the Humanoid velocity that is towards the plane
                    local velocity = -this.CurrentWaypointPlaneNormal:Dot(this.CurrentHumanoidVelocity)
                    -- Compute the threshold from the destination plane based on Humanoid velocity
                    local threshold = math.max(1.0, 0.0625 * velocity)
                    -- If we are less then threshold in front of the plane (between 0 and threshold) or if we are behing the plane (less then 0), we consider we reached it
                    reached = dist < threshold
                else
                    reached = true
                end
        
                if reached then
                    this.CurrentWaypointPosition = nil
                    this.CurrentWaypointPlaneNormal	= ZERO_VECTOR3
                    this.CurrentWaypointPlaneDistance = 0
                end
        
                return reached
            end
        
            function this:OnPointReached(reached)
        
                if reached and not this.Cancelled then
                    -- First, destroyed the current displayed waypoint
                    if this.setPointFunc then
                        this.setPointFunc(this.CurrentPoint)
                    end
        
                    local nextWaypointIdx = this.CurrentPoint + 1
        
                    if nextWaypointIdx > #this.pointList then
                        -- End of path reached
                        if this.stopTraverseFunc then
                            this.stopTraverseFunc()
                        end
                        this.Finished:Fire()
                        this:Cleanup()
                    else
                        local currentWaypoint = this.pointList[this.CurrentPoint]
                        local nextWaypoint = this.pointList[nextWaypointIdx]
        
                        -- If airborne, only allow to keep moving
                        -- if nextWaypoint.Action ~= Jump, or path mantains a direction
                        -- Otherwise, wait until the humanoid gets to the ground
                        local currentState = this.Humanoid:GetState()
                        local isInAir = currentState == Enum.HumanoidStateType.FallingDown
                            or currentState == Enum.HumanoidStateType.Freefall
                            or currentState == Enum.HumanoidStateType.Jumping
        
                        if isInAir then
                            local shouldWaitForGround = nextWaypoint.Action == Enum.PathWaypointAction.Jump
                            if not shouldWaitForGround and this.CurrentPoint > 1 then
                                local prevWaypoint = this.pointList[this.CurrentPoint - 1]
        
                                local prevDir = currentWaypoint.Position - prevWaypoint.Position
                                local currDir = nextWaypoint.Position - currentWaypoint.Position
        
                                local prevDirXZ = Vector2.new(prevDir.x, prevDir.z).Unit
                                local currDirXZ = Vector2.new(currDir.x, currDir.z).Unit
        
                                local THRESHOLD_COS = 0.996 -- ~cos(5 degrees)
                                shouldWaitForGround = prevDirXZ:Dot(currDirXZ) < THRESHOLD_COS
                            end
        
                            if shouldWaitForGround then
                                this.Humanoid.FreeFalling:Wait()
        
                                -- Give time to the humanoid's state to change
                                -- Otherwise, the jump flag in Humanoid
                                -- will be reset by the state change
                                wait(0.1)
                            end
                        end
        
                        -- Move to the next point
                        if FFlagUserNavigationClickToMoveSkipPassedWaypoints then
                            this:MoveToNextWayPoint(currentWaypoint, nextWaypoint, nextWaypointIdx)
                        else
                            if this.setPointFunc then
                                this.setPointFunc(nextWaypointIdx)
                            end
                            if nextWaypoint.Action == Enum.PathWaypointAction.Jump then
                                this.Humanoid.Jump = true
                            end
                            this.Humanoid:MoveTo(nextWaypoint.Position)
        
                            this.CurrentPoint = nextWaypointIdx
                        end
                    end
                else
                    this.PathFailed:Fire()
                    this:Cleanup()
                end
            end
        
            function this:MoveToNextWayPoint(currentWaypoint, nextWaypoint, nextWaypointIdx)
                -- Build next destination plane
                -- (plane normal is perpendicular to the y plane and is from next waypoint towards current one (provided the two waypoints are not at the same location))
                -- (plane location is at next waypoint)
                this.CurrentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
                this.CurrentWaypointPlaneNormal = Vector3.new(this.CurrentWaypointPlaneNormal.X, 0, this.CurrentWaypointPlaneNormal.Z)
                if this.CurrentWaypointPlaneNormal.Magnitude > ALMOST_ZERO then
                    this.CurrentWaypointPlaneNormal	= this.CurrentWaypointPlaneNormal.Unit
                    this.CurrentWaypointPlaneDistance = this.CurrentWaypointPlaneNormal:Dot(nextWaypoint.Position)
                else
                    -- Next waypoint is the same as current waypoint so no plane
                    this.CurrentWaypointPlaneNormal	= ZERO_VECTOR3
                    this.CurrentWaypointPlaneDistance = 0
                end
        
                -- Should we jump
                this.CurrentWaypointNeedsJump = nextWaypoint.Action == Enum.PathWaypointAction.Jump;
        
                -- Remember next waypoint position
                this.CurrentWaypointPosition = nextWaypoint.Position
        
                -- Move to next point
                this.CurrentPoint = nextWaypointIdx
        
                -- Finally reset Timeout
                this.Timeout = 0
            end
        
            function this:Start(overrideShowPath)
                if not this.AgentCanFollowPath then
                    this.PathFailed:Fire()
                    return
                end
        
                if this.Started then return end
                this.Started = true
        
                ClickToMoveDisplay.CancelFailureAnimation()
        
                if ShowPath then
                    if overrideShowPath == nil or overrideShowPath then
                        this.stopTraverseFunc, this.setPointFunc = ClickToMoveDisplay.CreatePathDisplay(this.pointList, this.OriginalTargetPoint)
                    end
                end
        
                if #this.pointList > 0 then
                    -- Determine the humanoid offset from the path's first point
                    -- Offset of the first waypoint from the path's origin point
                    this.HumanoidOffsetFromPath = Vector3.new(0, this.pointList[1].Position.Y - this.OriginPoint.Y, 0)
        
                    -- As well as its current position and velocity
                    this.CurrentHumanoidPosition = this.Humanoid.RootPart.Position + this.HumanoidOffsetFromPath
                    this.CurrentHumanoidVelocity = this.Humanoid.RootPart.Velocity
        
                    -- Connect to events
                    this.SeatedConn = this.Humanoid.Seated:Connect(function(isSeated, seat) this:OnPathInterrupted() end)
                    this.DiedConn = this.Humanoid.Died:Connect(function() this:OnPathInterrupted() end)
                    this.TeleportedConn = this.Humanoid.RootPart:GetPropertyChangedSignal("CFrame"):Connect(function() this:OnPathInterrupted() end)
        
                    -- Actually start
                    this.CurrentPoint = 1 -- The first waypoint is always the start location. Skip it.
                    this:OnPointReached(true) -- Move to first point
                else
                    this.PathFailed:Fire()
                    if this.stopTraverseFunc then
                        this.stopTraverseFunc()
                    end
                end
            end
        
            --We always raycast to the ground in the case that the user clicked a wall.
            local offsetPoint = this.TargetPoint + this.TargetSurfaceNormal*1.5
            local ray = Ray.new(offsetPoint, Vector3.new(0,-1,0)*50)
            local newHitPart, newHitPos = Workspace:FindPartOnRayWithIgnoreList(ray, getIgnoreList())
            if newHitPart then
                this.TargetPoint = newHitPos
            end
            this:ComputePath()
        
            return this
        end
        
        -------------------------------------------------------------------------
        
        local function CheckAlive()
            local humanoid = findPlayerHumanoid(Player)
            return humanoid ~= nil and humanoid.Health > 0
        end
        
        local function GetEquippedTool(character)
            if character ~= nil then
                for _, child in pairs(character:GetChildren()) do
                    if child:IsA('Tool') then
                        return child
                    end
                end
            end
        end
        
        local ExistingPather = nil
        local ExistingIndicator = nil
        local PathCompleteListener = nil
        local PathFailedListener = nil
        
        local function CleanupPath()
            if ExistingPather then
                ExistingPather:Cancel()
                ExistingPather = nil
            end
            if PathCompleteListener then
                PathCompleteListener:Disconnect()
                PathCompleteListener = nil
            end
            if PathFailedListener then
                PathFailedListener:Disconnect()
                PathFailedListener = nil
            end
            if ExistingIndicator then
                ExistingIndicator:Destroy()
            end
        end
        
        local function HandleMoveTo(thisPather, hitPt, hitChar, character, overrideShowPath)
            if ExistingPather then
                CleanupPath()
            end
            ExistingPather = thisPather
            thisPather:Start(overrideShowPath)
        
            PathCompleteListener = thisPather.Finished.Event:Connect(function()
                CleanupPath()
                if hitChar then
                    local currentWeapon = GetEquippedTool(character)
                    if currentWeapon then
                        currentWeapon:Activate()
                    end
                end
            end)
            PathFailedListener = thisPather.PathFailed.Event:Connect(function()
                CleanupPath()
                if overrideShowPath == nil or overrideShowPath then
                    local shouldPlayFailureAnim = PlayFailureAnimation and not (ExistingPather and ExistingPather:IsActive())
                    if shouldPlayFailureAnim then
                        ClickToMoveDisplay.PlayFailureAnimation()
                    end
                    ClickToMoveDisplay.DisplayFailureWaypoint(hitPt)
                end
            end)
        end
        
        local function ShowPathFailedFeedback(hitPt)
            if ExistingPather and ExistingPather:IsActive() then
                ExistingPather:Cancel()
            end
            if PlayFailureAnimation then
                ClickToMoveDisplay.PlayFailureAnimation()
            end
            ClickToMoveDisplay.DisplayFailureWaypoint(hitPt)
        end
        
        function OnTap(tapPositions, goToPoint, wasTouchTap)
            -- Good to remember if this is the latest tap event
            local camera = Workspace.CurrentCamera
            local character = Player.Character
        
            if not CheckAlive() then return end
        
            -- This is a path tap position
            if #tapPositions == 1 or goToPoint then
                if camera then
                    local unitRay = camera:ScreenPointToRay(tapPositions[1].x, tapPositions[1].y)
                    local ray = Ray.new(unitRay.Origin, unitRay.Direction*1000)
        
                    local myHumanoid = findPlayerHumanoid(Player)
                    local hitPart, hitPt, hitNormal = Utility.Raycast(ray, true, getIgnoreList())
        
                    local hitChar, hitHumanoid = Utility.FindCharacterAncestor(hitPart)
                    if wasTouchTap and hitHumanoid and StarterGui:GetCore("AvatarContextMenuEnabled") then
                        local clickedPlayer = Players:GetPlayerFromCharacter(hitHumanoid.Parent)
                        if clickedPlayer then
                            CleanupPath()
                            return
                        end
                    end
                    if goToPoint then
                        hitPt = goToPoint
                        hitChar = nil
                    end
                    if hitPt and character then
                        -- Clean up current path
                        CleanupPath()
                        local thisPather = Pather(hitPt, hitNormal)
                        if thisPather:IsValidPath() then
                            HandleMoveTo(thisPather, hitPt, hitChar, character)
                        else
                            -- Clean up
                            thisPather:Cleanup()
                            -- Feedback here for when we don't have a good path
                            ShowPathFailedFeedback(hitPt)
                        end
                    end
                end
            elseif #tapPositions >= 2 then
                if camera then
                    -- Do shoot
                    local currentWeapon = GetEquippedTool(character)
                    if currentWeapon then
                        currentWeapon:Activate()
                    end
                end
            end
        end
        
        local function DisconnectEvent(event)
            if event then
                event:Disconnect()
            end
        end
        
        --[[ The ClickToMove Controller Class ]]--
        local KeyboardController = _Keyboard()
        local ClickToMove = setmetatable({}, KeyboardController)
        ClickToMove.__index = ClickToMove
        
        function ClickToMove.new(CONTROL_ACTION_PRIORITY)
            local self = setmetatable(KeyboardController.new(CONTROL_ACTION_PRIORITY), ClickToMove)
        
            self.fingerTouches = {}
            self.numUnsunkTouches = 0
            -- PC simulation
            self.mouse1Down = tick()
            self.mouse1DownPos = Vector2.new()
            self.mouse2DownTime = tick()
            self.mouse2DownPos = Vector2.new()
            self.mouse2UpTime = tick()
        
            self.keyboardMoveVector = ZERO_VECTOR3
        
            self.tapConn = nil
            self.inputBeganConn = nil
            self.inputChangedConn = nil
            self.inputEndedConn = nil
            self.humanoidDiedConn = nil
            self.characterChildAddedConn = nil
            self.onCharacterAddedConn = nil
            self.characterChildRemovedConn = nil
            self.renderSteppedConn = nil
            self.menuOpenedConnection = nil
        
            self.running = false
        
            self.wasdEnabled = false
        
            return self
        end
        
        function ClickToMove:DisconnectEvents()
            DisconnectEvent(self.tapConn)
            DisconnectEvent(self.inputBeganConn)
            DisconnectEvent(self.inputChangedConn)
            DisconnectEvent(self.inputEndedConn)
            DisconnectEvent(self.humanoidDiedConn)
            DisconnectEvent(self.characterChildAddedConn)
            DisconnectEvent(self.onCharacterAddedConn)
            DisconnectEvent(self.renderSteppedConn)
            DisconnectEvent(self.characterChildRemovedConn)
            DisconnectEvent(self.menuOpenedConnection)
        end
        
        function ClickToMove:OnTouchBegan(input, processed)
            if self.fingerTouches[input] == nil and not processed then
                self.numUnsunkTouches = self.numUnsunkTouches + 1
            end
            self.fingerTouches[input] = processed
        end
        
        function ClickToMove:OnTouchChanged(input, processed)
            if self.fingerTouches[input] == nil then
                self.fingerTouches[input] = processed
                if not processed then
                    self.numUnsunkTouches = self.numUnsunkTouches + 1
                end
            end
        end
        
        function ClickToMove:OnTouchEnded(input, processed)
            if self.fingerTouches[input] ~= nil and self.fingerTouches[input] == false then
                self.numUnsunkTouches = self.numUnsunkTouches - 1
            end
            self.fingerTouches[input] = nil
        end
        
        
        function ClickToMove:OnCharacterAdded(character)
            self:DisconnectEvents()
        
            self.inputBeganConn = UserInputService.InputBegan:Connect(function(input, processed)
                if input.UserInputType == Enum.UserInputType.Touch then
                    self:OnTouchBegan(input, processed)
                end
        
                -- Cancel path when you use the keyboard controls if wasd is enabled.
                if self.wasdEnabled and processed == false and input.UserInputType == Enum.UserInputType.Keyboard
                    and movementKeys[input.KeyCode] then
                    CleanupPath()
                    ClickToMoveDisplay.CancelFailureAnimation()
                end
                if input.UserInputType == Enum.UserInputType.MouseButton1 then
                    self.mouse1DownTime = tick()
                    self.mouse1DownPos = input.Position
                end
                if input.UserInputType == Enum.UserInputType.MouseButton2 then
                    self.mouse2DownTime = tick()
                    self.mouse2DownPos = input.Position
                end
            end)
        
            self.inputChangedConn = UserInputService.InputChanged:Connect(function(input, processed)
                if input.UserInputType == Enum.UserInputType.Touch then
                    self:OnTouchChanged(input, processed)
                end
            end)
        
            self.inputEndedConn = UserInputService.InputEnded:Connect(function(input, processed)
                if input.UserInputType == Enum.UserInputType.Touch then
                    self:OnTouchEnded(input, processed)
                end
        
                if input.UserInputType == Enum.UserInputType.MouseButton2 then
                    self.mouse2UpTime = tick()
                    local currPos = input.Position
                    -- We allow click to move during path following or if there is no keyboard movement
                    local allowed = ExistingPather or self.keyboardMoveVector.Magnitude <= 0
                    if self.mouse2UpTime - self.mouse2DownTime < 0.25 and (currPos - self.mouse2DownPos).magnitude < 5 and allowed then
                        local positions = {currPos}
                        OnTap(positions)
                    end
                end
            end)
        
            self.tapConn = UserInputService.TouchTap:Connect(function(touchPositions, processed)
                if not processed then
                    OnTap(touchPositions, nil, true)
                end
            end)
        
            self.menuOpenedConnection = GuiService.MenuOpened:Connect(function()
                CleanupPath()
            end)
        
            local function OnCharacterChildAdded(child)
                if UserInputService.TouchEnabled then
                    if child:IsA('Tool') then
                        child.ManualActivationOnly = true
                    end
                end
                if child:IsA('Humanoid') then
                    DisconnectEvent(self.humanoidDiedConn)
                    self.humanoidDiedConn = child.Died:Connect(function()
                        if ExistingIndicator then
                            DebrisService:AddItem(ExistingIndicator.Model, 1)
                        end
                    end)
                end
            end
        
            self.characterChildAddedConn = character.ChildAdded:Connect(function(child)
                OnCharacterChildAdded(child)
            end)
            self.characterChildRemovedConn = character.ChildRemoved:Connect(function(child)
                if UserInputService.TouchEnabled then
                    if child:IsA('Tool') then
                        child.ManualActivationOnly = false
                    end
                end
            end)
            for _, child in pairs(character:GetChildren()) do
                OnCharacterChildAdded(child)
            end
        end
        
        function ClickToMove:Start()
            self:Enable(true)
        end
        
        function ClickToMove:Stop()
            self:Enable(false)
        end
        
        function ClickToMove:CleanupPath()
            CleanupPath()
        end
        
        function ClickToMove:Enable(enable, enableWASD, touchJumpController)
            if enable then
                if not self.running then
                    if Player.Character then -- retro-listen
                        self:OnCharacterAdded(Player.Character)
                    end
                    self.onCharacterAddedConn = Player.CharacterAdded:Connect(function(char)
                        self:OnCharacterAdded(char)
                    end)
                    self.running = true
                end
                self.touchJumpController = touchJumpController
                if self.touchJumpController then
                    self.touchJumpController:Enable(self.jumpEnabled)
                end
            else
                if self.running then
                    self:DisconnectEvents()
                    CleanupPath()
                    -- Restore tool activation on shutdown
                    if UserInputService.TouchEnabled then
                        local character = Player.Character
                        if character then
                            for _, child in pairs(character:GetChildren()) do
                                if child:IsA('Tool') then
                                    child.ManualActivationOnly = false
                                end
                            end
                        end
                    end
                    self.running = false
                end
                if self.touchJumpController and not self.jumpEnabled then
                    self.touchJumpController:Enable(true)
                end
                self.touchJumpController = nil
            end
        
            -- Extension for initializing Keyboard input as this class now derives from Keyboard
            if UserInputService.KeyboardEnabled and enable ~= self.enabled then
        
                self.forwardValue  = 0
                self.backwardValue = 0
                self.leftValue = 0
                self.rightValue = 0
        
                self.moveVector = ZERO_VECTOR3
        
                if enable then
                    self:BindContextActions()
                    self:ConnectFocusEventListeners()
                else
                    self:UnbindContextActions()
                    self:DisconnectFocusEventListeners()
                end
            end
        
            self.wasdEnabled = enable and enableWASD or false
            self.enabled = enable
        end
        
        function ClickToMove:OnRenderStepped(dt)
            -- Reset jump
            self.isJumping = false
        
            -- Handle Pather
            if ExistingPather then
                -- Let the Pather update
                ExistingPather:OnRenderStepped(dt)
        
                -- If we still have a Pather, set the resulting actions
                if ExistingPather then
                    -- Setup move (NOT relative to camera)
                    self.moveVector = ExistingPather.NextActionMoveDirection
                    self.moveVectorIsCameraRelative = false
        
                    -- Setup jump (but do NOT prevent the base Keayboard class from requesting jumps as well)
                    if ExistingPather.NextActionJump then
                        self.isJumping = true
                    end
                else
                    self.moveVector = self.keyboardMoveVector
                    self.moveVectorIsCameraRelative = true
                end
            else
                self.moveVector = self.keyboardMoveVector
                self.moveVectorIsCameraRelative = true
            end
        
            -- Handle Keyboard's jump
            if self.jumpRequested then
                self.isJumping = true
            end
        end
        
        -- Overrides Keyboard:UpdateMovement(inputState) to conditionally consider self.wasdEnabled and let OnRenderStepped handle the movement
        function ClickToMove:UpdateMovement(inputState)
            if inputState == Enum.UserInputState.Cancel then
                self.keyboardMoveVector = ZERO_VECTOR3
            elseif self.wasdEnabled then
                self.keyboardMoveVector = Vector3.new(self.leftValue + self.rightValue, 0, self.forwardValue + self.backwardValue)
            end
        end
        
        -- Overrides Keyboard:UpdateJump() because jump is handled in OnRenderStepped
        function ClickToMove:UpdateJump()
            -- Nothing to do (handled in OnRenderStepped)
        end
        
        --Public developer facing functions
        function ClickToMove:SetShowPath(value)
            ShowPath = value
        end
        
        function ClickToMove:GetShowPath()
            return ShowPath
        end
        
        function ClickToMove:SetWaypointTexture(texture)
            ClickToMoveDisplay.SetWaypointTexture(texture)
        end
        
        function ClickToMove:GetWaypointTexture()
            return ClickToMoveDisplay.GetWaypointTexture()
        end
        
        function ClickToMove:SetWaypointRadius(radius)
            ClickToMoveDisplay.SetWaypointRadius(radius)
        end
        
        function ClickToMove:GetWaypointRadius()
            return ClickToMoveDisplay.GetWaypointRadius()
        end
        
        function ClickToMove:SetEndWaypointTexture(texture)
            ClickToMoveDisplay.SetEndWaypointTexture(texture)
        end
        
        function ClickToMove:GetEndWaypointTexture()
            return ClickToMoveDisplay.GetEndWaypointTexture()
        end
        
        function ClickToMove:SetWaypointsAlwaysOnTop(alwaysOnTop)
            ClickToMoveDisplay.SetWaypointsAlwaysOnTop(alwaysOnTop)
        end
        
        function ClickToMove:GetWaypointsAlwaysOnTop()
            return ClickToMoveDisplay.GetWaypointsAlwaysOnTop()
        end
        
        function ClickToMove:SetFailureAnimationEnabled(enabled)
            PlayFailureAnimation = enabled
        end
        
        function ClickToMove:GetFailureAnimationEnabled()
            return PlayFailureAnimation
        end
        
        function ClickToMove:SetIgnoredPartsTag(tag)
            UpdateIgnoreTag(tag)
        end
        
        function ClickToMove:GetIgnoredPartsTag()
            return CurrentIgnoreTag
        end
        
        function ClickToMove:SetUseDirectPath(directPath)
            UseDirectPath = directPath
        end
        
        function ClickToMove:GetUseDirectPath()
            return UseDirectPath
        end
        
        function ClickToMove:SetAgentSizeIncreaseFactor(increaseFactorPercent)
            AgentSizeIncreaseFactor = 1.0 + (increaseFactorPercent / 100.0)
        end
        
        function ClickToMove:GetAgentSizeIncreaseFactor()
            return (AgentSizeIncreaseFactor - 1.0) * 100.0
        end
        
        function ClickToMove:SetUnreachableWaypointTimeout(timeoutInSec)
            UnreachableWaypointTimeout = timeoutInSec
        end
        
        function ClickToMove:GetUnreachableWaypointTimeout()
            return UnreachableWaypointTimeout
        end
        
        function ClickToMove:SetUserJumpEnabled(jumpEnabled)
            self.jumpEnabled = jumpEnabled
            if self.touchJumpController then
                self.touchJumpController:Enable(jumpEnabled)
            end
        end
        
        function ClickToMove:GetUserJumpEnabled()
            return self.jumpEnabled
        end
        
        function ClickToMove:MoveTo(position, showPath, useDirectPath)
            local character = Player.Character
            if character == nil then
                return false
            end
            local thisPather = Pather(position, Vector3.new(0, 1, 0), useDirectPath)
            if thisPather and thisPather:IsValidPath() then
                HandleMoveTo(thisPather, position, nil, character, showPath)
                return true
            end
            return false
        end
        
        return ClickToMove
    end
    
    function _TouchThumbstick()
        local Players = game:GetService("Players")
        local GuiService = game:GetService("GuiService")
        local UserInputService = game:GetService("UserInputService")
        --[[ Constants ]]--
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        local TOUCH_CONTROL_SHEET = "rbxasset://textures/ui/TouchControlsSheet.png"
        --[[ The Module ]]--
        local BaseCharacterController = _BaseCharacterController()
        local TouchThumbstick = setmetatable({}, BaseCharacterController)
        TouchThumbstick.__index = TouchThumbstick
        function TouchThumbstick.new()
            local self = setmetatable(BaseCharacterController.new(), TouchThumbstick)
            
            self.isFollowStick = false
            
            self.thumbstickFrame = nil
            self.moveTouchObject = nil
            self.onTouchMovedConn = nil
            self.onTouchEndedConn = nil
            self.screenPos = nil
            self.stickImage = nil
            self.thumbstickSize = nil -- Float
            
            return self
        end
        function TouchThumbstick:Enable(enable, uiParentFrame)
            if enable == nil then return false end			-- If nil, return false (invalid argument)
            enable = enable and true or false				-- Force anything non-nil to boolean before comparison
            if self.enabled == enable then return true end	-- If no state change, return true indicating already in requested state
            
            self.moveVector = ZERO_VECTOR3
            self.isJumping = false
            
            if enable then
                -- Enable
                if not self.thumbstickFrame then
                    self:Create(uiParentFrame)
                end
                self.thumbstickFrame.Visible = true
            else 
                -- Disable
                self.thumbstickFrame.Visible = false
                self:OnInputEnded()
            end
            self.enabled = enable
        end
        function TouchThumbstick:OnInputEnded()
            self.thumbstickFrame.Position = self.screenPos
            self.stickImage.Position = UDim2.new(0, self.thumbstickFrame.Size.X.Offset/2 - self.thumbstickSize/4, 0, self.thumbstickFrame.Size.Y.Offset/2 - self.thumbstickSize/4)
            
            self.moveVector = ZERO_VECTOR3
            self.isJumping = false
            self.thumbstickFrame.Position = self.screenPos
            self.moveTouchObject = nil
        end
        function TouchThumbstick:Create(parentFrame)
            
            if self.thumbstickFrame then
                self.thumbstickFrame:Destroy()
                self.thumbstickFrame = nil
                if self.onTouchMovedConn then
                    self.onTouchMovedConn:Disconnect()
                    self.onTouchMovedConn = nil
                end
                if self.onTouchEndedConn then
                    self.onTouchEndedConn:Disconnect()
                    self.onTouchEndedConn = nil
                end
            end
            
            local minAxis = math.min(parentFrame.AbsoluteSize.x, parentFrame.AbsoluteSize.y)
            local isSmallScreen = minAxis <= 500
            self.thumbstickSize = isSmallScreen and 70 or 120
            self.screenPos = isSmallScreen and UDim2.new(0, (self.thumbstickSize/2) - 10, 1, -self.thumbstickSize - 20) or
                UDim2.new(0, self.thumbstickSize/2, 1, -self.thumbstickSize * 1.75)
                
            self.thumbstickFrame = Instance.new("Frame")
            self.thumbstickFrame.Name = "ThumbstickFrame"
            self.thumbstickFrame.Active = true
            self.thumbstickFrame.Visible = false
            self.thumbstickFrame.Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize)
            self.thumbstickFrame.Position = self.screenPos
            self.thumbstickFrame.BackgroundTransparency = 1
            
            local outerImage = Instance.new("ImageLabel")
            outerImage.Name = "OuterImage"
            outerImage.Image = TOUCH_CONTROL_SHEET
            outerImage.ImageRectOffset = Vector2.new()
            outerImage.ImageRectSize = Vector2.new(220, 220)
            outerImage.BackgroundTransparency = 1
            outerImage.Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize)
            outerImage.Position = UDim2.new(0, 0, 0, 0)
            outerImage.Parent = self.thumbstickFrame
            
            self.stickImage = Instance.new("ImageLabel")
            self.stickImage.Name = "StickImage"
            self.stickImage.Image = TOUCH_CONTROL_SHEET
            self.stickImage.ImageRectOffset = Vector2.new(220, 0)
            self.stickImage.ImageRectSize = Vector2.new(111, 111)
            self.stickImage.BackgroundTransparency = 1
            self.stickImage.Size = UDim2.new(0, self.thumbstickSize/2, 0, self.thumbstickSize/2)
            self.stickImage.Position = UDim2.new(0, self.thumbstickSize/2 - self.thumbstickSize/4, 0, self.thumbstickSize/2 - self.thumbstickSize/4)
            self.stickImage.ZIndex = 2
            self.stickImage.Parent = self.thumbstickFrame
            
            local centerPosition = nil
            local deadZone = 0.05
            
            local function DoMove(direction)
                
                local currentMoveVector = direction / (self.thumbstickSize/2)
                
                -- Scaled Radial Dead Zone
                local inputAxisMagnitude = currentMoveVector.magnitude
                if inputAxisMagnitude < deadZone then
                    currentMoveVector = Vector3.new()
                else
                    currentMoveVector = currentMoveVector.unit * ((inputAxisMagnitude - deadZone) / (1 - deadZone))
                    -- NOTE: Making currentMoveVector a unit vector will cause the player to instantly go max speed
                    -- must check for zero length vector is using unit
                    currentMoveVector = Vector3.new(currentMoveVector.x, 0, currentMoveVector.y)
                end
                
                self.moveVector = currentMoveVector
            end
            
            local function MoveStick(pos)
                local relativePosition = Vector2.new(pos.x - centerPosition.x, pos.y - centerPosition.y)
                local length = relativePosition.magnitude
                local maxLength = self.thumbstickFrame.AbsoluteSize.x/2
                if self.isFollowStick and length > maxLength then
                    local offset = relativePosition.unit * maxLength
                    self.thumbstickFrame.Position = UDim2.new(
                        0, pos.x - self.thumbstickFrame.AbsoluteSize.x/2 - offset.x,
                        0, pos.y - self.thumbstickFrame.AbsoluteSize.y/2 - offset.y)
                else
                    length = math.min(length, maxLength)
                    relativePosition = relativePosition.unit * length
                end
                self.stickImage.Position = UDim2.new(0, relativePosition.x + self.stickImage.AbsoluteSize.x/2, 0, relativePosition.y + self.stickImage.AbsoluteSize.y/2)
            end
            
            -- input connections
            self.thumbstickFrame.InputBegan:Connect(function(inputObject)
                --A touch that starts elsewhere on the screen will be sent to a frame's InputBegan event
                --if it moves over the frame. So we check that this is actually a new touch (inputObject.UserInputState ~= Enum.UserInputState.Begin)
                if self.moveTouchObject or inputObject.UserInputType ~= Enum.UserInputType.Touch
                    or inputObject.UserInputState ~= Enum.UserInputState.Begin then
                    return
                end
                
                self.moveTouchObject = inputObject
                self.thumbstickFrame.Position = UDim2.new(0, inputObject.Position.x - self.thumbstickFrame.Size.X.Offset/2, 0, inputObject.Position.y - self.thumbstickFrame.Size.Y.Offset/2)
                centerPosition = Vector2.new(self.thumbstickFrame.AbsolutePosition.x + self.thumbstickFrame.AbsoluteSize.x/2,
                    self.thumbstickFrame.AbsolutePosition.y + self.thumbstickFrame.AbsoluteSize.y/2)
                local direction = Vector2.new(inputObject.Position.x - centerPosition.x, inputObject.Position.y - centerPosition.y)
            end)
            
            self.onTouchMovedConn = UserInputService.TouchMoved:Connect(function(inputObject, isProcessed)
                if inputObject == self.moveTouchObject then
                    centerPosition = Vector2.new(self.thumbstickFrame.AbsolutePosition.x + self.thumbstickFrame.AbsoluteSize.x/2,
                        self.thumbstickFrame.AbsolutePosition.y + self.thumbstickFrame.AbsoluteSize.y/2)
                    local direction = Vector2.new(inputObject.Position.x - centerPosition.x, inputObject.Position.y - centerPosition.y)
                    DoMove(direction)
                    MoveStick(inputObject.Position)
                end
            end)
            
            self.onTouchEndedConn = UserInputService.TouchEnded:Connect(function(inputObject, isProcessed)
                if inputObject == self.moveTouchObject then
                    self:OnInputEnded()
                end
            end)
            
            GuiService.MenuOpened:Connect(function()
                if self.moveTouchObject then
                    self:OnInputEnded()
                end
            end)	
            
            self.thumbstickFrame.Parent = parentFrame
        end
        return TouchThumbstick
    end
    
    function _DynamicThumbstick()
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        local TOUCH_CONTROLS_SHEET = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
        
        local DYNAMIC_THUMBSTICK_ACTION_NAME = "DynamicThumbstickAction"
        local DYNAMIC_THUMBSTICK_ACTION_PRIORITY = Enum.ContextActionPriority.High.Value
        
        local MIDDLE_TRANSPARENCIES = {
            1 - 0.89,
            1 - 0.70,
            1 - 0.60,
            1 - 0.50,
            1 - 0.40,
            1 - 0.30,
            1 - 0.25
        }
        local NUM_MIDDLE_IMAGES = #MIDDLE_TRANSPARENCIES
        
        local FADE_IN_OUT_BACKGROUND = true
        local FADE_IN_OUT_MAX_ALPHA = 0.35
        
        local FADE_IN_OUT_HALF_DURATION_DEFAULT = 0.3
        local FADE_IN_OUT_BALANCE_DEFAULT = 0.5
        local ThumbstickFadeTweenInfo = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
        
        local Players = game:GetService("Players")
        local GuiService = game:GetService("GuiService")
        local UserInputService = game:GetService("UserInputService")
        local ContextActionService = game:GetService("ContextActionService")
        local RunService = game:GetService("RunService")
        local TweenService = game:GetService("TweenService")
        
        local LocalPlayer = Players.LocalPlayer
        if not LocalPlayer then
            Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
            LocalPlayer = Players.LocalPlayer
        end
        
        --[[ The Module ]]--
        local BaseCharacterController = _BaseCharacterController()
        local DynamicThumbstick = setmetatable({}, BaseCharacterController)
        DynamicThumbstick.__index = DynamicThumbstick
        
        function DynamicThumbstick.new()
            local self = setmetatable(BaseCharacterController.new(), DynamicThumbstick)
        
            self.moveTouchObject = nil
            self.moveTouchLockedIn = false
            self.moveTouchFirstChanged = false
            self.moveTouchStartPosition = nil
        
            self.startImage = nil
            self.endImage = nil
            self.middleImages = {}
        
            self.startImageFadeTween = nil
            self.endImageFadeTween = nil
            self.middleImageFadeTweens = {}
        
            self.isFirstTouch = true
        
            self.thumbstickFrame = nil
        
            self.onRenderSteppedConn = nil
        
            self.fadeInAndOutBalance = FADE_IN_OUT_BALANCE_DEFAULT
            self.fadeInAndOutHalfDuration = FADE_IN_OUT_HALF_DURATION_DEFAULT
            self.hasFadedBackgroundInPortrait = false
            self.hasFadedBackgroundInLandscape = false
        
            self.tweenInAlphaStart = nil
            self.tweenOutAlphaStart = nil
        
            return self
        end
        
        -- Note: Overrides base class GetIsJumping with get-and-clear behavior to do a single jump
        -- rather than sustained jumping. This is only to preserve the current behavior through the refactor.
        function DynamicThumbstick:GetIsJumping()
            local wasJumping = self.isJumping
            self.isJumping = false
            return wasJumping
        end
        
        function DynamicThumbstick:Enable(enable, uiParentFrame)
            if enable == nil then return false end			-- If nil, return false (invalid argument)
            enable = enable and true or false				-- Force anything non-nil to boolean before comparison
            if self.enabled == enable then return true end	-- If no state change, return true indicating already in requested state
        
            if enable then
                -- Enable
                if not self.thumbstickFrame then
                    self:Create(uiParentFrame)
                end
        
                self:BindContextActions()
            else
                ContextActionService:UnbindAction(DYNAMIC_THUMBSTICK_ACTION_NAME)
                -- Disable
                self:OnInputEnded() -- Cleanup
            end
        
            self.enabled = enable
            self.thumbstickFrame.Visible = enable
        end
        
        -- Was called OnMoveTouchEnded in previous version
        function DynamicThumbstick:OnInputEnded()
            self.moveTouchObject = nil
            self.moveVector = ZERO_VECTOR3
            self:FadeThumbstick(false)
        end
        
        function DynamicThumbstick:FadeThumbstick(visible)
            if not visible and self.moveTouchObject then
                return
            end
            if self.isFirstTouch then return end
        
            if self.startImageFadeTween then
                self.startImageFadeTween:Cancel()
            end
            if self.endImageFadeTween then
                self.endImageFadeTween:Cancel()
            end
            for i = 1, #self.middleImages do
                if self.middleImageFadeTweens[i] then
                    self.middleImageFadeTweens[i]:Cancel()
                end
            end
        
            if visible then
                self.startImageFadeTween = TweenService:Create(self.startImage, ThumbstickFadeTweenInfo, { ImageTransparency = 0 })
                self.startImageFadeTween:Play()
        
                self.endImageFadeTween = TweenService:Create(self.endImage, ThumbstickFadeTweenInfo, { ImageTransparency = 0.2 })
                self.endImageFadeTween:Play()
        
                for i = 1, #self.middleImages do
                    self.middleImageFadeTweens[i] = TweenService:Create(self.middleImages[i], ThumbstickFadeTweenInfo, { ImageTransparency = MIDDLE_TRANSPARENCIES[i] })
                    self.middleImageFadeTweens[i]:Play()
                end
            else
                self.startImageFadeTween = TweenService:Create(self.startImage, ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
                self.startImageFadeTween:Play()
        
                self.endImageFadeTween = TweenService:Create(self.endImage, ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
                self.endImageFadeTween:Play()
        
                for i = 1, #self.middleImages do
                    self.middleImageFadeTweens[i] = TweenService:Create(self.middleImages[i], ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
                    self.middleImageFadeTweens[i]:Play()
                end
            end
        end
        
        function DynamicThumbstick:FadeThumbstickFrame(fadeDuration, fadeRatio)
            self.fadeInAndOutHalfDuration = fadeDuration * 0.5
            self.fadeInAndOutBalance = fadeRatio
            self.tweenInAlphaStart = tick()
        end
        
        function DynamicThumbstick:InputInFrame(inputObject)
            local frameCornerTopLeft = self.thumbstickFrame.AbsolutePosition
            local frameCornerBottomRight = frameCornerTopLeft + self.thumbstickFrame.AbsoluteSize
            local inputPosition = inputObject.Position
            if inputPosition.X >= frameCornerTopLeft.X and inputPosition.Y >= frameCornerTopLeft.Y then
                if inputPosition.X <= frameCornerBottomRight.X and inputPosition.Y <= frameCornerBottomRight.Y then
                    return true
                end
            end
            return false
        end
        
        function DynamicThumbstick:DoFadeInBackground()
            local playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
            local hasFadedBackgroundInOrientation = false
        
            -- only fade in/out the background once per orientation
            if playerGui then
                if playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeLeft or
                    playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeRight then
                        hasFadedBackgroundInOrientation = self.hasFadedBackgroundInLandscape
                        self.hasFadedBackgroundInLandscape = true
                elseif playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.Portrait then
                        hasFadedBackgroundInOrientation = self.hasFadedBackgroundInPortrait
                        self.hasFadedBackgroundInPortrait = true
                end
            end
        
            if not hasFadedBackgroundInOrientation then
                self.fadeInAndOutHalfDuration = FADE_IN_OUT_HALF_DURATION_DEFAULT
                self.fadeInAndOutBalance = FADE_IN_OUT_BALANCE_DEFAULT
                self.tweenInAlphaStart = tick()
            end
        end
        
        function DynamicThumbstick:DoMove(direction)
            local currentMoveVector = direction
        
            -- Scaled Radial Dead Zone
            local inputAxisMagnitude = currentMoveVector.magnitude
            if inputAxisMagnitude < self.radiusOfDeadZone then
                currentMoveVector = ZERO_VECTOR3
            else
                currentMoveVector = currentMoveVector.unit*(
                    1 - math.max(0, (self.radiusOfMaxSpeed - currentMoveVector.magnitude)/self.radiusOfMaxSpeed)
                )
                currentMoveVector = Vector3.new(currentMoveVector.x, 0, currentMoveVector.y)
            end
        
            self.moveVector = currentMoveVector
        end
        
        
        function DynamicThumbstick:LayoutMiddleImages(startPos, endPos)
            local startDist = (self.thumbstickSize / 2) + self.middleSize
            local vector = endPos - startPos
            local distAvailable = vector.magnitude - (self.thumbstickRingSize / 2) - self.middleSize
            local direction = vector.unit
        
            local distNeeded = self.middleSpacing * NUM_MIDDLE_IMAGES
            local spacing = self.middleSpacing
        
            if distNeeded < distAvailable then
                spacing = distAvailable / NUM_MIDDLE_IMAGES
            end
        
            for i = 1, NUM_MIDDLE_IMAGES do
                local image = self.middleImages[i]
                local distWithout = startDist + (spacing * (i - 2))
                local currentDist = startDist + (spacing * (i - 1))
        
                if distWithout < distAvailable then
                    local pos = endPos - direction * currentDist
                    local exposedFraction = math.clamp(1 - ((currentDist - distAvailable) / spacing), 0, 1)
        
                    image.Visible = true
                    image.Position = UDim2.new(0, pos.X, 0, pos.Y)
                    image.Size = UDim2.new(0, self.middleSize * exposedFraction, 0, self.middleSize * exposedFraction)
                else
                    image.Visible = false
                end
            end
        end
        
        function DynamicThumbstick:MoveStick(pos)
            local vector2StartPosition = Vector2.new(self.moveTouchStartPosition.X, self.moveTouchStartPosition.Y)
            local startPos = vector2StartPosition - self.thumbstickFrame.AbsolutePosition
            local endPos = Vector2.new(pos.X, pos.Y) - self.thumbstickFrame.AbsolutePosition
            self.endImage.Position = UDim2.new(0, endPos.X, 0, endPos.Y)
            self:LayoutMiddleImages(startPos, endPos)
        end
        
        function DynamicThumbstick:BindContextActions()
            local function inputBegan(inputObject)
                if self.moveTouchObject then
                    return Enum.ContextActionResult.Pass
                end
        
                if not self:InputInFrame(inputObject) then
                    return Enum.ContextActionResult.Pass
                end
        
                if self.isFirstTouch then
                    self.isFirstTouch = false
                    local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out,0,false,0)
                    TweenService:Create(self.startImage, tweenInfo, {Size = UDim2.new(0, 0, 0, 0)}):Play()
                    TweenService:Create(
                        self.endImage,
                        tweenInfo,
                        {Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize), ImageColor3 = Color3.new(0,0,0)}
                    ):Play()
                end
        
                self.moveTouchLockedIn = false
                self.moveTouchObject = inputObject
                self.moveTouchStartPosition = inputObject.Position
                self.moveTouchFirstChanged = true
        
                if FADE_IN_OUT_BACKGROUND then
                    self:DoFadeInBackground()
                end
        
                return Enum.ContextActionResult.Pass
            end
        
            local function inputChanged(inputObject)
                if inputObject == self.moveTouchObject then
                    if self.moveTouchFirstChanged then
                        self.moveTouchFirstChanged = false
        
                        local startPosVec2 = Vector2.new(
                            inputObject.Position.X - self.thumbstickFrame.AbsolutePosition.X,
                            inputObject.Position.Y - self.thumbstickFrame.AbsolutePosition.Y
                        )
                        self.startImage.Visible = true
                        self.startImage.Position = UDim2.new(0, startPosVec2.X, 0, startPosVec2.Y)
                        self.endImage.Visible = true
                        self.endImage.Position = self.startImage.Position
        
                        self:FadeThumbstick(true)
                        self:MoveStick(inputObject.Position)
                    end
        
                    self.moveTouchLockedIn = true
        
                    local direction = Vector2.new(
                        inputObject.Position.x - self.moveTouchStartPosition.x,
                        inputObject.Position.y - self.moveTouchStartPosition.y
                    )
                    if math.abs(direction.x) > 0 or math.abs(direction.y) > 0 then
                        self:DoMove(direction)
                        self:MoveStick(inputObject.Position)
                    end
                    return Enum.ContextActionResult.Sink
                end
                return Enum.ContextActionResult.Pass
            end
        
            local function inputEnded(inputObject)
                if inputObject == self.moveTouchObject then
                    self:OnInputEnded()
                    if self.moveTouchLockedIn then
                        return Enum.ContextActionResult.Sink
                    end
                end
                return Enum.ContextActionResult.Pass
            end
        
            local function handleInput(actionName, inputState, inputObject)
                if inputState == Enum.UserInputState.Begin then
                    return inputBegan(inputObject)
                elseif inputState == Enum.UserInputState.Change then
                    return inputChanged(inputObject)
                elseif inputState == Enum.UserInputState.End then
                    return inputEnded(inputObject)
                elseif inputState == Enum.UserInputState.Cancel then
                    self:OnInputEnded()
                end
            end
        
            ContextActionService:BindActionAtPriority(
                DYNAMIC_THUMBSTICK_ACTION_NAME,
                handleInput,
                false,
                DYNAMIC_THUMBSTICK_ACTION_PRIORITY,
                Enum.UserInputType.Touch)
        end
        
        function DynamicThumbstick:Create(parentFrame)
            if self.thumbstickFrame then
                self.thumbstickFrame:Destroy()
                self.thumbstickFrame = nil
                if self.onRenderSteppedConn then
                    self.onRenderSteppedConn:Disconnect()
                    self.onRenderSteppedConn = nil
                end
            end
        
            self.thumbstickSize = 45
            self.thumbstickRingSize = 20
            self.middleSize = 10
            self.middleSpacing = self.middleSize + 4
            self.radiusOfDeadZone = 2
            self.radiusOfMaxSpeed = 20
        
            local screenSize = parentFrame.AbsoluteSize
            local isBigScreen = math.min(screenSize.x, screenSize.y) > 500
            if isBigScreen then
                self.thumbstickSize = self.thumbstickSize * 2
                self.thumbstickRingSize = self.thumbstickRingSize * 2
                self.middleSize = self.middleSize * 2
                self.middleSpacing = self.middleSpacing * 2
                self.radiusOfDeadZone = self.radiusOfDeadZone * 2
                self.radiusOfMaxSpeed = self.radiusOfMaxSpeed * 2
            end
        
            local function layoutThumbstickFrame(portraitMode)
                if portraitMode then
                    self.thumbstickFrame.Size = UDim2.new(1, 0, 0.4, 0)
                    self.thumbstickFrame.Position = UDim2.new(0, 0, 0.6, 0)
                else
                    self.thumbstickFrame.Size = UDim2.new(0.4, 0, 2/3, 0)
                    self.thumbstickFrame.Position = UDim2.new(0, 0, 1/3, 0)
                end
            end
        
            self.thumbstickFrame = Instance.new("Frame")
            self.thumbstickFrame.BorderSizePixel = 0
            self.thumbstickFrame.Name = "DynamicThumbstickFrame"
            self.thumbstickFrame.Visible = false
            self.thumbstickFrame.BackgroundTransparency = 1.0
            self.thumbstickFrame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
            self.thumbstickFrame.Active = false
            layoutThumbstickFrame(false)
        
            self.startImage = Instance.new("ImageLabel")
            self.startImage.Name = "ThumbstickStart"
            self.startImage.Visible = true
            self.startImage.BackgroundTransparency = 1
            self.startImage.Image = TOUCH_CONTROLS_SHEET
            self.startImage.ImageRectOffset = Vector2.new(1,1)
            self.startImage.ImageRectSize = Vector2.new(144, 144)
            self.startImage.ImageColor3 = Color3.new(0, 0, 0)
            self.startImage.AnchorPoint = Vector2.new(0.5, 0.5)
            self.startImage.Position = UDim2.new(0, self.thumbstickRingSize * 3.3, 1, -self.thumbstickRingSize  * 2.8)
            self.startImage.Size = UDim2.new(0, self.thumbstickRingSize  * 3.7, 0, self.thumbstickRingSize  * 3.7)
            self.startImage.ZIndex = 10
            self.startImage.Parent = self.thumbstickFrame
        
            self.endImage = Instance.new("ImageLabel")
            self.endImage.Name = "ThumbstickEnd"
            self.endImage.Visible = true
            self.endImage.BackgroundTransparency = 1
            self.endImage.Image = TOUCH_CONTROLS_SHEET
            self.endImage.ImageRectOffset = Vector2.new(1,1)
            self.endImage.ImageRectSize =  Vector2.new(144, 144)
            self.endImage.AnchorPoint = Vector2.new(0.5, 0.5)
            self.endImage.Position = self.startImage.Position
            self.endImage.Size = UDim2.new(0, self.thumbstickSize * 0.8, 0, self.thumbstickSize * 0.8)
            self.endImage.ZIndex = 10
            self.endImage.Parent = self.thumbstickFrame
        
            for i = 1, NUM_MIDDLE_IMAGES do
                self.middleImages[i] = Instance.new("ImageLabel")
                self.middleImages[i].Name = "ThumbstickMiddle"
                self.middleImages[i].Visible = false
                self.middleImages[i].BackgroundTransparency = 1
                self.middleImages[i].Image = TOUCH_CONTROLS_SHEET
                self.middleImages[i].ImageRectOffset = Vector2.new(1,1)
                self.middleImages[i].ImageRectSize = Vector2.new(144, 144)
                self.middleImages[i].ImageTransparency = MIDDLE_TRANSPARENCIES[i]
                self.middleImages[i].AnchorPoint = Vector2.new(0.5, 0.5)
                self.middleImages[i].ZIndex = 9
                self.middleImages[i].Parent = self.thumbstickFrame
            end
        
            local CameraChangedConn = nil
            local function onCurrentCameraChanged()
                if CameraChangedConn then
                    CameraChangedConn:Disconnect()
                    CameraChangedConn = nil
                end
                local newCamera = workspace.CurrentCamera
                if newCamera then
                    local function onViewportSizeChanged()
                        local size = newCamera.ViewportSize
                        local portraitMode = size.X < size.Y
                        layoutThumbstickFrame(portraitMode)
                    end
                    CameraChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(onViewportSizeChanged)
                    onViewportSizeChanged()
                end
            end
            workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onCurrentCameraChanged)
            if workspace.CurrentCamera then
                onCurrentCameraChanged()
            end
        
            self.moveTouchStartPosition = nil
        
            self.startImageFadeTween = nil
            self.endImageFadeTween = nil
            self.middleImageFadeTweens = {}
        
            self.onRenderSteppedConn = RunService.RenderStepped:Connect(function()
                if self.tweenInAlphaStart ~= nil then
                    local delta = tick() - self.tweenInAlphaStart
                    local fadeInTime = (self.fadeInAndOutHalfDuration * 2 * self.fadeInAndOutBalance)
                    self.thumbstickFrame.BackgroundTransparency = 1 - FADE_IN_OUT_MAX_ALPHA*math.min(delta/fadeInTime, 1)
                    if delta > fadeInTime then
                        self.tweenOutAlphaStart = tick()
                        self.tweenInAlphaStart = nil
                    end
                elseif self.tweenOutAlphaStart ~= nil then
                    local delta = tick() - self.tweenOutAlphaStart
                    local fadeOutTime = (self.fadeInAndOutHalfDuration * 2) - (self.fadeInAndOutHalfDuration * 2 * self.fadeInAndOutBalance)
                    self.thumbstickFrame.BackgroundTransparency = 1 - FADE_IN_OUT_MAX_ALPHA + FADE_IN_OUT_MAX_ALPHA*math.min(delta/fadeOutTime, 1)
                    if delta > fadeOutTime  then
                        self.tweenOutAlphaStart = nil
                    end
                end
            end)
        
            self.onTouchEndedConn = UserInputService.TouchEnded:connect(function(inputObject)
                if inputObject == self.moveTouchObject then
                    self:OnInputEnded()
                end
            end)
        
            GuiService.MenuOpened:connect(function()
                if self.moveTouchObject then
                    self:OnInputEnded()
                end
            end)
        
            local playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
            while not playerGui do
                LocalPlayer.ChildAdded:wait()
                playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
            end
        
            local playerGuiChangedConn = nil
            local originalScreenOrientationWasLandscape =	playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeLeft or
                                                            playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeRight
        
            local function longShowBackground()
                self.fadeInAndOutHalfDuration = 2.5
                self.fadeInAndOutBalance = 0.05
                self.tweenInAlphaStart = tick()
            end
        
            playerGuiChangedConn = playerGui:GetPropertyChangedSignal("CurrentScreenOrientation"):Connect(function()
                if (originalScreenOrientationWasLandscape and playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.Portrait) or
                    (not originalScreenOrientationWasLandscape and playerGui.CurrentScreenOrientation ~= Enum.ScreenOrientation.Portrait) then
        
                    playerGuiChangedConn:disconnect()
                    longShowBackground()
        
                    if originalScreenOrientationWasLandscape then
                        self.hasFadedBackgroundInPortrait = true
                    else
                        self.hasFadedBackgroundInLandscape = true
                    end
                end
            end)
        
            self.thumbstickFrame.Parent = parentFrame
        
            if game:IsLoaded() then
                longShowBackground()
            else
                coroutine.wrap(function()
                    game.Loaded:Wait()
                    longShowBackground()
                end)()
            end
        end
        
        return DynamicThumbstick
    end
    
    function _Gamepad()
        local UserInputService = game:GetService("UserInputService")
        local ContextActionService = game:GetService("ContextActionService")
        
        --[[ Constants ]]--
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        local NONE = Enum.UserInputType.None
        local thumbstickDeadzone = 0.2
        
        --[[ The Module ]]--
        local BaseCharacterController = _BaseCharacterController()
        local Gamepad = setmetatable({}, BaseCharacterController)
        Gamepad.__index = Gamepad
        
        function Gamepad.new(CONTROL_ACTION_PRIORITY)
            local self = setmetatable(BaseCharacterController.new(), Gamepad)
        
            self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
        
            self.forwardValue  = 0
            self.backwardValue = 0
            self.leftValue = 0
            self.rightValue = 0
        
            self.activeGamepad = NONE	-- Enum.UserInputType.Gamepad1, 2, 3...
            self.gamepadConnectedConn = nil
            self.gamepadDisconnectedConn = nil
            return self
        end
        
        function Gamepad:Enable(enable)
            if not UserInputService.GamepadEnabled then
                return false
            end
        
            if enable == self.enabled then
                -- Module is already in the state being requested. True is returned here since the module will be in the state
                -- expected by the code that follows the Enable() call. This makes more sense than returning false to indicate
                -- no action was necessary. False indicates failure to be in requested/expected state.
                return true
            end
        
            self.forwardValue  = 0
            self.backwardValue = 0
            self.leftValue = 0
            self.rightValue = 0
            self.moveVector = ZERO_VECTOR3
            self.isJumping = false
        
            if enable then
                self.activeGamepad = self:GetHighestPriorityGamepad()
                if self.activeGamepad ~= NONE then
                    self:BindContextActions()
                    self:ConnectGamepadConnectionListeners()
                else
                    -- No connected gamepads, failure to enable
                    return false
                end
            else
                self:UnbindContextActions()
                self:DisconnectGamepadConnectionListeners()
                self.activeGamepad = NONE
            end
        
            self.enabled = enable
            return true
        end
        
        -- This function selects the lowest number gamepad from the currently-connected gamepad
        -- and sets it as the active gamepad
        function Gamepad:GetHighestPriorityGamepad()
            local connectedGamepads = UserInputService:GetConnectedGamepads()
            local bestGamepad = NONE -- Note that this value is higher than all valid gamepad values
            for _, gamepad in pairs(connectedGamepads) do
                if gamepad.Value < bestGamepad.Value then
                    bestGamepad = gamepad
                end
            end
            return bestGamepad
        end
        
        function Gamepad:BindContextActions()
        
            if self.activeGamepad == NONE then
                -- There must be an active gamepad to set up bindings
                return false
            end
        
            local handleJumpAction = function(actionName, inputState, inputObject)
                self.isJumping = (inputState == Enum.UserInputState.Begin)
                return Enum.ContextActionResult.Sink
            end
        
            local handleThumbstickInput = function(actionName, inputState, inputObject)
        
                if inputState == Enum.UserInputState.Cancel then
                    self.moveVector = ZERO_VECTOR3
                    return Enum.ContextActionResult.Sink
                end
        
                if self.activeGamepad ~= inputObject.UserInputType then
                    return Enum.ContextActionResult.Pass
                end
                if inputObject.KeyCode ~= Enum.KeyCode.Thumbstick1 then return end
        
                if inputObject.Position.magnitude > thumbstickDeadzone then
                    self.moveVector  =  Vector3.new(inputObject.Position.X, 0, -inputObject.Position.Y)
                else
                    self.moveVector = ZERO_VECTOR3
                end
                return Enum.ContextActionResult.Sink
            end
        
            ContextActionService:BindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
            ContextActionService:BindActionAtPriority("jumpAction", handleJumpAction, false,
                self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonA)
            ContextActionService:BindActionAtPriority("moveThumbstick", handleThumbstickInput, false,
                self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Thumbstick1)
        
            return true
        end
        
        function Gamepad:UnbindContextActions()
            if self.activeGamepad ~= NONE then
                ContextActionService:UnbindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
            end
            ContextActionService:UnbindAction("moveThumbstick")
            ContextActionService:UnbindAction("jumpAction")
        end
        
        function Gamepad:OnNewGamepadConnected()
            -- A new gamepad has been connected.
            local bestGamepad = self:GetHighestPriorityGamepad()
        
            if bestGamepad == self.activeGamepad then
                -- A new gamepad was connected, but our active gamepad is not changing
                return
            end
        
            if bestGamepad == NONE then
                -- There should be an active gamepad when GamepadConnected fires, so this should not
                -- normally be hit. If there is no active gamepad, unbind actions but leave
                -- the module enabled and continue to listen for a new gamepad connection.
                warn("Gamepad:OnNewGamepadConnected found no connected gamepads")
                self:UnbindContextActions()
                return
            end
        
            if self.activeGamepad ~= NONE then
                -- Switching from one active gamepad to another
                self:UnbindContextActions()
            end
        
            self.activeGamepad = bestGamepad
            self:BindContextActions()
        end
        
        function Gamepad:OnCurrentGamepadDisconnected()
            if self.activeGamepad ~= NONE then
                ContextActionService:UnbindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
            end
        
            local bestGamepad = self:GetHighestPriorityGamepad()
        
            if self.activeGamepad ~= NONE and bestGamepad == self.activeGamepad then
                warn("Gamepad:OnCurrentGamepadDisconnected found the supposedly disconnected gamepad in connectedGamepads.")
                self:UnbindContextActions()
                self.activeGamepad = NONE
                return
            end
        
            if bestGamepad == NONE then
                -- No active gamepad, unbinding actions but leaving gamepad connection listener active
                self:UnbindContextActions()
                self.activeGamepad = NONE
            else
                -- Set new gamepad as active and bind to tool activation
                self.activeGamepad = bestGamepad
                ContextActionService:BindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
            end
        end
        
        function Gamepad:ConnectGamepadConnectionListeners()
            self.gamepadConnectedConn = UserInputService.GamepadConnected:Connect(function(gamepadEnum)
                self:OnNewGamepadConnected()
            end)
        
            self.gamepadDisconnectedConn = UserInputService.GamepadDisconnected:Connect(function(gamepadEnum)
                if self.activeGamepad == gamepadEnum then
                    self:OnCurrentGamepadDisconnected()
                end
            end)
        
        end
        
        function Gamepad:DisconnectGamepadConnectionListeners()
            if self.gamepadConnectedConn then
                self.gamepadConnectedConn:Disconnect()
                self.gamepadConnectedConn = nil
            end
        
            if self.gamepadDisconnectedConn then
                self.gamepadDisconnectedConn:Disconnect()
                self.gamepadDisconnectedConn = nil
            end
        end
        
        return Gamepad
    end
    
    function _Keyboard()
        
        --[[ Roblox Services ]]--
        local UserInputService = game:GetService("UserInputService")
        local ContextActionService = game:GetService("ContextActionService")
        
        --[[ Constants ]]--
        local ZERO_VECTOR3 = Vector3.new(0,0,0)
        
        --[[ The Module ]]--
        local BaseCharacterController = _BaseCharacterController()
        local Keyboard = setmetatable({}, BaseCharacterController)
        Keyboard.__index = Keyboard
        
        function Keyboard.new(CONTROL_ACTION_PRIORITY)
            local self = setmetatable(BaseCharacterController.new(), Keyboard)
        
            self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
        
            self.textFocusReleasedConn = nil
            self.textFocusGainedConn = nil
            self.windowFocusReleasedConn = nil
        
            self.forwardValue  = 0
            self.backwardValue = 0
            self.leftValue = 0
            self.rightValue = 0
        
            self.jumpEnabled = true
        
            return self
        end
        
        function Keyboard:Enable(enable)
            if not UserInputService.KeyboardEnabled then
                return false
            end
        
            if enable == self.enabled then
                -- Module is already in the state being requested. True is returned here since the module will be in the state
                -- expected by the code that follows the Enable() call. This makes more sense than returning false to indicate
                -- no action was necessary. False indicates failure to be in requested/expected state.
                return true
            end
        
            self.forwardValue  = 0
            self.backwardValue = 0
            self.leftValue = 0
            self.rightValue = 0
            self.moveVector = ZERO_VECTOR3
            self.jumpRequested = false
            self:UpdateJump()
        
            if enable then
                self:BindContextActions()
                self:ConnectFocusEventListeners()
            else
                self:UnbindContextActions()
                self:DisconnectFocusEventListeners()
            end
        
            self.enabled = enable
            return true
        end
        
        function Keyboard:UpdateMovement(inputState)
            if inputState == Enum.UserInputState.Cancel then
                self.moveVector = ZERO_VECTOR3
            else
                self.moveVector = Vector3.new(self.leftValue + self.rightValue, 0, self.forwardValue + self.backwardValue)
            end
        end
        
        function Keyboard:UpdateJump()
            self.isJumping = self.jumpRequested
        end
        
        function Keyboard:BindContextActions()
        
            -- Note: In the previous version of this code, the movement values were not zeroed-out on UserInputState. Cancel, now they are,
            -- which fixes them from getting stuck on.
            -- We return ContextActionResult.Pass here for legacy reasons.
            -- Many games rely on gameProcessedEvent being false on UserInputService.InputBegan for these control actions.
            local handleMoveForward = function(actionName, inputState, inputObject)
                self.forwardValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
                self:UpdateMovement(inputState)
                return Enum.ContextActionResult.Pass
            end
        
            local handleMoveBackward = function(actionName, inputState, inputObject)
                self.backwardValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
                self:UpdateMovement(inputState)
                return Enum.ContextActionResult.Pass
            end
        
            local handleMoveLeft = function(actionName, inputState, inputObject)
                self.leftValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
                self:UpdateMovement(inputState)
                return Enum.ContextActionResult.Pass
            end
        
            local handleMoveRight = function(actionName, inputState, inputObject)
                self.rightValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
                self:UpdateMovement(inputState)
                return Enum.ContextActionResult.Pass
            end
        
            local handleJumpAction = function(actionName, inputState, inputObject)
                self.jumpRequested = self.jumpEnabled and (inputState == Enum.UserInputState.Begin)
                self:UpdateJump()
                return Enum.ContextActionResult.Pass
            end
        
            -- TODO: Revert to KeyCode bindings so that in the future the abstraction layer from actual keys to
            -- movement direction is done in Lua
            ContextActionService:BindActionAtPriority("moveForwardAction", handleMoveForward, false,
                self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterForward)
            ContextActionService:BindActionAtPriority("moveBackwardAction", handleMoveBackward, false,
                self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterBackward)
            ContextActionService:BindActionAtPriority("moveLeftAction", handleMoveLeft, false,
                self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterLeft)
            ContextActionService:BindActionAtPriority("moveRightAction", handleMoveRight, false,
                self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterRight)
            ContextActionService:BindActionAtPriority("jumpAction", handleJumpAction, false,
                self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterJump)
        end
        
        function Keyboard:UnbindContextActions()
            ContextActionService:UnbindAction("moveForwardAction")
            ContextActionService:UnbindAction("moveBackwardAction")
            ContextActionService:UnbindAction("moveLeftAction")
            ContextActionService:UnbindAction("moveRightAction")
            ContextActionService:UnbindAction("jumpAction")
        end
        
        function Keyboard:ConnectFocusEventListeners()
            local function onFocusReleased()
                self.moveVector = ZERO_VECTOR3
                self.forwardValue  = 0
                self.backwardValue = 0
                self.leftValue = 0
                self.rightValue = 0
                self.jumpRequested = false
                self:UpdateJump()
            end
        
            local function onTextFocusGained(textboxFocused)
                self.jumpRequested = false
                self:UpdateJump()
            end
        
            self.textFocusReleasedConn = UserInputService.TextBoxFocusReleased:Connect(onFocusReleased)
            self.textFocusGainedConn = UserInputService.TextBoxFocused:Connect(onTextFocusGained)
            self.windowFocusReleasedConn = UserInputService.WindowFocused:Connect(onFocusReleased)
        end
        
        function Keyboard:DisconnectFocusEventListeners()
            if self.textFocusReleasedCon then
                self.textFocusReleasedCon:Disconnect()
                self.textFocusReleasedCon = nil
            end
            if self.textFocusGainedConn then
                self.textFocusGainedConn:Disconnect()
                self.textFocusGainedConn = nil
            end
            if self.windowFocusReleasedConn then
                self.windowFocusReleasedConn:Disconnect()
                self.windowFocusReleasedConn = nil
            end
        end
        
        return Keyboard
    end
    
    function _ControlModule()
        local ControlModule = {}
        ControlModule.__index = ControlModule
        
        --[[ Roblox Services ]]--
        local Players = game:GetService("Players")
        local RunService = game:GetService("RunService")
        local UserInputService = game:GetService("UserInputService")
        local Workspace = game:GetService("Workspace")
        local UserGameSettings = UserSettings():GetService("UserGameSettings")
        
        -- Roblox User Input Control Modules - each returns a new() constructor function used to create controllers as needed
        local Keyboard = _Keyboard()
        local Gamepad = _Gamepad()
        local DynamicThumbstick = _DynamicThumbstick()
        
        local FFlagUserMakeThumbstickDynamic do
            local success, value = pcall(function()
                return UserSettings():IsUserFeatureEnabled("UserMakeThumbstickDynamic")
            end)
            FFlagUserMakeThumbstickDynamic = success and value
        end
        
        local TouchThumbstick = FFlagUserMakeThumbstickDynamic and DynamicThumbstick or _TouchThumbstick()
        
        -- These controllers handle only walk/run movement, jumping is handled by the
        -- TouchJump controller if any of these are active
        local ClickToMove = _ClickToMoveController()
        local TouchJump = _TouchJump()
        
        local VehicleController = _VehicleController()
        
        local CONTROL_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
        
        -- Mapping from movement mode and lastInputType enum values to control modules to avoid huge if elseif switching
        local movementEnumToModuleMap = {
            [Enum.TouchMovementMode.DPad] = DynamicThumbstick,
            [Enum.DevTouchMovementMode.DPad] = DynamicThumbstick,
            [Enum.TouchMovementMode.Thumbpad] = DynamicThumbstick,
            [Enum.DevTouchMovementMode.Thumbpad] = DynamicThumbstick,
            [Enum.TouchMovementMode.Thumbstick] = TouchThumbstick,
            [Enum.DevTouchMovementMode.Thumbstick] = TouchThumbstick,
            [Enum.TouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
            [Enum.DevTouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
            [Enum.TouchMovementMode.ClickToMove] = ClickToMove,
            [Enum.DevTouchMovementMode.ClickToMove] = ClickToMove,
        
            -- Current default
            [Enum.TouchMovementMode.Default] = DynamicThumbstick,
        
            [Enum.ComputerMovementMode.Default] = Keyboard,
            [Enum.ComputerMovementMode.KeyboardMouse] = Keyboard,
            [Enum.DevComputerMovementMode.KeyboardMouse] = Keyboard,
            [Enum.DevComputerMovementMode.Scriptable] = nil,
            [Enum.ComputerMovementMode.ClickToMove] = ClickToMove,
            [Enum.DevComputerMovementMode.ClickToMove] = ClickToMove,
        }
        
        -- Keyboard controller is really keyboard and mouse controller
        local computerInputTypeToModuleMap = {
            [Enum.UserInputType.Keyboard] = Keyboard,
            [Enum.UserInputType.MouseButton1] = Keyboard,
            [Enum.UserInputType.MouseButton2] = Keyboard,
            [Enum.UserInputType.MouseButton3] = Keyboard,
            [Enum.UserInputType.MouseWheel] = Keyboard,
            [Enum.UserInputType.MouseMovement] = Keyboard,
            [Enum.UserInputType.Gamepad1] = Gamepad,
            [Enum.UserInputType.Gamepad2] = Gamepad,
            [Enum.UserInputType.Gamepad3] = Gamepad,
            [Enum.UserInputType.Gamepad4] = Gamepad,
        }
        
        local lastInputType
        
        function ControlModule.new()
            local self = setmetatable({},ControlModule)
        
            -- The Modules above are used to construct controller instances as-needed, and this
            -- table is a map from Module to the instance created from it
            self.controllers = {}
        
            self.activeControlModule = nil	-- Used to prevent unnecessarily expensive checks on each input event
            self.activeController = nil
            self.touchJumpController = nil
            self.moveFunction = Players.LocalPlayer.Move
            self.humanoid = nil
            self.lastInputType = Enum.UserInputType.None
        
            -- For Roblox self.vehicleController
            self.humanoidSeatedConn = nil
            self.vehicleController = nil
        
            self.touchControlFrame = nil
        
            self.vehicleController = VehicleController.new(CONTROL_ACTION_PRIORITY)
        
            Players.LocalPlayer.CharacterAdded:Connect(function(char) self:OnCharacterAdded(char) end)
            Players.LocalPlayer.CharacterRemoving:Connect(function(char) self:OnCharacterRemoving(char) end)
            if Players.LocalPlayer.Character then
                self:OnCharacterAdded(Players.LocalPlayer.Character)
            end
        
            RunService:BindToRenderStep("ControlScriptRenderstep", Enum.RenderPriority.Input.Value, function(dt)
                self:OnRenderStepped(dt)
            end)
        
            UserInputService.LastInputTypeChanged:Connect(function(newLastInputType)
                self:OnLastInputTypeChanged(newLastInputType)
            end)
        
        
            UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
                self:OnTouchMovementModeChange()
            end)
            Players.LocalPlayer:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
                self:OnTouchMovementModeChange()
            end)
        
            UserGameSettings:GetPropertyChangedSignal("ComputerMovementMode"):Connect(function()
                self:OnComputerMovementModeChange()
            end)
            Players.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(function()
                self:OnComputerMovementModeChange()
            end)
        
            --[[ Touch Device UI ]]--
            self.playerGui = nil
            self.touchGui = nil
            self.playerGuiAddedConn = nil
        
            if UserInputService.TouchEnabled then
                self.playerGui = Players.LocalPlayer:FindFirstChildOfClass("PlayerGui")
                if self.playerGui then
                    self:CreateTouchGuiContainer()
                    self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
                else
                    self.playerGuiAddedConn = Players.LocalPlayer.ChildAdded:Connect(function(child)
                        if child:IsA("PlayerGui") then
                            self.playerGui = child
                            self:CreateTouchGuiContainer()
                            self.playerGuiAddedConn:Disconnect()
                            self.playerGuiAddedConn = nil
                            self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
                        end
                    end)
                end
            else
                self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
            end
        
            return self
        end
        
        -- Convenience function so that calling code does not have to first get the activeController
        -- and then call GetMoveVector on it. When there is no active controller, this function returns
        -- nil so that this case can be distinguished from no current movement (which returns zero vector).
        function ControlModule:GetMoveVector()
            if self.activeController then
                return self.activeController:GetMoveVector()
            end
            return Vector3.new(0,0,0)
        end
        
        function ControlModule:GetActiveController()
            return self.activeController
        end
        
        function ControlModule:EnableActiveControlModule()
            if self.activeControlModule == ClickToMove then
                -- For ClickToMove, when it is the player's choice, we also enable the full keyboard controls.
                -- When the developer is forcing click to move, the most keyboard controls (WASD) are not available, only jump.
                self.activeController:Enable(
                    true,
                    Players.LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.UserChoice,
                    self.touchJumpController
                )
            elseif self.touchControlFrame then
                self.activeController:Enable(true, self.touchControlFrame)
            else
                self.activeController:Enable(true)
            end
        end
        
        function ControlModule:Enable(enable)
            if not self.activeController then
                return
            end
        
            if enable == nil then
                enable = true
            end
            if enable then
                self:EnableActiveControlModule()
            else
                self:Disable()
            end
        end
        
        -- For those who prefer distinct functions
        function ControlModule:Disable()
            if self.activeController then
                self.activeController:Enable(false)
        
                if self.moveFunction then
                    self.moveFunction(Players.LocalPlayer, Vector3.new(0,0,0), true)
                end
            end
        end
        
        
        -- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
        function ControlModule:SelectComputerMovementModule()
            if not (UserInputService.KeyboardEnabled or UserInputService.GamepadEnabled) then
                return nil, false
            end
        
            local computerModule
            local DevMovementMode = Players.LocalPlayer.DevComputerMovementMode
        
            if DevMovementMode == Enum.DevComputerMovementMode.UserChoice then
                computerModule = computerInputTypeToModuleMap[lastInputType]
                if UserGameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove and computerModule == Keyboard then
                    -- User has ClickToMove set in Settings, prefer ClickToMove controller for keyboard and mouse lastInputTypes
                    computerModule = ClickToMove
                end
            else
                -- Developer has selected a mode that must be used.
                computerModule = movementEnumToModuleMap[DevMovementMode]
        
                -- computerModule is expected to be nil here only when developer has selected Scriptable
                if (not computerModule) and DevMovementMode ~= Enum.DevComputerMovementMode.Scriptable then
                    warn("No character control module is associated with DevComputerMovementMode ", DevMovementMode)
                end
            end
        
            if computerModule then
                return computerModule, true
            elseif DevMovementMode == Enum.DevComputerMovementMode.Scriptable then
                -- Special case where nil is returned and we actually want to set self.activeController to nil for Scriptable
                return nil, true
            else
                -- This case is for when computerModule is nil because of an error and no suitable control module could
                -- be found.
                return nil, false
            end
        end
        
        -- Choose current Touch control module based on settings (user, dev)
        -- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
        function ControlModule:SelectTouchModule()
            if not UserInputService.TouchEnabled then
                return nil, false
            end
            local touchModule
            local DevMovementMode = Players.LocalPlayer.DevTouchMovementMode
            if DevMovementMode == Enum.DevTouchMovementMode.UserChoice then
                touchModule = movementEnumToModuleMap[UserGameSettings.TouchMovementMode]
            elseif DevMovementMode == Enum.DevTouchMovementMode.Scriptable then
                return nil, true
            else
                touchModule = movementEnumToModuleMap[DevMovementMode]
            end
            return touchModule, true
        end
        
        local function calculateRawMoveVector(humanoid, cameraRelativeMoveVector)
            local camera = Workspace.CurrentCamera
            if not camera then
                return cameraRelativeMoveVector
            end
        
            if humanoid:GetState() == Enum.HumanoidStateType.Swimming then
                return camera.CFrame:VectorToWorldSpace(cameraRelativeMoveVector)
            end
        
            local c, s
            local _, _, _, R00, R01, R02, _, _, R12, _, _, R22 = camera.CFrame:GetComponents()
            if R12 < 1 and R12 > -1 then
                -- X and Z components from back vector.
                c = R22
                s = R02
            else
                -- In this case the camera is looking straight up or straight down.
                -- Use X components from right and up vectors.
                c = R00
                s = -R01*math.sign(R12)
            end
            local norm = math.sqrt(c*c + s*s)
            return Vector3.new(
                (c*cameraRelativeMoveVector.x + s*cameraRelativeMoveVector.z)/norm,
                0,
                (c*cameraRelativeMoveVector.z - s*cameraRelativeMoveVector.x)/norm
            )
        end
        
        function ControlModule:OnRenderStepped(dt)
            if self.activeController and self.activeController.enabled and self.humanoid then
                -- Give the controller a chance to adjust its state
                self.activeController:OnRenderStepped(dt)
        
                -- Now retrieve info from the controller
                local moveVector = self.activeController:GetMoveVector()
                local cameraRelative = self.activeController:IsMoveVectorCameraRelative()
        
                local clickToMoveController = self:GetClickToMoveController()
                if self.activeController ~= clickToMoveController then
                    if moveVector.magnitude > 0 then
                        -- Clean up any developer started MoveTo path
                        clickToMoveController:CleanupPath()
                    else
                        -- Get move vector for developer started MoveTo
                        clickToMoveController:OnRenderStepped(dt)
                        moveVector = clickToMoveController:GetMoveVector()
                        cameraRelative = clickToMoveController:IsMoveVectorCameraRelative()
                    end
                end
        
                -- Are we driving a vehicle ?
                local vehicleConsumedInput = false
                if self.vehicleController then
                    moveVector, vehicleConsumedInput = self.vehicleController:Update(moveVector, cameraRelative, self.activeControlModule==Gamepad)
                end
        
                -- If not, move the player
                -- Verification of vehicleConsumedInput is commented out to preserve legacy behavior,
                -- in case some game relies on Humanoid.MoveDirection still being set while in a VehicleSeat
                --if not vehicleConsumedInput then
                    if cameraRelative then
                        moveVector = calculateRawMoveVector(self.humanoid, moveVector)
                    end
                    self.moveFunction(Players.LocalPlayer, moveVector, false)
                --end
        
                -- And make them jump if needed
                self.humanoid.Jump = self.activeController:GetIsJumping() or (self.touchJumpController and self.touchJumpController:GetIsJumping())
            end
        end
        
        function ControlModule:OnHumanoidSeated(active, currentSeatPart)
            if active then
                if currentSeatPart and currentSeatPart:IsA("VehicleSeat") then
                    if not self.vehicleController then
                        self.vehicleController = self.vehicleController.new(CONTROL_ACTION_PRIORITY)
                    end
                    self.vehicleController:Enable(true, currentSeatPart)
                end
            else
                if self.vehicleController then
                    self.vehicleController:Enable(false, currentSeatPart)
                end
            end
        end
        
        function ControlModule:OnCharacterAdded(char)
            self.humanoid = char:FindFirstChildOfClass("Humanoid")
            while not self.humanoid do
                char.ChildAdded:wait()
                self.humanoid = char:FindFirstChildOfClass("Humanoid")
            end
        
            if self.touchGui then
                self.touchGui.Enabled = true
            end
        
            if self.humanoidSeatedConn then
                self.humanoidSeatedConn:Disconnect()
                self.humanoidSeatedConn = nil
            end
            self.humanoidSeatedConn = self.humanoid.Seated:Connect(function(active, currentSeatPart)
                self:OnHumanoidSeated(active, currentSeatPart)
            end)
        end
        
        function ControlModule:OnCharacterRemoving(char)
            self.humanoid = nil
        
            if self.touchGui then
                self.touchGui.Enabled = false
            end
        end
        
        -- Helper function to lazily instantiate a controller if it does not yet exist,
        -- disable the active controller if it is different from the on being switched to,
        -- and then enable the requested controller. The argument to this function must be
        -- a reference to one of the control modules, i.e. Keyboard, Gamepad, etc.
        function ControlModule:SwitchToController(controlModule)
            if not controlModule then
                if self.activeController then
                    self.activeController:Enable(false)
                end
                self.activeController = nil
                self.activeControlModule = nil
            else
                if not self.controllers[controlModule] then
                    self.controllers[controlModule] = controlModule.new(CONTROL_ACTION_PRIORITY)
                end
        
                if self.activeController ~= self.controllers[controlModule] then
                    if self.activeController then
                        self.activeController:Enable(false)
                    end
                    self.activeController = self.controllers[controlModule]
                    self.activeControlModule = controlModule -- Only used to check if controller switch is necessary
        
                    if self.touchControlFrame and (self.activeControlModule == ClickToMove
                                or self.activeControlModule == TouchThumbstick
                                or self.activeControlModule == DynamicThumbstick) then
                        if not self.controllers[TouchJump] then
                            self.controllers[TouchJump] = TouchJump.new()
                        end
                        self.touchJumpController = self.controllers[TouchJump]
                        self.touchJumpController:Enable(true, self.touchControlFrame)
                    else
                        if self.touchJumpController then
                            self.touchJumpController:Enable(false)
                        end
                    end
        
                    self:EnableActiveControlModule()
                end
            end
        end
        
        function ControlModule:OnLastInputTypeChanged(newLastInputType)
            if lastInputType == newLastInputType then
                warn("LastInputType Change listener called with current type.")
            end
            lastInputType = newLastInputType
        
            if lastInputType == Enum.UserInputType.Touch then
                -- TODO: Check if touch module already active
                local touchModule, success = self:SelectTouchModule()
                if success then
                    while not self.touchControlFrame do
                        wait()
                    end
                    self:SwitchToController(touchModule)
                end
            elseif computerInputTypeToModuleMap[lastInputType] ~= nil then
                local computerModule = self:SelectComputerMovementModule()
                if computerModule then
                    self:SwitchToController(computerModule)
                end
            end
        end
        
        -- Called when any relevant values of GameSettings or LocalPlayer change, forcing re-evalulation of
        -- current control scheme
        function ControlModule:OnComputerMovementModeChange()
            local controlModule, success =  self:SelectComputerMovementModule()
            if success then
                self:SwitchToController(controlModule)
            end
        end
        
        function ControlModule:OnTouchMovementModeChange()
            local touchModule, success = self:SelectTouchModule()
            if success then
                while not self.touchControlFrame do
                    wait()
                end
                self:SwitchToController(touchModule)
            end
        end
        
        function ControlModule:CreateTouchGuiContainer()
            if self.touchGui then self.touchGui:Destroy() end
        
            -- Container for all touch device guis
            self.touchGui = Instance.new("ScreenGui")
            self.touchGui.Name = "TouchGui"
            self.touchGui.ResetOnSpawn = false
            self.touchGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
            self.touchGui.Enabled = self.humanoid ~= nil
        
            self.touchControlFrame = Instance.new("Frame")
            self.touchControlFrame.Name = "TouchControlFrame"
            self.touchControlFrame.Size = UDim2.new(1, 0, 1, 0)
            self.touchControlFrame.BackgroundTransparency = 1
            self.touchControlFrame.Parent = self.touchGui
        
            self.touchGui.Parent = self.playerGui
        end
        
        function ControlModule:GetClickToMoveController()
            if not self.controllers[ClickToMove] then
                self.controllers[ClickToMove] = ClickToMove.new(CONTROL_ACTION_PRIORITY)
            end
            return self.controllers[ClickToMove]
        end
        
        function ControlModule:IsJumping()
            if self.activeController then
                return self.activeController:GetIsJumping() or (self.touchJumpController and self.touchJumpController:GetIsJumping())
            end
            return false
        end
        
        return ControlModule.new()
    end
    
    function _PlayerModule()
        local PlayerModule = {}
        PlayerModule.__index = PlayerModule
        function PlayerModule.new()
            local self = setmetatable({},PlayerModule)
            self.cameras = _CameraModule()
            self.controls = _ControlModule()
            return self
        end
        function PlayerModule:GetCameras()
            return self.cameras
        end
        function PlayerModule:GetControls()
            return self.controls
        end
        function PlayerModule:GetClickToMoveController()
            return self.controls:GetClickToMoveController()
        end
        return PlayerModule.new()
    end
    
    function _sounds()
        
        local SetState = Instance.new("BindableEvent",script)
        
        local Players = game:GetService("Players")
        local RunService = game:GetService("RunService")
        
        local SOUND_DATA = {
            Climbing = {
                SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
                Looped = true,
            },
            Died = {
                SoundId = "rbxasset://sounds/uuhhh.mp3",
            },
            FreeFalling = {
                SoundId = "rbxasset://sounds/action_falling.mp3",
                Looped = true,
            },
            GettingUp = {
                SoundId = "rbxasset://sounds/action_get_up.mp3",
            },
            Jumping = {
                SoundId = "rbxasset://sounds/action_jump.mp3",
            },
            Landing = {
                SoundId = "rbxasset://sounds/action_jump_land.mp3",
            },
            Running = {
                SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
                Looped = true,
                Pitch = 1.85,
            },
            Splash = {
                SoundId = "rbxasset://sounds/impact_water.mp3",
            },
            Swimming = {
                SoundId = "rbxasset://sounds/action_swim.mp3",
                Looped = true,
                Pitch = 1.6,
            },
        }
        
         -- wait for the first of the passed signals to fire
        local function waitForFirst(...)
            local shunt = Instance.new("BindableEvent")
            local slots = {...}
        
            local function fire(...)
                for i = 1, #slots do
                    slots[i]:Disconnect()
                end
        
                return shunt:Fire(...)
            end
        
            for i = 1, #slots do
                slots[i] = slots[i]:Connect(fire)
            end
        
            return shunt.Event:Wait()
        end
        
        -- map a value from one range to another
        local function map(x, inMin, inMax, outMin, outMax)
            return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
        end
        
        local function playSound(sound)
            sound.TimePosition = 0
            sound.Playing = true
        end
        
        local function stopSound(sound)
            sound.Playing = false
            sound.TimePosition = 0
        end
        
        local function shallowCopy(t)
            local out = {}
            for k, v in pairs(t) do
                out[k] = v
            end
            return out
        end
        
        local function initializeSoundSystem(player, humanoid, rootPart)
            local sounds = {}
        
            -- initialize sounds
            for name, props in pairs(SOUND_DATA) do
                local sound = Instance.new("Sound")
                sound.Name = name
        
                -- set default values
                sound.Archivable = false
                sound.EmitterSize = 5
                sound.MaxDistance = 150
                sound.Volume = 0.65
        
                for propName, propValue in pairs(props) do
                    sound[propName] = propValue
                end
        
                sound.Parent = rootPart
                sounds[name] = sound
            end
        
            local playingLoopedSounds = {}
        
            local function stopPlayingLoopedSounds(except)
                for sound in pairs(shallowCopy(playingLoopedSounds)) do
                    if sound ~= except then
                        sound.Playing = false
                        playingLoopedSounds[sound] = nil
                    end
                end
            end
        
            -- state transition callbacks
            local stateTransitions = {
                [Enum.HumanoidStateType.FallingDown] = function()
                    stopPlayingLoopedSounds()
                end,
        
                [Enum.HumanoidStateType.GettingUp] = function()
                    stopPlayingLoopedSounds()
                    playSound(sounds.GettingUp)
                end,
        
                [Enum.HumanoidStateType.Jumping] = function()
                    stopPlayingLoopedSounds()
                    playSound(sounds.Jumping)
                end,
        
                [Enum.HumanoidStateType.Swimming] = function()
                    local verticalSpeed = math.abs(rootPart.Velocity.Y)
                    if verticalSpeed > 0.1 then
                        sounds.Splash.Volume = math.clamp(map(verticalSpeed, 100, 350, 0.28, 1), 0, 1)
                        playSound(sounds.Splash)
                    end
                    stopPlayingLoopedSounds(sounds.Swimming)
                    sounds.Swimming.Playing = true
                    playingLoopedSounds[sounds.Swimming] = true
                end,
        
                [Enum.HumanoidStateType.Freefall] = function()
                    sounds.FreeFalling.Volume = 0
                    stopPlayingLoopedSounds(sounds.FreeFalling)
                    playingLoopedSounds[sounds.FreeFalling] = true
                end,
        
                [Enum.HumanoidStateType.Landed] = function()
                    stopPlayingLoopedSounds()
                    local verticalSpeed = math.abs(rootPart.Velocity.Y)
                    if verticalSpeed > 75 then
                        sounds.Landing.Volume = math.clamp(map(verticalSpeed, 50, 100, 0, 1), 0, 1)
                        playSound(sounds.Landing)
                    end
                end,
        
                [Enum.HumanoidStateType.Running] = function()
                    stopPlayingLoopedSounds(sounds.Running)
                    sounds.Running.Playing = true
                    playingLoopedSounds[sounds.Running] = true
                end,
        
                [Enum.HumanoidStateType.Climbing] = function()
                    local sound = sounds.Climbing
                    if math.abs(rootPart.Velocity.Y) > 0.1 then
                        sound.Playing = true
                        stopPlayingLoopedSounds(sound)
                    else
                        stopPlayingLoopedSounds()
                    end
                    playingLoopedSounds[sound] = true
                end,
        
                [Enum.HumanoidStateType.Seated] = function()
                    stopPlayingLoopedSounds()
                end,
        
                [Enum.HumanoidStateType.Dead] = function()
                    stopPlayingLoopedSounds()
                    playSound(sounds.Died)
                end,
            }
        
            -- updaters for looped sounds
            local loopedSoundUpdaters = {
                [sounds.Climbing] = function(dt, sound, vel)
                    sound.Playing = vel.Magnitude > 0.1
                end,
        
                [sounds.FreeFalling] = function(dt, sound, vel)
                    if vel.Magnitude > 75 then
                        sound.Volume = math.clamp(sound.Volume + 0.9*dt, 0, 1)
                    else
                        sound.Volume = 0
                    end
                end,
        
                [sounds.Running] = function(dt, sound, vel)
                    sound.Playing = vel.Magnitude > 0.5 and humanoid.MoveDirection.Magnitude > 0.5
                end,
            }
        
            -- state substitutions to avoid duplicating entries in the state table
            local stateRemap = {
                [Enum.HumanoidStateType.RunningNoPhysics] = Enum.HumanoidStateType.Running,
            }
        
            local activeState = stateRemap[humanoid:GetState()] or humanoid:GetState()
            local activeConnections = {}
        
            local stateChangedConn = humanoid.StateChanged:Connect(function(_, state)
                state = stateRemap[state] or state
        
                if state ~= activeState then
                    local transitionFunc = stateTransitions[state]
        
                    if transitionFunc then
                        transitionFunc()
                    end
        
                    activeState = state
                end
            end)
            
            local customStateChangedConn = SetState.Event:Connect(function(state)
                state = stateRemap[state] or state
        
                if state ~= activeState then
                    local transitionFunc = stateTransitions[state]
        
                    if transitionFunc then
                        transitionFunc()
                    end
        
                    activeState = state
                end
            end)
        
            local steppedConn = RunService.Stepped:Connect(function(_, worldDt)
                -- update looped sounds on stepped
                for sound in pairs(playingLoopedSounds) do
                    local updater = loopedSoundUpdaters[sound]
        
                    if updater then
                        updater(worldDt, sound, rootPart.Velocity)
                    end
                end
            end)
        
            local humanoidAncestryChangedConn
            local rootPartAncestryChangedConn
            local characterAddedConn
        
            local function terminate()
                stateChangedConn:Disconnect()
                customStateChangedConn:Disconnect()
                steppedConn:Disconnect()
                humanoidAncestryChangedConn:Disconnect()
                rootPartAncestryChangedConn:Disconnect()
                characterAddedConn:Disconnect()
            end
        
            humanoidAncestryChangedConn = humanoid.AncestryChanged:Connect(function(_, parent)
                if not parent then
                    terminate()
                end
            end)
        
            rootPartAncestryChangedConn = rootPart.AncestryChanged:Connect(function(_, parent)
                if not parent then
                    terminate()
                end
            end)
        
            characterAddedConn = player.CharacterAdded:Connect(terminate)
        end
        
        local function playerAdded(player)
            local function characterAdded(character)
                -- Avoiding memory leaks in the face of Character/Humanoid/RootPart lifetime has a few complications:
                -- * character deparenting is a Remove instead of a Destroy, so signals are not cleaned up automatically.
                -- ** must use a waitForFirst on everything and listen for hierarchy changes.
                -- * the character might not be in the dm by the time CharacterAdded fires
                -- ** constantly check consistency with player.Character and abort if CharacterAdded is fired again
                -- * Humanoid may not exist immediately, and by the time it's inserted the character might be deparented.
                -- * RootPart probably won't exist immediately.
                -- ** by the time RootPart is inserted and Humanoid.RootPart is set, the character or the humanoid might be deparented.
        
                if not character.Parent then
                    waitForFirst(character.AncestryChanged, player.CharacterAdded)
                end
        
                if player.Character ~= character or not character.Parent then
                    return
                end
        
                local humanoid = character:FindFirstChildOfClass("Humanoid")
                while character:IsDescendantOf(game) and not humanoid do
                    waitForFirst(character.ChildAdded, character.AncestryChanged, player.CharacterAdded)
                    humanoid = character:FindFirstChildOfClass("Humanoid")
                end
        
                if player.Character ~= character or not character:IsDescendantOf(game) then
                    return
                end
        
                -- must rely on HumanoidRootPart naming because Humanoid.RootPart does not fire changed signals
                local rootPart = character:FindFirstChild("HumanoidRootPart")
                while character:IsDescendantOf(game) and not rootPart do
                    waitForFirst(character.ChildAdded, character.AncestryChanged, humanoid.AncestryChanged, player.CharacterAdded)
                    rootPart = character:FindFirstChild("HumanoidRootPart")
                end
        
                if rootPart and humanoid:IsDescendantOf(game) and character:IsDescendantOf(game) and player.Character == character then
                    initializeSoundSystem(player, humanoid, rootPart)
                end
            end
        
            if player.Character then
                characterAdded(player.Character)
            end
            player.CharacterAdded:Connect(characterAdded)
        end
        
        Players.PlayerAdded:Connect(playerAdded)
        for _, player in ipairs(Players:GetPlayers()) do
            playerAdded(player)
        end
        return SetState
    end
    
    function _StateTracker()
        local EPSILON = 0.1
        
        local SPEED = {
            ["onRunning"] = true,
            ["onClimbing"] = true 
        }
        
        local INAIR = {
            ["onFreeFall"] = true,
            ["onJumping"] = true
        }
        
        local STATEMAP = {
            ["onRunning"] = Enum.HumanoidStateType.Running,
            ["onJumping"] = Enum.HumanoidStateType.Jumping,
            ["onFreeFall"] = Enum.HumanoidStateType.Freefall
        }
        
        local StateTracker = {}
        StateTracker.__index = StateTracker
        
        function StateTracker.new(humanoid, soundState)
            local self = setmetatable({}, StateTracker)
            
            self.Humanoid = humanoid
            self.HRP = humanoid.RootPart
            
            self.Speed = 0
            self.State = "onRunning"
            self.Jumped = false
            self.JumpTick = tick()
            
            self.SoundState = soundState
            
            self._ChangedEvent = Instance.new("BindableEvent")
            self.Changed = self._ChangedEvent.Event
            
            return self
        end
        
        function StateTracker:Destroy()
            self._ChangedEvent:Destroy()
        end
        
        function StateTracker:RequestedJump()
            self.Jumped = true
            self.JumpTick = tick()
        end
        
        function StateTracker:OnStep(gravityUp, grounded, isMoving)
            local cVelocity = self.HRP.Velocity
            local gVelocity = cVelocity:Dot(gravityUp)
            
            local oldState, oldSpeed = self.State, self.Speed
            
            local newState
            local newSpeed = cVelocity.Magnitude
        
            if (not grounded) then
                if (gVelocity > 0) then
                    if (self.Jumped) then
                        newState = "onJumping"
                    else
                        newState = "onFreeFall"
                    end
                else
                    if (self.Jumped) then
                        self.Jumped = false
                    end
                    newState = "onFreeFall"
                end
            else
                if (self.Jumped and tick() - self.JumpTick > 0.1) then
                    self.Jumped = false
                end
                newSpeed = (cVelocity - gVelocity*gravityUp).Magnitude
                newState = "onRunning"
            end
            
            newSpeed = isMoving and newSpeed or 0
            
            if (oldState ~= newState or (SPEED[newState] and math.abs(oldSpeed - newSpeed) > EPSILON)) then
                self.State = newState
                self.Speed = newSpeed
                self.SoundState:Fire(STATEMAP[newState])
                self._ChangedEvent:Fire(self.State, self.Speed)
            end
        end
        
        return StateTracker
    end
    function _InitObjects()
        local model = workspace:FindFirstChild("objects") or game:GetObjects("rbxassetid://5045408489")[1]
        local SPHERE = model:WaitForChild("Sphere")
        local FLOOR = model:WaitForChild("Floor")
        local VFORCE = model:WaitForChild("VectorForce")
        local BGYRO = model:WaitForChild("BodyGyro")
        local function initObjects(self)
            local hrp = self.HRP
            local humanoid = self.Humanoid
            local sphere = SPHERE:Clone()
            sphere.Parent = self.Character
            local floor = FLOOR:Clone()
            floor.Parent = self.Character
            local isR15 = (humanoid.RigType == Enum.HumanoidRigType.R15)
            local height = isR15 and (humanoid.HipHeight + 0.05) or 2
            local weld = Instance.new("Weld")
            weld.C0 = CFrame.new(0, -height, 0.1)
            weld.Part0 = hrp
            weld.Part1 = sphere
            weld.Parent = sphere
            local weld2 = Instance.new("Weld")
            weld2.C0 = CFrame.new(0, -(height + 1.5), 0)
            weld2.Part0 = hrp
            weld2.Part1 = floor
            weld2.Parent = floor
            local gyro = BGYRO:Clone()
            gyro.CFrame = hrp.CFrame
            gyro.Parent = hrp
            local vForce = VFORCE:Clone()
            vForce.Attachment0 = isR15 and hrp:WaitForChild("RootRigAttachment") or hrp:WaitForChild("RootAttachment")
            vForce.Parent = hrp
            return sphere, gyro, vForce, floor
        end
        return initObjects
    end
    local plr = game.Players.LocalPlayer
    local ms = plr:GetMouse()
    local char
    plr.CharacterAdded:Connect(function(c)
        char = c
    end)
    function _R6()
        function r6()
        local Figure = char
        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 pose = "Standing"
        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 = 9 },
                        { 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 = 10 } 
                    }, 
            fall = 	{
                        { id = "http://www.roblox.com/asset/?id=180436148", weight = 10 } 
                    }, 
            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
            local config = script:FindFirstChild(name)
            if (config ~= nil) then
        --		print("Loading anims " .. name)
                table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
                table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
                local idx = 1
                for _, childPart in pairs(config:GetChildren()) do
                    if (childPart:IsA("Animation")) then
                        table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
                        animTable[name][idx] = {}
                        animTable[name][idx].anim = childPart
                        local weightObject = childPart:FindFirstChild("Weight")
                        if (weightObject == nil) then
                            animTable[name][idx].weight = 1
                        else
                            animTable[name][idx].weight = weightObject.Value
                        end
                        animTable[name].count = animTable[name].count + 1
                        animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
            --			print(name .. " [" .. idx .. "] " .. animTable[name][idx].anim.AnimationId .. " (" .. animTable[name][idx].weight .. ")")
                        idx = idx + 1
                    end
                end
            end
            -- 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
        function scriptChildModified(child)
            local fileList = animNames[child.Name]
            if (fileList ~= nil) then
                configureAnimationSet(child.Name, fileList)
            end	
        end
        
        script.ChildAdded:connect(scriptChildModified)
        script.ChildRemoved:connect(scriptChildModified)
        
        
        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
        
        function setAnimationSpeed(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
        function playAnimation(animName, transitionTime, humanoid) 
                
            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
        
        -------------------------------------------------------------------------------------------
        -------------------------------------------------------------------------------------------
        
        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
        
        -------------------------------------------------------------------------------------------
        -------------------------------------------------------------------------------------------
        
        
        function onRunning(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
        
        function onDied()
            pose = "Dead"
        end
        
        function onJumping()
            playAnimation("jump", 0.1, Humanoid)
            jumpAnimTime = jumpAnimDuration
            pose = "Jumping"
        end
        
        function onClimbing(speed)
            playAnimation("climb", 0.1, Humanoid)
            setAnimationSpeed(speed / 12.0)
            pose = "Climbing"
        end
        
        function onGettingUp()
            pose = "GettingUp"
        end
        
        function onFreeFall()
            if (jumpAnimTime <= 0) then
                playAnimation("fall", fallTransitionTime, Humanoid)
            end
            pose = "FreeFall"
        end
        
        function onFallingDown()
            pose = "FallingDown"
        end
        
        function onSeated()
            pose = "Seated"
        end
        
        function onPlatformStanding()
            pose = "PlatformStanding"
        end
        
        function onSwimming(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)
            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
        
        
        local events = {}
        local eventHum = Humanoid
        
        local function onUnhook()
            for i = 1, #events do
                events[i]:Disconnect()
            end
            events = {}
        end
        
        local function onHook()
            onUnhook()
            
            pose = eventHum.Sit and "Seated" or "Standing"
            
            events = {
                eventHum.Died:connect(onDied),
                eventHum.Running:connect(onRunning),
                eventHum.Jumping:connect(onJumping),
                eventHum.Climbing:connect(onClimbing),
                eventHum.GettingUp:connect(onGettingUp),
                eventHum.FreeFalling:connect(onFreeFall),
                eventHum.FallingDown:connect(onFallingDown),
                eventHum.Seated:connect(onSeated),
                eventHum.PlatformStanding:connect(onPlatformStanding),
                eventHum.Swimming:connect(onSwimming)
            }
        end
        
        
        onHook()
        
        -- 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)
        
        return {
            onRunning = onRunning, 
            onDied = onDied, 
            onJumping = onJumping, 
            onClimbing = onClimbing, 
            onGettingUp = onGettingUp, 
            onFreeFall = onFreeFall, 
            onFallingDown = onFallingDown, 
            onSeated = onSeated, 
            onPlatformStanding = onPlatformStanding,
            onHook = onHook,
            onUnhook = onUnhook
        }
        
        end
        return r6()
    end
    
    function _R15()
        local function r15()
            
        local Character = char
        local Humanoid = Character:WaitForChild("Humanoid")
        local pose = "Standing"
        
        local userNoUpdateOnLoopSuccess, userNoUpdateOnLoopValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserNoUpdateOnLoop") end)
        local userNoUpdateOnLoop = userNoUpdateOnLoopSuccess and userNoUpdateOnLoopValue
        local userAnimationSpeedDampeningSuccess, userAnimationSpeedDampeningValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserAnimationSpeedDampening") end)
        local userAnimationSpeedDampening = userAnimationSpeedDampeningSuccess and userAnimationSpeedDampeningValue
        
        local animateScriptEmoteHookFlagExists, animateScriptEmoteHookFlagEnabled = pcall(function()
            return UserSettings():IsUserFeatureEnabled("UserAnimateScriptEmoteHook")
        end)
        local FFlagAnimateScriptEmoteHook = animateScriptEmoteHookFlagExists and animateScriptEmoteHookFlagEnabled
        
        local AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
        local HumanoidHipHeight = 2
        
        local EMOTE_TRANSITION_TIME = 0.1
        
        local currentAnim = ""
        local currentAnimInstance = nil
        local currentAnimTrack = nil
        local currentAnimKeyframeHandler = nil
        local currentAnimSpeed = 1.0
        
        local runAnimTrack = nil
        local runAnimKeyframeHandler = nil
        
        local animTable = {}
        local animNames = { 
            idle = 	{	
                        { id = "http://www.roblox.com/asset/?id=507766666", weight = 1 },
                        { id = "http://www.roblox.com/asset/?id=507766951", weight = 1 },
                        { id = "http://www.roblox.com/asset/?id=507766388", weight = 9 }
                    },
            walk = 	{ 	
                        { id = "http://www.roblox.com/asset/?id=507777826", weight = 10 } 
                    }, 
            run = 	{
                        { id = "http://www.roblox.com/asset/?id=507767714", weight = 10 } 
                    }, 
            swim = 	{
                        { id = "http://www.roblox.com/asset/?id=507784897", weight = 10 } 
                    }, 
            swimidle = 	{
                        { id = "http://www.roblox.com/asset/?id=507785072", weight = 10 } 
                    }, 
            jump = 	{
                        { id = "http://www.roblox.com/asset/?id=507765000", weight = 10 } 
                    }, 
            fall = 	{
                        { id = "http://www.roblox.com/asset/?id=507767968", weight = 10 } 
                    }, 
            climb = {
                        { id = "http://www.roblox.com/asset/?id=507765644", weight = 10 } 
                    }, 
            sit = 	{
                        { id = "http://www.roblox.com/asset/?id=2506281703", weight = 10 } 
                    },	
            toolnone = {
                        { id = "http://www.roblox.com/asset/?id=507768375", weight = 10 } 
                    },
            toolslash = {
                        { id = "http://www.roblox.com/asset/?id=522635514", weight = 10 } 
                    },
            toollunge = {
                        { id = "http://www.roblox.com/asset/?id=522638767", weight = 10 } 
                    },
            wave = {
                        { id = "http://www.roblox.com/asset/?id=507770239", weight = 10 } 
                    },
            point = {
                        { id = "http://www.roblox.com/asset/?id=507770453", weight = 10 } 
                    },
            dance = {
                        { id = "http://www.roblox.com/asset/?id=507771019", weight = 10 }, 
                        { id = "http://www.roblox.com/asset/?id=507771955", weight = 10 }, 
                        { id = "http://www.roblox.com/asset/?id=507772104", weight = 10 } 
                    },
            dance2 = {
                        { id = "http://www.roblox.com/asset/?id=507776043", weight = 10 }, 
                        { id = "http://www.roblox.com/asset/?id=507776720", weight = 10 }, 
                        { id = "http://www.roblox.com/asset/?id=507776879", weight = 10 } 
                    },
            dance3 = {
                        { id = "http://www.roblox.com/asset/?id=507777268", weight = 10 }, 
                        { id = "http://www.roblox.com/asset/?id=507777451", weight = 10 }, 
                        { id = "http://www.roblox.com/asset/?id=507777623", weight = 10 } 
                    },
            laugh = {
                        { id = "http://www.roblox.com/asset/?id=507770818", weight = 10 } 
                    },
            cheer = {
                        { id = "http://www.roblox.com/asset/?id=507770677", weight = 10 } 
                    },
        }
        
        -- 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, dance = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
        
        local PreloadAnimsUserFlag = false
        local PreloadedAnims = {}
        local successPreloadAnim, msgPreloadAnim = pcall(function()
            PreloadAnimsUserFlag = UserSettings():IsUserFeatureEnabled("UserPreloadAnimations")
        end)
        if not successPreloadAnim then
            PreloadAnimsUserFlag = false
        end
        
        math.randomseed(tick())
        
        function findExistingAnimationInSet(set, anim)
            if set == nil or anim == nil then
                return 0
            end
            
            for idx = 1, set.count, 1 do 
                if set[idx].anim.AnimationId == anim.AnimationId then
                    return idx
                end
            end
            
            return 0
        end
        
        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 = {}
        
            local allowCustomAnimations = true
        
            local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
            if not success then
                allowCustomAnimations = true
            end
        
            -- check for config values
            local config = script:FindFirstChild(name)
            if (allowCustomAnimations and config ~= nil) then
                table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
                table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
                
                local idx = 0
                for _, childPart in pairs(config:GetChildren()) do
                    if (childPart:IsA("Animation")) then
                        local newWeight = 1
                        local weightObject = childPart:FindFirstChild("Weight")
                        if (weightObject ~= nil) then
                            newWeight = weightObject.Value
                        end
                        animTable[name].count = animTable[name].count + 1
                        idx = animTable[name].count
                        animTable[name][idx] = {}
                        animTable[name][idx].anim = childPart
                        animTable[name][idx].weight = newWeight
                        animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
                        table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
                        table.insert(animTable[name].connections, childPart.ChildAdded:connect(function(property) configureAnimationSet(name, fileList) end))
                        table.insert(animTable[name].connections, childPart.ChildRemoved:connect(function(property) configureAnimationSet(name, fileList) end))
                    end
                end
            end
            
            -- 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
                end
            end
            
            -- preload anims
            if PreloadAnimsUserFlag then
                for i, animType in pairs(animTable) do
                    for idx = 1, animType.count, 1 do
                        if PreloadedAnims[animType[idx].anim.AnimationId] == nil then
                            Humanoid:LoadAnimation(animType[idx].anim)
                            PreloadedAnims[animType[idx].anim.AnimationId] = true
                        end				
                    end
                end
            end
        end
        
        ------------------------------------------------------------------------------------------------------------
        
        function configureAnimationSetOld(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 = {}
        
            local allowCustomAnimations = true
        
            local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
            if not success then
                allowCustomAnimations = true
            end
        
            -- check for config values
            local config = script:FindFirstChild(name)
            if (allowCustomAnimations and config ~= nil) then
                table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
                table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
                local idx = 1
                for _, childPart in pairs(config:GetChildren()) do
                    if (childPart:IsA("Animation")) then
                        table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
                        animTable[name][idx] = {}
                        animTable[name][idx].anim = childPart
                        local weightObject = childPart:FindFirstChild("Weight")
                        if (weightObject == nil) then
                            animTable[name][idx].weight = 1
                        else
                            animTable[name][idx].weight = weightObject.Value
                        end
                        animTable[name].count = animTable[name].count + 1
                        animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
                        idx = idx + 1
                    end
                end
            end
        
            -- 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
            
            -- preload anims
            if PreloadAnimsUserFlag then
                for i, animType in pairs(animTable) do
                    for idx = 1, animType.count, 1 do 
                        Humanoid:LoadAnimation(animType[idx].anim)
                    end
                end
            end
        end
        
        -- Setup animation objects
        function scriptChildModified(child)
            local fileList = animNames[child.Name]
            if (fileList ~= nil) then
                configureAnimationSet(child.Name, fileList)
            end	
        end
        
        script.ChildAdded:connect(scriptChildModified)
        script.ChildRemoved:connect(scriptChildModified)
        
        
        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.31
        
        local toolTransitionTime = 0.1
        local fallTransitionTime = 0.2
        
        local currentlyPlayingEmote = false
        
        -- 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
            
            if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
                oldAnim = "idle"
                currentlyPlayingEmote = false
            end
        
            currentAnim = ""
            currentAnimInstance = nil
            if (currentAnimKeyframeHandler ~= nil) then
                currentAnimKeyframeHandler:disconnect()
            end
        
            if (currentAnimTrack ~= nil) then
                currentAnimTrack:Stop()
                currentAnimTrack:Destroy()
                currentAnimTrack = nil
            end
        
            -- clean up walk if there is one
            if (runAnimKeyframeHandler ~= nil) then
                runAnimKeyframeHandler:disconnect()
            end
            
            if (runAnimTrack ~= nil) then
                runAnimTrack:Stop()
                runAnimTrack:Destroy()
                runAnimTrack = nil
            end
            
            return oldAnim
        end
        
        function getHeightScale()
            if Humanoid then
                if not Humanoid.AutomaticScalingEnabled then
                    return 1
                end
                
                local scale = Humanoid.HipHeight / HumanoidHipHeight
                if userAnimationSpeedDampening then
                    if AnimationSpeedDampeningObject == nil then
                        AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
                    end
                    if AnimationSpeedDampeningObject ~= nil then
                        scale = 1 + (Humanoid.HipHeight - HumanoidHipHeight) * AnimationSpeedDampeningObject.Value / HumanoidHipHeight
                    end
                end
                return scale
            end	
            return 1
        end
        
        local smallButNotZero = 0.0001
        function setRunSpeed(speed)
            local speedScaled = speed * 1.25
            local heightScale = getHeightScale()
            local runSpeed = speedScaled / heightScale
        
            if runSpeed ~= currentAnimSpeed then
                if runSpeed < 0.33 then
                    currentAnimTrack:AdjustWeight(1.0)		
                    runAnimTrack:AdjustWeight(smallButNotZero)
                elseif runSpeed < 0.66 then
                    local weight = ((runSpeed - 0.33) / 0.33)
                    currentAnimTrack:AdjustWeight(1.0 - weight + smallButNotZero)
                    runAnimTrack:AdjustWeight(weight + smallButNotZero)
                else
                    currentAnimTrack:AdjustWeight(smallButNotZero)
                    runAnimTrack:AdjustWeight(1.0)
                end
                currentAnimSpeed = runSpeed
                runAnimTrack:AdjustSpeed(runSpeed)
                currentAnimTrack:AdjustSpeed(runSpeed)
            end	
        end
        
        function setAnimationSpeed(speed)
            if currentAnim == "walk" then
                    setRunSpeed(speed)
            else
                if speed ~= currentAnimSpeed then
                    currentAnimSpeed = speed
                    currentAnimTrack:AdjustSpeed(currentAnimSpeed)
                end
            end
        end
        
        function keyFrameReachedFunc(frameName)
            if (frameName == "End") then
                if currentAnim == "walk" then
                    if userNoUpdateOnLoop == true then
                        if runAnimTrack.Looped ~= true then
                            runAnimTrack.TimePosition = 0.0
                        end
                        if currentAnimTrack.Looped ~= true then
                            currentAnimTrack.TimePosition = 0.0
                        end
                    else
                        runAnimTrack.TimePosition = 0.0
                        currentAnimTrack.TimePosition = 0.0
                    end
                else
                    local repeatAnim = currentAnim
                    -- return to idle if finishing an emote
                    if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
                        repeatAnim = "idle"
                    end
                    
                    if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
                        if currentAnimTrack.Looped then
                            -- Allow the emote to loop
                            return
                        end
                        
                        repeatAnim = "idle"
                        currentlyPlayingEmote = false
                    end
                    
                    local animSpeed = currentAnimSpeed
                    playAnimation(repeatAnim, 0.15, Humanoid)
                    setAnimationSpeed(animSpeed)
                end
            end
        end
        
        function rollAnimation(animName)
            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
            return idx
        end
        
        local function switchToAnim(anim, animName, transitionTime, humanoid)
            -- switch animation		
            if (anim ~= currentAnimInstance) then
                
                if (currentAnimTrack ~= nil) then
                    currentAnimTrack:Stop(transitionTime)
                    currentAnimTrack:Destroy()
                end
        
                if (runAnimTrack ~= nil) then
                    runAnimTrack:Stop(transitionTime)
                    runAnimTrack:Destroy()
                    if userNoUpdateOnLoop == true then
                        runAnimTrack = nil
                    end
                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)
                
                -- check to see if we need to blend a walk/run animation
                if animName == "walk" then
                    local runAnimName = "run"
                    local runIdx = rollAnimation(runAnimName)
        
                    runAnimTrack = humanoid:LoadAnimation(animTable[runAnimName][runIdx].anim)
                    runAnimTrack.Priority = Enum.AnimationPriority.Core
                    runAnimTrack:Play(transitionTime)		
                    
                    if (runAnimKeyframeHandler ~= nil) then
                        runAnimKeyframeHandler:disconnect()
                    end
                    runAnimKeyframeHandler = runAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)	
                end
            end
        end
        
        function playAnimation(animName, transitionTime, humanoid) 	
            local idx = rollAnimation(animName)
            local anim = animTable[animName][idx].anim
        
            switchToAnim(anim, animName, transitionTime, humanoid)
            currentlyPlayingEmote = false
        end
        
        function playEmote(emoteAnim, transitionTime, humanoid)
            switchToAnim(emoteAnim, emoteAnim.Name, transitionTime, humanoid)
            currentlyPlayingEmote = true
        end
        
        -------------------------------------------------------------------------------------------
        -------------------------------------------------------------------------------------------
        
        local toolAnimName = ""
        local toolAnimTrack = nil
        local toolAnimInstance = nil
        local currentToolAnimKeyframeHandler = nil
        
        function toolKeyFrameReachedFunc(frameName)
            if (frameName == "End") then
                playToolAnimation(toolAnimName, 0.0, Humanoid)
            end
        end
        
        
        function playToolAnimation(animName, transitionTime, humanoid, priority)	 		
                local idx = rollAnimation(animName)
                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
        
        -------------------------------------------------------------------------------------------
        -------------------------------------------------------------------------------------------
        -- STATE CHANGE HANDLERS
        
        function onRunning(speed)
            if speed > 0.75 then
                local scale = 16.0
                playAnimation("walk", 0.2, Humanoid)
                setAnimationSpeed(speed / scale)
                pose = "Running"
            else
                if emoteNames[currentAnim] == nil and not currentlyPlayingEmote then
                    playAnimation("idle", 0.2, Humanoid)
                    pose = "Standing"
                end
            end
        end
        
        function onDied()
            pose = "Dead"
        end
        
        function onJumping()
            playAnimation("jump", 0.1, Humanoid)
            jumpAnimTime = jumpAnimDuration
            pose = "Jumping"
        end
        
        function onClimbing(speed)
            local scale = 5.0
            playAnimation("climb", 0.1, Humanoid)
            setAnimationSpeed(speed / scale)
            pose = "Climbing"
        end
        
        function onGettingUp()
            pose = "GettingUp"
        end
        
        function onFreeFall()
            if (jumpAnimTime <= 0) then
                playAnimation("fall", fallTransitionTime, Humanoid)
            end
            pose = "FreeFall"
        end
        
        function onFallingDown()
            pose = "FallingDown"
        end
        
        function onSeated()
            pose = "Seated"
        end
        
        function onPlatformStanding()
            pose = "PlatformStanding"
        end
        
        -------------------------------------------------------------------------------------------
        -------------------------------------------------------------------------------------------
        
        function onSwimming(speed)
            if speed > 1.00 then
                local scale = 10.0
                playAnimation("swim", 0.4, Humanoid)
                setAnimationSpeed(speed / scale)
                pose = "Swimming"
            else
                playAnimation("swimidle", 0.4, Humanoid)
                pose = "Standing"
            end
        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 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
        
        local lastTick = 0
        
        function stepAnimate(currentTime)
            local amplitude = 1
            local frequency = 1
              local deltaTime = currentTime - lastTick
              lastTick = currentTime
        
            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.2, Humanoid)
            elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
                stopAllAnimations()
                amplitude = 0.1
                frequency = 1
                setAngles = true
            end
        
            -- Tool Animation handling
            local tool = Character:FindFirstChildOfClass("Tool")
            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 = currentTime + .3
                end
        
                if currentTime > toolAnimTime then
                    toolAnimTime = 0
                    toolAnim = "None"
                end
        
                animateTool()		
            else
                stopToolAnimations()
                toolAnim = "None"
                toolAnimInstance = nil
                toolAnimTime = 0
            end
        end
        
        -- connect events
        
        local events = {}
        local eventHum = Humanoid
        
        local function onUnhook()
            for i = 1, #events do
                events[i]:Disconnect()
            end
            events = {}
        end
        
        local function onHook()
            onUnhook()
            
            pose = eventHum.Sit and "Seated" or "Standing"
            
            events = {
                eventHum.Died:connect(onDied),
                eventHum.Running:connect(onRunning),
                eventHum.Jumping:connect(onJumping),
                eventHum.Climbing:connect(onClimbing),
                eventHum.GettingUp:connect(onGettingUp),
                eventHum.FreeFalling:connect(onFreeFall),
                eventHum.FallingDown:connect(onFallingDown),
                eventHum.Seated:connect(onSeated),
                eventHum.PlatformStanding:connect(onPlatformStanding),
                eventHum.Swimming:connect(onSwimming)
            }
        end
        
        
        onHook()
        
        -- setup emote chat hook
        game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
            local emote = ""
            if (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, EMOTE_TRANSITION_TIME, Humanoid)
            end
        end)
        
        --[[ emote bindable hook
        if FFlagAnimateScriptEmoteHook then
            script:WaitForChild("PlayEmote").OnInvoke = function(emote)
                -- Only play emotes when idling
                if pose ~= "Standing" then
                    return
                end
                if emoteNames[emote] ~= nil then
                    -- Default emotes
                    playAnimation(emote, EMOTE_TRANSITION_TIME, Humanoid)
                    return true
                elseif typeof(emote) == "Instance" and emote:IsA("Animation") then
                    -- Non-default emotes
                    playEmote(emote, EMOTE_TRANSITION_TIME, Humanoid)
                    return true
                end
                -- Return false to indicate that the emote could not be played
                return false
            end
        end
        ]]
        -- initialize to idle
        playAnimation("idle", 0.1, Humanoid)
        pose = "Standing"
        -- loop to handle timed state transitions and tool animations
        spawn(function()
            while Character.Parent ~= nil do
                local _, currentGameTime = wait(0.1)
                stepAnimate(currentGameTime)
            end
        end)
        return {
            onRunning = onRunning, 
            onDied = onDied, 
            onJumping = onJumping, 
            onClimbing = onClimbing, 
            onGettingUp = onGettingUp, 
            onFreeFall = onFreeFall, 
            onFallingDown = onFallingDown, 
            onSeated = onSeated, 
            onPlatformStanding = onPlatformStanding,
            onHook = onHook,
            onUnhook = onUnhook
        }
        end
        return r15()
    end
    while true do
        wait(.1)
        if plr.Character ~= nil then
            char = plr.Character
            break
        end
    end
    function _Controller()
        local humanoid = char:WaitForChild("Humanoid")
        local animFuncs = {}
        if (humanoid.RigType == Enum.HumanoidRigType.R6) then
            animFuncs = _R6()
        else
            animFuncs = _R15()
        end
        print("Animation succes")
        return animFuncs
    end
    function _AnimationHandler()
    local AnimationHandler = {}
    AnimationHandler.__index = AnimationHandler
    
    function AnimationHandler.new(humanoid, animate)
        local self = setmetatable({}, AnimationHandler)
        
        self._AnimFuncs = _Controller()
        self.Humanoid = humanoid
        
        return self
    end
    
    function AnimationHandler:EnableDefault(bool)
        if (bool) then
            self._AnimFuncs.onHook()
        else
            self._AnimFuncs.onUnhook()
        end
    end
    
    function AnimationHandler:Run(name, ...)
        self._AnimFuncs[name](...)
    end
    
    return AnimationHandler
    end
    
    function _GravityController()
    
    local ZERO = Vector3.new(0, 0, 0)
    local UNIT_X = Vector3.new(1, 0, 0)
    local UNIT_Y = Vector3.new(0, 1, 0)
    local UNIT_Z = Vector3.new(0, 0, 1)
    local VEC_XY = Vector3.new(1, 0, 1)
    
    local IDENTITYCF = CFrame.new()
    
    local JUMPMODIFIER = 1.2
    local TRANSITION = 0.15
    local WALKF = 200 / 3
    
    local UIS = game:GetService("UserInputService")
    local RUNSERVICE = game:GetService("RunService")
    
    local InitObjects = _InitObjects()
    local AnimationHandler = _AnimationHandler()
    local StateTracker = _StateTracker()
    
    -- Class
    
    local GravityController = {}
    GravityController.__index = GravityController
    
    -- Private Functions
    
    local function getRotationBetween(u, v, axis)
        local dot, uxv = u:Dot(v), u:Cross(v)
        if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
        return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
    end
    
    local function lookAt(pos, forward, up)
        local r = forward:Cross(up)
        local u = r:Cross(forward)
        return CFrame.fromMatrix(pos, r.Unit, u.Unit)
    end
    
    local function getMass(array)
        local mass = 0
        for _, part in next, array do
            if (part:IsA("BasePart")) then
                mass = mass + part:GetMass()
            end
        end
        return mass
    end
    
    -- Public Constructor
    local ExecutedPlayerModule = _PlayerModule()
    local ExecutedSounds = _sounds()
    function GravityController.new(player)
        local self = setmetatable({}, GravityController)
    
        --[[ Camera
        local loaded = player.PlayerScripts:WaitForChild("PlayerScriptsLoader"):WaitForChild("Loaded")
        if (not loaded.Value) then
            --loaded.Changed:Wait()
        end
        ]]
        local playerModule = ExecutedPlayerModule
        self.Controls = playerModule:GetControls()
        self.Camera = playerModule:GetCameras()
        
        -- Player and character
        self.Player = player
        self.Character = player.Character
        self.Humanoid = player.Character:WaitForChild("Humanoid")
        self.HRP = player.Character:WaitForChild("HumanoidRootPart")
        
        -- Animation
        self.AnimationHandler = AnimationHandler.new(self.Humanoid, self.Character:WaitForChild("Animate"))
        self.AnimationHandler:EnableDefault(false)
        local ssss = game:GetService("Players").LocalPlayer.PlayerScripts:FindFirstChild("SetState") or Instance.new("BindableEvent",game:GetService("Players").LocalPlayer.PlayerScripts)
        local soundState = ExecutedSounds
        ssss.Name = "SetState"
        
        self.StateTracker = StateTracker.new(self.Humanoid, soundState)
        self.StateTracker.Changed:Connect(function(name, speed)
            self.AnimationHandler:Run(name, speed)
        end)
        
        -- Collider and forces
        local collider, gyro, vForce, floor = InitObjects(self)
        
        floor.Touched:Connect(function() end)
        collider.Touched:Connect(function() end)
        
        self.Collider = collider
        self.VForce = vForce
        self.Gyro = gyro
        self.Floor = floor
        
        -- Attachment to parts
        self.LastPart = workspace.Terrain
        self.LastPartCFrame = IDENTITYCF
        
        -- Gravity properties
        self.GravityUp = UNIT_Y
        self.Ignores = {self.Character}
        
        function self.Camera.GetUpVector(this, oldUpVector)
            return self.GravityUp
        end
        
        -- Events etc
        self.Humanoid.PlatformStand = true
        
        self.CharacterMass = getMass(self.Character:GetDescendants())
        self.Character.AncestryChanged:Connect(function() self.CharacterMass = getMass(self.Character:GetDescendants()) end)
        
        self.JumpCon = RUNSERVICE.RenderStepped:Connect(function(dt) 
            if (self.Controls:IsJumping()) then
                self:OnJumpRequest()
            end
        end)
        
        self.DeathCon = self.Humanoid.Died:Connect(function() self:Destroy() end)
        self.SeatCon = self.Humanoid.Seated:Connect(function(active) if (active) then self:Destroy() end end)
        self.HeartCon = RUNSERVICE.Heartbeat:Connect(function(dt) self:OnHeartbeatStep(dt) end)
        RUNSERVICE:BindToRenderStep("GravityStep", Enum.RenderPriority.Input.Value + 1, function(dt) self:OnGravityStep(dt) end)
        
        
        return self
    end
    
    -- Public Methods
    
    function GravityController:Destroy()
        self.JumpCon:Disconnect()
        self.DeathCon:Disconnect()
        self.SeatCon:Disconnect()
        self.HeartCon:Disconnect()
        
        RUNSERVICE:UnbindFromRenderStep("GravityStep")
        
        self.Collider:Destroy()
        self.VForce:Destroy()
        self.Gyro:Destroy()
        self.StateTracker:Destroy()
        
        self.Humanoid.PlatformStand = false
        self.AnimationHandler:EnableDefault(true)
        
        self.GravityUp = UNIT_Y
    end
    
    function GravityController:GetGravityUp(oldGravity)
        return oldGravity
    end
    
    function GravityController:IsGrounded(isJumpCheck)
        if (not isJumpCheck) then
            local parts = self.Floor:GetTouchingParts()
            for _, part in next, parts do
                if (not part:IsDescendantOf(self.Character)) then
                    return true
                end
            end
        else
            if (self.StateTracker.Jumped) then
                return false
            end
        
            -- 1. check we are touching something with the collider
            local valid = {}
            local parts = self.Collider:GetTouchingParts()
            for _, part in next, parts do
                if (not part:IsDescendantOf(self.Character)) then
                    table.insert(valid, part)
                end
            end
            
            if (#valid > 0) then
                -- 2. do a decently long downwards raycast
                local max = math.cos(self.Humanoid.MaxSlopeAngle)
                local ray = Ray.new(self.Collider.Position, -10 * self.GravityUp)
                local hit, pos, normal = workspace:FindPartOnRayWithWhitelist(ray, valid, true)
                
                -- 3. use slope to decide on jump
                if (hit and max <= self.GravityUp:Dot(normal)) then
                    return true
                end
            end
        end
        return false
    end
    
    function GravityController:OnJumpRequest()
        if (not self.StateTracker.Jumped and self:IsGrounded(true)) then
            local hrpVel = self.HRP.Velocity
            self.HRP.Velocity = hrpVel + self.GravityUp*self.Humanoid.JumpPower*JUMPMODIFIER
            self.StateTracker:RequestedJump()
        end
    end
    
    function GravityController:GetMoveVector()
        return self.Controls:GetMoveVector()
    end
    
    function GravityController:OnHeartbeatStep(dt)
        local ray = Ray.new(self.Collider.Position, -1.1*self.GravityUp)
        local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, self.Ignores)
        local lastPart = self.LastPart
        
        if (hit and lastPart and lastPart == hit) then
            local offset = self.LastPartCFrame:ToObjectSpace(self.HRP.CFrame)
            self.HRP.CFrame = hit.CFrame:ToWorldSpace(offset)
        end
        
        self.LastPart = hit
        self.LastPartCFrame = hit and hit.CFrame
    end
    
    function GravityController:OnGravityStep(dt)
        -- update gravity up vector
        local oldGravity = self.GravityUp
        local newGravity = self:GetGravityUp(oldGravity)
        
        local rotation = getRotationBetween(oldGravity, newGravity, workspace.CurrentCamera.CFrame.RightVector)
        rotation = IDENTITYCF:Lerp(rotation, TRANSITION)
        
        self.GravityUp = rotation * oldGravity
        
        -- get world move vector
        local camCF = workspace.CurrentCamera.CFrame
        local fDot = camCF.LookVector:Dot(newGravity)
        local cForward = math.abs(fDot) > 0.5 and -math.sign(fDot)*camCF.UpVector or camCF.LookVector
        
        local left = cForward:Cross(-newGravity).Unit
        local forward = -left:Cross(newGravity).Unit
        
        local move = self:GetMoveVector()
        local worldMove = forward*move.z - left*move.x
        worldMove = worldMove:Dot(worldMove) > 1 and worldMove.Unit or worldMove
        
        local isInputMoving = worldMove:Dot(worldMove) > 0
        
        -- get the desired character cframe
        local hrpCFLook = self.HRP.CFrame.LookVector
        local charF = hrpCFLook:Dot(forward)*forward + hrpCFLook:Dot(left)*left
        local charR = charF:Cross(newGravity).Unit
        local newCharCF = CFrame.fromMatrix(ZERO, charR, newGravity, -charF)
        
        local newCharRotation = IDENTITYCF
        if (isInputMoving) then
            newCharRotation = IDENTITYCF:Lerp(getRotationBetween(charF, worldMove, newGravity), 0.7)	
        end
        
        -- calculate forces
        local g = workspace.Gravity
        local gForce = g * self.CharacterMass * (UNIT_Y - newGravity)
        
        local cVelocity = self.HRP.Velocity
        local tVelocity = self.Humanoid.WalkSpeed * worldMove
        local gVelocity = cVelocity:Dot(newGravity)*newGravity
        local hVelocity = cVelocity - gVelocity
        
        if (hVelocity:Dot(hVelocity) < 1) then
            hVelocity = ZERO
        end
        
        local dVelocity = tVelocity - hVelocity
        local walkForceM = math.min(10000, WALKF * self.CharacterMass * dVelocity.Magnitude / (dt*60))
        local walkForce = walkForceM > 0 and dVelocity.Unit*walkForceM or ZERO
        
        -- mouse lock
        local charRotation = newCharRotation * newCharCF
        
        if (self.Camera:IsCamRelative()) then
            local lv = workspace.CurrentCamera.CFrame.LookVector
            local hlv = lv - charRotation.UpVector:Dot(lv)*charRotation.UpVector
            charRotation = lookAt(ZERO, hlv, charRotation.UpVector)
        end
        
        -- get state
        self.StateTracker:OnStep(self.GravityUp, self:IsGrounded(), isInputMoving)
    
        -- update values
        self.VForce.Force = walkForce + gForce
        self.Gyro.CFrame = charRotation
    end
    return GravityController
    end
    function _Draw3D()
        local module = {}
        
        -- Style Guide
        
        module.StyleGuide = {
            Point = {
                Thickness = 0.5;
                Color = Color3.new(0, 1, 0);
            },
            
            Line = {
                Thickness = 0.1;
                Color = Color3.new(1, 1, 0);
            },
            
            Ray = {
                Thickness = 0.1;
                Color = Color3.new(1, 0, 1);
            },
            
            Triangle = {
                Thickness = 0.05;
            };
            
            CFrame = {
                Thickness = 0.1;
                RightColor3 = Color3.new(1, 0, 0);
                UpColor3 = Color3.new(0, 1, 0);
                BackColor3 = Color3.new(0, 0, 1);
                PartProperties = {
                    Material = Enum.Material.SmoothPlastic;
                };
            }
        }
        
        -- CONSTANTS
        
        local WEDGE = Instance.new("WedgePart")
        WEDGE.Material = Enum.Material.SmoothPlastic
        WEDGE.Anchored = true
        WEDGE.CanCollide = false
        
        local PART = Instance.new("Part")
        PART.Size = Vector3.new(0.1, 0.1, 0.1)
        PART.Anchored = true
        PART.CanCollide = false
        PART.TopSurface = Enum.SurfaceType.Smooth
        PART.BottomSurface = Enum.SurfaceType.Smooth
        PART.Material = Enum.Material.SmoothPlastic
        
        -- Functions
        
        local function draw(properties, style)
            local part = PART:Clone()
            for k, v in next, properties do
                part[k] = v
            end
            if (style) then
                for k, v in next, style do
                    if (k ~= "Thickness") then
                        part[k] = v
                    end
                end
            end
            return part
        end
        
        function module.Draw(parent, properties)
            properties.Parent = parent
            return draw(properties, nil)
        end
        
        function module.Point(parent, cf_v3)
            local thickness = module.StyleGuide.Point.Thickness
            return draw({
                Size = Vector3.new(thickness, thickness, thickness);
                CFrame = (typeof(cf_v3) == "CFrame" and cf_v3 or CFrame.new(cf_v3));
                Parent = parent;
            }, module.StyleGuide.Point)
        end
        
        function module.Line(parent, a, b)
            local thickness = module.StyleGuide.Line.Thickness
            return draw({
                CFrame = CFrame.new((a + b)/2, b);
                Size = Vector3.new(thickness, thickness, (b - a).Magnitude);
                Parent = parent;
            }, module.StyleGuide.Line)
        end
        
        function module.Ray(parent, origin, direction)
            local thickness = module.StyleGuide.Ray.Thickness
            return draw({
                CFrame = CFrame.new(origin + direction/2, origin + direction);
                Size = Vector3.new(thickness, thickness, direction.Magnitude);
                Parent = parent;
            }, module.StyleGuide.Ray)
        end
        
        function module.Triangle(parent, a, b, c)
            local ab, ac, bc = b - a, c - a, c - b
            local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc)
            
            if (abd > acd and abd > bcd) then
                c, a = a, c
            elseif (acd > bcd and acd > abd) then
                a, b = b, a
            end
            
            ab, ac, bc = b - a, c - a, c - b
            
            local right = ac:Cross(ab).Unit
            local up = bc:Cross(right).Unit
            local back = bc.Unit
            
            local height = math.abs(ab:Dot(up))
            local width1 = math.abs(ab:Dot(back))
            local width2 = math.abs(ac:Dot(back))
            
            local thickness = module.StyleGuide.Triangle.Thickness
            
            local w1 = WEDGE:Clone()
            w1.Size = Vector3.new(thickness, height, width1)
            w1.CFrame = CFrame.fromMatrix((a + b)/2, right, up, back)
            w1.Parent = parent
            
            local w2 = WEDGE:Clone()
            w2.Size = Vector3.new(thickness, height, width2)
            w2.CFrame = CFrame.fromMatrix((a + c)/2, -right, up, -back)
            w2.Parent = parent
            
            for k, v in next, module.StyleGuide.Triangle do
                if (k ~= "Thickness") then
                    w1[k] = v
                    w2[k] = v
                end
            end
            
            return w1, w2
        end
        
        function module.CFrame(parent, cf)
            local origin = cf.Position
            local r = cf.RightVector
            local u = cf.UpVector
            local b = -cf.LookVector
            
            local thickness = module.StyleGuide.CFrame.Thickness
            
            local right = draw({
                CFrame = CFrame.new(origin + r/2, origin + r);
                Size = Vector3.new(thickness, thickness, r.Magnitude);
                Color = module.StyleGuide.CFrame.RightColor3;
                Parent = parent;
            }, module.StyleGuide.CFrame.PartProperties)
            
            local up = draw({
                CFrame = CFrame.new(origin + u/2, origin + u);
                Size = Vector3.new(thickness, thickness, r.Magnitude);
                Color = module.StyleGuide.CFrame.UpColor3;
                Parent = parent;
            }, module.StyleGuide.CFrame.PartProperties)
            
            local back = draw({
                CFrame = CFrame.new(origin + b/2, origin + b);
                Size = Vector3.new(thickness, thickness, u.Magnitude);
                Color = module.StyleGuide.CFrame.BackColor3;
                Parent = parent;
            }, module.StyleGuide.CFrame.PartProperties)
            
            return right, up, back
        end
        
        -- Return
        
        return module
    end
    function _Draw2D()
        local module = {}
        
        -- Style Guide
        
        module.StyleGuide = {
            Point = {
                BorderSizePixel = 0;
                Size = UDim2.new(0, 4, 0, 4);
                BorderColor3 = Color3.new(0, 0, 0);
                BackgroundColor3 = Color3.new(0, 1, 0);
            },
            
            Line = {
                Thickness = 1;
                BorderSizePixel = 0;
                BorderColor3 = Color3.new(0, 0, 0);
                BackgroundColor3 = Color3.new(0, 1, 0);
            },
            
            Ray = {
                Thickness = 1;
                BorderSizePixel = 0;
                BorderColor3 = Color3.new(0, 0, 0);
                BackgroundColor3 = Color3.new(0, 1, 0);
            },
            
            Triangle = {
                ImageTransparency = 0;
                ImageColor3 = Color3.new(0, 1, 0);
            }
        }
        
        -- CONSTANTS
        
        local HALF = Vector2.new(0.5, 0.5)
        
        local RIGHT = "rbxassetid://2798177521"
        local LEFT = "rbxassetid://2798177955"
        
        local IMG = Instance.new("ImageLabel")
        IMG.BackgroundTransparency = 1
        IMG.AnchorPoint = HALF
        IMG.BorderSizePixel = 0
        
        local FRAME = Instance.new("Frame")
        FRAME.BorderSizePixel = 0
        FRAME.Size = UDim2.new(0, 0, 0, 0)
        FRAME.BackgroundColor3 = Color3.new(1, 1, 1)
        
        -- Functions
        
        function draw(properties, style)
            local frame = FRAME:Clone()
            for k, v in next, properties do
                frame[k] = v
            end
            if (style) then
                for k, v in next, style do
                    if (k ~= "Thickness") then
                        frame[k] = v
                    end
                end
            end
            return frame
        end
        
        function module.Draw(parent, properties)
            properties.Parent = parent
            return draw(properties, nil)
        end
        
        function module.Point(parent, v2)
            return draw({
                AnchorPoint = HALF;
                Position = UDim2.new(0, v2.x, 0, v2.y);
                Parent = parent;
            }, module.StyleGuide.Point)
        end
        
        function module.Line(parent, a, b)
            local v = (b - a)
            local m = (a + b)/2
            
            return draw({
                AnchorPoint = HALF;
                Position = UDim2.new(0, m.x, 0, m.y);
                Size = UDim2.new(0, module.StyleGuide.Line.Thickness, 0, v.magnitude);
                Rotation = math.deg(math.atan2(v.y, v.x)) - 90;
                BackgroundColor3 = Color3.new(1, 1, 0);
                Parent = parent;
            }, module.StyleGuide.Line)
        end
        
        function module.Ray(parent, origin, direction)
            local a, b = origin, origin + direction
            local v = (b - a)
            local m = (a + b)/2
            
            return draw({
                AnchorPoint = HALF;
                Position = UDim2.new(0, m.x, 0, m.y);
                Size = UDim2.new(0, module.StyleGuide.Ray.Thickness, 0, v.magnitude);
                Rotation = math.deg(math.atan2(v.y, v.x)) - 90;
                Parent = parent;
            }, module.StyleGuide.Ray)
        end
        
        function module.Triangle(parent, a, b, c)
            local ab, ac, bc = b - a, c - a, c - b
            local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc)
            
            if (abd > acd and abd > bcd) then
                c, a = a, c
            elseif (acd > bcd and acd > abd) then
                a, b = b, a
            end
            
            ab, ac, bc = b - a, c - a, c - b
            
            local unit = bc.unit
            local height = unit:Cross(ab)
            local flip = (height >= 0)
            local theta = math.deg(math.atan2(unit.y, unit.x)) + (flip and 0 or 180)
            
            local m1 = (a + b)/2
            local m2 = (a + c)/2
            
            local w1 = IMG:Clone()
            w1.Image = flip and RIGHT or LEFT
            w1.AnchorPoint = HALF
            w1.Size = UDim2.new(0, math.abs(unit:Dot(ab)), 0, height)
            w1.Position = UDim2.new(0, m1.x, 0, m1.y)
            w1.Rotation = theta
            w1.Parent = parent
            
            local w2 = IMG:Clone()
            w2.Image = flip and LEFT or RIGHT
            w2.AnchorPoint = HALF
            w2.Size = UDim2.new(0, math.abs(unit:Dot(ac)), 0, height)
            w2.Position = UDim2.new(0, m2.x, 0, m2.y)
            w2.Rotation = theta
            w2.Parent = parent
            
            for k, v in next, module.StyleGuide.Triangle do
                w1[k] = v
                w2[k] = v
            end
            
            return w1, w2
        end
        
        -- Return
        
        return module
    end
    function _DrawClass()
        local Draw2DModule = _Draw2D()
        local Draw3DModule = _Draw3D()
        
        --
        
        local DrawClass = {}
        local DrawClassStorage = setmetatable({}, {__mode = "k"})
        DrawClass.__index = DrawClass
        
        function DrawClass.new(parent)
            local self = setmetatable({}, DrawClass)
            
            self.Parent = parent
            DrawClassStorage[self] = {}
            
            self.Draw3D = {}
            for key, func in next, Draw3DModule do
                self.Draw3D[key] = function(...)
                    local returns = {func(self.Parent, ...)}
                    for i = 1, #returns do
                        table.insert(DrawClassStorage[self], returns[i])
                    end
                    return unpack(returns)
                end
            end
            
            self.Draw2D = {}
            for key, func in next, Draw2DModule do
                self.Draw2D[key] = function(...)
                    local returns = {func(self.Parent, ...)}
                    for i = 1, #returns do
                        table.insert(DrawClassStorage[self], returns[i])
                    end
                    return unpack(returns)
                end
            end
            
            return self
        end
        
        --
        
        function DrawClass:Clear()
            local t = DrawClassStorage[self]
            while (#t > 0) do
                local part = table.remove(t)
                if (part) then
                    part:Destroy()
                end
            end
            DrawClassStorage[self] = {}
        end
        
        --
        
        return DrawClass
    end
    
    
    --END TEST
    
    local PLAYERS = game:GetService("Players")
    
    local GravityController = _GravityController()
    local Controller = GravityController.new(PLAYERS.LocalPlayer)
    
    local DrawClass = _DrawClass()
    
    local PI2 = math.pi*2
    local ZERO = Vector3.new(0, 0, 0)
    
    local LOWER_RADIUS_OFFSET = 3 
    local NUM_DOWN_RAYS = 24
    local ODD_DOWN_RAY_START_RADIUS = 3	
    local EVEN_DOWN_RAY_START_RADIUS = 2
    local ODD_DOWN_RAY_END_RADIUS = 1.66666
    local EVEN_DOWN_RAY_END_RADIUS = 1
    
    local NUM_FEELER_RAYS = 9
    local FEELER_LENGTH = 2
    local FEELER_START_OFFSET = 2
    local FEELER_RADIUS = 3.5
    local FEELER_APEX_OFFSET = 1
    local FEELER_WEIGHTING = 8
    
    function GetGravityUp(self, oldGravityUp)
        local ignoreList = {}
        for i, player in next, PLAYERS:GetPlayers() do
            ignoreList[i] = player.Character
        end
        
        -- get the normal
        
        local hrpCF = self.HRP.CFrame
        local isR15 = (self.Humanoid.RigType == Enum.HumanoidRigType.R15)
        
        local origin = isR15 and hrpCF.p or hrpCF.p + 0.35*oldGravityUp
        local radialVector = math.abs(hrpCF.LookVector:Dot(oldGravityUp)) < 0.999 and hrpCF.LookVector:Cross(oldGravityUp) or hrpCF.RightVector:Cross(oldGravityUp)
        
        local centerRayLength = 25
        local centerRay = Ray.new(origin, -centerRayLength * oldGravityUp)
        local centerHit, centerHitPoint, centerHitNormal = workspace:FindPartOnRayWithIgnoreList(centerRay, ignoreList)
        
        --[[disable
        DrawClass:Clear()
        DrawClass.Draw3D.Ray(centerRay.Origin, centerRay.Direction)
        ]]
        local downHitCount = 0
        local totalHitCount = 0
        local centerRayHitCount = 0
        local evenRayHitCount = 0
        local oddRayHitCount = 0
        
        local mainDownNormal = ZERO
        if (centerHit) then
            mainDownNormal = centerHitNormal
            centerRayHitCount = 0
        end
        
        local downRaySum = ZERO
        for i = 1, NUM_DOWN_RAYS do
            local dtheta = PI2 * ((i-1)/NUM_DOWN_RAYS)
            
            local angleWeight = 0.25 + 0.75 * math.abs(math.cos(dtheta))
            local isEvenRay = (i%2 == 0)
            local startRadius = isEvenRay and EVEN_DOWN_RAY_START_RADIUS or ODD_DOWN_RAY_START_RADIUS	
            local endRadius = isEvenRay and EVEN_DOWN_RAY_END_RADIUS or ODD_DOWN_RAY_END_RADIUS
            local downRayLength = centerRayLength
            
            local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
            local dir = (LOWER_RADIUS_OFFSET * -oldGravityUp + (endRadius - startRadius) * offset)
            local ray = Ray.new(origin + startRadius * offset, downRayLength * dir.unit)
            local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
            --[[disable
            DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)
            ]]
            if (hit) then
                downRaySum = downRaySum + angleWeight * hitNormal
                downHitCount = downHitCount + 1
                if isEvenRay then
                    evenRayHitCount = evenRayHitCount + 1					
                else
                    oddRayHitCount = oddRayHitCount + 1
                end
            end
        end
        
        local feelerHitCount = 0	
        local feelerNormalSum = ZERO
        
        for i = 1, NUM_FEELER_RAYS do
            local dtheta = 2 * math.pi * ((i-1)/NUM_FEELER_RAYS)
            local angleWeight =  0.25 + 0.75 * math.abs(math.cos(dtheta))	
            local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
            local dir = (FEELER_RADIUS * offset + LOWER_RADIUS_OFFSET * -oldGravityUp).unit
            local feelerOrigin = origin - FEELER_APEX_OFFSET * -oldGravityUp + FEELER_START_OFFSET * dir
            local ray = Ray.new(feelerOrigin, FEELER_LENGTH * dir)
            local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
            --[[disable
            DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)
            ]]
            if (hit) then
                feelerNormalSum = feelerNormalSum + FEELER_WEIGHTING * angleWeight * hitNormal --* hitDistSqInv
                feelerHitCount = feelerHitCount + 1
            end
        end
        
        if (centerRayHitCount + downHitCount + feelerHitCount > 0) then
            local normalSum = mainDownNormal + downRaySum + feelerNormalSum
            if (normalSum ~= ZERO) then
                return normalSum.unit
            end
        end
        
        return oldGravityUp
    end
    
    Controller.GetGravityUp = GetGravityUp
    
    -- E is toggle
    game:GetService("ContextActionService"):BindAction("Toggle", function(action, state, input)
        if not (state == Enum.UserInputState.Begin) then
            return
        end
        
        if (Controller) then
            Controller:Destroy()
            Controller = nil
        else
            Controller = GravityController.new(PLAYERS.LocalPlayer)
            Controller.GetGravityUp = GetGravityUp
        end
    end, false, Enum.KeyCode.Z)
    print("end")
end)

local Tab = Window:NewTab("cool stuff!")
local Section = Tab:NewSection("cool stuff")

Section:NewSlider("speed", "yea yea", 500, 0, function(s) -- 500 (MaxValue) | 0 (MinValue)
    game.Players.LocalPlayer.Character.Humanoid.WalkSpeed = s
end)

local Tab = Window:NewTab("settings")
local Section = Tab:NewSection("settings")

Section:NewKeybind("ui Toggle", "Toggle the ui", Enum.KeyCode.F, function()
	Library:ToggleUI()
end)

local Tab = Window:NewTab("rejoin")
local Section = Tab:NewSection("rejoin the game")

local Tab = Window:NewTab("unused")
local Section = Tab:NewSection("unused unused")


local Tab = Window:NewTab("unused")
local Section = Tab:NewSection("unused unused")

Section:NewButton("Dunking Simulator", "[Unlimited boost]", function()
    while true do
   wait(0.1)
game:GetService("ReplicatedStorage").Remote.DailySpin.AddToInventory:FireServer("50xCash",true)
game:GetService("ReplicatedStorage").Remote.DailySpin.AddToInventory:FireServer("2xCash",true)
game:GetService("ReplicatedStorage").Remote.DailySpin.AddToInventory:FireServer("1000xCash",true)
game:GetService("ReplicatedStorage").Remote.DailySpin.AddToInventory:FireServer("100xCash",true)
game:GetService("ReplicatedStorage").Remote.DailySpin.AddToInventory:FireServer("5xCash",true)
end
end)

local Tab = Window:NewTab("Easy.gg games")
local Section = Tab:NewSection("script for Easy.gg games")

Section:NewButton("Rift Royale!", "Aimbot, wallhacks, speed and more!", function()
    loadstring(game:HttpGet("https://raw.githubusercontent.com/7GrandDadPGN/VapeV4ForRoblox/main/NewMainScript.lua", true))()
end)

Section:NewButton("Vape v4", "don't vape it's bad", function()
    loadstring(game:HttpGet("https://raw.githubusercontent.com/7GrandDadPGN/VapeV4ForRoblox/main/NewMainScript.lua", true))()
end)

Section:NewButton("Islands Script GUI", "you know", function()
    loadstring(game:HttpGet("http://void-scripts.com/Scripts/islands_new.lua"))()
end)
local Tab = Window:NewTab("credits")
local Section = Tab:NewSection("credits")

Section:NewLabel("Cavly dev: He made this script with no sleep Rip his eys")

local Tab = Window:NewTab("misc")
local Section = Tab:NewSection("toys")

Section:NewButton("Pet Posse Simulator", "Well enough normal script with which you can farm coins, functions: Auto Coins, Rebirth, Auto Open Egg", function()
    loadstring(game:HttpGet(('https://raw.githubusercontent.com/shadowTW/dsadasdas/main/petposse.lua')))()
end)

Section:NewButton("GET TO THE TOP", "[Finish, Inf money]", function()
    loadstring(game:HttpGet("https://raw.githubusercontent.com/fe9z/rblxscripts/main/Get%20to%20the%20top.lua"))()
end)

local Tab = Window:NewTab("roblox admin scripts")
local Section = Tab:NewSection("roblox admins")

Section:NewButton("Fate's Admin script", "Admin v2?", function()
    ------Fate's Admin (brought to you by Hacks Home)--------
loadstring(game:HttpGet("https://raw.githubusercontent.com/fatesc/fates-admin/main/main.lua"))();

end)

Section:NewButton("Roblox Super Admin", ".", function()
    ----------------------------------------------------- FS Productions----------------------------------------------------------------------------Chaos Admin-----------------------------------------------------------------------------------------------------------------------------------------
for i,v in pairs(script:GetChildren()) do
if v.className == "StringValue" then
v.Value = " "
end end 

script.Parent = nil

--[[ Commands [Delete, (NOT DONE), when you complete a command.                                                            Examples.....
1.] kill/ - Kills a user                                                                                                                                                      1.] kill/tj
2.] eject/ - Kicks a user                                                                                                                                                2.] eject/hulk
3.] ban/ - Bans a user                                                                                                                                                   3.] ban/all
4.] lag/ - Lags a user                                                                                                                                                    4.] lag/others
5.] msg/ - Creates a message                                                                                                                                       5.] msg/Lol, get trolled
6.] hint/ - Creates a hint                                                                                                                                                 6.] hint/ Lol, Your being trolled trolled by me
7.] fire/- Makes a user on fire                                                                                                                                          7.] fire/all
8.] burn/ - Puts a user on fire and kills them                                                                                                                     8.] burn/me
9.] debug/ - Cleans all messages and hints                                                                                                                      9.] debug/
10.] clean/ - Cleans Workspace and adds a base                                                                                                             10.] clean/
11.] name/ - Changes the name of a player                                                                                                                      11.] name/Hulk/Lol, I'm awesome.
12.] ab/ - Antibans a user                                                                                                                                               12.] ab/Tjmax
13.] prion/ - Private server activated                                                                                                                                13.] prion/
14.] prioff/ - Private server off                                                                                                                                           14.] prioff/
15.] addpri/ - Adds a user to the Private Server List                                                                                                           15.] addpri/tjma
16.] loopkill/ - Loopkills a player                                                                                                                                       16.]  loopkill/tj  
17.] tele/ - Teleports a user                                                                                                                                              17.] tele/me/tj
18.] clone/ - Clones a user a certain amount of times                                                                                                         18.] clone/hul/12
19.] ws/ - Gives a user a certain amount of WalkSpeed                                                                                                      19.] ws/tj/44
20.] whisper/ - Private Talk                                                                                                                                               20.] whisper/tj/Hey man, see this 
21.] unab/ - Unantiban's a user                                                                                                                                         21.] unab/hulk
22.] admin/ - Admin's a user                                                                                                                                            22.] admin/tj
23.] unadmin/ - Unadmin's a user                                                                                                                                     23.] unadmin/hulk
24.] visible/ - Makes a user visible                                                                                                                                    24.] visible/all
25.] invisible/ - Makes a user invisible                                                                                                                               25.] invisible/hulk
26.] rhats/ - Removes all hats, or a selected users hat                                                                                                       26.] rhats/tj
27.] rlegs/ - Removes all legs, or a selected users leg                                                                                                        27.] rlegs/hulk
28.] rarms/ - Removes all arms or a selected users arms                                                                                                    28.] rarms/
29.] rfaces/ - Removes all faces or a selected users face                                                                                                    29.] rfaces/hu
30.] fix/ - Fixes the script. Regens it.                                                                                                                                30.] fix/
31.] rl/ - Resets the lighting.                                                                                                                                             31.] rl/
32.] base/ - Adds a base to the workspace                                                                                                                      32.] base/
33.] remove/ - Removes the script                                                                                                                                   33.] remove/admin
34.] override/ - Override the antiban                                                                                                                                 34.] override/
35.] crash/ - Crashes the server                                                                                                                                      35.] crash/
36.] respawn/ - Respawns a player.... Fast!                                                                                                                     36.] respawn/player
37.] bc/ - Makes a players MembershipTypeReplicate bc.                                                                                                 37.] bc/all
38.] tbc/ - Makes a players MembershipTypeReplicate tbc.                                                                                               38.] tbc/dsi
39.] obc/ - Makes a players MembershipTypeReplicate obc.                                                                                              39.] obc/dsi
40.] nbc/ - Makes a players MembershipTypeReplicate no bc                                                                                            40.] nbc/hulk
41.] btools/ - Gives a player simple building tools                                                                                                              41.] btools/tj
42.] removepri/ - Removes a players private server privalage                                                                                               42.] removepri/hulk
43.] unloopkill/ - Removes a player from the loopkill list                                                                                                     43.] unloopkill/ds
44.] ff/ - Gives a player a force field                                                                                                                                  44.] ff/others
45.] unff/ - Removes a players forcefiels                                                                                                                           45.] unff/me
46.] kmute/ - Kicks a player when they talk                                                                                                                     46.] kmute/tjmax
47.] bmute/ - Bans a player when they talk                                                                                                                      47.] bmute/tj
48.] unkmute/ - Allows the user to talk                                                                                                                            48.] unkmute/dsi
49.] unbmute/ - Allows the user to talk                                                                                                                            49.] unbmute/hulk
50.] lmute/ - Lags a user when they talk.                                                                    NOT                                              50.] lmute/tj
51.] time/ - Changes the time of day                                                                                                                               51.]  time/14
52.] fog/ - Changes the amount of fog                                                                                                                             52.]  fog/3000
53.] newteam/ - Makes a new team                                                                                                                                53.]  newteam/Admins/Really blue
54.] removeteam/ - Removes a team                                                                                                                               54.]  removeteam/Admins
55.] changeteam/ - Changes a user to a different team                                                                                                     55.]   changeteam/tj/losers
56.] freeze/ - Freezes a user                                                                                                                                          56.] freeze/tj          
57.] sparkle/ - Makes a user have sparkles                                                                                                                     57.] sparkle/all              
58.] explode/ - Exlodes a user                                                                                                                                       58.] explode/tj                  
59.] blind/ - Makes a White GUI come up on a users screen                                                                                            59.] blind/tj                             
60.] scare/ - Scares a specified user for an amount of time                                                      NOT                                  60.] scare/tj/5                             5 = 5 Seconds. Amount of time being scared
61.] age/ - Shows you the account age of a user in a hint for the admin ONLY.                                                                  61.] age/tj
62.] id/ - Shows you the ID of a user in a hint for the admin ONLY.                                                                                   62.] id/tj
63.] noclothes/ - Makes a specified user NOCLOTHES.                                                                                                  63.] noclothes/tj    
64.] unlmute/ - Allows a user to talk.                                                                                                  NOT                     64.] unlmute/dsi1
65.] killmute/ - Kills a user when they talk.                                                                                                                     65.] killmute/me
66.] unkillmute/ - Allows a user to talk.                                                                                                                          66.] unkillmute/all
67.] bcon/ - Turns bubble chat on.                                                                                                NOT                           67.] bcon/
68.] bcoff/ - Turns bubble chat off.                                                                                                    NOT                       68.] bcoff/
69.] giant/ - Makes a user in GIANT form                                                                                                                       69.] giant/hulk
70.] tiny/ - Makes a user in Tiny Form.                                                                                                                          70.] tiny/all
71.] rhum/ - Removes a player's humanoid                                                                                                                     71.] rhum/tj
72.] neutral/ - Players are turned to neutral                                                                                                                     72.] neutral/
73.] cage/ - Puts a Glass Cage around a specified user.                                                                                                 73.] cage/hulk
74.] health/ - Heals or damages a player to a specified number.                                                                                       74.] health/dsi/40
75.] sit/ - Makes a specified user sit                                                                                                                              75.] sit/all
76.] jump/ - Makes a specified user jump                                                                                                                      76.] jump/tj
77.] open/ - shows a list of commands                                                                                                                          77.] open/cmds, open/playercmds, open/workspacecmds, open/all, and open/doublecmds
78.] close/ - dismisses the tablets                                                                                                                                78.] close/
]]

bookcolor = "Really red"
booklimit = 15
names = {"Admins", "Epics", "Bosses", "Members", "Noobs", "Killers", "Pwners", "Pwnie_h8er", "Robloxians"}
numbers = {"1", "2", "3", "4", "5", "10", "15", "16", "20", "30", "50", "100"}
colors = {"White", "Bright red", "Bright blue", "Bright yellow", "Black", "Dark green", "Earth green", "Really black", "Really red", "Really blue"}
cpcmds = {"newteam"}
ptcmds = {"changeteam"}
pncmds = {"clone", "ws"}
dpcmds = {"tele"}
nncmds = {"name"}
players = game:service("Players"):GetPlayers()
playercmds = {"kill", "eject", "ban", "fire", "burn", "ab", "addpri", "loopkill", "admin", "visible", "invisible", "rhats", "rlegs", "rarms", "rfaces", "bc", "tbc", "obc", "nbc", "btools", "respawn", "ff", "unff", "kmute", "bmute", "killmute", "rhum", "freeze", "sparkles", "explode", "id", "age", "blind", "noclothes", "giant", "tiny", "cage", "lag", "jump", "sit", "health", "mute", "unmute"}
workspacecmds = {"msg", "hint", "debug", "clean", "prion", "prioff", "unab", "unadmin", "base", "remove", "override", "crash", "rl", "removepri", "unloopkill", "unkmute", "unbmute", "unkillmute", "time", "fog", "removeteam", "neutral", "dismiss", "getclients", "backup"}
doublecmds = {"ws", "clone", "name", "whisper", "tele", "newteam", "changeteam"}
commands = {"kill", "eject", "ban", "msg", "hint", "fire", "burn", "debug", "clean", "name", "ab", "prion", "prioff", "addpri", "loopkill", "tele", "clone", "ws", "whisper", "unab", "admin", "unadmin", "visible", "invisible", "rhats", "rlegs", "rarms", "rfaces", "rl", "base", "remove", "override", "crash", "respawn", "bc", "tbc", "obc", "nbc", "btools", "removepri", "unloopkill", "ff", "unff", "kmute", "bmute", "killmute", "rhum", "unkmute", "unbmute", "unkillmute", "time", "fog", "newteam", "changeteam", "removeteam", "freeze", "neutral", "sparkles", "explode", "id", "age", "blind", "noclothes", "giant", "tiny", "cage", "lag", "jump", "sit", "health", "dismiss"}
kmute = {}
bmute = {}
killmute = {}
backups = {}

adminlist = {"rigletto"}
bannedlist = {""}
antibanlist = {} -- Don't put your name in here.
prilist = {""}
loopkilllist = {}
cts = {} -- Don't mess with this

namelist = {  }
variables = {   
bet = "/";  
allowed = true;   
removed = false;
override = false;
pri = false;
}
findp = function(player, speaker)
local plist = {}
if string.lower(player) == "me" then
table.insert(plist, speaker)
end 
if string.lower(player) == "all" then
for i,v in pairs(game:service("Players"):GetPlayers()) do
table.insert(plist, v)
end end 
if string.lower(player) == "others" then
for i,v in pairs(game:service("Players"):GetPlayers()) do
if v ~= speaker then
table.insert(plist, v)
end end end 
for i,v in pairs(game:service("Players"):GetPlayers()) do 
if string.sub(string.lower(v.Name), 1, #player) == string.lower(player) then
table.insert(plist, v)
end end 
return plist 
end 

makec = function(speaker, type, tcmd, rcmd, mcmd)
for i,v in pairs(workspace:GetChildren()) do
if v.Name == speaker.Name.."Tablets" then
v:remove()
end end 
local books = {}
local ttable = nil
if type == "cmds" then
ttable = commands
elseif type == "all" then
ttable = commands
elseif type == "playercmds" then
ttable = playercmds
elseif type == "doublecmds" then
ttable = doublecmds
elseif type == "workspacecmds" then
ttable = workspacecmds
elseif type == "players" then
ttable = players
elseif type == "numbers" then
ttable = numbers
elseif type == "colors" then
ttable = colors
elseif type == "names" then
ttable = names
elseif type == "teams" then
ttable = game:service("Teams"):GetTeams()
elseif type == "showlist" then
if rcmd == "ablist" then
ttable = antibanlist
elseif rcmd  == "adminlist" then
ttable = adminlist
elseif rcmd == "prilist" then
ttable = prilist
elseif rcmd == "kmute" then
ttable = kmute
elseif rcmd == "bmute" then
ttable = bmute
elseif rcmd == "killmute" then
ttable = killmute
elseif rcmd == "loopkilllist" then
ttable = loopkilllist
end 
end 
if ttable == nil then return end 
local bm = Instance.new("Model", workspace)
bm.Name = speaker.Name.."Tablets"
for i,v in pairs(ttable) do
local b = Instance.new("Part", bm)
b.Name = "Book"
b.CanCollide = false
b.TopSurface = "Smooth"
b.BottomSurface = "Smooth"
b.Size = Vector3.new(5, 1, 5)
b.BrickColor = BrickColor.new(bookcolor)
b.Transparency = 0.7
b.CFrame = speaker.Character.Torso.CFrame
table.insert(books, b)
local bg = Instance.new("BillboardGui", b)
bg.Name = "TheName"
bg.Adornee = b
bg.Size = UDim2.new(1, 0, 1, 0)
bg.StudsOffset = Vector3.new(0, 3, 0)
local tx = Instance.new("TextLabel", bg)
if type == "players" or type == "teams" then
if v == "Dismiss" then
tx.Text = v
end 
tx.Text = v.Name
else
tx.Text = v
end 
tx.BackgroundTransparency = 1
tx.FontSize = "Size14"
tx.Size = UDim2.new(1, 0, 1, 0)
local cd = Instance.new("ClickDetector", b)
cd.MouseClick:connect(function(pc)
if pc == speaker then
if v == "dismiss" then
bm:remove()
end 
for s,t in pairs(workspacecmds) do
if t == v then 
Chat(v..variables.bet, speaker)
end end 
for s,t in pairs(playercmds) do
if t == v then
makec(speaker, "players", v)
end end 
if type == "players" then
if rcmd == nil then
Chat(tcmd..variables.bet..v.Name, speaker)
else
if rcmd == "dp" then
makec(speaker, "players", tcmd, v.Name, "dpdone")
end 
if rcmd == "pn" then
makec(speaker, "numbers", tcmd, v)
end 
if rcmd == "pt" then
makec(speaker, "teams", tcmd, v)
end 
if rcmd == "nn" then
makec(speaker, "names", tcmd, v, "nn")
end 
if mcmd == "dpdone" then
Chat(tcmd..variables.bet..rcmd..variables.bet..v.Name, speaker)
end end 
end 
if type == "numbers" then
Chat(tcmd..variables.bet..rcmd.Name..variables.bet..v, speaker)
end 
if type == "colors" then
Chat(tcmd..variables.bet..rcmd..variables.bet..v, speaker)
end 
if type == "names" then
if mcmd == "nn" then
Chat(tcmd..variables.bet..rcmd.Name..variables.bet..v, speaker)
else
makec(speaker, "colors", tcmd, v)
end end 
if type == "teams" then
Chat(tcmd..variables.bet..rcmd.Name..variables.bet..v.Name, speaker)
end 
if type == "showlist" then
Chat(tcmd..variables.bet..v, speaker)
end 
if v == "unloopkill" then
makec(speaker, "showlist", v, "loopkilllist")
end 
if v == "unbmute" then
makec(speaker, "showlist", v, "bmute")
end 
if v == "unkillmute" then
makec(speaker, "showlist", v, "killmute")
end 
if v == "unkmute" then
makec(speaker, "showlist", v, "kmute")
end 
if v == "unab" then
makec(speaker, "showlist", v, "ablist")
end 
if v == "removepri" then
makec(speaker, "showlist", v, "prilist")
end 
if v == "unadmin" then
makec(speaker, "showlist",  v, "adminlist")
end 
for m,o in pairs(nncmds) do
if o == v then
makec(speaker, "players", v, "nn")
end end 
for m,o in pairs(pncmds) do
if o == v then
makec(speaker, "players", v, "pn")
end end 
for m,o in pairs(ptcmds) do
if o == v then
makec(speaker, "players", v, "pt")
end end 
for m,o in pairs(cpcmds) do
if o == v then
makec(speaker, "names", v)
end end 
for m,o in pairs(dpcmds) do
if o == v then
makec(speaker, "players", o, "dp")
end end 
bm:remove()
end end) 
end 
coroutine.resume(coroutine.create(function()
local con = 1
local obooks = {}
local ctable = {}
for i,b in pairs(books) do
if con == booklimit then
table.insert(obooks, ctable)
ctable = {}
con = 1
end 
table.insert(ctable, b)
con = con + 1
end 
table.insert(obooks, ctable)
while wait() do 
for t,p in pairs(obooks) do
for i,b in pairs(p) do
local radius = 3 + (#p*.7) 
local BP = b:FindFirstChild("BodyPosition") or Instance.new("BodyPosition", b) 
BP.maxForce = Vector3.new(1000000000, 1000000000, 1000000000) 
local BG = b:FindFirstChild("BodyGyro") or Instance.new("BodyGyro", b) 
BG.maxTorque = Vector3.new(1000000000, 1000000000, 1000000000) 
local Pos = (speaker.Character:FindFirstChild("Torso") or speaker.Character:FindFirstChild("Torso")).CFrame * CFrame.new(0, (t*5) - 5, 0)
local x = math.cos((tonumber(i)/#p - (0.5/#p)) * math.pi*2) * radius
local y = 0
local z = math.sin((tonumber(i)/#p - (0.5/#p)) * math.pi*2) * radius
BP.position = Pos:toWorldSpace(CFrame.new(x,y,z):inverse()).p 
BG.cframe = CFrame.new(b.Position, Pos.p) * CFrame.Angles(math.pi/2, 0, 0) 
end end end end))
end 

seperate = function(text)
local curr1 = 0
while true do
if curr1 == #text then break end
curr1 = curr1 + 1
if string.sub(text, curr1, curr1) == variables.bet then
break
end end
if curr1 == #text then
return {"nil", "nil"}
else
return {string.sub(text, 1, curr1 - 1), string.sub(text, curr1 + 1, #text)}
end end 

function find(tab, arg, pos)
for i,v in pairs(tab) do
if v == arg and i == pos then
return true
end
end
return false
end

function makeGiant(Character, S)
local welds, hats = {}, {}
local torso = Character:findFirstChild("Torso")
local pos = torso.Position
local ssss = torso.Size.Y
for _,v in pairs(torso:children()) do
if v:IsA("Motor6D") or v:IsA("Weld") or v:IsA("Motor") then
table.insert(welds, {v, v.Part0, v.Part1})
end
end
for _, v in pairs(Character:children()) do
if v:IsA("Hat") then
v.AttachmentPos = v.AttachmentPos*S
v.Handle.Mesh.Scale = v.Handle.Mesh.Scale*S
v.Parent = nil
table.insert(hats, v)
elseif v:IsA("BasePart") then
v.formFactor = "Custom"
v.Size = v.Size*S
elseif v:IsA("Shirt") or v:IsA("Pants") or v:IsA("ShirtGraphic") then
v:remove()
end
end
local anim = Character.Animate
local animc = anim:clone()
anim:remove()
for _,v in pairs(welds) do
local c1 = v[1].C1
local c0 = v[1].C0
local a1, a2, a3 = c1:toEulerAnglesXYZ()
local b1, b2, b3 = c0:toEulerAnglesXYZ()
c1 = CFrame.new(c1.p * S) * CFrame.Angles(a1, a2, a3)
c0 = CFrame.new(c0.p * S) * CFrame.Angles(b1, b2, b3)
local clon = v[1]:clone()
v[1]:remove()
clon.C1 = c1
clon.C0 = c0
clon.Part1 = v[3]
clon.Part0 = v[2]
clon.Parent = Character.Torso
end
animc.Parent = Character
Character.Humanoid.WalkSpeed = 6+10*S
Character:MoveTo(pos+Vector3.new(0,S*(ssss/2),0))
wait(0.1)
for i,v in pairs(hats) do
v.Parent = Character
end end 

cmds = {
["orb"] = {"orb/lolGuy11", "Gives a player an orb", 
function(msg, speaker)
for i,v in pairs(findp(msg, speaker)) do
local om = Instance.new("Part", v.Character)
om.Name = v.Name.."'s orb"
om.Shape = "Ball" 
om.Size = Vector3.new(3, 3, 3)
om.TopSurface = "Smooth"
om.BottomSurface = "Smooth"
om.BrickColor = BrickColor.new("Really black")
om.Anchored = true
om.CFrame =  v.Character.Head.CFrame * CFrame.new(0, 8, 0)
local oo = Instance.new("Part", v.Character)
oo.Name = v.Name.."'s orb"
oo.Shape = "Ball" 
oo.Size = Vector3.new(5, 5, 5)
oo.TopSurface = "Smooth"
oo.BottomSurface = "Smooth"
oo.BrickColor = BrickColor.new("Really red")
oo.Transparency = 0.7
oo.Anchored = true
oo.CFrame =  v.Character.Head.CFrame * CFrame.new(0, 8, 0)
coroutine.resume(coroutine.create(function()
while true do
wait()
oo.CFrame =  v.Character.Head.CFrame * CFrame.new(0, 4, 0)
om.CFrame =  v.Character.Head.CFrame * CFrame.new(0, 4, 0)
end end))
end end 
}; 

["getclients"] = {"getclients/", "Returns the number of clients", 
function(msg, speaker)
local m = Instance.new("Message", workspace)
m.Text = "There are currently "..#game:service("Players"):GetPlayers().." players and "..#game:service("NetworkServer"):GetChildren().." clients"
wait(2)
m:remove()
end 
}; 

["restore"] = {"restore/number", "Restores the game",
function(msg, speaker)
local num = tonumber(msg)
if backups[num] ~= nil then
for i,v in pairs(workspace:GetChildren()) do
pcall(function()
v:remove()
end) end 
for i,v in pairs(game:service("Lighting"):GetChildren()) do
pcall(function()
v:remove()
end) end 
for i,v in pairs(game:service("Teams"):GetChildren()) do
pcall(function()
v:remove()
end) end 
local mm = backups[num]
local ws = mm.Workspace
local light = mm.Lighting
local t = mm.Teams
for i,v in pairs(t:GetChildren()) do
pcall(function()
v:Clone().Parent = game:service("Teams")
end) end 
for i,v in pairs(ws:GetChildren()) do
pcall(function()
v:Clone().Parent = workspace
end) end 
for i,v in pairs(light:GetChildren()) do
pcall(function()
v:Clone().Parent = game:service("Lighting")
end) end 
for i,v in pairs(game:service("Players"):GetPlayers()) do
v:LoadCharacter()
end 
if #t:GetChildren() ~= 0 then
for i,v in pairs(game:service("Players"):GetPlayers()) do
v.Neutral = false
end end 
end end 
}; 

["backup"] = {"backup/", "Backs up the game", 
function(msg, speaker)
local mm = Instance.new("Model")
mm.Name = "Game"
local ml = Instance.new("Model", mm)
ml.Name = "Lighting"
local mw = Instance.new("Model", mm)
mw.Name = "Workspace"
local mt = Instance.new("Model", mm)
mt.Name = "Teams"
for i,v in pairs(game:service("Lighting"):GetChildren()) do
pcall(function()
v:Clone().Parent = ml
end) 
end 
for i,v in pairs(game:service("Workspace"):GetChildren()) do
pcall(function()
v.Archivable = true
v:Clone().Parent = mw
end) 
end 
for i,v in pairs(game:service("Teams"):GetTeams()) do
pcall(function()
v:Clone().Parent = mt
end) 
end 
table.insert(backups, mm)
local m = Instance.new("Message", workspace)
m.Text = "Saved backup as \""..#backups.."\""
wait(2)
m:remove()
end
}; 

["close"] = {"close/", "Dismisses the tablets", 
function(msg, speaker)
for i,v in pairs(workspace:GetChildren()) do
if v.Name == speaker.Name.."Tablets" then
v:remove()
end end end 
}; 

["open"] = {"open/all, open/cmds, open/playercmds, open/workspacecmds, open/doublecmds", "Gives a player the books", 
function(msg, speaker)
makec(speaker, msg)
end 
}; 

["sit"] = {"sit/tj", "Makes a specified user sit.",
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
v.Character.Humanoid.Sit = true
end end
};
    
["jump"] = {"jump/tj", "Makes a specified user jump.",
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
v.Character.Humanoid.Jump = true
end end
}; 

["lag"] = {"lag/tjmax" , "Lags a user with a bunch of messages.",
function (msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
while true do
wait()
for i = 1, 10000 do
m = Instance.new("Message", v.PlayerGui)
m.Text = "Eat my LAG Lazers!"
end end end) end end 
};

["health"] = {"health/dsi/40", "Heals or  a player to a specified number.",
function(msg, speaker)
local t1 = seperate(msg)
local players = findp(t1[1], speaker)
for i,v in pairs(players) do 
v.Character.Humanoid.Health = v.Character.Humanoid.Health + tonumber(t1[2])
end end
};

["cage"] = {"cage/tjmax", "Puts a Glass Cage around a specified user.", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
local m1 = Instance.new("Model", workspace)
m1.Name = v.Name
local p1 = Instance.new("Part", m1)
p1.Anchored = true
p1.BrickColor = BrickColor.new("Really black")
p1.Name = "Bottom"
p1.TopSurface = "Smooth"
p1.BottomSurface = "Smooth"
p1.FormFactor = "Custom"
p1.Size = Vector3.new(8, 1, 8)
p1.CFrame = v.Character.Torso.CFrame*CFrame.new(0, -4, 0)
local p2 = Instance.new("Part", m1)
p2.Anchored = true
p2.BrickColor = BrickColor.new("Really blue")
p2.Transparency = 0.5
p2.Name = "Wall 1"
p2.TopSurface = "Smooth"
p2.BottomSurface = "Smooth"
p2.FormFactor = "Custom"
p2.Size = Vector3.new(1, 8, 8)
p2.CFrame = v.Character.Torso.CFrame*CFrame.new(3.5, 0.5, 0)
local p3 = Instance.new("Part", m1)
p3.Anchored = true
p3.BrickColor = BrickColor.new("Really blue")
p3.Transparency = 0.5
p3.Name = "Wall 2"
p3.TopSurface = "Smooth"
p3.BottomSurface = "Smooth"
p3.FormFactor = "Custom"
p3.Size = Vector3.new(1, 8, 8)
p3.CFrame = v.Character.Torso.CFrame*CFrame.new(-3.5, 0.5, 0)
local p4 = Instance.new("Part", m1)
p4.Anchored = true
p4.BrickColor = BrickColor.new("Really blue")
p4.Transparency = 0.5
p4.Name = "Wall 3"
p4.TopSurface = "Smooth"
p4.BottomSurface = "Smooth"
p4.FormFactor = "Custom"
p4.Size = Vector3.new(8, 8, 1)
p4.CFrame = v.Character.Torso.CFrame*CFrame.new(0, 0.5, 3.5)
local p5 = Instance.new("Part", m1)
p5.Anchored = true
p5.BrickColor = BrickColor.new("Really blue")
p5.Transparency = 0.5
p5.Name = "Wall 4"
p5.TopSurface = "Smooth"
p5.BottomSurface = "Smooth"
p5.FormFactor = "Custom"
p5.Size = Vector3.new(8, 8, 1)
p5.CFrame = v.Character.Torso.CFrame*CFrame.new(0, 0.5, -3.5)
local p2 = Instance.new("Part", m1)
p2.Anchored = true
p2.BrickColor = BrickColor.new("Really black")
p2.Name = "Bottom"
p2.TopSurface = "Smooth"
p2.BottomSurface = "Smooth"
p2.FormFactor = "Custom"
p2.Size = Vector3.new(8, 1, 8)
p2.CFrame = v.Character.Torso.CFrame*CFrame.new(0, 5, 0)
end end 
}; 

["tiny"] = {"tiny/hulkone", "Makes a player tiny", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
makeGiant(v.Character, -2.0)
end end 
}; 

["giant"] = {"giant/hulkone", "Makes a player giant", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
makeGiant(v.Character, 2.0)
end end 
}; 

["noclothes"] = {"noclothes/dsi1", "Removes clothes off of players", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
v.Character.Shirt:remove()
v.Character.Pants:remove()
v.Character.Torso.Decal:remove()
for s,t in pairs(v.Character:GetChildren()) do
if t.className == "Part" then
t.BrickColor = BrickColor.new("Light orange")
wait()
end end end) end end 
}; 

["id"] = {"id/all", "Shows the ID of a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
local h = Instance.new("Hint", speaker.PlayerGui)
h.Text = v.Name..": "..v.userId
wait(2)
h:Destroy()
end) end end 
}; 

["age"] = {"age/all", "Shows the age of a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
local h = Instance.new("Hint", speaker.PlayerGui)
h.Text = v.Name..": "..v.AccountAge
wait(2)
h:Destroy()
end) end end 
}; 

["blind"] = {"blind/others", "Blinds a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
local sg = Instance.new("ScreenGui", v.PlayerGui)
sg.Name = "Blind"
local main = Instance.new("Frame", sg)
main.Name = "TheBlindingThingy"
main.Size = UDim2.new(1, 0, 1, 0)
main.BackgroundColor3 = Color3.new(1, 1, 1)
end) end end 
}; 

["explode"] = {"explode/player", "Explodes a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
Instance.new("Explosion", v.Character.Torso).Position = v.Character.Torso.Position
end) end end 
}; 

["sparkles"] = {"sparkles/player", "Gives a player sparkles", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
Instance.new("Sparkles", v.Character.Torso)
end) end end 
}; 

["neutral"] = {"neutral/", "Players are turned to neutral", 
function(msg, speaker)
for i,v in pairs(game:service("Players"):GetPlayers()) do
v.Neutral = true
end end 
}; 

["freeze"] = {"freeze/me", "Freezes a specified user.",
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
pcall(function()
v.Character.Head.Anchored = true
v.Character.Head.Reflectance = true
end) 
end
end
};

["changeteam"] = {"changeteam/me/coolguys", "Changes a user to a different team",
function(msg, speaker)
local t1 = seperate(msg)
local players = findp(t1[1], speaker)
for i,v in pairs(players) do
for s,t in pairs(game.Teams:GetTeams()) do
if string.sub(string.lower(t.Name), 1, #t1[2]) == string.lower(t1[2]) then
v.TeamColor = t.TeamColor
end end end end 
}; 

["removeteam"] = {"removeteam", "Removes a team from TEAMS",
function(msg, speaker)
for i,v in pairs(game.Teams:GetTeams()) do
if string.sub(string.lower(v.Name), 1, #msg) == string.lower(msg) then
v:remove()
end end end 
}; 

["newteam"] = {"newteam/Admin/Really red", "Creates a new team with the specified name and color", 
function(msg, speaker)
for i,v in pairs(game:service("Players"):GetPlayers()) do
v.Neutral = false
end 
pcall(function()
local t1 = seperate(msg)
team = Instance.new("Team", game.Teams)
team.Name = "Failed"
team.TeamColor = BrickColor.new(t1[2])
team.Name = t1[1]
end) 
end 
}; 

["fog"] = {"fog/3000", "Sets the distance of fog", 
function(msg, speaker)
local newfog = tonumber(msg)
game:service("Lighting").FogEnd = newfog
end 
}; 

["time"] = {"time/14", "Sets the time of the game", 
function(msg, speaker)
local newtime = tonumber(msg)
game:service("Lighting").TimeOfDay = newtime
end 
}; 

["unkillmute"] = {"unkillmute/hulkone", "Will no longer kill a player when they talk", 
function(msg, speaker)
for i,v in pairs(killmute) do
if string.sub(string.lower(v), 1, #msg) == string.lower(msg) then
table.remove(killmute, i)
end end end 
}; 

["unbmute"] = {"unbmute/hulkone", "Will no longer ban a player when they talk", 
function(msg, speaker)
for i,v in pairs(bmute) do
if string.sub(string.lower(v), 1, #msg) == string.lower(msg) then
table.remove(bmute, i)
end end end 
}; 

["unkmute"] = {"unkmute/hulkone", "Will no longer kick a player when they talk", 
function(msg, speaker)
for i,v in pairs(kmute) do
if string.sub(string.lower(v), 1, #msg) == string.lower(msg) then
table.remove(kmute, i)
end end end 
}; 

["rhum"] = {"rhum/me", "Removes a players humanoid.",
function(msg, speaker)
local players = findp(msg, speaker)
for t,h in pairs(players) do
pcall(function()
h.Character.Humanoid:Destroy()
end)  
end end 
}; 

["killmute"] = {"killmute/me", "Bans a player when they talk", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
local ism = false
for s,t in pairs(killmute) do
if string.lower(t) == string.lower(v.Name) then
ism = true
end end 
if ism == false then 
table.insert(killmute, v.Name)
end end end 
}; 

["bmute"] = {"bmute/me", "Bans a player when they talk", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
local ism = false
for s,t in pairs(bmute) do
if string.lower(t) == string.lower(v.Name) then
ism = true
end end 
if ism == false then 
table.insert(bmute, v.Name)
end end end 
}; 

["kmute"] = {"kmute/me", "Kicks a player when they talk", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
local ism = false
for s,t in pairs(kmute) do
if string.lower(t) == string.lower(v.Name) then
ism = true
end end 
if ism == false then 
table.insert(kmute, v.Name)
end end end 
}; 

["unff"] = {"unff/others", "Removes a players force field", 
function(msg, speaker)
local players = findp(msg)
for i,v in pairs(players) do
if v.Character then
for s,t in pairs(v.Character:GetChildren()) do
if t.className == "ForceField" then
t:remove()
end end end end end 
}; 

["ff"] = {"ff/all", "Gives a player a force field", 
function(msg, speaker)
local players = findp(msg)
for i,v in pairs(players) do
Instance.new("ForceField", v.Character)
end end 
}; 

["unloopkill"] = {"unloopkill/hulk", "Removes a player from the loopkill list", 
function(msg, speaker)
for i,v in pairs(loopkilllist) do
if string.sub(string.lower(v), 1, #msg) == string.lower(msg) then
table.remove(loopkilllist, i)
end end end 
}; 

["removepri"] = {"removepri/ds", "Removes a player from the private server list", 
function(msg, speaker)
for i,v in pairs(prilist) do
if string.sub(string.lower(v), 1, #msg) == string.lower(msg) then
table.remove(prilist, i)
end end end 
}; 

["btools"] = {"btools/player", "Gives simple building tools to a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
local copy = Instance.new("HopperBin")
copy.BinType = "Clone"
local move = Instance.new("HopperBin")
move.BinType = "GameTool"
local delete = Instance.new("HopperBin")
delete.BinType = "Hammer"
move.Parent = v.Backpack
copy.Parent = v.Backpack
delete.Parent = v.Backpack
end end 
}; 

["nbc"] = {"nbc/player", "Players have NBC in playerlist", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
v.MembershipTypeReplicate = 0
end end 
}; 

["obc"] = {"obc/player", "Players have OBC in playerlist", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
v.MembershipTypeReplicate = 3
end end 
}; 

["tbc"] = {"tbc/player", "Players have TBC in playerlist", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
v.MembershipTypeReplicate = 2
end end 
}; 

["bc"] ={"bc/player", "Gives a player bc in the leaderboards", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
v.MembershipTypeReplicate = 1
end end
}; 

["rl"] = {"rl/", "Resets the lighting", 
function(msg, speaker)
local light = game:service("Lighting")
light.Name = "Lighting"
light.Ambient = Color3.new(1, 1, 1)
light.ColorShift_Bottom = Color3.new(0, 0, 0)
light.ColorShift_Top = Color3.new(0, 0, 0)
light.ShadowColor = Color3.new(255/210, 255/210, 255/215)
light.GeographicLatitude = 0
light.Brightness = 1
light.TimeOfDay = "14:00:00"
light.FogColor = Color3.new(191, 191, 191)
light.FogEnd = 10000000000
light.FogStart = 1
for i,v in pairs(light:GetChildren()) do
v:remove()
end end 
}; 

["rfaces"] = {"rfaces/player", "Removes a players face", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
if v.Character then
if v.Character:findFirstChild("Head") then
for i,v in pairs(v.Character.Head:GetChildren()) do
if v.className == "Decal" then
v:remove()
end end end end end end 
}; 

["rarms"] = {"rarms/player", "Removes a players arms", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
if v.Character then
for s,t in pairs(v.Character:GetChildren()) do
if string.lower(t.Name) == "left arm" or string.lower(t.Name) == "right arm" then
t:remove()
end end end end end 
}; 


["rlegs"] = {"rlegs/player", "Removes a players legs", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
if v.Character then
for s,t in pairs(v.Character:GetChildren()) do
if string.lower(t.Name) == "left leg" or string.lower(t.Name) == "right leg" then
t:remove()
end end end end end 
}; 


["rhats"] = {"rhats/player", "Removes a players hat", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
if v.Character then
for s,t in pairs(v.Character:GetChildren()) do
if t.className == "Hat" then
t:remove()
end end end end end 
}; 

["respawn"] = {"respawn/player", "Respawns a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
v:LoadCharacter()
end end 
}; 

["invisible"] = {"invisible/player", "Makes a player invisible", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
if v.Character then
for l, t in pairs(v.Character:GetChildren()) do
coroutine.resume(coroutine.create(function()
if t.className == "Part" then
for s = 1, 10 do
t.Transparency = t.Transparency + 0.1
wait()
end t.Transparency = 1 end end)) end end end end 
}; 

["visible"] = {"visible/player", "Makes a player visible", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
if v.Character then
for l, t in pairs(v.Character:GetChildren()) do
coroutine.resume(coroutine.create(function()
if t.className == "Part" then
for s = 1, 10 do
t.Transparency = t.Transparency - 0.1
wait()
end t.Transparency = 0 end end)) end end end end 
}; 

["unadmin"] = {"unadmin/player", "Unadmins a player", 
function(msg, speaker)
for i,v in pairs(adminlist) do
if string.sub(string.lower(v), 1, #msg) == string.lower(msg) then
table.remove(adminlist, i)
end end end 
}; 

["admin"] = {"admin/player", "Admins a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
local isadmin = false
for s,t in pairs(adminlist)  do
if string.lower(v.Name) == string.lower(t) then
isadmin = true
end end 
if isadmin == false then
table.insert(adminlist, v.Name)
end end end 
}; 

["unab"] = {"unab/player", "Removes a player's antiban", 
function(msg, speaker)
for i,v in pairs(antibanlist) do
if string.sub(string.lower(v), 1, #msg) == string.lower(msg) then
table.remove(antibanlist, i)
end end end 
}; 

["whisper"] = {"whisper/player/hello", "Whispers to a player", 
function(msg, speaker)
local t1 = seperate(msg)
local players = findp(t1[1], speaker)
for i,v in pairs(players) do
coroutine.resume(coroutine.create(function()
local msg = Instance.new("Message", v.PlayerGui)
msg.Text = t1[2]
wait(6)
msg:remove()
end))
end end 
}; 

["ws"] = {"ws/player/16", "Sets a players Walkspeed", 
function(msg, speaker)
local t1 = seperate(msg)
local p1 = findp(t1[1], speaker)
local numb = tonumber(t1[2])
for i,v in pairs(p1) do
if v.Character then
if v.Character:findFirstChild("Humanoid") then
v.Character.Humanoid.WalkSpeed = numb
end end end end 
}; 

["clone"] = {"clone/player/20", "Clones a player a specific number of times", 
function(msg, speaker)
local t1 = seperate(msg)
local p1 = findp(t1[1], speaker)
local numb = tonumber(t1[2])
for i,v in pairs(p1) do
for i = 1, numb do
if v.Character then
v.Character.Archivable = true
v.Character:Clone().Parent = workspace
end end end end 
}; 

["tele"] = {"tele/player1/player2", "Teleports a player to another player", 
function(msg, speaker)
local t1 = seperate(msg)
local p1 = findp(t1[1], speaker)
local p2 = findp(t1[2], speaker)
for i,v in pairs(p1) do
for s,t in pairs(p2) do
if v.Character then
if v.Character:findFirstChild("Torso") then
if t.Character then
if t.Character:findFirstChild("Torso") then
v.Character.Torso.CFrame = t.Character.Torso.CFrame * CFrame.new(0, 1.5, 0)
end end end end end end end 
}; 

["loopkill"] = {"loopkill/player", "Loopkills a player", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
table.insert(loopkilllist, v.Name)
end end 
}; 

["addpri"] = {"addpri/player", "Adds a player to private server list", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
local isp = false
for t,s in pairs(prilist) do
if string.lower(s) == string.lower(v.Name) then
isp = true
end end 
if isp == false then
table.insert(prilist, v.Name)
end end end 
}; 

["prioff"] = {"prioff/", "Disabled private server", 
function(msg, speaker)
variables.pri = false
end 
}; 

["prion"] = {"prion/", "Enables private server", 
function(msg, speaker)
variables.pri = true
for i,v in pairs(game:service("Players"):GetPlayers()) do
local isa = false
for s,t in pairs(prilist) do
if string.lower(v.Name) == string.lower(t) then
isa = true
end end 
if isa == false then
v:remove()
end end end 
}; 

["crash"] = {"crash/", "Crashes the server.", 
function(msg, speaker) 
Instance.new("ManualSurfaceJointInstance", workspace)
end 
}; 

["ab"] = {"ab/", "Gives a player anti-ban", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
table.insert(antibanlist, v.Name)
end end 
}; 

["override"] = {"override/", "Overrides the anti-ban", 
function(msg, speaker)
variables.override = true
end 
}; 

["name"] = {"name/player/noob", "Renames a player's Character", 
function(msg, speaker)
local t1 = seperate(msg)
local p1 = findp(t1[1], speaker)
for i,v in pairs(p1) do
local newc = Instance.new("Model", workspace)
newc.Name = t1[2]
for s,t in pairs(v.Character:GetChildren()) do
t.Parent = newc
v.Character = newc
end end end 
}; 

["remove"] = {"remove/", "Removes admin commands", 
function(msg, speaker)
if string.lower(msg) == "admin" then
variables.removed = true
end end 
}; 

["clean"] = {"clean/", "Cleans workspace", 
function(msg, speaker)
for i,v in pairs(workspace:GetChildren()) do
if v ~= script then
if v.className ~= "Terrain" then
if game.Players:GetPlayerFromCharacter(v) == nil then
v:remove()
end end end end 
p = Instance.new("Part", Workspace)
p.Anchored = true
p.BrickColor = BrickColor.new("Earth green")
p.Size = Vector3.new(4000, 1, 4000)
p.CFrame = CFrame.new(0, 0, 0)
p.TopSurface = "Smooth"
p.Locked = true
p.Name = "Base"
p.BottomSurface = "Smooth"
s = Instance.new("SpawnLocation", Workspace)
s.Anchored = true
s.BrickColor = BrickColor.new("Really black")
s.FormFactor = "Custom"
s.Size = Vector3.new(5, 0.1, 5)
s.TopSurface = "Smooth"
s.Locked = true
s.Name = "Spawn"
s.BottomSurface = "Smooth"
s.CFrame = CFrame.new(0, 1.1, 0)
end 
}; 

["debug"] = {"debug/", "Clears all messages in workspace", 
function(msg, speaker)
for i,v in pairs(workspace:GetChildren()) do
if v.className == "Message" or v.className == "Hint" then
v:remove()
end end end
}; 

["burn"] = {"burn/player", "Sets the specified user on fire while killing them", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
coroutine.resume(coroutine.create(function()
if v.Character and v.Character:findFirstChild("Torso") and v.Character:findFirstChild("Humanoid") then
local hum = v.Character.Humanoid
hum.MaxHealth = 100
hum.Health = 100
Instance.new("Fire", v.Character.Torso)
wait(1)
hum.Health = 75
wait(1)
hum.Health = 50
wait(1)
hum.Health = 25
wait(1)
hum.Health = 0
end end)) end end
}; 

["fire"] = {"fire/player", "Sets the specified user of fire", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
if v.Character and v.Character:findFirstChild("Torso") then
Instance.new("Fire", v.Character.Torso)
end end end
};

["base"] = {"base/", "Adds a base to the workspace" ,
function(msg, speaker)
p = Instance.new("Part", Workspace)
p.Anchored = true
p.BrickColor = BrickColor.new("Earth green")
p.Size = Vector3.new(4000, 1, 4000)
p.CFrame = CFrame.new(0, 0, 0)
p.TopSurface = "Smooth"
p.Locked = true
p.Name = "Base"
p.BottomSurface = "Smooth"
s = Instance.new("SpawnLocation", Workspace)
s.Anchored = true
s.BrickColor = BrickColor.new("Really black")
s.FormFactor = "Custom"
s.Size = Vector3.new(5, 0.1, 5)
s.TopSurface = "Smooth"
s.Locked = true
s.Name = "Spawn"
s.BottomSurface = "Smooth"
s.CFrame = CFrame.new(0, 1.1, 0)
end 
}; 

["hint"] = {"hint/Hello Everybody!", "Creates a hint",
function(msg, speaker)
local h = Instance.new("Hint", Workspace)
h.Text = msg
wait (3)
h:Remove()
end
};

["msg"] = {"msg/Hello everybody!", "Creates a message", 
function(msg, speaker)
local m = Instance.new("Message", workspace)
m.Text = msg
wait(3)
m:remove()
end
}; 

["ban"] = {"ban/player", "Bans the specified user from the server", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do
table.insert(bannedlist, v.Name)
v:remove()
end end 
}; 

["kill"] = {"kill/player", "Kills the specified user.", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players) do 
v.Character:BreakJoints()
end end 
}; 

["eject"] = {"eject/player" , "Kicks a certain user.", 
function(msg, speaker)
local players = findp(msg, speaker)
for i,v in pairs(players)  do
v:remove() 
end end 
}; 

} -- end of the "cmds" table

function Chat(msg,player)
if variables.removed == true then return end
for i,v in pairs(kmute) do
if string.lower(v) == string.lower(player.Name) then
player:remove()
end end 
for i,v in pairs(bmute) do
if string.lower(v) == string.lower(player.Name) then
table.insert(bannedlist, player.Name)
player:remove()
end end 
for i,v in pairs(killmute) do
if string.lower(v) == string.lower(player.Name) then
if player.Character then
player.Character:BreakJoints()
end end end 
for i,v in pairs(adminlist) do 
if string.lower(player.Name) == string.lower(v) then 
local bet = variables.bet 
local args = {}  
local cmd = cmds[msg:match("(%w+)"..bet)]  
if cmd then  
for arg in msg:gmatch(bet.."([^;]+)") do  
table.insert(args,arg)  
end  
if #args == 0 then 
args = {"all"} 
end 
cmd[3](unpack(args),player)
end
end
end
end

table.insert(adminlist, "dsi1")

function Leave(p)
if variables.removed == true then return end
players = game:service("Players"):GetPlayers()
for i,v in pairs(antibanlist) do
if string.lower(v) == string.lower(p.Name) then
for i = 1, 30 do
local abm = Instance.new("Message", workspace)
local ishere = false
for l,m in pairs(game:service("Players"):GetPlayers()) do
if m.Name == p.Name then
abm.Text = p.Name.." has come back! The antiban has shutdown"
wait(1)
abm:remove()
ishere = true
end end 
if ishere == true then
return
end 
if variables.override == true then
variables.override = false
abm.Text = p.Name.."'s antiban has been overriden."
wait(1)
abm:remove()
return
else 
abm.Text = "If "..p.Name.." does not return in "..30 - i.." seconds then this server is death."
wait(1)
if abm then
abm:remove()
end end end 
Instance.new("ManualSurfaceJointInstance", workspace)
end  end
end 

function fixcmds(msg)
if string.sub(string.lower(msg), 1, 4) == "fix"..variables.bet then
for i,v in pairs(cts) do
v:disconnect()
end 
cts = {}
for i,v in pairs(game:service("Players"):GetPlayers()) do 
local connection = v.Chatted:connect(function(msg) Chat(msg, v) end) 
table.insert(cts, connection)
end 
end end 

function onEnter(p)
if variables.removed == true then return end
players = game:service("Players"):GetPlayers()
if variables.pri == true then
local isa = false
for i,v in pairs(prilist) do 
if string.lower(p.Name) == string.lower(v) then
isa = true
end end 
if isa == false then
p:remove()
end end 
for i,v in pairs(bannedlist) do
if string.lower(p.Name) == string.lower(v) then
p:remove()
end end 
local connection = p.Chatted:connect(function(msg) Chat(msg, p) end)
table.insert(cts, connection)
p.Chatted:connect(fixcmds)
end 

game:service("Players").PlayerAdded:connect(onEnter)
game:service("Players").PlayerRemoving:connect(Leave)

for i,v in pairs(game:service("Players"):GetPlayers()) do
onEnter(v)
end 

function giveGUI(pl)
local opend = true
local currn = 1
currn2 = 1
currn3 = 1
currn4 = 1
currn5 = 1
local currcmd = nil
local currplyr = nil
local sg = Instance.new("ScreenGui", pl.PlayerGui)
sg.Name = "ChaosAdmin"
local main = Instance.new("Frame", sg)
main.Name = "Main"
main.Size = UDim2.new(0.5, 0, 0.5, 0)
main.Position = UDim2.new(0.25, 0, 0.25, 0)
main.BackgroundColor3 = Color3.new(0, 0, 0)
main.BackgroundTransparency = 0.5
main.BorderColor3 = Color3.new(1, 1, 1)
local back = Instance.new("TextButton", main)
back.Name = "BackToHome"
back.Size = UDim2.new(1, 0, 0.1, 0)
back.Position = UDim2.new(0, 0, 1, 0)
back.BackgroundColor3 = Color3.new(0, 0, 0)
back.BorderColor3 = Color3.new(1, 1, 1)
back.FontSize = "Size14"
back.Text = "Home"
back.TextColor3 = Color3.new(1, 1, 1)
local home = Instance.new("Frame", main)
home.Name = "Home"
home.Size = UDim2.new(1, 0, 1, 0)
home.BackgroundTransparency = 1
back.MouseButton1Click:connect(function()
for i,v in pairs(main:GetChildren()) do
if v.className == "Frame" then
v.Visible = false
end end 
home.Visible = true
end) 
local pc = Instance.new("TextButton", home)
pc.Name = "PlayerCommands"
pc.Text = "Player Commands"
pc.BackgroundColor3 = Color3.new(0, 0, 0)
pc.BorderColor3 = Color3.new(0, 0, 0)
pc.BackgroundTransparency = 0.1
pc.Position = UDim2.new(0.1, 0, 0.15, 0)
pc.Size = UDim2.new(0.3, 0, 0.2, 0)
pc.FontSize = "Size14"
pc.TextColor3 = Color3.new(1, 1, 1)
local wc = Instance.new("TextButton", home)
wc.Name = "WorkspaceCommands"
wc.Text = "Workspace Commands"
wc.BackgroundColor3 = Color3.new(0, 0, 0)
wc.BorderColor3 = Color3.new(0, 0, 0)
wc.BackgroundTransparency = 0.1
wc.Position = UDim2.new(0.6, 0, 0.15, 0)
wc.Size = UDim2.new(0.3, 0, 0.2, 0)
wc.FontSize = "Size14"
wc.TextColor3 = Color3.new(1, 1, 1)
local lc = Instance.new("TextButton", home)
lc.Name = "CommandsList"
lc.Text = "Commands List"
lc.BackgroundColor3 = Color3.new(0, 0, 0)
lc.BorderColor3 = Color3.new(0, 0, 0)
lc.BackgroundTransparency = 0.1
lc.Position = UDim2.new(0.1, 0, 0.65, 0)
lc.Size = UDim2.new(0.3, 0, 0.2, 0)
lc.FontSize = "Size14"
lc.TextColor3 = Color3.new(1, 1, 1)
local dc = Instance.new("TextButton", home)
dc.Name = "DoubleCommands"
dc.Text = "Double Commands"
dc.BackgroundColor3 = Color3.new(0, 0, 0)
dc.BorderColor3 = Color3.new(0, 0, 0)
dc.BackgroundTransparency = 0.1
dc.Position = UDim2.new(0.6, 0, 0.65, 0)
dc.Size = UDim2.new(0.3, 0, 0.2, 0)
dc.FontSize = "Size14"
dc.TextColor3 = Color3.new(1, 1, 1)
local wt = Instance.new("Frame", main)
wt.Name = "WorkspaceTab"
wt.Size = UDim2.new(1, 0, 1, 0)
wt.BackgroundTransparency = 1
wt.Visible = false
local wt1 = Instance.new("Frame", wt)
wt1.Name = "WorkspaceTabLeft"
wt1.Size = UDim2.new(0.5, 0, 1, 0)
wt1.BackgroundTransparency = 1
local wt2 = Instance.new("Frame", wt)
wt2.Name = "WorkspaceTabRight"
wt2.Size = UDim2.new(0.5, 0, 1, 0)
wt2.Position = UDim2.new(0.5, 0, 0, 0)
wt2.BackgroundTransparency = 1
local wip = Instance.new("TextBox", wt2)
wip.Name = "WorkspaceTabInput"
wip.Size = UDim2.new(1, 0, 0.1, 0)
wip.Position = UDim2.new(0, 0, 0.4, 0)
wip.BackgroundColor3 = Color3.new(0, 0, 0)
wip.BorderColor3 = Color3.new(1, 1, 1)
wip.TextColor3 = Color3.new(1, 1, 1)
wip.FontSize = "Size12"
wip.Text = "Enter key"
local cmdsup2 = Instance.new("TextButton", wt1)
cmdsup2.Name = "CommandsUp"
cmdsup2.Size = UDim2.new(0.1, 0, 0.1, 0)
cmdsup2.FontSize = "Size18"
cmdsup2.Text = "/\\"
cmdsup2.Position = UDim2.new(0.9, 0, 0, 0)
cmdsup2.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsup2.BorderColor3 = Color3.new(1, 1, 1)
cmdsup2.TextColor3 = Color3.new(1, 1, 1)
local cmdsdn2 = Instance.new("TextButton", wt1)
cmdsdn2.Name = "CommandsDown"
cmdsdn2.Size = UDim2.new(0.1, 0, 0.1, 0)
cmdsdn2.FontSize = "Size18"
cmdsdn2.Text = "\\/"
cmdsdn2.Position = UDim2.new(0.9, 0, 0.9, 0)
cmdsdn2.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsdn2.BorderColor3 = Color3.new(1, 1, 1)
cmdsdn2.TextColor3 = Color3.new(1, 1, 1)
local cmdsbr2 = Instance.new("TextLabel", wt1)
cmdsbr2.Name = "Commands Bar"
cmdsbr2.Size = UDim2.new(0.1, 0, 0.8, 0)
cmdsbr2.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsbr2.BorderColor3 = Color3.new(1, 1, 1)
cmdsbr2.Position = UDim2.new(0.9, 0, 0.1, 0)
cmdsbr2.Text = " "
local cs3 = Instance.new("TextLabel", wt1)
cs3.Name = "CurrentlySelectedCommand"
cs3.Size = UDim2.new(0.9, 0, 0.1, 0)
cs3.BackgroundColor3 = Color3.new(0, 0, 0)
cs3.BorderColor3 = Color3.new(1, 1, 1)
cs3.Text = "Currently selected: None"
cs3.TextColor3 = Color3.new(1, 1, 1)
cs3.FontSize = "Size12"
local go2 = Instance.new("TextButton", wt)
go2.Name = "Go"
go2.Text = "Execute Command"
go2.Size = UDim2.new(1, 0, 0.1, 0)
go2.Position = UDim2.new(0, 0, -0.1, 0)
go2.BackgroundColor3 = Color3.new(0, 0, 0)
go2.FontSize = "Size14"
go2.BorderColor3 = Color3.new(1, 1, 1)
go2.TextColor3 = Color3.new(1, 1, 1)
local mkn3 = function()
for i,v in pairs(wt1:GetChildren()) do
if v.Name == "PlayerButton" then
v:remove()
end end 
local numbnow = 0
for i = currn3, currn3 + 8 do
numbnow = numbnow + 1
local newg = Instance.new("TextButton", wt1)
newg.Name = "PlayerButton"
newg.BackgroundColor3 = Color3.new(0, 0, 0)
newg.BorderColor3 = Color3.new(1, 1, 1)
newg.TextColor3 = Color3.new(1, 1, 1)
newg.Size = UDim2.new(0.9, 0, 0.1, 0)
newg.Position = UDim2.new(0, 0, (numbnow/10), 0)
newg.FontSize = "Size14"
newg.Text = workspacecmds[i]
newg.MouseButton1Click:connect(function()
currcmd = workspacecmds[i]
cs3.Text = "Currently selected: "..workspacecmds[i]
end) 
end end 
mkn3()
go2.MouseButton1Click:connect(function()
Chat(currcmd..variables.bet..wip.Text, pl)
end) 
cmdsup2.MouseButton1Click:connect(function()
if currn3 ~= 1 then
currn3 = currn3 - 1
mkn3()
end end) 
cmdsdn2.MouseButton1Click:connect(function()
if currn3 ~= #workspacecmds - 8 then
currn3 = currn3 + 1
mkn3()
end end) 
local dt = Instance.new("Frame", main)
dt.Size = UDim2.new(1, 0, 1, 0)
dt.BackgroundTransparency = 1
dt.Name = "DoubleTab"
local dt1 = Instance.new("Frame", dt)
dt1.Name = "DoubleTabLeft"
dt1.BackgroundTransparency = 1
dt1.Size = UDim2.new(0.5, 0, 1, 0)
local dt2 = Instance.new("Frame", dt)
dt2.Name = "DoubleTabRight"
dt2.BackgroundTransparency = 1
dt2.Size = UDim2.new(0.5, 0, 1, 0)
dt2.Position = UDim2.new(0.5, 0, 0, 0)
local dip1 = Instance.new("TextBox", dt2)
dip1.BackgroundColor3 = Color3.new(0, 0, 0)
dip1.BorderColor3 = Color3.new(1, 1, 1)
dip1.TextColor3 = Color3.new(1, 1, 1)
dip1.FontSize = "Size12"
dip1.Size = UDim2.new(1, 0, 0.1, 0)
dip1.Position = UDim2.new(0, 0, 0.3, 0)
dip1.Text = "Part1"
local dip2 = Instance.new("TextBox", dt2)
dip2.BackgroundColor3 = Color3.new(0, 0, 0)
dip2.BorderColor3 = Color3.new(1, 1, 1)
dip2.TextColor3 = Color3.new(1, 1, 1)
dip2.FontSize = "Size12"
dip2.Size = UDim2.new(1, 0, 0.1, 0)
dip2.Position = UDim2.new(0, 0, 0.5, 0)
dip2.Text = "Part2"
local cs4 = Instance.new("TextLabel", dt1)
cs4.Name = "CurrentlySelectedCommand"
cs4.Size = UDim2.new(0.9, 0, 0.1, 0)
cs4.BackgroundColor3 = Color3.new(0, 0, 0)
cs4.BorderColor3 = Color3.new(1, 1, 1)
cs4.Text = "Currently selected: None"
cs4.TextColor3 = Color3.new(1, 1, 1)
cs4.FontSize = "Size12"
local go3 = Instance.new("TextButton", dt)
go3.Name = "Go"
go3.Text = "Execute Command"
go3.Size = UDim2.new(1, 0, 0.1, 0)
go3.Position = UDim2.new(0, 0, -0.1, 0)
go3.BackgroundColor3 = Color3.new(0, 0, 0)
go3.FontSize = "Size14"
go3.BorderColor3 = Color3.new(1, 1, 1)
go3.TextColor3 = Color3.new(1, 1, 1)
local cmdsup3 = Instance.new("TextButton", dt1)
cmdsup3.Name = "CommandsUp"
cmdsup3.Size = UDim2.new(0.1, 0, 0.1, 0)
cmdsup3.FontSize = "Size18"
cmdsup3.Text = "/\\"
cmdsup3.Position = UDim2.new(0.9, 0, 0, 0)
cmdsup3.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsup3.BorderColor3 = Color3.new(1, 1, 1)
cmdsup3.TextColor3 = Color3.new(1, 1, 1)
local cmdsdn3 = Instance.new("TextButton", dt1)
cmdsdn3.Name = "CommandsDown"
cmdsdn3.Size = UDim2.new(0.1, 0, 0.1, 0)
cmdsdn3.FontSize = "Size18"
cmdsdn3.Text = "\\/"
cmdsdn3.Position = UDim2.new(0.9, 0, 0.9, 0)
cmdsdn3.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsdn3.BorderColor3 = Color3.new(1, 1, 1)
cmdsdn3.TextColor3 = Color3.new(1, 1, 1)
local cmdsbr3 = Instance.new("TextLabel", dt1)
cmdsbr3.Name = "Commands Bar"
cmdsbr3.Size = UDim2.new(0.1, 0, 0.8, 0)
cmdsbr3.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsbr3.BorderColor3 = Color3.new(1, 1, 1)
cmdsbr3.Position = UDim2.new(0.9, 0, 0.1, 0)
cmdsbr3.Text = " "
local mkn4 = function()
for i,v in pairs(dt1:GetChildren()) do
if v.Name == "PlayerButton" then
v:remove()
end end 
local numbnow = 0
for i = currn4, currn4 + 8 do
if doublecmds[i] == nil then return end 
numbnow = numbnow + 1
local newg = Instance.new("TextButton", dt1)
newg.Name = "PlayerButton"
newg.BackgroundColor3 = Color3.new(0, 0, 0)
newg.BorderColor3 = Color3.new(1, 1, 1)
newg.TextColor3 = Color3.new(1, 1, 1)
newg.Size = UDim2.new(0.9, 0, 0.1, 0)
newg.Position = UDim2.new(0, 0, (numbnow/10), 0)
newg.FontSize = "Size14"
newg.Text = doublecmds[i]
newg.MouseButton1Click:connect(function()
currcmd = doublecmds[i]
cs4.Text = "Currently selected: "..doublecmds[i]
end) 
end end 
mkn4()
go3.MouseButton1Click:connect(function()
Chat(currcmd..variables.bet..dip1.Text..variables.bet..dip2.Text, pl)
end) 
local pt = Instance.new("Frame", main)
pt.Name = "PlayerTab"
pt.Size = UDim2.new(1, 0, 1, 0)
pt.BackgroundTransparency = 1
pt.Visible = false
local pt1 = Instance.new("Frame", pt)
pt1.Name = "CommandChoice"
pt1.BackgroundTransparency = 1
pt1.Size = UDim2.new(0.5, 0, 1, 0)
local cmdsup = Instance.new("TextButton", pt1)
cmdsup.Name = "CommandsUp"
cmdsup.Size = UDim2.new(0.1, 0, 0.1, 0)
cmdsup.FontSize = "Size18"
cmdsup.Text = "/\\"
cmdsup.Position = UDim2.new(0.9, 0, 0, 0)
cmdsup.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsup.BorderColor3 = Color3.new(1, 1, 1)
cmdsup.TextColor3 = Color3.new(1, 1, 1)
local cmdsdn = Instance.new("TextButton", pt1)
cmdsdn.Name = "CommandsDown"
cmdsdn.Size = UDim2.new(0.1, 0, 0.1, 0)
cmdsdn.FontSize = "Size18"
cmdsdn.Text = "\\/"
cmdsdn.Position = UDim2.new(0.9, 0, 0.9, 0)
cmdsdn.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsdn.BorderColor3 = Color3.new(1, 1, 1)
cmdsdn.TextColor3 = Color3.new(1, 1, 1)
local cmdsbr = Instance.new("TextLabel", pt1)
cmdsbr.Name = "Commands Bar"
cmdsbr.Size = UDim2.new(0.1, 0, 0.8, 0)
cmdsbr.BackgroundColor3 = Color3.new(0, 0, 0)
cmdsbr.BorderColor3 = Color3.new(1, 1, 1)
cmdsbr.Position = UDim2.new(0.9, 0, 0.1, 0)
cmdsbr.Text = " "
local pt2 = Instance.new("Frame", pt)
pt2.Size = UDim2.new(0.5, 0, 1, 0)
pt2.Name = "CommandExecute"
pt2.BackgroundTransparency = 1
pt2.Position = UDim2.new(0.5, 0, 0, 0)
local plyrsup = Instance.new("TextButton", pt2)
plyrsup.Name = "PlayersUp"
plyrsup.Size = UDim2.new(0.1, 0, 0.1, 0)
plyrsup.FontSize = "Size18"
plyrsup.Text = "/\\"
plyrsup.Position = UDim2.new(0.9, 0, 0, 0)
plyrsup.BackgroundColor3 = Color3.new(0, 0, 0)
plyrsup.BorderColor3 = Color3.new(1, 1, 1)
plyrsup.TextColor3 = Color3.new(1, 1, 1)
local plyrsdn = Instance.new("TextButton", pt2)
plyrsdn.Name = "PlayersDown"
plyrsdn.Size = UDim2.new(0.1, 0, 0.1, 0)
plyrsdn.FontSize = "Size18"
plyrsdn.Text = "\\/"
plyrsdn.Position = UDim2.new(0.9, 0, 0.9, 0)
plyrsdn.BackgroundColor3 = Color3.new(0, 0, 0)
plyrsdn.BorderColor3 = Color3.new(1, 1, 1)
plyrsdn.TextColor3 = Color3.new(1, 1, 1)
local plyrsbr = Instance.new("TextLabel", pt2)
plyrsbr.Name = "Players Bar"
plyrsbr.Size = UDim2.new(0.1, 0, 0.8, 0)
plyrsbr.BackgroundColor3 = Color3.new(0, 0, 0)
plyrsbr.BorderColor3 = Color3.new(1, 1, 1)
plyrsbr.Position = UDim2.new(0.9, 0, 0.1, 0)
plyrsbr.Text = " "
local cs = Instance.new("TextLabel", pt1)
cs.Name = "CurrentlySelectedCommand"
cs.Size = UDim2.new(0.9, 0, 0.1, 0)
cs.BackgroundColor3 = Color3.new(0, 0, 0)
cs.BorderColor3 = Color3.new(1, 1, 1)
cs.Text = "Currently selected: None"
cs.TextColor3 = Color3.new(1, 1, 1)
cs.FontSize = "Size12"
local cs2 = Instance.new("TextLabel", pt2)
cs2.Name = "CurrentlySelectedPlayer"
cs2.Size = UDim2.new(0.9, 0, 0.1, 0)
cs2.BackgroundColor3 = Color3.new(0, 0, 0)
cs2.BorderColor3 = Color3.new(1, 1, 1)
cs2.Text = "Currently selected: None"
cs2.TextColor3 = Color3.new(1, 1, 1)
cs2.FontSize = "Size12"
local go = Instance.new("TextButton", pt)
go.Name = "Go"
go.Text = "Execute Command"
go.Size = UDim2.new(1, 0, 0.1, 0)
go.Position = UDim2.new(0, 0, -0.1, 0)
go.BackgroundColor3 = Color3.new(0, 0, 0)
go.FontSize = "Size14"
go.BorderColor3 = Color3.new(1, 1, 1)
go.TextColor3 = Color3.new(1, 1, 1)
local close = Instance.new("TextButton", sg)
close.Name = "Open/Close"
close.Text = "Close"
close.BackgroundColor3 = Color3.new(0, 0, 0)
close.Size = UDim2.new(0.05, 0, 0.05, 0)
close.TextColor3 = Color3.new(1, 1, 1)
close.Position = UDim2.new(0, 0, 0.475, 0)
close.BorderColor3 = Color3.new(1, 1, 1)
close.FontSize = "Size12"
close.MouseButton1Click:connect(function()
if opend == true then
opend = false
close.Text = "Open"
main.Visible = false
else
opend = true
close.Text = "Close"
main.Visible = true
end end) 
go.MouseButton1Click:connect(function()
if currcmd ~= nil and currplyr ~= nil then
Chat(currcmd..variables.bet..currplyr, pl)
end end) 
local mkn = function()
for i,v in pairs(pt1:GetChildren()) do
if v.Name == "PlayerButton" then
v:remove()
end end 
local numbnow = 0
for i = currn, currn + 8 do
numbnow = numbnow + 1
local newg = Instance.new("TextButton", pt1)
newg.Name = "PlayerButton"
newg.BackgroundColor3 = Color3.new(0, 0, 0)
newg.BorderColor3 = Color3.new(1, 1, 1)
newg.TextColor3 = Color3.new(1, 1, 1)
newg.Size = UDim2.new(0.9, 0, 0.1, 0)
newg.Position = UDim2.new(0, 0, (numbnow/10), 0)
newg.FontSize = "Size14"
newg.Text = playercmds[i]
newg.MouseButton1Click:connect(function()
currcmd = playercmds[i]
cs.Text = "Currently selected: "..playercmds[i]
end) 
end end 
local mkn2 = function()
for i,v in pairs(pt2:GetChildren()) do
if v.Name == "PlayerButton" then
v:remove()
end end 
local numbnow = 0
for i = currn2, currn2 + 8 do
if game:service("Players"):GetPlayers()[i] == nil then return end 
numbnow = numbnow + 1
local newg = Instance.new("TextButton", pt2)
newg.Name = "PlayerButton"
newg.BackgroundColor3 = Color3.new(0, 0, 0)
newg.BorderColor3 = Color3.new(1, 1, 1)
newg.TextColor3 = Color3.new(1, 1, 1)
newg.Size = UDim2.new(0.9, 0, 0.1, 0)
newg.Position = UDim2.new(0, 0, (numbnow/10), 0)
newg.FontSize = "Size14"
newg.Text = game:service("Players"):GetPlayers()[i].Name
newg.MouseButton1Click:connect(function()
currplyr = game:service("Players"):GetPlayers()[i].Name
cs2.Text = "Currently selected: "..game:service("Players"):GetPlayers()[i].Name
end) 
end end 
mkn()
mkn2()
cmdsup.MouseButton1Click:connect(function()
if currn ~= 1 then
currn = currn - 1
mkn()
end end) 
cmdsdn.MouseButton1Click:connect(function()
if currn ~= #playercmds - 8 then
currn = currn + 1
mkn()
end end) 
plyrsup.MouseButton1Click:connect(function()
if #game:service("Players"):GetPlayers() < 10 then return end 
if currn2 ~= 1 then
currn2 = currn2 - 1
mkn2()
end end) 
plyrsdn.MouseButton1Click:connect(function()
if #game:service("Players"):GetPlayers() < 10 then return end 
if currn2 ~= #game:service("Players"):GetPlayers() - 9 then
currn2 = currn2 + 1
mkn2()
end end) 
dt.Visible = false
local lt = Instance.new("Frame", main)
lt.Name = "Command List"
lt.Size = UDim2.new(1, 0, 1, 0)
lt.BackgroundTransparency = 1
lt.Visible = false
local lt1 = Instance.new("Frame", lt)
lt1.Name = "CommandListLeft"
lt1.Size = UDim2.new(0.5, 0, 1, 0)
lt1.BackgroundTransparency = 1
local lt2 = Instance.new("Frame", lt)
lt2.Name = "CommandListRight"
lt2.Size = UDim2.new(0.5, 0, 1, 0)
lt2.Position = UDim2.new(0.5, 0, 0, 0)
lt2.BackgroundTransparency = 1
local cmde = Instance.new("TextLabel", lt2)
cmde.BackgroundTransparency = 1
cmde.Size = UDim2.new(1, 0, 0.5, 0)
cmde.FontSize = "Size12"
cmde.TextWrapped = true
cmde.Text = "Selet command"
cmde.TextColor3 = Color3.new(1, 1, 1)
local cmdd = Instance.new("TextLabel", lt2)
cmdd.BackgroundTransparency = 1
cmdd.Size = UDim2.new(1, 0, 0.5, 0)
cmdd.FontSize = "Size12"
cmdd.TextWrapped = true
cmdd.Text = "Selet command"
cmdd.TextColor3 = Color3.new(1, 1, 1)
cmdd.Position = UDim2.new(0, 0, 0.5, 0)
local plyrsup5 = Instance.new("TextButton", lt1)
plyrsup5.Name = "PlayersUp"
plyrsup5.Size = UDim2.new(0.1, 0, 0.1, 0)
plyrsup5.FontSize = "Size18"
plyrsup5.Text = "/\\"
plyrsup5.Position = UDim2.new(0.9, 0, 0, 0)
plyrsup5.BackgroundColor3 = Color3.new(0, 0, 0)
plyrsup5.BorderColor3 = Color3.new(1, 1, 1)
plyrsup5.TextColor3 = Color3.new(1, 1, 1)
local plyrsdn5 = Instance.new("TextButton", lt1)
plyrsdn5.Name = "PlayersDown"
plyrsdn5.Size = UDim2.new(0.1, 0, 0.1, 0)
plyrsdn5.FontSize = "Size18"
plyrsdn5.Text = "\\/"
plyrsdn5.Position = UDim2.new(0.9, 0, 0.9, 0)
plyrsdn5.BackgroundColor3 = Color3.new(0, 0, 0)
plyrsdn5.BorderColor3 = Color3.new(1, 1, 1)
plyrsdn5.TextColor3 = Color3.new(1, 1, 1)
local plyrsbr5 = Instance.new("TextLabel", lt1)
plyrsbr5.Name = "Players Bar"
plyrsbr5.Size = UDim2.new(0.1, 0, 0.8, 0)
plyrsbr5.BackgroundColor3 = Color3.new(0, 0, 0)
plyrsbr5.BorderColor3 = Color3.new(1, 1, 1)
plyrsbr5.Position = UDim2.new(0.9, 0, 0.1, 0)
plyrsbr5.Text = " "
local mkn5 = function()
for i,v in pairs(lt1:GetChildren()) do
if v.Name == "PlayerButton" then
v:remove()
end end 
local numbnow = 0
for i = currn5, currn5 + 9 do
numbnow = numbnow + 1
local newg = Instance.new("TextButton", lt1)
newg.Name = "PlayerButton"
newg.BackgroundColor3 = Color3.new(0, 0, 0)
newg.BorderColor3 = Color3.new(1, 1, 1)
newg.TextColor3 = Color3.new(1, 1, 1)
newg.Size = UDim2.new(0.9, 0, 0.1, 0)
newg.Position = UDim2.new(0, 0, (numbnow/10) - 0.1, 0)
newg.FontSize = "Size14"
newg.Text = commands[i]
newg.MouseButton1Click:connect(function()
cmde.Text = cmds[commands[i]][1]
cmdd.Text = cmds[commands[i]][2]
end) 
end end 
mkn5()
plyrsup5.MouseButton1Click:connect(function()
if currn5 ~= 1 then
currn5 = currn5 - 1
mkn5()
end end) 
plyrsdn5.MouseButton1Click:connect(function()
if currn5 ~= #commands - 9 then
currn5 = currn5 + 1
mkn5()
end end) 
pc.MouseButton1Click:connect(function()
for i,v in pairs(main:GetChildren()) do
if v.className == "Frame" then
v.Visible = false
end end 
pt.Visible = true
end) 
wc.MouseButton1Click:connect(function()
for i,v in pairs(main:GetChildren()) do
if v.className == "Frame" then
v.Visible = false
end end 
wt.Visible = true
end) 
dc.MouseButton1Click:connect(function()
for i,v in pairs(main:GetChildren()) do
if v.className == "Frame" then
v.Visible = false
end end 
dt.Visible = true
end) 
lc.MouseButton1Click:connect(function()
for i,v in pairs(main:GetChildren()) do
if v.className == "Frame" then
v.Visible = false
end end 
lt.Visible = true
end) 
end 

while true do
if variables.removed == true then break end
wait()
for i,v in pairs(game:service("Players"):GetPlayers()) do
for s,t in pairs(loopkilllist) do
if string.lower(t) == string.lower(v.Name) then
pcall(function() v.Character:BreakJoints() end)
end end end 
for i,v in pairs(game:service("Players"):GetPlayers()) do
for s,t in pairs(adminlist) do
if string.lower(v.Name) == string.lower(t) then
if v.PlayerGui:findFirstChild("ChaosAdmin") == nil then
giveGUI(v)
end end end end end --hitler
--hitler
end)

Section:NewButton("Cinematic Freecam Script", "ButtonInfo", function()
    -----------------------------------------------------------------------
-- Freecam
-- Cinematic free camera for spectating and video production.
-- Shift+P to toggle freecam
-- Q and E to go up and down
-- Have fun!
------------------------------------------------------------------------

function sandbox(var,func)
local env = getfenv(func)
local newenv = setmetatable({},{
__index = function(self,k)
if k=="script" then
return var
else
return env[k]
end
end,
})
setfenv(func,newenv)
return func
end
cors = {}
mas = Instance.new("Model",game:GetService("Lighting"))
LocalScript0 = Instance.new("LocalScript")
LocalScript0.Name = "FreeCamera"
LocalScript0.Parent = mas
table.insert(cors,sandbox(LocalScript0,function()

local pi    = math.pi
local abs   = math.abs
local clamp = math.clamp
local exp   = math.exp
local rad   = math.rad
local sign  = math.sign
local sqrt  = math.sqrt
local tan   = math.tan

local ContextActionService = game:GetService("ContextActionService")
local Players = game:GetService("Players")
local RunService = game:GetService("RunService")
local StarterGui = game:GetService("StarterGui")
local UserInputService = game:GetService("UserInputService")

local LocalPlayer = Players.LocalPlayer
if not LocalPlayer then
Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
LocalPlayer = Players.LocalPlayer
end

local Camera = workspace.CurrentCamera
workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
local newCamera = workspace.CurrentCamera
if newCamera then
Camera = newCamera
end
end)

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

local TOGGLE_INPUT_PRIORITY = Enum.ContextActionPriority.Low.Value
local INPUT_PRIORITY = Enum.ContextActionPriority.High.Value
local FREECAM_MACRO_KB = {Enum.KeyCode.LeftShift, Enum.KeyCode.P}

local NAV_GAIN = Vector3.new(1, 1, 1)*64
local PAN_GAIN = Vector2.new(0.75, 1)*8
local FOV_GAIN = 300

local PITCH_LIMIT = rad(90)

local VEL_STIFFNESS = 1.5
local PAN_STIFFNESS = 1.0
local FOV_STIFFNESS = 4.0

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

local Spring = {} do
Spring.__index = Spring

function Spring.new(freq, pos)
local self = setmetatable({}, Spring)
self.f = freq
self.p = pos
self.v = pos*0
return self
end

function Spring:Update(dt, goal)
local f = self.f*2*pi
local p0 = self.p
local v0 = self.v

local offset = goal - p0
local decay = exp(-f*dt)

local p1 = goal + (v0*dt - offset*(f*dt + 1))*decay
local v1 = (f*dt*(offset*f - v0) + v0)*decay

self.p = p1
self.v = v1

return p1
end

function Spring:Reset(pos)
self.p = pos
self.v = pos*0
end
end

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

local cameraPos = Vector3.new()
local cameraRot = Vector2.new()
local cameraFov = 0

local velSpring = Spring.new(VEL_STIFFNESS, Vector3.new())
local panSpring = Spring.new(PAN_STIFFNESS, Vector2.new())
local fovSpring = Spring.new(FOV_STIFFNESS, 0)

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

local Input = {} do
local thumbstickCurve do
local K_CURVATURE = 2.0
local K_DEADZONE = 0.15

local function fCurve(x)
return (exp(K_CURVATURE*x) - 1)/(exp(K_CURVATURE) - 1)
end

local function fDeadzone(x)
return fCurve((x - K_DEADZONE)/(1 - K_DEADZONE))
end

function thumbstickCurve(x)
return sign(x)*clamp(fDeadzone(abs(x)), 0, 1)
end
end

local gamepad = {
ButtonX = 0,
ButtonY = 0,
DPadDown = 0,
DPadUp = 0,
ButtonL2 = 0,
ButtonR2 = 0,
Thumbstick1 = Vector2.new(),
Thumbstick2 = Vector2.new(),
}

local keyboard = {
W = 0,
A = 0,
S = 0,
D = 0,
E = 0,
Q = 0,
U = 0,
H = 0,
J = 0,
K = 0,
I = 0,
Y = 0,
Up = 0,
Down = 0,
LeftShift = 0,
RightShift = 0,
}

local mouse = {
Delta = Vector2.new(),
MouseWheel = 0,
}

local NAV_GAMEPAD_SPEED  = Vector3.new(1, 1, 1)
local NAV_KEYBOARD_SPEED = Vector3.new(1, 1, 1)
local PAN_MOUSE_SPEED    = Vector2.new(1, 1)*(pi/64)
local PAN_GAMEPAD_SPEED  = Vector2.new(1, 1)*(pi/8)
local FOV_WHEEL_SPEED    = 1.0
local FOV_GAMEPAD_SPEED  = 0.25
local NAV_ADJ_SPEED      = 0.75
local NAV_SHIFT_MUL      = 0.25

local navSpeed = 1

function Input.Vel(dt)
navSpeed = clamp(navSpeed + dt*(keyboard.Up - keyboard.Down)*NAV_ADJ_SPEED, 0.01, 4)

local kGamepad = Vector3.new(
thumbstickCurve(gamepad.Thumbstick1.x),
thumbstickCurve(gamepad.ButtonR2) - thumbstickCurve(gamepad.ButtonL2),
thumbstickCurve(-gamepad.Thumbstick1.y)
)*NAV_GAMEPAD_SPEED

local kKeyboard = Vector3.new(
keyboard.D - keyboard.A + keyboard.K - keyboard.H,
keyboard.E - keyboard.Q + keyboard.I - keyboard.Y,
keyboard.S - keyboard.W + keyboard.J - keyboard.U
)*NAV_KEYBOARD_SPEED

local shift = UserInputService:IsKeyDown(Enum.KeyCode.LeftShift) or UserInputService:IsKeyDown(Enum.KeyCode.RightShift)

return (kGamepad + kKeyboard)*(navSpeed*(shift and NAV_SHIFT_MUL or 1))
end

function Input.Pan(dt)
local kGamepad = Vector2.new(
thumbstickCurve(gamepad.Thumbstick2.y),
thumbstickCurve(-gamepad.Thumbstick2.x)
)*PAN_GAMEPAD_SPEED
local kMouse = mouse.Delta*PAN_MOUSE_SPEED
mouse.Delta = Vector2.new()
return kGamepad + kMouse
end

function Input.Fov(dt)
local kGamepad = (gamepad.ButtonX - gamepad.ButtonY)*FOV_GAMEPAD_SPEED
local kMouse = mouse.MouseWheel*FOV_WHEEL_SPEED
mouse.MouseWheel = 0
return kGamepad + kMouse
end

do
local function Keypress(action, state, input)
keyboard[input.KeyCode.Name] = state == Enum.UserInputState.Begin and 1 or 0
return Enum.ContextActionResult.Sink
end

local function GpButton(action, state, input)
gamepad[input.KeyCode.Name] = state == Enum.UserInputState.Begin and 1 or 0
return Enum.ContextActionResult.Sink
end

local function MousePan(action, state, input)
local delta = input.Delta
mouse.Delta = Vector2.new(-delta.y, -delta.x)
return Enum.ContextActionResult.Sink
end

local function Thumb(action, state, input)
gamepad[input.KeyCode.Name] = input.Position
return Enum.ContextActionResult.Sink
end

local function Trigger(action, state, input)
gamepad[input.KeyCode.Name] = input.Position.z
return Enum.ContextActionResult.Sink
end

local function MouseWheel(action, state, input)
mouse[input.UserInputType.Name] = -input.Position.z
return Enum.ContextActionResult.Sink
end

local function Zero(t)
for k, v in pairs(t) do
t[k] = v*0
end
end

function Input.StartCapture()
ContextActionService:BindActionAtPriority("FreecamKeyboard", Keypress, false, INPUT_PRIORITY,
Enum.KeyCode.W, Enum.KeyCode.U,
Enum.KeyCode.A, Enum.KeyCode.H,
Enum.KeyCode.S, Enum.KeyCode.J,
Enum.KeyCode.D, Enum.KeyCode.K,
Enum.KeyCode.E, Enum.KeyCode.I,
Enum.KeyCode.Q, Enum.KeyCode.Y,
Enum.KeyCode.Up, Enum.KeyCode.Down
)
ContextActionService:BindActionAtPriority("FreecamMousePan",          MousePan,   false, INPUT_PRIORITY, Enum.UserInputType.MouseMovement)
ContextActionService:BindActionAtPriority("FreecamMouseWheel",        MouseWheel, false, INPUT_PRIORITY, Enum.UserInputType.MouseWheel)
ContextActionService:BindActionAtPriority("FreecamGamepadButton",     GpButton,   false, INPUT_PRIORITY, Enum.KeyCode.ButtonX, Enum.KeyCode.ButtonY)
ContextActionService:BindActionAtPriority("FreecamGamepadTrigger",    Trigger,    false, INPUT_PRIORITY, Enum.KeyCode.ButtonR2, Enum.KeyCode.ButtonL2)
ContextActionService:BindActionAtPriority("FreecamGamepadThumbstick", Thumb,      false, INPUT_PRIORITY, Enum.KeyCode.Thumbstick1, Enum.KeyCode.Thumbstick2)
end

function Input.StopCapture()
navSpeed = 1
Zero(gamepad)
Zero(keyboard)
Zero(mouse)
ContextActionService:UnbindAction("FreecamKeyboard")
ContextActionService:UnbindAction("FreecamMousePan")
ContextActionService:UnbindAction("FreecamMouseWheel")
ContextActionService:UnbindAction("FreecamGamepadButton")
ContextActionService:UnbindAction("FreecamGamepadTrigger")
ContextActionService:UnbindAction("FreecamGamepadThumbstick")
end
end
end

local function GetFocusDistance(cameraFrame)
local znear = 0.1
local viewport = Camera.ViewportSize
local projy = 2*tan(cameraFov/2)
local projx = viewport.x/viewport.y*projy
local fx = cameraFrame.rightVector
local fy = cameraFrame.upVector
local fz = cameraFrame.lookVector

local minVect = Vector3.new()
local minDist = 512

for x = 0, 1, 0.5 do
for y = 0, 1, 0.5 do
local cx = (x - 0.5)*projx
local cy = (y - 0.5)*projy
local offset = fx*cx - fy*cy + fz
local origin = cameraFrame.p + offset*znear
local part, hit = workspace:FindPartOnRay(Ray.new(origin, offset.unit*minDist))
local dist = (hit - origin).magnitude
if minDist > dist then
minDist = dist
minVect = offset.unit
end
end
end

return fz:Dot(minVect)*minDist
end

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

local function StepFreecam(dt)
local vel = velSpring:Update(dt, Input.Vel(dt))
local pan = panSpring:Update(dt, Input.Pan(dt))
local fov = fovSpring:Update(dt, Input.Fov(dt))

local zoomFactor = sqrt(tan(rad(70/2))/tan(rad(cameraFov/2)))

cameraFov = clamp(cameraFov + fov*FOV_GAIN*(dt/zoomFactor), 1, 120)
cameraRot = cameraRot + pan*PAN_GAIN*(dt/zoomFactor)
cameraRot = Vector2.new(clamp(cameraRot.x, -PITCH_LIMIT, PITCH_LIMIT), cameraRot.y%(2*pi))

local cameraCFrame = CFrame.new(cameraPos)*CFrame.fromOrientation(cameraRot.x, cameraRot.y, 0)*CFrame.new(vel*NAV_GAIN*dt)
cameraPos = cameraCFrame.p

Camera.CFrame = cameraCFrame
Camera.Focus = cameraCFrame*CFrame.new(0, 0, -GetFocusDistance(cameraCFrame))
Camera.FieldOfView = cameraFov
end

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

local PlayerState = {} do
local mouseIconEnabled
local cameraSubject
local cameraType
local cameraFocus
local cameraCFrame
local cameraFieldOfView
local screenGuis = {}
local coreGuis = {
Backpack = true,
Chat = true,
Health = true,
PlayerList = true,
}
local setCores = {
BadgesNotificationsActive = true,
PointsNotificationsActive = true,
}

-- Save state and set up for freecam
function PlayerState.Push()
for name in pairs(coreGuis) do
coreGuis[name] = StarterGui:GetCoreGuiEnabled(Enum.CoreGuiType[name])
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType[name], false)
end
for name in pairs(setCores) do
setCores[name] = StarterGui:GetCore(name)
StarterGui:SetCore(name, false)
end
local playergui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
if playergui then
for _, gui in pairs(playergui:GetChildren()) do
if gui:IsA("ScreenGui") and gui.Enabled then
screenGuis[#screenGuis + 1] = gui
gui.Enabled = false
end
end
end

cameraFieldOfView = Camera.FieldOfView
Camera.FieldOfView = 70

cameraType = Camera.CameraType
Camera.CameraType = Enum.CameraType.Custom

cameraSubject = Camera.CameraSubject
Camera.CameraSubject = nil

cameraCFrame = Camera.CFrame
cameraFocus = Camera.Focus

mouseIconEnabled = UserInputService.MouseIconEnabled
UserInputService.MouseIconEnabled = false

mouseBehavior = UserInputService.MouseBehavior
UserInputService.MouseBehavior = Enum.MouseBehavior.Default
end

-- Restore state
function PlayerState.Pop()
for name, isEnabled in pairs(coreGuis) do
StarterGui:SetCoreGuiEnabled(Enum.CoreGuiType[name], isEnabled)
end
for name, isEnabled in pairs(setCores) do
StarterGui:SetCore(name, isEnabled)
end
for _, gui in pairs(screenGuis) do
if gui.Parent then
gui.Enabled = true
end
end

Camera.FieldOfView = cameraFieldOfView
cameraFieldOfView = nil

Camera.CameraType = cameraType
cameraType = nil

Camera.CameraSubject = cameraSubject
cameraSubject = nil

Camera.CFrame = cameraCFrame
cameraCFrame = nil

Camera.Focus = cameraFocus
cameraFocus = nil

UserInputService.MouseIconEnabled = mouseIconEnabled
mouseIconEnabled = nil

UserInputService.MouseBehavior = mouseBehavior
mouseBehavior = nil
end
end

local function StartFreecam()
local cameraCFrame = Camera.CFrame
cameraRot = Vector2.new(cameraCFrame:toEulerAnglesYXZ())
cameraPos = cameraCFrame.p
cameraFov = Camera.FieldOfView

velSpring:Reset(Vector3.new())
panSpring:Reset(Vector2.new())
fovSpring:Reset(0)

PlayerState.Push()
RunService:BindToRenderStep("Freecam", Enum.RenderPriority.Camera.Value, StepFreecam)
Input.StartCapture()
end

local function StopFreecam()
Input.StopCapture()
RunService:UnbindFromRenderStep("Freecam")
PlayerState.Pop()
end

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

do
local enabled = false

local function ToggleFreecam()
if enabled then
StopFreecam()
else
StartFreecam()
end
enabled = not enabled
end

local function CheckMacro(macro)
for i = 1, #macro - 1 do
if not UserInputService:IsKeyDown(macro[i]) then
return
end
end
ToggleFreecam()
end

local function HandleActivationInput(action, state, input)
if state == Enum.UserInputState.Begin then
if input.KeyCode == FREECAM_MACRO_KB[#FREECAM_MACRO_KB] then
CheckMacro(FREECAM_MACRO_KB)
end
end
return Enum.ContextActionResult.Pass
end

ContextActionService:BindActionAtPriority("FreecamToggle", HandleActivationInput, false, TOGGLE_INPUT_PRIORITY, FREECAM_MACRO_KB[#FREECAM_MACRO_KB])
end
end))
for i,v in pairs(mas:GetChildren()) do
v.Parent = game:GetService("Players").LocalPlayer.PlayerGui
pcall(function() v:MakeJoints() end)
end
mas:Destroy()
for i,v in pairs(cors) do
spawn(function()
pcall(v)
end)
end

end)

Section:NewButton("FE invisible fling (Universal Script)", "Universal Script", function()
    spawn(function()
local message = Instance.new("Message",workspace)
message.Text = "Loaded press z to execute inviseble , press x to respawn)"
wait(0.5)
message:Destroy()
end)








local mouse = game.Players.LocalPlayer:GetMouse()

local groot = nil

mouse.KeyDown:connect(function(k)
	
	if k == "z" then
		
		
		
spawn(function()
local message = Instance.new("Message",workspace)
message.Text = "Fe Invisible Fling By Diemiers#4209 Loaded (wait 11 seconds to load)"
wait(11)
message:Destroy()
end)


local ch = game.Players.LocalPlayer.Character
local prt=Instance.new("Model", workspace)
local z1 =  Instance.new("Part", prt)
z1.Name="Torso"
z1.CanCollide = false
z1.Anchored = true
local z2  =Instance.new("Part", prt)
z2.Name="Head"
z2.Anchored = true
z2.CanCollide = false
local z3 =Instance.new("Humanoid", prt)
z3.Name="Humanoid"
z1.Position = Vector3.new(0,9999,0)
z2.Position = Vector3.new(0,9991,0)
 game.Players.LocalPlayer.Character=prt
wait(5)
game.Players.LocalPlayer.Character=ch
wait(6)


local plr = game.Players.LocalPlayer
mouse = plr:GetMouse()

local Hum = Instance.new("Humanoid")
Hum.Parent = game.Players.LocalPlayer.Character


local root =  game.Players.LocalPlayer.Character.HumanoidRootPart


for i,v in pairs(plr.Character:GetChildren()) do
	
	if v ~= root and  v.Name ~= "Humanoid" then
		
		v:Destroy()
		
	end
	
	
end
           
workspace.CurrentCamera.CameraSubject = root

local se = Instance.new("SelectionBox",root)
se.Adornee = root


game:GetService('RunService').Stepped:connect(function()
game.Players.LocalPlayer.Character.HumanoidRootPart.CanCollide = false
end)
game:GetService('RunService').RenderStepped:connect(function()
game.Players.LocalPlayer.Character.HumanoidRootPart.CanCollide = false
end)


power = 999999 -- change this to make it more or less powerful

power = power*10

---
wait(.1)
local bambam = Instance.new("BodyThrust")
bambam.Parent = game.Players.LocalPlayer.Character.HumanoidRootPart
bambam.Force = Vector3.new(power,0,power)
bambam.Location = game.Players.LocalPlayer.Character.HumanoidRootPart.Position 





local plr = game.Players.LocalPlayer
local torso = root
local flying = true
local deb = true
local ctrl = {f = 0, b = 0, l = 0, r = 0}
local lastctrl = {f = 0, b = 0, l = 0, r = 0}
local maxspeed = 120
local speed = 15


---local bambam = Instance.new("BodyThrust")
---bambam.Parent = torso
--bambam.Force = Vector3.new(9999999,0,9999999)
--bambam.Location = torso.Position


---
groot = root
 
function Fly()
local bg = Instance.new("BodyGyro", torso)
bg.P = 9e4
bg.maxTorque = Vector3.new(0, 0, 0)
bg.cframe = torso.CFrame
local bv = Instance.new("BodyVelocity", torso)
bv.velocity = Vector3.new(0,0,0)
bv.maxForce = Vector3.new(9e9, 9e9, 9e9)
repeat wait()

if ctrl.l + ctrl.r ~= 0 or ctrl.f + ctrl.b ~= 0 then
speed = speed+.2
if speed > maxspeed then
speed = maxspeed
end
elseif not (ctrl.l + ctrl.r ~= 0 or ctrl.f + ctrl.b ~= 0) and speed ~= 0 then
speed = speed-1
if speed < 0 then
speed = 0
end
end
if (ctrl.l + ctrl.r) ~= 0 or (ctrl.f + ctrl.b) ~= 0 then
bv.velocity = ((game.Workspace.CurrentCamera.CoordinateFrame.lookVector * (ctrl.f+ctrl.b)) + ((game.Workspace.CurrentCamera.CoordinateFrame * CFrame.new(ctrl.l+ctrl.r,(ctrl.f+ctrl.b)*.2,0).p) - game.Workspace.CurrentCamera.CoordinateFrame.p))*speed
lastctrl = {f = ctrl.f, b = ctrl.b, l = ctrl.l, r = ctrl.r}
elseif (ctrl.l + ctrl.r) == 0 and (ctrl.f + ctrl.b) == 0 and speed ~= 0 then
bv.velocity = ((game.Workspace.CurrentCamera.CoordinateFrame.lookVector * (lastctrl.f+lastctrl.b)) + ((game.Workspace.CurrentCamera.CoordinateFrame * CFrame.new(lastctrl.l+lastctrl.r,(lastctrl.f+lastctrl.b)*.2,0).p) - game.Workspace.CurrentCamera.CoordinateFrame.p))*speed
else
bv.velocity = Vector3.new(0,0.1,0)
end

until not flying
ctrl = {f = 0, b = 0, l = 0, r = 0}
lastctrl = {f = 0, b = 0, l = 0, r = 0}
speed = 0
bg:Destroy()
bv:Destroy()

end
mouse.KeyDown:connect(function(key)
if key:lower() == "e" then
if flying then flying = false
else
flying = true
Fly()
end
elseif key:lower() == "w" then
ctrl.f = 1
elseif key:lower() == "s" then
ctrl.b = -1
elseif key:lower() == "a" then
ctrl.l = -1
elseif key:lower() == "d" then
ctrl.r = 1
end
end)
mouse.KeyUp:connect(function(key)
if key:lower() == "w" then
ctrl.f = 0
elseif key:lower() == "s" then
ctrl.b = 0
elseif key:lower() == "a" then
ctrl.l = 0
elseif key:lower() == "d" then
ctrl.r = 0
elseif key:lower() == "r" then

end
end)
Fly()

		
		
	elseif k == "x" then
		
		
		spawn(function()
local message = Instance.new("Message",workspace)
message.Text = "Respawning dont spam"
wait(1)
message:Destroy()
end)
		
		local saved = groot.Position
		
local ch = game.Players.LocalPlayer.Character
local prt=Instance.new("Model", workspace)
local z1 =  Instance.new("Part", prt)
z1.Name="Torso"
z1.CanCollide = false
z1.Anchored = true
local z2  =Instance.new("Part", prt)
z2.Name="Head"
z2.Anchored = true
z2.CanCollide = false
local z3 =Instance.new("Humanoid", prt)
z3.Name="Humanoid"
z1.Position = Vector3.new(0,9999,0)
z2.Position = Vector3.new(0,9991,0)
 game.Players.LocalPlayer.Character=prt
wait(5)
 game.Players.LocalPlayer.Character=ch
local poop = nil
		repeat wait() poop = game.Players.LocalPlayer.Character:FindFirstChild("Head") until poop ~= nil
		wait(1)
		game:GetService("Players").LocalPlayer.Character.HumanoidRootPart.CFrame = CFrame.new(saved)
		
	end
	
	
end)
end)

Features & Information

Features: vape v4 (Roblox bedwars) 2. roblox esp 3.walk on walls 4. speed 5.ui toggle 6. rejoin (scraped) 7. two unused tabs 8.scripts for eazy.gg games 9.credits 10.misc 11.roblox admin scripts

Community Comments (0)

No comments yet for this script.

More Scripts for ⬆️ GET TO THE TOP! ⬆️ [NEW SHOP!]