yoitzErrorYT

Gravity Controller

Jun 11th, 2023
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 373.65 KB | Gaming | 0 0
  1. local _p = game:WaitForChild("Players")
  2. local _plr = _p.ChildAdded:Wait()
  3. if _plr == _p.LocalPlayer then
  4.     _plr.ChildAdded:Connect(function(cccc)
  5.         if c.Name == "PlayerScriptsLoader" then
  6.             c.Disabled = true
  7.         end
  8.     end)
  9. end
  10. ]]
  11. repeat wait()
  12. a = pcall(function()
  13.     game:WaitForChild("Players").LocalPlayer:WaitForChild("PlayerScripts").ChildAdded:Connect(function(c)
  14.         if c.Name == "PlayerScriptsLoader"then
  15.             c.Disabled = true
  16.         end
  17.     end)
  18.     end)
  19.     if a == true then break end
  20. until true == false
  21. game:WaitForChild("Players").LocalPlayer:WaitForChild("PlayerScripts").ChildAdded:Connect(function(c)
  22.     if c.Name == "PlayerScriptsLoader"then
  23.         c.Disabled = true
  24.     end
  25. end)
  26.  
  27.  
  28. function _CameraUI()
  29.     local Players = game:GetService("Players")
  30.     local TweenService = game:GetService("TweenService")
  31.    
  32.     local LocalPlayer = Players.LocalPlayer
  33.     if not LocalPlayer then
  34.         Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  35.         LocalPlayer = Players.LocalPlayer
  36.     end
  37.    
  38.     local function waitForChildOfClass(parent, class)
  39.         local child = parent:FindFirstChildOfClass(class)
  40.         while not child or child.ClassName ~= class do
  41.             child = parent.ChildAdded:Wait()
  42.         end
  43.         return child
  44.     end
  45.    
  46.     local PlayerGui = waitForChildOfClass(LocalPlayer, "PlayerGui")
  47.    
  48.     local TOAST_OPEN_SIZE = UDim2.new(0, 326, 0, 58)
  49.     local TOAST_CLOSED_SIZE = UDim2.new(0, 80, 0, 58)
  50.     local TOAST_BACKGROUND_COLOR = Color3.fromRGB(32, 32, 32)
  51.     local TOAST_BACKGROUND_TRANS = 0.4
  52.     local TOAST_FOREGROUND_COLOR = Color3.fromRGB(200, 200, 200)
  53.     local TOAST_FOREGROUND_TRANS = 0
  54.    
  55.     -- Convenient syntax for creating a tree of instanes
  56.     local function create(className)
  57.         return function(props)
  58.             local inst = Instance.new(className)
  59.             local parent = props.Parent
  60.             props.Parent = nil
  61.             for name, val in pairs(props) do
  62.                 if type(name) == "string" then
  63.                     inst[name] = val
  64.                 else
  65.                     val.Parent = inst
  66.                 end
  67.             end
  68.             -- Only set parent after all other properties are initialized
  69.             inst.Parent = parent
  70.             return inst
  71.         end
  72.     end
  73.    
  74.     local initialized = false
  75.    
  76.     local uiRoot
  77.     local toast
  78.     local toastIcon
  79.     local toastUpperText
  80.     local toastLowerText
  81.    
  82.     local function initializeUI()
  83.         assert(not initialized)
  84.    
  85.         uiRoot = create("ScreenGui"){
  86.             Name = "RbxCameraUI",
  87.             AutoLocalize = false,
  88.             Enabled = true,
  89.             DisplayOrder = -1, -- Appears behind default developer UI
  90.             IgnoreGuiInset = false,
  91.             ResetOnSpawn = false,
  92.             ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
  93.    
  94.             create("ImageLabel"){
  95.                 Name = "Toast",
  96.                 Visible = false,
  97.                 AnchorPoint = Vector2.new(0.5, 0),
  98.                 BackgroundTransparency = 1,
  99.                 BorderSizePixel = 0,
  100.                 Position = UDim2.new(0.5, 0, 0, 8),
  101.                 Size = TOAST_CLOSED_SIZE,
  102.                 Image = "rbxasset://textures/ui/Camera/CameraToast9Slice.png",
  103.                 ImageColor3 = TOAST_BACKGROUND_COLOR,
  104.                 ImageRectSize = Vector2.new(6, 6),
  105.                 ImageTransparency = 1,
  106.                 ScaleType = Enum.ScaleType.Slice,
  107.                 SliceCenter = Rect.new(3, 3, 3, 3),
  108.                 ClipsDescendants = true,
  109.    
  110.                 create("Frame"){
  111.                     Name = "IconBuffer",
  112.                     BackgroundTransparency = 1,
  113.                     BorderSizePixel = 0,
  114.                     Position = UDim2.new(0, 0, 0, 0),
  115.                     Size = UDim2.new(0, 80, 1, 0),
  116.    
  117.                     create("ImageLabel"){
  118.                         Name = "Icon",
  119.                         AnchorPoint = Vector2.new(0.5, 0.5),
  120.                         BackgroundTransparency = 1,
  121.                         Position = UDim2.new(0.5, 0, 0.5, 0),
  122.                         Size = UDim2.new(0, 48, 0, 48),
  123.                         ZIndex = 2,
  124.                         Image = "rbxasset://textures/ui/Camera/CameraToastIcon.png",
  125.                         ImageColor3 = TOAST_FOREGROUND_COLOR,
  126.                         ImageTransparency = 1,
  127.                     }
  128.                 },
  129.    
  130.                 create("Frame"){
  131.                     Name = "TextBuffer",
  132.                     BackgroundTransparency = 1,
  133.                     BorderSizePixel = 0,
  134.                     Position = UDim2.new(0, 80, 0, 0),
  135.                     Size = UDim2.new(1, -80, 1, 0),
  136.                     ClipsDescendants = true,
  137.    
  138.                     create("TextLabel"){
  139.                         Name = "Upper",
  140.                         AnchorPoint = Vector2.new(0, 1),
  141.                         BackgroundTransparency = 1,
  142.                         Position = UDim2.new(0, 0, 0.5, 0),
  143.                         Size = UDim2.new(1, 0, 0, 19),
  144.                         Font = Enum.Font.GothamSemibold,
  145.                         Text = "Camera control enabled",
  146.                         TextColor3 = TOAST_FOREGROUND_COLOR,
  147.                         TextTransparency = 1,
  148.                         TextSize = 19,
  149.                         TextXAlignment = Enum.TextXAlignment.Left,
  150.                         TextYAlignment = Enum.TextYAlignment.Center,
  151.                     },
  152.    
  153.                     create("TextLabel"){
  154.                         Name = "Lower",
  155.                         AnchorPoint = Vector2.new(0, 0),
  156.                         BackgroundTransparency = 1,
  157.                         Position = UDim2.new(0, 0, 0.5, 3),
  158.                         Size = UDim2.new(1, 0, 0, 15),
  159.                         Font = Enum.Font.Gotham,
  160.                         Text = "Right mouse button to toggle",
  161.                         TextColor3 = TOAST_FOREGROUND_COLOR,
  162.                         TextTransparency = 1,
  163.                         TextSize = 15,
  164.                         TextXAlignment = Enum.TextXAlignment.Left,
  165.                         TextYAlignment = Enum.TextYAlignment.Center,
  166.                     },
  167.                 },
  168.             },
  169.    
  170.             Parent = PlayerGui,
  171.         }
  172.    
  173.         toast = uiRoot.Toast
  174.         toastIcon = toast.IconBuffer.Icon
  175.         toastUpperText = toast.TextBuffer.Upper
  176.         toastLowerText = toast.TextBuffer.Lower
  177.    
  178.         initialized = true
  179.     end
  180.    
  181.     local CameraUI = {}
  182.    
  183.     do
  184.         -- Instantaneously disable the toast or enable for opening later on. Used when switching camera modes.
  185.         function CameraUI.setCameraModeToastEnabled(enabled)
  186.             if not enabled and not initialized then
  187.                 return
  188.             end
  189.    
  190.             if not initialized then
  191.                 initializeUI()
  192.             end
  193.    
  194.             toast.Visible = enabled
  195.             if not enabled then
  196.                 CameraUI.setCameraModeToastOpen(false)
  197.             end
  198.         end
  199.    
  200.         local tweenInfo = TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
  201.    
  202.         -- Tween the toast in or out. Toast must be enabled with setCameraModeToastEnabled.
  203.         function CameraUI.setCameraModeToastOpen(open)
  204.             assert(initialized)
  205.    
  206.             TweenService:Create(toast, tweenInfo, {
  207.                 Size = open and TOAST_OPEN_SIZE or TOAST_CLOSED_SIZE,
  208.                 ImageTransparency = open and TOAST_BACKGROUND_TRANS or 1,
  209.             }):Play()
  210.    
  211.             TweenService:Create(toastIcon, tweenInfo, {
  212.                 ImageTransparency = open and TOAST_FOREGROUND_TRANS or 1,
  213.             }):Play()
  214.    
  215.             TweenService:Create(toastUpperText, tweenInfo, {
  216.                 TextTransparency = open and TOAST_FOREGROUND_TRANS or 1,
  217.             }):Play()
  218.    
  219.             TweenService:Create(toastLowerText, tweenInfo, {
  220.                 TextTransparency = open and TOAST_FOREGROUND_TRANS or 1,
  221.             }):Play()
  222.         end
  223.     end
  224.    
  225.     return CameraUI
  226. end
  227.  
  228. function _CameraToggleStateController()
  229.     local Players = game:GetService("Players")
  230.     local UserInputService = game:GetService("UserInputService")
  231.     local GameSettings = UserSettings():GetService("UserGameSettings")
  232.    
  233.     local LocalPlayer = Players.LocalPlayer
  234.     if not LocalPlayer then
  235.         Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  236.         LocalPlayer = Players.LocalPlayer
  237.     end
  238.    
  239.     local Mouse = LocalPlayer:GetMouse()
  240.    
  241.     local Input = _CameraInput()
  242.     local CameraUI = _CameraUI()
  243.    
  244.     local lastTogglePan = false
  245.     local lastTogglePanChange = tick()
  246.    
  247.     local CROSS_MOUSE_ICON = "rbxasset://textures/Cursors/CrossMouseIcon.png"
  248.    
  249.     local lockStateDirty = false
  250.     local wasTogglePanOnTheLastTimeYouWentIntoFirstPerson = false
  251.     local lastFirstPerson = false
  252.    
  253.     CameraUI.setCameraModeToastEnabled(false)
  254.    
  255.     return function(isFirstPerson)
  256.         local togglePan = Input.getTogglePan()
  257.         local toastTimeout = 3
  258.    
  259.         if isFirstPerson and togglePan ~= lastTogglePan then
  260.             lockStateDirty = true
  261.         end
  262.    
  263.         if lastTogglePan ~= togglePan or tick() - lastTogglePanChange > toastTimeout then
  264.             local doShow = togglePan and tick() - lastTogglePanChange < toastTimeout
  265.    
  266.             CameraUI.setCameraModeToastOpen(doShow)
  267.    
  268.             if togglePan then
  269.                 lockStateDirty = false
  270.             end
  271.             lastTogglePanChange = tick()
  272.             lastTogglePan = togglePan
  273.         end
  274.    
  275.         if isFirstPerson ~= lastFirstPerson then
  276.             if isFirstPerson then
  277.                 wasTogglePanOnTheLastTimeYouWentIntoFirstPerson = Input.getTogglePan()
  278.                 Input.setTogglePan(true)
  279.             elseif not lockStateDirty then
  280.                 Input.setTogglePan(wasTogglePanOnTheLastTimeYouWentIntoFirstPerson)
  281.             end
  282.         end
  283.    
  284.         if isFirstPerson then
  285.             if Input.getTogglePan() then
  286.                 Mouse.Icon = CROSS_MOUSE_ICON
  287.                 UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
  288.                 --GameSettings.RotationType = Enum.RotationType.CameraRelative
  289.             else
  290.                 Mouse.Icon = ""
  291.                 UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  292.                 --GameSettings.RotationType = Enum.RotationType.CameraRelative
  293.             end
  294.    
  295.         elseif Input.getTogglePan() then
  296.             Mouse.Icon = CROSS_MOUSE_ICON
  297.             UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
  298.             GameSettings.RotationType = Enum.RotationType.MovementRelative
  299.    
  300.         elseif Input.getHoldPan() then
  301.             Mouse.Icon = ""
  302.             UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
  303.             GameSettings.RotationType = Enum.RotationType.MovementRelative
  304.    
  305.         else
  306.             Mouse.Icon = ""
  307.             UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  308.             GameSettings.RotationType = Enum.RotationType.MovementRelative
  309.         end
  310.    
  311.         lastFirstPerson = isFirstPerson
  312.     end
  313. end
  314.  
  315. function _CameraInput()
  316.     local UserInputService = game:GetService("UserInputService")
  317.    
  318.     local MB_TAP_LENGTH = 0.3 -- length of time for a short mouse button tap to be registered
  319.    
  320.     local rmbDown, rmbUp
  321.     do
  322.         local rmbDownBindable = Instance.new("BindableEvent")
  323.         local rmbUpBindable = Instance.new("BindableEvent")
  324.    
  325.         rmbDown = rmbDownBindable.Event
  326.         rmbUp = rmbUpBindable.Event
  327.    
  328.         UserInputService.InputBegan:Connect(function(input, gpe)
  329.             if not gpe and input.UserInputType == Enum.UserInputType.MouseButton2 then
  330.                 rmbDownBindable:Fire()
  331.             end
  332.         end)
  333.    
  334.         UserInputService.InputEnded:Connect(function(input, gpe)
  335.             if input.UserInputType == Enum.UserInputType.MouseButton2 then
  336.                 rmbUpBindable:Fire()
  337.             end
  338.         end)
  339.     end
  340.    
  341.     local holdPan = false
  342.     local togglePan = false
  343.     local lastRmbDown = 0 -- tick() timestamp of the last right mouse button down event
  344.    
  345.     local CameraInput = {}
  346.    
  347.     function CameraInput.getHoldPan()
  348.         return holdPan
  349.     end
  350.    
  351.     function CameraInput.getTogglePan()
  352.         return togglePan
  353.     end
  354.    
  355.     function CameraInput.getPanning()
  356.         return togglePan or holdPan
  357.     end
  358.    
  359.     function CameraInput.setTogglePan(value)
  360.         togglePan = value
  361.     end
  362.    
  363.     local cameraToggleInputEnabled = false
  364.     local rmbDownConnection
  365.     local rmbUpConnection
  366.    
  367.     function CameraInput.enableCameraToggleInput()
  368.         if cameraToggleInputEnabled then
  369.             return
  370.         end
  371.         cameraToggleInputEnabled = true
  372.    
  373.         holdPan = false
  374.         togglePan = false
  375.    
  376.         if rmbDownConnection then
  377.             rmbDownConnection:Disconnect()
  378.         end
  379.    
  380.         if rmbUpConnection then
  381.             rmbUpConnection:Disconnect()
  382.         end
  383.    
  384.         rmbDownConnection = rmbDown:Connect(function()
  385.             holdPan = true
  386.             lastRmbDown = tick()
  387.         end)
  388.    
  389.         rmbUpConnection = rmbUp:Connect(function()
  390.             holdPan = false
  391.             if tick() - lastRmbDown < MB_TAP_LENGTH and (togglePan or UserInputService:GetMouseDelta().Magnitude < 2) then
  392.                 togglePan = not togglePan
  393.             end
  394.         end)
  395.     end
  396.    
  397.     function CameraInput.disableCameraToggleInput()
  398.         if not cameraToggleInputEnabled then
  399.             return
  400.         end
  401.         cameraToggleInputEnabled = false
  402.    
  403.         if rmbDownConnection then
  404.             rmbDownConnection:Disconnect()
  405.             rmbDownConnection = nil
  406.         end
  407.         if rmbUpConnection then
  408.             rmbUpConnection:Disconnect()
  409.             rmbUpConnection = nil
  410.         end
  411.     end
  412.    
  413.     return CameraInput
  414. end
  415.  
  416. function _BaseCamera()
  417.     --[[
  418.         BaseCamera - Abstract base class for camera control modules
  419.         2018 Camera Update - AllYourBlox
  420.     --]]
  421.    
  422.     --[[ Local Constants ]]--
  423.     local UNIT_Z = Vector3.new(0,0,1)
  424.     local X1_Y0_Z1 = Vector3.new(1,0,1) --Note: not a unit vector, used for projecting onto XZ plane
  425.    
  426.     local THUMBSTICK_DEADZONE = 0.2
  427.     local DEFAULT_DISTANCE = 12.5   -- Studs
  428.     local PORTRAIT_DEFAULT_DISTANCE = 25        -- Studs
  429.     local FIRST_PERSON_DISTANCE_THRESHOLD = 1.0 -- Below this value, snap into first person
  430.    
  431.     local CAMERA_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
  432.    
  433.     -- Note: DotProduct check in CoordinateFrame::lookAt() prevents using values within about
  434.     -- 8.11 degrees of the +/- Y axis, that's why these limits are currently 80 degrees
  435.     local MIN_Y = math.rad(-80)
  436.     local MAX_Y = math.rad(80)
  437.    
  438.     local TOUCH_ADJUST_AREA_UP = math.rad(30)
  439.     local TOUCH_ADJUST_AREA_DOWN = math.rad(-15)
  440.    
  441.     local TOUCH_SENSITIVTY_ADJUST_MAX_Y = 2.1
  442.     local TOUCH_SENSITIVTY_ADJUST_MIN_Y = 0.5
  443.    
  444.     local VR_ANGLE = math.rad(15)
  445.     local VR_LOW_INTENSITY_ROTATION = Vector2.new(math.rad(15), 0)
  446.     local VR_HIGH_INTENSITY_ROTATION = Vector2.new(math.rad(45), 0)
  447.     local VR_LOW_INTENSITY_REPEAT = 0.1
  448.     local VR_HIGH_INTENSITY_REPEAT = 0.4
  449.    
  450.     local ZERO_VECTOR2 = Vector2.new(0,0)
  451.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  452.    
  453.     local TOUCH_SENSITIVTY = Vector2.new(0.00945 * math.pi, 0.003375 * math.pi)
  454.     local MOUSE_SENSITIVITY = Vector2.new( 0.002 * math.pi, 0.0015 * math.pi )
  455.    
  456.     local SEAT_OFFSET = Vector3.new(0,5,0)
  457.     local VR_SEAT_OFFSET = Vector3.new(0,4,0)
  458.     local HEAD_OFFSET = Vector3.new(0,1.5,0)
  459.     local R15_HEAD_OFFSET = Vector3.new(0, 1.5, 0)
  460.     local R15_HEAD_OFFSET_NO_SCALING = Vector3.new(0, 2, 0)
  461.     local HUMANOID_ROOT_PART_SIZE = Vector3.new(2, 2, 1)
  462.    
  463.     local GAMEPAD_ZOOM_STEP_1 = 0
  464.     local GAMEPAD_ZOOM_STEP_2 = 10
  465.     local GAMEPAD_ZOOM_STEP_3 = 20
  466.    
  467.     local PAN_SENSITIVITY = 20
  468.     local ZOOM_SENSITIVITY_CURVATURE = 0.5
  469.    
  470.     local abs = math.abs
  471.     local sign = math.sign
  472.    
  473.     local FFlagUserCameraToggle do
  474.         local success, result = pcall(function()
  475.             return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  476.         end)
  477.         FFlagUserCameraToggle = success and result
  478.     end
  479.    
  480.     local FFlagUserDontAdjustSensitvityForPortrait do
  481.         local success, result = pcall(function()
  482.             return UserSettings():IsUserFeatureEnabled("UserDontAdjustSensitvityForPortrait")
  483.         end)
  484.         FFlagUserDontAdjustSensitvityForPortrait = success and result
  485.     end
  486.    
  487.     local FFlagUserFixZoomInZoomOutDiscrepancy do
  488.         local success, result = pcall(function()
  489.             return UserSettings():IsUserFeatureEnabled("UserFixZoomInZoomOutDiscrepancy")
  490.         end)
  491.         FFlagUserFixZoomInZoomOutDiscrepancy = success and result
  492.     end
  493.    
  494.     local Util = _CameraUtils()
  495.     local ZoomController = _ZoomController()
  496.     local CameraToggleStateController = _CameraToggleStateController()
  497.     local CameraInput = _CameraInput()
  498.     local CameraUI = _CameraUI()
  499.    
  500.     --[[ Roblox Services ]]--
  501.     local Players = game:GetService("Players")
  502.     local UserInputService = game:GetService("UserInputService")
  503.     local StarterGui = game:GetService("StarterGui")
  504.     local GuiService = game:GetService("GuiService")
  505.     local ContextActionService = game:GetService("ContextActionService")
  506.     local VRService = game:GetService("VRService")
  507.     local UserGameSettings = UserSettings():GetService("UserGameSettings")
  508.    
  509.     local player = Players.LocalPlayer
  510.    
  511.     --[[ The Module ]]--
  512.     local BaseCamera = {}
  513.     BaseCamera.__index = BaseCamera
  514.    
  515.     function BaseCamera.new()
  516.         local self = setmetatable({}, BaseCamera)
  517.    
  518.         -- So that derived classes have access to this
  519.         self.FIRST_PERSON_DISTANCE_THRESHOLD = FIRST_PERSON_DISTANCE_THRESHOLD
  520.    
  521.         self.cameraType = nil
  522.         self.cameraMovementMode = nil
  523.    
  524.         self.lastCameraTransform = nil
  525.         self.rotateInput = ZERO_VECTOR2
  526.         self.userPanningCamera = false
  527.         self.lastUserPanCamera = tick()
  528.    
  529.         self.humanoidRootPart = nil
  530.         self.humanoidCache = {}
  531.    
  532.         -- Subject and position on last update call
  533.         self.lastSubject = nil
  534.         self.lastSubjectPosition = Vector3.new(0,5,0)
  535.    
  536.         -- These subject distance members refer to the nominal camera-to-subject follow distance that the camera
  537.         -- is trying to maintain, not the actual measured value.
  538.         -- The default is updated when screen orientation or the min/max distances change,
  539.         -- to be sure the default is always in range and appropriate for the orientation.
  540.         self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  541.         self.currentSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  542.    
  543.         self.inFirstPerson = false
  544.         self.inMouseLockedMode = false
  545.         self.portraitMode = false
  546.         self.isSmallTouchScreen = false
  547.    
  548.         -- Used by modules which want to reset the camera angle on respawn.
  549.         self.resetCameraAngle = true
  550.    
  551.         self.enabled = false
  552.    
  553.         -- Input Event Connections
  554.         self.inputBeganConn = nil
  555.         self.inputChangedConn = nil
  556.         self.inputEndedConn = nil
  557.    
  558.         self.startPos = nil
  559.         self.lastPos = nil
  560.         self.panBeginLook = nil
  561.    
  562.         self.panEnabled = true
  563.         self.keyPanEnabled = true
  564.         self.distanceChangeEnabled = true
  565.    
  566.         self.PlayerGui = nil
  567.    
  568.         self.cameraChangedConn = nil
  569.         self.viewportSizeChangedConn = nil
  570.    
  571.         self.boundContextActions = {}
  572.    
  573.         -- VR Support
  574.         self.shouldUseVRRotation = false
  575.         self.VRRotationIntensityAvailable = false
  576.         self.lastVRRotationIntensityCheckTime = 0
  577.         self.lastVRRotationTime = 0
  578.         self.vrRotateKeyCooldown = {}
  579.         self.cameraTranslationConstraints = Vector3.new(1, 1, 1)
  580.         self.humanoidJumpOrigin = nil
  581.         self.trackingHumanoid = nil
  582.         self.cameraFrozen = false
  583.         self.subjectStateChangedConn = nil
  584.    
  585.         -- Gamepad support
  586.         self.activeGamepad = nil
  587.         self.gamepadPanningCamera = false
  588.         self.lastThumbstickRotate = nil
  589.         self.numOfSeconds = 0.7
  590.         self.currentSpeed = 0
  591.         self.maxSpeed = 6
  592.         self.vrMaxSpeed = 4
  593.         self.lastThumbstickPos = Vector2.new(0,0)
  594.         self.ySensitivity = 0.65
  595.         self.lastVelocity = nil
  596.         self.gamepadConnectedConn = nil
  597.         self.gamepadDisconnectedConn = nil
  598.         self.currentZoomSpeed = 1.0
  599.         self.L3ButtonDown = false
  600.         self.dpadLeftDown = false
  601.         self.dpadRightDown = false
  602.    
  603.         -- Touch input support
  604.         self.isDynamicThumbstickEnabled = false
  605.         self.fingerTouches = {}
  606.         self.dynamicTouchInput = nil
  607.         self.numUnsunkTouches = 0
  608.         self.inputStartPositions = {}
  609.         self.inputStartTimes = {}
  610.         self.startingDiff = nil
  611.         self.pinchBeginZoom = nil
  612.         self.userPanningTheCamera = false
  613.         self.touchActivateConn = nil
  614.    
  615.         -- Mouse locked formerly known as shift lock mode
  616.         self.mouseLockOffset = ZERO_VECTOR3
  617.    
  618.         -- [[ NOTICE ]] --
  619.         -- Initialization things used to always execute at game load time, but now these camera modules are instantiated
  620.         -- when needed, so the code here may run well after the start of the game
  621.    
  622.         if player.Character then
  623.             self:OnCharacterAdded(player.Character)
  624.         end
  625.    
  626.         player.CharacterAdded:Connect(function(char)
  627.             self:OnCharacterAdded(char)
  628.         end)
  629.    
  630.         if self.cameraChangedConn then self.cameraChangedConn:Disconnect() end
  631.         self.cameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
  632.             self:OnCurrentCameraChanged()
  633.         end)
  634.         self:OnCurrentCameraChanged()
  635.    
  636.         if self.playerCameraModeChangeConn then self.playerCameraModeChangeConn:Disconnect() end
  637.         self.playerCameraModeChangeConn = player:GetPropertyChangedSignal("CameraMode"):Connect(function()
  638.             self:OnPlayerCameraPropertyChange()
  639.         end)
  640.    
  641.         if self.minDistanceChangeConn then self.minDistanceChangeConn:Disconnect() end
  642.         self.minDistanceChangeConn = player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(function()
  643.             self:OnPlayerCameraPropertyChange()
  644.         end)
  645.    
  646.         if self.maxDistanceChangeConn then self.maxDistanceChangeConn:Disconnect() end
  647.         self.maxDistanceChangeConn = player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(function()
  648.             self:OnPlayerCameraPropertyChange()
  649.         end)
  650.    
  651.         if self.playerDevTouchMoveModeChangeConn then self.playerDevTouchMoveModeChangeConn:Disconnect() end
  652.         self.playerDevTouchMoveModeChangeConn = player:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
  653.             self:OnDevTouchMovementModeChanged()
  654.         end)
  655.         self:OnDevTouchMovementModeChanged() -- Init
  656.    
  657.         if self.gameSettingsTouchMoveMoveChangeConn then self.gameSettingsTouchMoveMoveChangeConn:Disconnect() end
  658.         self.gameSettingsTouchMoveMoveChangeConn = UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
  659.             self:OnGameSettingsTouchMovementModeChanged()
  660.         end)
  661.         self:OnGameSettingsTouchMovementModeChanged() -- Init
  662.    
  663.         UserGameSettings:SetCameraYInvertVisible()
  664.         UserGameSettings:SetGamepadCameraSensitivityVisible()
  665.    
  666.         self.hasGameLoaded = game:IsLoaded()
  667.         if not self.hasGameLoaded then
  668.             self.gameLoadedConn = game.Loaded:Connect(function()
  669.                 self.hasGameLoaded = true
  670.                 self.gameLoadedConn:Disconnect()
  671.                 self.gameLoadedConn = nil
  672.             end)
  673.         end
  674.    
  675.         self:OnPlayerCameraPropertyChange()
  676.    
  677.         return self
  678.     end
  679.    
  680.     function BaseCamera:GetModuleName()
  681.         return "BaseCamera"
  682.     end
  683.    
  684.     function BaseCamera:OnCharacterAdded(char)
  685.         self.resetCameraAngle = self.resetCameraAngle or self:GetEnabled()
  686.         self.humanoidRootPart = nil
  687.         if UserInputService.TouchEnabled then
  688.             self.PlayerGui = player:WaitForChild("PlayerGui")
  689.             for _, child in ipairs(char:GetChildren()) do
  690.                 if child:IsA("Tool") then
  691.                     self.isAToolEquipped = true
  692.                 end
  693.             end
  694.             char.ChildAdded:Connect(function(child)
  695.                 if child:IsA("Tool") then
  696.                     self.isAToolEquipped = true
  697.                 end
  698.             end)
  699.             char.ChildRemoved:Connect(function(child)
  700.                 if child:IsA("Tool") then
  701.                     self.isAToolEquipped = false
  702.                 end
  703.             end)
  704.         end
  705.     end
  706.    
  707.     function BaseCamera:GetHumanoidRootPart()
  708.         if not self.humanoidRootPart then
  709.             if player.Character then
  710.                 local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
  711.                 if humanoid then
  712.                     self.humanoidRootPart = humanoid.RootPart
  713.                 end
  714.             end
  715.         end
  716.         return self.humanoidRootPart
  717.     end
  718.    
  719.     function BaseCamera:GetBodyPartToFollow(humanoid, isDead)
  720.         -- If the humanoid is dead, prefer the head part if one still exists as a sibling of the humanoid
  721.         if humanoid:GetState() == Enum.HumanoidStateType.Dead then
  722.             local character = humanoid.Parent
  723.             if character and character:IsA("Model") then
  724.                 return character:FindFirstChild("Head") or humanoid.RootPart
  725.             end
  726.         end
  727.    
  728.         return humanoid.RootPart
  729.     end
  730.    
  731.     function BaseCamera:GetSubjectPosition()
  732.         local result = self.lastSubjectPosition
  733.         local camera = game.Workspace.CurrentCamera
  734.         local cameraSubject = camera and camera.CameraSubject
  735.    
  736.         if cameraSubject then
  737.             if cameraSubject:IsA("Humanoid") then
  738.                 local humanoid = cameraSubject
  739.                 local humanoidIsDead = humanoid:GetState() == Enum.HumanoidStateType.Dead
  740.    
  741.                 if VRService.VREnabled and humanoidIsDead and humanoid == self.lastSubject then
  742.                     result = self.lastSubjectPosition
  743.                 else
  744.                     local bodyPartToFollow = humanoid.RootPart
  745.    
  746.                     -- If the humanoid is dead, prefer their head part as a follow target, if it exists
  747.                     if humanoidIsDead then
  748.                         if humanoid.Parent and humanoid.Parent:IsA("Model") then
  749.                             bodyPartToFollow = humanoid.Parent:FindFirstChild("Head") or bodyPartToFollow
  750.                         end
  751.                     end
  752.    
  753.                     if bodyPartToFollow and bodyPartToFollow:IsA("BasePart") then
  754.                         local heightOffset
  755.                         if humanoid.RigType == Enum.HumanoidRigType.R15 then
  756.                             if humanoid.AutomaticScalingEnabled then
  757.                                 heightOffset = R15_HEAD_OFFSET
  758.                                 if bodyPartToFollow == humanoid.RootPart then
  759.                                     local rootPartSizeOffset = (humanoid.RootPart.Size.Y/2) - (HUMANOID_ROOT_PART_SIZE.Y/2)
  760.                                     heightOffset = heightOffset + Vector3.new(0, rootPartSizeOffset, 0)
  761.                                 end
  762.                             else
  763.                                 heightOffset = R15_HEAD_OFFSET_NO_SCALING
  764.                             end
  765.                         else
  766.                             heightOffset = HEAD_OFFSET
  767.                         end
  768.    
  769.                         if humanoidIsDead then
  770.                             heightOffset = ZERO_VECTOR3
  771.                         end
  772.    
  773.                         result = bodyPartToFollow.CFrame.p + bodyPartToFollow.CFrame:vectorToWorldSpace(heightOffset + humanoid.CameraOffset)
  774.                     end
  775.                 end
  776.    
  777.             elseif cameraSubject:IsA("VehicleSeat") then
  778.                 local offset = SEAT_OFFSET
  779.                 if VRService.VREnabled then
  780.                     offset = VR_SEAT_OFFSET
  781.                 end
  782.                 result = cameraSubject.CFrame.p + cameraSubject.CFrame:vectorToWorldSpace(offset)
  783.             elseif cameraSubject:IsA("SkateboardPlatform") then
  784.                 result = cameraSubject.CFrame.p + SEAT_OFFSET
  785.             elseif cameraSubject:IsA("BasePart") then
  786.                 result = cameraSubject.CFrame.p
  787.             elseif cameraSubject:IsA("Model") then
  788.                 if cameraSubject.PrimaryPart then
  789.                     result = cameraSubject:GetPrimaryPartCFrame().p
  790.                 else
  791.                     result = cameraSubject:GetModelCFrame().p
  792.                 end
  793.             end
  794.         else
  795.             -- cameraSubject is nil
  796.             -- Note: Previous RootCamera did not have this else case and let self.lastSubject and self.lastSubjectPosition
  797.             -- both get set to nil in the case of cameraSubject being nil. This function now exits here to preserve the
  798.             -- last set valid values for these, as nil values are not handled cases
  799.             return
  800.         end
  801.    
  802.         self.lastSubject = cameraSubject
  803.         self.lastSubjectPosition = result
  804.    
  805.         return result
  806.     end
  807.    
  808.     function BaseCamera:UpdateDefaultSubjectDistance()
  809.         if self.portraitMode then
  810.             self.defaultSubjectDistance = math.clamp(PORTRAIT_DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  811.         else
  812.             self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  813.         end
  814.     end
  815.    
  816.     function BaseCamera:OnViewportSizeChanged()
  817.         local camera = game.Workspace.CurrentCamera
  818.         local size = camera.ViewportSize
  819.         self.portraitMode = size.X < size.Y
  820.         self.isSmallTouchScreen = UserInputService.TouchEnabled and (size.Y < 500 or size.X < 700)
  821.    
  822.         self:UpdateDefaultSubjectDistance()
  823.     end
  824.    
  825.     -- Listener for changes to workspace.CurrentCamera
  826.     function BaseCamera:OnCurrentCameraChanged()
  827.         if UserInputService.TouchEnabled then
  828.             if self.viewportSizeChangedConn then
  829.                 self.viewportSizeChangedConn:Disconnect()
  830.                 self.viewportSizeChangedConn = nil
  831.             end
  832.    
  833.             local newCamera = game.Workspace.CurrentCamera
  834.    
  835.             if newCamera then
  836.                 self:OnViewportSizeChanged()
  837.                 self.viewportSizeChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
  838.                     self:OnViewportSizeChanged()
  839.                 end)
  840.             end
  841.         end
  842.    
  843.         -- VR support additions
  844.         if self.cameraSubjectChangedConn then
  845.             self.cameraSubjectChangedConn:Disconnect()
  846.             self.cameraSubjectChangedConn = nil
  847.         end
  848.    
  849.         local camera = game.Workspace.CurrentCamera
  850.         if camera then
  851.             self.cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
  852.                 self:OnNewCameraSubject()
  853.             end)
  854.             self:OnNewCameraSubject()
  855.         end
  856.     end
  857.    
  858.     function BaseCamera:OnDynamicThumbstickEnabled()
  859.         if UserInputService.TouchEnabled then
  860.             self.isDynamicThumbstickEnabled = true
  861.         end
  862.     end
  863.    
  864.     function BaseCamera:OnDynamicThumbstickDisabled()
  865.         self.isDynamicThumbstickEnabled = false
  866.     end
  867.    
  868.     function BaseCamera:OnGameSettingsTouchMovementModeChanged()
  869.         if player.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
  870.             if (UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.DynamicThumbstick
  871.                 or UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.Default) then
  872.                 self:OnDynamicThumbstickEnabled()
  873.             else
  874.                 self:OnDynamicThumbstickDisabled()
  875.             end
  876.         end
  877.     end
  878.    
  879.     function BaseCamera:OnDevTouchMovementModeChanged()
  880.         if player.DevTouchMovementMode.Name == "DynamicThumbstick" then
  881.             self:OnDynamicThumbstickEnabled()
  882.         else
  883.             self:OnGameSettingsTouchMovementModeChanged()
  884.         end
  885.     end
  886.    
  887.     function BaseCamera:OnPlayerCameraPropertyChange()
  888.         -- This call forces re-evaluation of player.CameraMode and clamping to min/max distance which may have changed
  889.         self:SetCameraToSubjectDistance(self.currentSubjectDistance)
  890.     end
  891.    
  892.     function BaseCamera:GetCameraHeight()
  893.         if VRService.VREnabled and not self.inFirstPerson then
  894.             return math.sin(VR_ANGLE) * self.currentSubjectDistance
  895.         end
  896.         return 0
  897.     end
  898.    
  899.     function BaseCamera:InputTranslationToCameraAngleChange(translationVector, sensitivity)
  900.         if not FFlagUserDontAdjustSensitvityForPortrait then
  901.             local camera = game.Workspace.CurrentCamera
  902.             if camera and camera.ViewportSize.X > 0 and camera.ViewportSize.Y > 0 and (camera.ViewportSize.Y > camera.ViewportSize.X) then
  903.                 -- Screen has portrait orientation, swap X and Y sensitivity
  904.                 return translationVector * Vector2.new( sensitivity.Y, sensitivity.X)
  905.             end
  906.         end
  907.         return translationVector * sensitivity
  908.     end
  909.    
  910.     function BaseCamera:Enable(enable)
  911.         if self.enabled ~= enable then
  912.             self.enabled = enable
  913.             if self.enabled then
  914.                 self:ConnectInputEvents()
  915.                 self:BindContextActions()
  916.    
  917.                 if player.CameraMode == Enum.CameraMode.LockFirstPerson then
  918.                     self.currentSubjectDistance = 0.5
  919.                     if not self.inFirstPerson then
  920.                         self:EnterFirstPerson()
  921.                     end
  922.                 end
  923.             else
  924.                 self:DisconnectInputEvents()
  925.                 self:UnbindContextActions()
  926.                 -- Clean up additional event listeners and reset a bunch of properties
  927.                 self:Cleanup()
  928.             end
  929.         end
  930.     end
  931.    
  932.     function BaseCamera:GetEnabled()
  933.         return self.enabled
  934.     end
  935.    
  936.     function BaseCamera:OnInputBegan(input, processed)
  937.         if input.UserInputType == Enum.UserInputType.Touch then
  938.             self:OnTouchBegan(input, processed)
  939.         elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
  940.             self:OnMouse2Down(input, processed)
  941.         elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
  942.             self:OnMouse3Down(input, processed)
  943.         end
  944.     end
  945.    
  946.     function BaseCamera:OnInputChanged(input, processed)
  947.         if input.UserInputType == Enum.UserInputType.Touch then
  948.             self:OnTouchChanged(input, processed)
  949.         elseif input.UserInputType == Enum.UserInputType.MouseMovement then
  950.             self:OnMouseMoved(input, processed)
  951.         end
  952.     end
  953.    
  954.     function BaseCamera:OnInputEnded(input, processed)
  955.         if input.UserInputType == Enum.UserInputType.Touch then
  956.             self:OnTouchEnded(input, processed)
  957.         elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
  958.             self:OnMouse2Up(input, processed)
  959.         elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
  960.             self:OnMouse3Up(input, processed)
  961.         end
  962.     end
  963.    
  964.     function BaseCamera:OnPointerAction(wheel, pan, pinch, processed)
  965.         if processed then
  966.             return
  967.         end
  968.    
  969.         if pan.Magnitude > 0 then
  970.             local inversionVector = Vector2.new(1, UserGameSettings:GetCameraYInvertValue())
  971.             local rotateDelta = self:InputTranslationToCameraAngleChange(PAN_SENSITIVITY*pan, MOUSE_SENSITIVITY)*inversionVector
  972.             self.rotateInput = self.rotateInput + rotateDelta
  973.         end
  974.    
  975.         local zoom = self.currentSubjectDistance
  976.         local zoomDelta = -(wheel + pinch)
  977.    
  978.         if abs(zoomDelta) > 0 then
  979.             local newZoom
  980.             if self.inFirstPerson and zoomDelta > 0 then
  981.                 newZoom = FIRST_PERSON_DISTANCE_THRESHOLD
  982.             else
  983.                 if FFlagUserFixZoomInZoomOutDiscrepancy then
  984.                     if (zoomDelta > 0) then
  985.                         newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
  986.                     else
  987.                         newZoom = (zoom + zoomDelta) / (1 - zoomDelta*ZOOM_SENSITIVITY_CURVATURE)
  988.                     end
  989.                 else
  990.                     newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
  991.                 end
  992.             end
  993.    
  994.             self:SetCameraToSubjectDistance(newZoom)
  995.         end
  996.     end
  997.    
  998.     function BaseCamera:ConnectInputEvents()
  999.         self.pointerActionConn = UserInputService.PointerAction:Connect(function(wheel, pan, pinch, processed)
  1000.             self:OnPointerAction(wheel, pan, pinch, processed)
  1001.         end)
  1002.    
  1003.         self.inputBeganConn = UserInputService.InputBegan:Connect(function(input, processed)
  1004.             self:OnInputBegan(input, processed)
  1005.         end)
  1006.    
  1007.         self.inputChangedConn = UserInputService.InputChanged:Connect(function(input, processed)
  1008.             self:OnInputChanged(input, processed)
  1009.         end)
  1010.    
  1011.         self.inputEndedConn = UserInputService.InputEnded:Connect(function(input, processed)
  1012.             self:OnInputEnded(input, processed)
  1013.         end)
  1014.    
  1015.         self.menuOpenedConn = GuiService.MenuOpened:connect(function()
  1016.             self:ResetInputStates()
  1017.         end)
  1018.    
  1019.         self.gamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
  1020.             if self.activeGamepad ~= gamepadEnum then return end
  1021.             self.activeGamepad = nil
  1022.             self:AssignActivateGamepad()
  1023.         end)
  1024.    
  1025.         self.gamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum)
  1026.             if self.activeGamepad == nil then
  1027.                 self:AssignActivateGamepad()
  1028.             end
  1029.         end)
  1030.    
  1031.         self:AssignActivateGamepad()
  1032.         if not FFlagUserCameraToggle then
  1033.             self:UpdateMouseBehavior()
  1034.         end
  1035.     end
  1036.    
  1037.     function BaseCamera:BindContextActions()
  1038.         self:BindGamepadInputActions()
  1039.         self:BindKeyboardInputActions()
  1040.     end
  1041.    
  1042.     function BaseCamera:AssignActivateGamepad()
  1043.         local connectedGamepads = UserInputService:GetConnectedGamepads()
  1044.         if #connectedGamepads > 0 then
  1045.             for i = 1, #connectedGamepads do
  1046.                 if self.activeGamepad == nil then
  1047.                     self.activeGamepad = connectedGamepads[i]
  1048.                 elseif connectedGamepads[i].Value < self.activeGamepad.Value then
  1049.                     self.activeGamepad = connectedGamepads[i]
  1050.                 end
  1051.             end
  1052.         end
  1053.    
  1054.         if self.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
  1055.             self.activeGamepad = Enum.UserInputType.Gamepad1
  1056.         end
  1057.     end
  1058.    
  1059.     function BaseCamera:DisconnectInputEvents()
  1060.         if self.inputBeganConn then
  1061.             self.inputBeganConn:Disconnect()
  1062.             self.inputBeganConn = nil
  1063.         end
  1064.         if self.inputChangedConn then
  1065.             self.inputChangedConn:Disconnect()
  1066.             self.inputChangedConn = nil
  1067.         end
  1068.         if self.inputEndedConn then
  1069.             self.inputEndedConn:Disconnect()
  1070.             self.inputEndedConn = nil
  1071.         end
  1072.     end
  1073.    
  1074.     function BaseCamera:UnbindContextActions()
  1075.         for i = 1, #self.boundContextActions do
  1076.             ContextActionService:UnbindAction(self.boundContextActions[i])
  1077.         end
  1078.         self.boundContextActions = {}
  1079.     end
  1080.    
  1081.     function BaseCamera:Cleanup()
  1082.         if self.pointerActionConn then
  1083.             self.pointerActionConn:Disconnect()
  1084.             self.pointerActionConn = nil
  1085.         end
  1086.         if self.menuOpenedConn then
  1087.             self.menuOpenedConn:Disconnect()
  1088.             self.menuOpenedConn = nil
  1089.         end
  1090.         if self.mouseLockToggleConn then
  1091.             self.mouseLockToggleConn:Disconnect()
  1092.             self.mouseLockToggleConn = nil
  1093.         end
  1094.         if self.gamepadConnectedConn then
  1095.             self.gamepadConnectedConn:Disconnect()
  1096.             self.gamepadConnectedConn = nil
  1097.         end
  1098.         if self.gamepadDisconnectedConn then
  1099.             self.gamepadDisconnectedConn:Disconnect()
  1100.             self.gamepadDisconnectedConn = nil
  1101.         end
  1102.         if self.subjectStateChangedConn then
  1103.             self.subjectStateChangedConn:Disconnect()
  1104.             self.subjectStateChangedConn = nil
  1105.         end
  1106.         if self.viewportSizeChangedConn then
  1107.             self.viewportSizeChangedConn:Disconnect()
  1108.             self.viewportSizeChangedConn = nil
  1109.         end
  1110.         if self.touchActivateConn then
  1111.             self.touchActivateConn:Disconnect()
  1112.             self.touchActivateConn = nil
  1113.         end
  1114.    
  1115.         self.turningLeft = false
  1116.         self.turningRight = false
  1117.         self.lastCameraTransform = nil
  1118.         self.lastSubjectCFrame = nil
  1119.         self.userPanningTheCamera = false
  1120.         self.rotateInput = Vector2.new()
  1121.         self.gamepadPanningCamera = Vector2.new(0,0)
  1122.    
  1123.         -- Reset input states
  1124.         self.startPos = nil
  1125.         self.lastPos = nil
  1126.         self.panBeginLook = nil
  1127.         self.isRightMouseDown = false
  1128.         self.isMiddleMouseDown = false
  1129.    
  1130.         self.fingerTouches = {}
  1131.         self.dynamicTouchInput = nil
  1132.         self.numUnsunkTouches = 0
  1133.    
  1134.         self.startingDiff = nil
  1135.         self.pinchBeginZoom = nil
  1136.    
  1137.         -- Unlock mouse for example if right mouse button was being held down
  1138.         if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
  1139.             UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  1140.         end
  1141.     end
  1142.    
  1143.     -- This is called when settings menu is opened
  1144.     function BaseCamera:ResetInputStates()
  1145.         self.isRightMouseDown = false
  1146.         self.isMiddleMouseDown = false
  1147.         self:OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters
  1148.    
  1149.         if UserInputService.TouchEnabled then
  1150.             --[[menu opening was causing serious touch issues
  1151.             this should disable all active touch events if
  1152.             they're active when menu opens.]]
  1153.             for inputObject in pairs(self.fingerTouches) do
  1154.                 self.fingerTouches[inputObject] = nil
  1155.             end
  1156.             self.dynamicTouchInput = nil
  1157.             self.panBeginLook = nil
  1158.             self.startPos = nil
  1159.             self.lastPos = nil
  1160.             self.userPanningTheCamera = false
  1161.             self.startingDiff = nil
  1162.             self.pinchBeginZoom = nil
  1163.             self.numUnsunkTouches = 0
  1164.         end
  1165.     end
  1166.    
  1167.     function BaseCamera:GetGamepadPan(name, state, input)
  1168.         if input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
  1169.     --      if self.L3ButtonDown then
  1170.     --          -- L3 Thumbstick is depressed, right stick controls dolly in/out
  1171.     --          if (input.Position.Y > THUMBSTICK_DEADZONE) then
  1172.     --              self.currentZoomSpeed = 0.96
  1173.     --          elseif (input.Position.Y < -THUMBSTICK_DEADZONE) then
  1174.     --              self.currentZoomSpeed = 1.04
  1175.     --          else
  1176.     --              self.currentZoomSpeed = 1.00
  1177.     --          end
  1178.     --      else
  1179.                 if state == Enum.UserInputState.Cancel then
  1180.                     self.gamepadPanningCamera = ZERO_VECTOR2
  1181.                     return
  1182.                 end
  1183.    
  1184.                 local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
  1185.                 if inputVector.magnitude > THUMBSTICK_DEADZONE then
  1186.                     self.gamepadPanningCamera = Vector2.new(input.Position.X, -input.Position.Y)
  1187.                 else
  1188.                     self.gamepadPanningCamera = ZERO_VECTOR2
  1189.                 end
  1190.             --end
  1191.             return Enum.ContextActionResult.Sink
  1192.         end
  1193.         return Enum.ContextActionResult.Pass
  1194.     end
  1195.    
  1196.     function BaseCamera:DoKeyboardPanTurn(name, state, input)
  1197.         if not self.hasGameLoaded and VRService.VREnabled then
  1198.             return Enum.ContextActionResult.Pass
  1199.         end
  1200.    
  1201.         if state == Enum.UserInputState.Cancel then
  1202.             self.turningLeft = false
  1203.             self.turningRight = false
  1204.             return Enum.ContextActionResult.Sink
  1205.         end
  1206.    
  1207.         if self.panBeginLook == nil and self.keyPanEnabled then
  1208.             if input.KeyCode == Enum.KeyCode.Left then
  1209.                 self.turningLeft = state == Enum.UserInputState.Begin
  1210.             elseif input.KeyCode == Enum.KeyCode.Right then
  1211.                 self.turningRight = state == Enum.UserInputState.Begin
  1212.             end
  1213.             return Enum.ContextActionResult.Sink
  1214.         end
  1215.         return Enum.ContextActionResult.Pass
  1216.     end
  1217.    
  1218.     function BaseCamera:DoPanRotateCamera(rotateAngle)
  1219.         local angle = Util.RotateVectorByAngleAndRound(self:GetCameraLookVector() * Vector3.new(1,0,1), rotateAngle, math.pi*0.25)
  1220.         if angle ~= 0 then
  1221.             self.rotateInput = self.rotateInput + Vector2.new(angle, 0)
  1222.             self.lastUserPanCamera = tick()
  1223.             self.lastCameraTransform = nil
  1224.         end
  1225.     end
  1226.    
  1227.     function BaseCamera:DoGamepadZoom(name, state, input)
  1228.         if input.UserInputType == self.activeGamepad then
  1229.             if input.KeyCode == Enum.KeyCode.ButtonR3 then
  1230.                 if state == Enum.UserInputState.Begin then
  1231.                     if self.distanceChangeEnabled then
  1232.                         local dist = self:GetCameraToSubjectDistance()
  1233.    
  1234.                         if dist > (GAMEPAD_ZOOM_STEP_2 + GAMEPAD_ZOOM_STEP_3)/2 then
  1235.                             self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_2)
  1236.                         elseif dist > (GAMEPAD_ZOOM_STEP_1 + GAMEPAD_ZOOM_STEP_2)/2 then
  1237.                             self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_1)
  1238.                         else
  1239.                             self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_3)
  1240.                         end
  1241.                     end
  1242.                 end
  1243.             elseif input.KeyCode == Enum.KeyCode.DPadLeft then
  1244.                 self.dpadLeftDown = (state == Enum.UserInputState.Begin)
  1245.             elseif input.KeyCode == Enum.KeyCode.DPadRight then
  1246.                 self.dpadRightDown = (state == Enum.UserInputState.Begin)
  1247.             end
  1248.    
  1249.             if self.dpadLeftDown then
  1250.                 self.currentZoomSpeed = 1.04
  1251.             elseif self.dpadRightDown then
  1252.                 self.currentZoomSpeed = 0.96
  1253.             else
  1254.                 self.currentZoomSpeed = 1.00
  1255.             end
  1256.             return Enum.ContextActionResult.Sink
  1257.         end
  1258.         return Enum.ContextActionResult.Pass
  1259.     --  elseif input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonL3 then
  1260.     --      if (state == Enum.UserInputState.Begin) then
  1261.     --          self.L3ButtonDown = true
  1262.     --      elseif (state == Enum.UserInputState.End) then
  1263.     --          self.L3ButtonDown = false
  1264.     --          self.currentZoomSpeed = 1.00
  1265.     --      end
  1266.     --  end
  1267.     end
  1268.    
  1269.     function BaseCamera:DoKeyboardZoom(name, state, input)
  1270.         if not self.hasGameLoaded and VRService.VREnabled then
  1271.             return Enum.ContextActionResult.Pass
  1272.         end
  1273.    
  1274.         if state ~= Enum.UserInputState.Begin then
  1275.             return Enum.ContextActionResult.Pass
  1276.         end
  1277.    
  1278.         if self.distanceChangeEnabled and player.CameraMode ~= Enum.CameraMode.LockFirstPerson then
  1279.             if input.KeyCode == Enum.KeyCode.I then
  1280.                 self:SetCameraToSubjectDistance( self.currentSubjectDistance - 5 )
  1281.             elseif input.KeyCode == Enum.KeyCode.O then
  1282.                 self:SetCameraToSubjectDistance( self.currentSubjectDistance + 5 )
  1283.             end
  1284.             return Enum.ContextActionResult.Sink
  1285.         end
  1286.         return Enum.ContextActionResult.Pass
  1287.     end
  1288.    
  1289.     function BaseCamera:BindAction(actionName, actionFunc, createTouchButton, ...)
  1290.         table.insert(self.boundContextActions, actionName)
  1291.         ContextActionService:BindActionAtPriority(actionName, actionFunc, createTouchButton,
  1292.             CAMERA_ACTION_PRIORITY, ...)
  1293.     end
  1294.    
  1295.     function BaseCamera:BindGamepadInputActions()
  1296.         self:BindAction("BaseCameraGamepadPan", function(name, state, input) return self:GetGamepadPan(name, state, input) end,
  1297.             false, Enum.KeyCode.Thumbstick2)
  1298.         self:BindAction("BaseCameraGamepadZoom", function(name, state, input) return self:DoGamepadZoom(name, state, input) end,
  1299.             false, Enum.KeyCode.DPadLeft, Enum.KeyCode.DPadRight, Enum.KeyCode.ButtonR3)
  1300.     end
  1301.    
  1302.     function BaseCamera:BindKeyboardInputActions()
  1303.         self:BindAction("BaseCameraKeyboardPanArrowKeys", function(name, state, input) return self:DoKeyboardPanTurn(name, state, input) end,
  1304.             false, Enum.KeyCode.Left, Enum.KeyCode.Right)
  1305.         self:BindAction("BaseCameraKeyboardZoom", function(name, state, input) return self:DoKeyboardZoom(name, state, input) end,
  1306.             false, Enum.KeyCode.I, Enum.KeyCode.O)
  1307.     end
  1308.    
  1309.     local function isInDynamicThumbstickArea(input)
  1310.         local playerGui = player:FindFirstChildOfClass("PlayerGui")
  1311.         local touchGui = playerGui and playerGui:FindFirstChild("TouchGui")
  1312.         local touchFrame = touchGui and touchGui:FindFirstChild("TouchControlFrame")
  1313.         local thumbstickFrame = touchFrame and touchFrame:FindFirstChild("DynamicThumbstickFrame")
  1314.    
  1315.         if not thumbstickFrame then
  1316.             return false
  1317.         end
  1318.    
  1319.         local frameCornerTopLeft = thumbstickFrame.AbsolutePosition
  1320.         local frameCornerBottomRight = frameCornerTopLeft + thumbstickFrame.AbsoluteSize
  1321.         if input.Position.X >= frameCornerTopLeft.X and input.Position.Y >= frameCornerTopLeft.Y then
  1322.             if input.Position.X <= frameCornerBottomRight.X and input.Position.Y <= frameCornerBottomRight.Y then
  1323.                 return true
  1324.             end
  1325.         end
  1326.    
  1327.         return false
  1328.     end
  1329.    
  1330.     ---Adjusts the camera Y touch Sensitivity when moving away from the center and in the TOUCH_SENSITIVTY_ADJUST_AREA
  1331.     function BaseCamera:AdjustTouchSensitivity(delta, sensitivity)
  1332.         local cameraCFrame = game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame
  1333.         if not cameraCFrame then
  1334.             return sensitivity
  1335.         end
  1336.         local currPitchAngle = cameraCFrame:ToEulerAnglesYXZ()
  1337.    
  1338.         local multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y
  1339.         if currPitchAngle > TOUCH_ADJUST_AREA_UP and delta.Y < 0 then
  1340.             local fractionAdjust = (currPitchAngle - TOUCH_ADJUST_AREA_UP)/(MAX_Y - TOUCH_ADJUST_AREA_UP)
  1341.             fractionAdjust = 1 - (1 - fractionAdjust)^3
  1342.             multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y - fractionAdjust * (
  1343.                 TOUCH_SENSITIVTY_ADJUST_MAX_Y - TOUCH_SENSITIVTY_ADJUST_MIN_Y)
  1344.         elseif currPitchAngle < TOUCH_ADJUST_AREA_DOWN and delta.Y > 0 then
  1345.             local fractionAdjust = (currPitchAngle - TOUCH_ADJUST_AREA_DOWN)/(MIN_Y - TOUCH_ADJUST_AREA_DOWN)
  1346.             fractionAdjust = 1 - (1 - fractionAdjust)^3
  1347.             multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y - fractionAdjust * (
  1348.                 TOUCH_SENSITIVTY_ADJUST_MAX_Y - TOUCH_SENSITIVTY_ADJUST_MIN_Y)
  1349.         end
  1350.    
  1351.         return Vector2.new(
  1352.             sensitivity.X,
  1353.             sensitivity.Y * multiplierY
  1354.         )
  1355.     end
  1356.    
  1357.     function BaseCamera:OnTouchBegan(input, processed)
  1358.         local canUseDynamicTouch = self.isDynamicThumbstickEnabled and not processed
  1359.         if canUseDynamicTouch then
  1360.             if self.dynamicTouchInput == nil and isInDynamicThumbstickArea(input) then
  1361.                 -- First input in the dynamic thumbstick area should always be ignored for camera purposes
  1362.                 -- Even if the dynamic thumbstick does not process it immediately
  1363.                 self.dynamicTouchInput = input
  1364.                 return
  1365.             end
  1366.             self.fingerTouches[input] = processed
  1367.             self.inputStartPositions[input] = input.Position
  1368.             self.inputStartTimes[input] = tick()
  1369.             self.numUnsunkTouches = self.numUnsunkTouches + 1
  1370.         end
  1371.     end
  1372.    
  1373.     function BaseCamera:OnTouchChanged(input, processed)
  1374.         if self.fingerTouches[input] == nil then
  1375.             if self.isDynamicThumbstickEnabled then
  1376.                 return
  1377.             end
  1378.             self.fingerTouches[input] = processed
  1379.             if not processed then
  1380.                 self.numUnsunkTouches = self.numUnsunkTouches + 1
  1381.             end
  1382.         end
  1383.    
  1384.         if self.numUnsunkTouches == 1 then
  1385.             if self.fingerTouches[input] == false then
  1386.                 self.panBeginLook = self.panBeginLook or self:GetCameraLookVector()
  1387.                 self.startPos = self.startPos or input.Position
  1388.                 self.lastPos = self.lastPos or self.startPos
  1389.                 self.userPanningTheCamera = true
  1390.    
  1391.                 local delta = input.Position - self.lastPos
  1392.                 delta = Vector2.new(delta.X, delta.Y * UserGameSettings:GetCameraYInvertValue())
  1393.                 if self.panEnabled then
  1394.                     local adjustedTouchSensitivity = TOUCH_SENSITIVTY
  1395.                     self:AdjustTouchSensitivity(delta, TOUCH_SENSITIVTY)
  1396.    
  1397.                     local desiredXYVector = self:InputTranslationToCameraAngleChange(delta, adjustedTouchSensitivity)
  1398.                     self.rotateInput = self.rotateInput + desiredXYVector
  1399.                 end
  1400.                 self.lastPos = input.Position
  1401.             end
  1402.         else
  1403.             self.panBeginLook = nil
  1404.             self.startPos = nil
  1405.             self.lastPos = nil
  1406.             self.userPanningTheCamera = false
  1407.         end
  1408.         if self.numUnsunkTouches == 2 then
  1409.             local unsunkTouches = {}
  1410.             for touch, wasSunk in pairs(self.fingerTouches) do
  1411.                 if not wasSunk then
  1412.                     table.insert(unsunkTouches, touch)
  1413.                 end
  1414.             end
  1415.             if #unsunkTouches == 2 then
  1416.                 local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
  1417.                 if self.startingDiff and self.pinchBeginZoom then
  1418.                     local scale = difference / math.max(0.01, self.startingDiff)
  1419.                     local clampedScale = math.clamp(scale, 0.1, 10)
  1420.                     if self.distanceChangeEnabled then
  1421.                         self:SetCameraToSubjectDistance(self.pinchBeginZoom / clampedScale)
  1422.                     end
  1423.                 else
  1424.                     self.startingDiff = difference
  1425.                     self.pinchBeginZoom = self:GetCameraToSubjectDistance()
  1426.                 end
  1427.             end
  1428.         else
  1429.             self.startingDiff = nil
  1430.             self.pinchBeginZoom = nil
  1431.         end
  1432.     end
  1433.    
  1434.     function BaseCamera:OnTouchEnded(input, processed)
  1435.         if input == self.dynamicTouchInput then
  1436.             self.dynamicTouchInput = nil
  1437.             return
  1438.         end
  1439.    
  1440.         if self.fingerTouches[input] == false then
  1441.             if self.numUnsunkTouches == 1 then
  1442.                 self.panBeginLook = nil
  1443.                 self.startPos = nil
  1444.                 self.lastPos = nil
  1445.                 self.userPanningTheCamera = false
  1446.             elseif self.numUnsunkTouches == 2 then
  1447.                 self.startingDiff = nil
  1448.                 self.pinchBeginZoom = nil
  1449.             end
  1450.         end
  1451.    
  1452.         if self.fingerTouches[input] ~= nil and self.fingerTouches[input] == false then
  1453.             self.numUnsunkTouches = self.numUnsunkTouches - 1
  1454.         end
  1455.         self.fingerTouches[input] = nil
  1456.         self.inputStartPositions[input] = nil
  1457.         self.inputStartTimes[input] = nil
  1458.     end
  1459.    
  1460.     function BaseCamera:OnMouse2Down(input, processed)
  1461.         if processed then return end
  1462.    
  1463.         self.isRightMouseDown = true
  1464.         self:OnMousePanButtonPressed(input, processed)
  1465.     end
  1466.    
  1467.     function BaseCamera:OnMouse2Up(input, processed)
  1468.         self.isRightMouseDown = false
  1469.         self:OnMousePanButtonReleased(input, processed)
  1470.     end
  1471.    
  1472.     function BaseCamera:OnMouse3Down(input, processed)
  1473.         if processed then return end
  1474.    
  1475.         self.isMiddleMouseDown = true
  1476.         self:OnMousePanButtonPressed(input, processed)
  1477.     end
  1478.    
  1479.     function BaseCamera:OnMouse3Up(input, processed)
  1480.         self.isMiddleMouseDown = false
  1481.         self:OnMousePanButtonReleased(input, processed)
  1482.     end
  1483.    
  1484.     function BaseCamera:OnMouseMoved(input, processed)
  1485.         if not self.hasGameLoaded and VRService.VREnabled then
  1486.             return
  1487.         end
  1488.    
  1489.         local inputDelta = input.Delta
  1490.         inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * UserGameSettings:GetCameraYInvertValue())
  1491.    
  1492.         local isInputPanning = FFlagUserCameraToggle and CameraInput.getPanning()
  1493.         local isBeginLook = self.startPos and self.lastPos and self.panBeginLook
  1494.         local isPanning = isBeginLook or self.inFirstPerson or self.inMouseLockedMode or isInputPanning
  1495.    
  1496.         if self.panEnabled and isPanning then
  1497.             local desiredXYVector = self:InputTranslationToCameraAngleChange(inputDelta, MOUSE_SENSITIVITY)
  1498.             self.rotateInput = self.rotateInput + desiredXYVector
  1499.         end
  1500.    
  1501.         if self.startPos and self.lastPos and self.panBeginLook then
  1502.             self.lastPos = self.lastPos + input.Delta
  1503.         end
  1504.     end
  1505.    
  1506.     function BaseCamera:OnMousePanButtonPressed(input, processed)
  1507.         if processed then return end
  1508.         if not FFlagUserCameraToggle then
  1509.             self:UpdateMouseBehavior()
  1510.         end
  1511.         self.panBeginLook = self.panBeginLook or self:GetCameraLookVector()
  1512.         self.startPos = self.startPos or input.Position
  1513.         self.lastPos = self.lastPos or self.startPos
  1514.         self.userPanningTheCamera = true
  1515.     end
  1516.    
  1517.     function BaseCamera:OnMousePanButtonReleased(input, processed)
  1518.         if not FFlagUserCameraToggle then
  1519.             self:UpdateMouseBehavior()
  1520.         end
  1521.         if not (self.isRightMouseDown or self.isMiddleMouseDown) then
  1522.             self.panBeginLook = nil
  1523.             self.startPos = nil
  1524.             self.lastPos = nil
  1525.             self.userPanningTheCamera = false
  1526.         end
  1527.     end
  1528.    
  1529.     function BaseCamera:UpdateMouseBehavior()
  1530.         if FFlagUserCameraToggle and self.isCameraToggle then
  1531.             CameraUI.setCameraModeToastEnabled(true)
  1532.             CameraInput.enableCameraToggleInput()
  1533.             CameraToggleStateController(self.inFirstPerson)
  1534.         else
  1535.             if FFlagUserCameraToggle then
  1536.                 CameraUI.setCameraModeToastEnabled(false)
  1537.                 CameraInput.disableCameraToggleInput()
  1538.             end
  1539.             -- first time transition to first person mode or mouse-locked third person
  1540.             if self.inFirstPerson or self.inMouseLockedMode then
  1541.                 --UserGameSettings.RotationType = Enum.RotationType.CameraRelative
  1542.                 UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
  1543.             else
  1544.                 UserGameSettings.RotationType = Enum.RotationType.MovementRelative
  1545.                 if self.isRightMouseDown or self.isMiddleMouseDown then
  1546.                     UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
  1547.                 else
  1548.                     UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  1549.                 end
  1550.             end
  1551.         end
  1552.     end
  1553.    
  1554.     function BaseCamera:UpdateForDistancePropertyChange()
  1555.         -- Calling this setter with the current value will force checking that it is still
  1556.         -- in range after a change to the min/max distance limits
  1557.         self:SetCameraToSubjectDistance(self.currentSubjectDistance)
  1558.     end
  1559.    
  1560.     function BaseCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
  1561.         local lastSubjectDistance = self.currentSubjectDistance
  1562.    
  1563.         -- By default, camera modules will respect LockFirstPerson and override the currentSubjectDistance with 0
  1564.         -- regardless of what Player.CameraMinZoomDistance is set to, so that first person can be made
  1565.         -- available by the developer without needing to allow players to mousewheel dolly into first person.
  1566.         -- Some modules will override this function to remove or change first-person capability.
  1567.         if player.CameraMode == Enum.CameraMode.LockFirstPerson then
  1568.             self.currentSubjectDistance = 0.5
  1569.             if not self.inFirstPerson then
  1570.                 self:EnterFirstPerson()
  1571.             end
  1572.         else
  1573.             local newSubjectDistance = math.clamp(desiredSubjectDistance, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  1574.             if newSubjectDistance < FIRST_PERSON_DISTANCE_THRESHOLD then
  1575.                 self.currentSubjectDistance = 0.5
  1576.                 if not self.inFirstPerson then
  1577.                     self:EnterFirstPerson()
  1578.                 end
  1579.             else
  1580.                 self.currentSubjectDistance = newSubjectDistance
  1581.                 if self.inFirstPerson then
  1582.                     self:LeaveFirstPerson()
  1583.                 end
  1584.             end
  1585.         end
  1586.    
  1587.         -- Pass target distance and zoom direction to the zoom controller
  1588.         ZoomController.SetZoomParameters(self.currentSubjectDistance, math.sign(desiredSubjectDistance - lastSubjectDistance))
  1589.    
  1590.         -- Returned only for convenience to the caller to know the outcome
  1591.         return self.currentSubjectDistance
  1592.     end
  1593.    
  1594.     function BaseCamera:SetCameraType( cameraType )
  1595.         --Used by derived classes
  1596.         self.cameraType = cameraType
  1597.     end
  1598.    
  1599.     function BaseCamera:GetCameraType()
  1600.         return self.cameraType
  1601.     end
  1602.    
  1603.     -- Movement mode standardized to Enum.ComputerCameraMovementMode values
  1604.     function BaseCamera:SetCameraMovementMode( cameraMovementMode )
  1605.         self.cameraMovementMode = cameraMovementMode
  1606.     end
  1607.    
  1608.     function BaseCamera:GetCameraMovementMode()
  1609.         return self.cameraMovementMode
  1610.     end
  1611.    
  1612.     function BaseCamera:SetIsMouseLocked(mouseLocked)
  1613.         self.inMouseLockedMode = mouseLocked
  1614.         if not FFlagUserCameraToggle then
  1615.             self:UpdateMouseBehavior()
  1616.         end
  1617.     end
  1618.    
  1619.     function BaseCamera:GetIsMouseLocked()
  1620.         return self.inMouseLockedMode
  1621.     end
  1622.    
  1623.     function BaseCamera:SetMouseLockOffset(offsetVector)
  1624.         self.mouseLockOffset = offsetVector
  1625.     end
  1626.    
  1627.     function BaseCamera:GetMouseLockOffset()
  1628.         return self.mouseLockOffset
  1629.     end
  1630.    
  1631.     function BaseCamera:InFirstPerson()
  1632.         return self.inFirstPerson
  1633.     end
  1634.    
  1635.     function BaseCamera:EnterFirstPerson()
  1636.         -- Overridden in ClassicCamera, the only module which supports FirstPerson
  1637.     end
  1638.    
  1639.     function BaseCamera:LeaveFirstPerson()
  1640.         -- Overridden in ClassicCamera, the only module which supports FirstPerson
  1641.     end
  1642.    
  1643.     -- Nominal distance, set by dollying in and out with the mouse wheel or equivalent, not measured distance
  1644.     function BaseCamera:GetCameraToSubjectDistance()
  1645.         return self.currentSubjectDistance
  1646.     end
  1647.    
  1648.     -- Actual measured distance to the camera Focus point, which may be needed in special circumstances, but should
  1649.     -- never be used as the starting point for updating the nominal camera-to-subject distance (self.currentSubjectDistance)
  1650.     -- since that is a desired target value set only by mouse wheel (or equivalent) input, PopperCam, and clamped to min max camera distance
  1651.     function BaseCamera:GetMeasuredDistanceToFocus()
  1652.         local camera = game.Workspace.CurrentCamera
  1653.         if camera then
  1654.             return (camera.CoordinateFrame.p - camera.Focus.p).magnitude
  1655.         end
  1656.         return nil
  1657.     end
  1658.    
  1659.     function BaseCamera:GetCameraLookVector()
  1660.         return game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame.lookVector or UNIT_Z
  1661.     end
  1662.    
  1663.     -- Replacements for RootCamera:RotateCamera() which did not actually rotate the camera
  1664.     -- suppliedLookVector is not normally passed in, it's used only by Watch camera
  1665.     function BaseCamera:CalculateNewLookCFrame(suppliedLookVector)
  1666.         local currLookVector = suppliedLookVector or self:GetCameraLookVector()
  1667.         local currPitchAngle = math.asin(currLookVector.y)
  1668.         local yTheta = math.clamp(self.rotateInput.y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
  1669.         local constrainedRotateInput = Vector2.new(self.rotateInput.x, yTheta)
  1670.         local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
  1671.         local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)
  1672.         return newLookCFrame
  1673.     end
  1674.     function BaseCamera:CalculateNewLookVector(suppliedLookVector)
  1675.         local newLookCFrame = self:CalculateNewLookCFrame(suppliedLookVector)
  1676.         return newLookCFrame.lookVector
  1677.     end
  1678.    
  1679.     function BaseCamera:CalculateNewLookVectorVR()
  1680.         local subjectPosition = self:GetSubjectPosition()
  1681.         local vecToSubject = (subjectPosition - game.Workspace.CurrentCamera.CFrame.p)
  1682.         local currLookVector = (vecToSubject * X1_Y0_Z1).unit
  1683.         local vrRotateInput = Vector2.new(self.rotateInput.x, 0)
  1684.         local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
  1685.         local yawRotatedVector = (CFrame.Angles(0, -vrRotateInput.x, 0) * startCFrame * CFrame.Angles(-vrRotateInput.y,0,0)).lookVector
  1686.         return (yawRotatedVector * X1_Y0_Z1).unit
  1687.     end
  1688.    
  1689.     function BaseCamera:GetHumanoid()
  1690.         local character = player and player.Character
  1691.         if character then
  1692.             local resultHumanoid = self.humanoidCache[player]
  1693.             if resultHumanoid and resultHumanoid.Parent == character then
  1694.                 return resultHumanoid
  1695.             else
  1696.                 self.humanoidCache[player] = nil -- Bust Old Cache
  1697.                 local humanoid = character:FindFirstChildOfClass("Humanoid")
  1698.                 if humanoid then
  1699.                     self.humanoidCache[player] = humanoid
  1700.                 end
  1701.                 return humanoid
  1702.             end
  1703.         end
  1704.         return nil
  1705.     end
  1706.    
  1707.     function BaseCamera:GetHumanoidPartToFollow(humanoid, humanoidStateType)
  1708.         if humanoidStateType == Enum.HumanoidStateType.Dead then
  1709.             local character = humanoid.Parent
  1710.             if character then
  1711.                 return character:FindFirstChild("Head") or humanoid.Torso
  1712.             else
  1713.                 return humanoid.Torso
  1714.             end
  1715.         else
  1716.             return humanoid.Torso
  1717.         end
  1718.     end
  1719.    
  1720.     function BaseCamera:UpdateGamepad()
  1721.         local gamepadPan = self.gamepadPanningCamera
  1722.         if gamepadPan and (self.hasGameLoaded or not VRService.VREnabled) then
  1723.             gamepadPan = Util.GamepadLinearToCurve(gamepadPan)
  1724.             local currentTime = tick()
  1725.             if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
  1726.                 self.userPanningTheCamera = true
  1727.             elseif gamepadPan == ZERO_VECTOR2 then
  1728.                 self.lastThumbstickRotate = nil
  1729.                 if self.lastThumbstickPos == ZERO_VECTOR2 then
  1730.                     self.currentSpeed = 0
  1731.                 end
  1732.             end
  1733.    
  1734.             local finalConstant = 0
  1735.    
  1736.             if self.lastThumbstickRotate then
  1737.                 if VRService.VREnabled then
  1738.                     self.currentSpeed = self.vrMaxSpeed
  1739.                 else
  1740.                     local elapsedTime = (currentTime - self.lastThumbstickRotate) * 10
  1741.                     self.currentSpeed = self.currentSpeed + (self.maxSpeed * ((elapsedTime*elapsedTime)/self.numOfSeconds))
  1742.    
  1743.                     if self.currentSpeed > self.maxSpeed then self.currentSpeed = self.maxSpeed end
  1744.    
  1745.                     if self.lastVelocity then
  1746.                         local velocity = (gamepadPan - self.lastThumbstickPos)/(currentTime - self.lastThumbstickRotate)
  1747.                         local velocityDeltaMag = (velocity - self.lastVelocity).magnitude
  1748.    
  1749.                         if velocityDeltaMag > 12 then
  1750.                             self.currentSpeed = self.currentSpeed * (20/velocityDeltaMag)
  1751.                             if self.currentSpeed > self.maxSpeed then self.currentSpeed = self.maxSpeed end
  1752.                         end
  1753.                     end
  1754.                 end
  1755.    
  1756.                 finalConstant = UserGameSettings.GamepadCameraSensitivity * self.currentSpeed
  1757.                 self.lastVelocity = (gamepadPan - self.lastThumbstickPos)/(currentTime - self.lastThumbstickRotate)
  1758.             end
  1759.    
  1760.             self.lastThumbstickPos = gamepadPan
  1761.             self.lastThumbstickRotate = currentTime
  1762.    
  1763.             return Vector2.new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * self.ySensitivity * UserGameSettings:GetCameraYInvertValue())
  1764.         end
  1765.    
  1766.         return ZERO_VECTOR2
  1767.     end
  1768.    
  1769.     -- [[ VR Support Section ]] --
  1770.    
  1771.     function BaseCamera:ApplyVRTransform()
  1772.         if not VRService.VREnabled then
  1773.             return
  1774.         end
  1775.    
  1776.         --we only want this to happen in first person VR
  1777.         local rootJoint = self.humanoidRootPart and self.humanoidRootPart:FindFirstChild("RootJoint")
  1778.         if not rootJoint then
  1779.             return
  1780.         end
  1781.    
  1782.         local cameraSubject = game.Workspace.CurrentCamera.CameraSubject
  1783.         local isInVehicle = cameraSubject and cameraSubject:IsA("VehicleSeat")
  1784.    
  1785.         if self.inFirstPerson and not isInVehicle then
  1786.             local vrFrame = VRService:GetUserCFrame(Enum.UserCFrame.Head)
  1787.             local vrRotation = vrFrame - vrFrame.p
  1788.             rootJoint.C0 = CFrame.new(vrRotation:vectorToObjectSpace(vrFrame.p)) * CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
  1789.         else
  1790.             rootJoint.C0 = CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
  1791.         end
  1792.     end
  1793.    
  1794.     function BaseCamera:IsInFirstPerson()
  1795.         return self.inFirstPerson
  1796.     end
  1797.    
  1798.     function BaseCamera:ShouldUseVRRotation()
  1799.         if not VRService.VREnabled then
  1800.             return false
  1801.         end
  1802.    
  1803.         if not self.VRRotationIntensityAvailable and tick() - self.lastVRRotationIntensityCheckTime < 1 then
  1804.             return false
  1805.         end
  1806.    
  1807.         local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
  1808.         self.VRRotationIntensityAvailable = success and vrRotationIntensity ~= nil
  1809.         self.lastVRRotationIntensityCheckTime = tick()
  1810.    
  1811.         self.shouldUseVRRotation = success and vrRotationIntensity ~= nil and vrRotationIntensity ~= "Smooth"
  1812.    
  1813.         return self.shouldUseVRRotation
  1814.     end
  1815.    
  1816.     function BaseCamera:GetVRRotationInput()
  1817.         local vrRotateSum = ZERO_VECTOR2
  1818.         local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
  1819.    
  1820.         if not success then
  1821.             return
  1822.         end
  1823.    
  1824.         local vrGamepadRotation = self.GamepadPanningCamera or ZERO_VECTOR2
  1825.         local delayExpired = (tick() - self.lastVRRotationTime) >= self:GetRepeatDelayValue(vrRotationIntensity)
  1826.    
  1827.         if math.abs(vrGamepadRotation.x) >= self:GetActivateValue() then
  1828.             if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]) then
  1829.                 local sign = 1
  1830.                 if vrGamepadRotation.x < 0 then
  1831.                     sign = -1
  1832.                 end
  1833.                 vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity) * sign
  1834.                 self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = true
  1835.             end
  1836.         elseif math.abs(vrGamepadRotation.x) < self:GetActivateValue() - 0.1 then
  1837.             self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = nil
  1838.         end
  1839.         if self.turningLeft then
  1840.             if delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Left] then
  1841.                 vrRotateSum = vrRotateSum - self:GetRotateAmountValue(vrRotationIntensity)
  1842.                 self.vrRotateKeyCooldown[Enum.KeyCode.Left] = true
  1843.             end
  1844.         else
  1845.             self.vrRotateKeyCooldown[Enum.KeyCode.Left] = nil
  1846.         end
  1847.         if self.turningRight then
  1848.             if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Right]) then
  1849.                 vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity)
  1850.                 self.vrRotateKeyCooldown[Enum.KeyCode.Right] = true
  1851.             end
  1852.         else
  1853.             self.vrRotateKeyCooldown[Enum.KeyCode.Right] = nil
  1854.         end
  1855.    
  1856.         if vrRotateSum ~= ZERO_VECTOR2 then
  1857.             self.lastVRRotationTime = tick()
  1858.         end
  1859.    
  1860.         return vrRotateSum
  1861.     end
  1862.    
  1863.     function BaseCamera:CancelCameraFreeze(keepConstraints)
  1864.         if not keepConstraints then
  1865.             self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 1, self.cameraTranslationConstraints.z)
  1866.         end
  1867.         if self.cameraFrozen then
  1868.             self.trackingHumanoid = nil
  1869.             self.cameraFrozen = false
  1870.         end
  1871.     end
  1872.    
  1873.     function BaseCamera:StartCameraFreeze(subjectPosition, humanoidToTrack)
  1874.         if not self.cameraFrozen then
  1875.             self.humanoidJumpOrigin = subjectPosition
  1876.             self.trackingHumanoid = humanoidToTrack
  1877.             self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 0, self.cameraTranslationConstraints.z)
  1878.             self.cameraFrozen = true
  1879.         end
  1880.     end
  1881.    
  1882.     function BaseCamera:OnNewCameraSubject()
  1883.         if self.subjectStateChangedConn then
  1884.             self.subjectStateChangedConn:Disconnect()
  1885.             self.subjectStateChangedConn = nil
  1886.         end
  1887.    
  1888.         local humanoid = workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject
  1889.         if self.trackingHumanoid ~= humanoid then
  1890.             self:CancelCameraFreeze()
  1891.         end
  1892.         if humanoid and humanoid:IsA("Humanoid") then
  1893.             self.subjectStateChangedConn = humanoid.StateChanged:Connect(function(oldState, newState)
  1894.                 if VRService.VREnabled and newState == Enum.HumanoidStateType.Jumping and not self.inFirstPerson then
  1895.                     self:StartCameraFreeze(self:GetSubjectPosition(), humanoid)
  1896.                 elseif newState ~= Enum.HumanoidStateType.Jumping and newState ~= Enum.HumanoidStateType.Freefall then
  1897.                     self:CancelCameraFreeze(true)
  1898.                 end
  1899.             end)
  1900.         end
  1901.     end
  1902.    
  1903.     function BaseCamera:GetVRFocus(subjectPosition, timeDelta)
  1904.         local lastFocus = self.LastCameraFocus or subjectPosition
  1905.         if not self.cameraFrozen then
  1906.             self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, math.min(1, self.cameraTranslationConstraints.y + 0.42 * timeDelta), self.cameraTranslationConstraints.z)
  1907.         end
  1908.    
  1909.         local newFocus
  1910.         if self.cameraFrozen and self.humanoidJumpOrigin and self.humanoidJumpOrigin.y > lastFocus.y then
  1911.             newFocus = CFrame.new(Vector3.new(subjectPosition.x, math.min(self.humanoidJumpOrigin.y, lastFocus.y + 5 * timeDelta), subjectPosition.z))
  1912.         else
  1913.             newFocus = CFrame.new(Vector3.new(subjectPosition.x, lastFocus.y, subjectPosition.z):lerp(subjectPosition, self.cameraTranslationConstraints.y))
  1914.         end
  1915.    
  1916.         if self.cameraFrozen then
  1917.             -- No longer in 3rd person
  1918.             if self.inFirstPerson then -- not VRService.VREnabled
  1919.                 self:CancelCameraFreeze()
  1920.             end
  1921.             -- This case you jumped off a cliff and want to keep your character in view
  1922.             -- 0.5 is to fix floating point error when not jumping off cliffs
  1923.             if self.humanoidJumpOrigin and subjectPosition.y < (self.humanoidJumpOrigin.y - 0.5) then
  1924.                 self:CancelCameraFreeze()
  1925.             end
  1926.         end
  1927.    
  1928.         return newFocus
  1929.     end
  1930.    
  1931.     function BaseCamera:GetRotateAmountValue(vrRotationIntensity)
  1932.         vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
  1933.         if vrRotationIntensity then
  1934.             if vrRotationIntensity == "Low" then
  1935.                 return VR_LOW_INTENSITY_ROTATION
  1936.             elseif vrRotationIntensity == "High" then
  1937.                 return VR_HIGH_INTENSITY_ROTATION
  1938.             end
  1939.         end
  1940.         return ZERO_VECTOR2
  1941.     end
  1942.    
  1943.     function BaseCamera:GetRepeatDelayValue(vrRotationIntensity)
  1944.         vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
  1945.         if vrRotationIntensity then
  1946.             if vrRotationIntensity == "Low" then
  1947.                 return VR_LOW_INTENSITY_REPEAT
  1948.             elseif vrRotationIntensity == "High" then
  1949.                 return VR_HIGH_INTENSITY_REPEAT
  1950.             end
  1951.         end
  1952.         return 0
  1953.     end
  1954.    
  1955.     function BaseCamera:Update(dt)
  1956.         error("BaseCamera:Update() This is a virtual function that should never be getting called.", 2)
  1957.     end
  1958.    
  1959.     BaseCamera.UpCFrame = CFrame.new()
  1960.    
  1961.     function BaseCamera:UpdateUpCFrame(cf)
  1962.         self.UpCFrame = cf
  1963.     end
  1964.     local ZERO = Vector3.new(0, 0, 0)
  1965.     function BaseCamera:CalculateNewLookCFrame(suppliedLookVector)
  1966.         local currLookVector = suppliedLookVector or self:GetCameraLookVector()
  1967.         currLookVector = self.UpCFrame:VectorToObjectSpace(currLookVector)
  1968.        
  1969.         local currPitchAngle = math.asin(currLookVector.y)
  1970.         local yTheta = math.clamp(self.rotateInput.y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
  1971.         local constrainedRotateInput = Vector2.new(self.rotateInput.x, yTheta)
  1972.         local startCFrame = CFrame.new(ZERO, currLookVector)
  1973.         local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)
  1974.        
  1975.         return newLookCFrame
  1976.     end
  1977.    
  1978.     return BaseCamera
  1979. end
  1980.  
  1981. function _BaseOcclusion()
  1982.     --[[ The Module ]]--
  1983.     local BaseOcclusion = {}
  1984.     BaseOcclusion.__index = BaseOcclusion
  1985.     setmetatable(BaseOcclusion, {
  1986.         __call = function(_, ...)
  1987.             return BaseOcclusion.new(...)
  1988.         end
  1989.     })
  1990.    
  1991.     function BaseOcclusion.new()
  1992.         local self = setmetatable({}, BaseOcclusion)
  1993.         return self
  1994.     end
  1995.    
  1996.     -- Called when character is added
  1997.     function BaseOcclusion:CharacterAdded(char, player)
  1998.     end
  1999.    
  2000.     -- Called when character is about to be removed
  2001.     function BaseOcclusion:CharacterRemoving(char, player)
  2002.     end
  2003.    
  2004.     function BaseOcclusion:OnCameraSubjectChanged(newSubject)
  2005.     end
  2006.    
  2007.     --[[ Derived classes are required to override and implement all of the following functions ]]--
  2008.     function BaseOcclusion:GetOcclusionMode()
  2009.         -- Must be overridden in derived classes to return an Enum.DevCameraOcclusionMode value
  2010.         warn("BaseOcclusion GetOcclusionMode must be overridden by derived classes")
  2011.         return nil
  2012.     end
  2013.    
  2014.     function BaseOcclusion:Enable(enabled)
  2015.         warn("BaseOcclusion Enable must be overridden by derived classes")
  2016.     end
  2017.    
  2018.     function BaseOcclusion:Update(dt, desiredCameraCFrame, desiredCameraFocus)
  2019.         warn("BaseOcclusion Update must be overridden by derived classes")
  2020.         return desiredCameraCFrame, desiredCameraFocus
  2021.     end
  2022.    
  2023.     return BaseOcclusion
  2024. end
  2025.  
  2026. function _Popper()
  2027.    
  2028.     local Players = game:GetService("Players")
  2029.    
  2030.     local camera = game.Workspace.CurrentCamera
  2031.    
  2032.     local min = math.min
  2033.     local tan = math.tan
  2034.     local rad = math.rad
  2035.     local inf = math.huge
  2036.     local ray = Ray.new
  2037.    
  2038.     local function getTotalTransparency(part)
  2039.         return 1 - (1 - part.Transparency)*(1 - part.LocalTransparencyModifier)
  2040.     end
  2041.    
  2042.     local function eraseFromEnd(t, toSize)
  2043.         for i = #t, toSize + 1, -1 do
  2044.             t[i] = nil
  2045.         end
  2046.     end
  2047.    
  2048.     local nearPlaneZ, projX, projY do
  2049.         local function updateProjection()
  2050.             local fov = rad(camera.FieldOfView)
  2051.             local view = camera.ViewportSize
  2052.             local ar = view.X/view.Y
  2053.    
  2054.             projY = 2*tan(fov/2)
  2055.             projX = ar*projY
  2056.         end
  2057.    
  2058.         camera:GetPropertyChangedSignal("FieldOfView"):Connect(updateProjection)
  2059.         camera:GetPropertyChangedSignal("ViewportSize"):Connect(updateProjection)
  2060.    
  2061.         updateProjection()
  2062.    
  2063.         nearPlaneZ = camera.NearPlaneZ
  2064.         camera:GetPropertyChangedSignal("NearPlaneZ"):Connect(function()
  2065.             nearPlaneZ = camera.NearPlaneZ
  2066.         end)
  2067.     end
  2068.    
  2069.     local blacklist = {} do
  2070.         local charMap = {}
  2071.    
  2072.         local function refreshIgnoreList()
  2073.             local n = 1
  2074.             blacklist = {}
  2075.             for _, character in pairs(charMap) do
  2076.                 blacklist[n] = character
  2077.                 n = n + 1
  2078.             end
  2079.         end
  2080.    
  2081.         local function playerAdded(player)
  2082.             local function characterAdded(character)
  2083.                 charMap[player] = character
  2084.                 refreshIgnoreList()
  2085.             end
  2086.             local function characterRemoving()
  2087.                 charMap[player] = nil
  2088.                 refreshIgnoreList()
  2089.             end
  2090.    
  2091.             player.CharacterAdded:Connect(characterAdded)
  2092.             player.CharacterRemoving:Connect(characterRemoving)
  2093.             if player.Character then
  2094.                 characterAdded(player.Character)
  2095.             end
  2096.         end
  2097.    
  2098.         local function playerRemoving(player)
  2099.             charMap[player] = nil
  2100.             refreshIgnoreList()
  2101.         end
  2102.    
  2103.         Players.PlayerAdded:Connect(playerAdded)
  2104.         Players.PlayerRemoving:Connect(playerRemoving)
  2105.    
  2106.         for _, player in ipairs(Players:GetPlayers()) do
  2107.             playerAdded(player)
  2108.         end
  2109.         refreshIgnoreList()
  2110.     end
  2111.    
  2112.     --------------------------------------------------------------------------------------------
  2113.     -- Popper uses the level geometry find an upper bound on subject-to-camera distance.
  2114.     --
  2115.     -- Hard limits are applied immediately and unconditionally. They are generally caused
  2116.     -- when level geometry intersects with the near plane (with exceptions, see below).
  2117.     --
  2118.     -- Soft limits are only applied under certain conditions.
  2119.     -- They are caused when level geometry occludes the subject without actually intersecting
  2120.     -- with the near plane at the target distance.
  2121.     --
  2122.     -- Soft limits can be promoted to hard limits and hard limits can be demoted to soft limits.
  2123.     -- We usually don"t want the latter to happen.
  2124.     --
  2125.     -- A soft limit will be promoted to a hard limit if an obstruction
  2126.     -- lies between the current and target camera positions.
  2127.     --------------------------------------------------------------------------------------------
  2128.    
  2129.     local subjectRoot
  2130.     local subjectPart
  2131.    
  2132.     camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
  2133.         local subject = camera.CameraSubject
  2134.         if subject:IsA("Humanoid") then
  2135.             subjectPart = subject.RootPart
  2136.         elseif subject:IsA("BasePart") then
  2137.             subjectPart = subject
  2138.         else
  2139.             subjectPart = nil
  2140.         end
  2141.     end)
  2142.    
  2143.     local function canOcclude(part)
  2144.         -- Occluders must be:
  2145.         -- 1. Opaque
  2146.         -- 2. Interactable
  2147.         -- 3. Not in the same assembly as the subject
  2148.    
  2149.         return
  2150.             getTotalTransparency(part) < 0.25 and
  2151.             part.CanCollide and
  2152.             subjectRoot ~= (part:GetRootPart() or part) and
  2153.             not part:IsA("TrussPart")
  2154.     end
  2155.    
  2156.     -- Offsets for the volume visibility test
  2157.     local SCAN_SAMPLE_OFFSETS = {
  2158.         Vector2.new( 0.4, 0.0),
  2159.         Vector2.new(-0.4, 0.0),
  2160.         Vector2.new( 0.0,-0.4),
  2161.         Vector2.new( 0.0, 0.4),
  2162.         Vector2.new( 0.0, 0.2),
  2163.     }
  2164.    
  2165.     --------------------------------------------------------------------------------
  2166.     -- Piercing raycasts
  2167.    
  2168.     local function getCollisionPoint(origin, dir)
  2169.         local originalSize = #blacklist
  2170.         repeat
  2171.             local hitPart, hitPoint = workspace:FindPartOnRayWithIgnoreList(
  2172.                 ray(origin, dir), blacklist, false, true
  2173.             )
  2174.    
  2175.             if hitPart then
  2176.                 if hitPart.CanCollide then
  2177.                     eraseFromEnd(blacklist, originalSize)
  2178.                     return hitPoint, true
  2179.                 end
  2180.                 blacklist[#blacklist + 1] = hitPart
  2181.             end
  2182.         until not hitPart
  2183.    
  2184.         eraseFromEnd(blacklist, originalSize)
  2185.         return origin + dir, false
  2186.     end
  2187.    
  2188.     --------------------------------------------------------------------------------
  2189.    
  2190.     local function queryPoint(origin, unitDir, dist, lastPos)
  2191.         debug.profilebegin("queryPoint")
  2192.    
  2193.         local originalSize = #blacklist
  2194.    
  2195.         dist = dist + nearPlaneZ
  2196.         local target = origin + unitDir*dist
  2197.    
  2198.         local softLimit = inf
  2199.         local hardLimit = inf
  2200.         local movingOrigin = origin
  2201.    
  2202.         repeat
  2203.             local entryPart, entryPos = workspace:FindPartOnRayWithIgnoreList(ray(movingOrigin, target - movingOrigin), blacklist, false, true)
  2204.    
  2205.             if entryPart then
  2206.                 if canOcclude(entryPart) then
  2207.                     local wl = {entryPart}
  2208.                     local exitPart = workspace:FindPartOnRayWithWhitelist(ray(target, entryPos - target), wl, true)
  2209.    
  2210.                     local lim = (entryPos - origin).Magnitude
  2211.    
  2212.                     if exitPart then
  2213.                         local promote = false
  2214.                         if lastPos then
  2215.                             promote =
  2216.                                 workspace:FindPartOnRayWithWhitelist(ray(lastPos, target - lastPos), wl, true) or
  2217.                                 workspace:FindPartOnRayWithWhitelist(ray(target, lastPos - target), wl, true)
  2218.                         end
  2219.    
  2220.                         if promote then
  2221.                             -- Ostensibly a soft limit, but the camera has passed through it in the last frame, so promote to a hard limit.
  2222.                             hardLimit = lim
  2223.                         elseif dist < softLimit then
  2224.                             -- Trivial soft limit
  2225.                             softLimit = lim
  2226.                         end
  2227.                     else
  2228.                         -- Trivial hard limit
  2229.                         hardLimit = lim
  2230.                     end
  2231.                 end
  2232.    
  2233.                 blacklist[#blacklist + 1] = entryPart
  2234.                 movingOrigin = entryPos - unitDir*1e-3
  2235.             end
  2236.         until hardLimit < inf or not entryPart
  2237.    
  2238.         eraseFromEnd(blacklist, originalSize)
  2239.    
  2240.         debug.profileend()
  2241.         return softLimit - nearPlaneZ, hardLimit - nearPlaneZ
  2242.     end
  2243.    
  2244.     local function queryViewport(focus, dist)
  2245.         debug.profilebegin("queryViewport")
  2246.    
  2247.         local fP =  focus.p
  2248.         local fX =  focus.rightVector
  2249.         local fY =  focus.upVector
  2250.         local fZ = -focus.lookVector
  2251.    
  2252.         local viewport = camera.ViewportSize
  2253.    
  2254.         local hardBoxLimit = inf
  2255.         local softBoxLimit = inf
  2256.    
  2257.         -- Center the viewport on the PoI, sweep points on the edge towards the target, and take the minimum limits
  2258.         for viewX = 0, 1 do
  2259.             local worldX = fX*((viewX - 0.5)*projX)
  2260.    
  2261.             for viewY = 0, 1 do
  2262.                 local worldY = fY*((viewY - 0.5)*projY)
  2263.    
  2264.                 local origin = fP + nearPlaneZ*(worldX + worldY)
  2265.                 local lastPos = camera:ViewportPointToRay(
  2266.                     viewport.x*viewX,
  2267.                     viewport.y*viewY
  2268.                 ).Origin
  2269.    
  2270.                 local softPointLimit, hardPointLimit = queryPoint(origin, fZ, dist, lastPos)
  2271.    
  2272.                 if hardPointLimit < hardBoxLimit then
  2273.                     hardBoxLimit = hardPointLimit
  2274.                 end
  2275.                 if softPointLimit < softBoxLimit then
  2276.                     softBoxLimit = softPointLimit
  2277.                 end
  2278.             end
  2279.         end
  2280.         debug.profileend()
  2281.    
  2282.         return softBoxLimit, hardBoxLimit
  2283.     end
  2284.    
  2285.     local function testPromotion(focus, dist, focusExtrapolation)
  2286.         debug.profilebegin("testPromotion")
  2287.    
  2288.         local fP = focus.p
  2289.         local fX = focus.rightVector
  2290.         local fY = focus.upVector
  2291.         local fZ = -focus.lookVector
  2292.    
  2293.         do
  2294.             -- Dead reckoning the camera rotation and focus
  2295.             debug.profilebegin("extrapolate")
  2296.    
  2297.             local SAMPLE_DT = 0.0625
  2298.             local SAMPLE_MAX_T = 1.25
  2299.    
  2300.             local maxDist = (getCollisionPoint(fP, focusExtrapolation.posVelocity*SAMPLE_MAX_T) - fP).Magnitude
  2301.             -- Metric that decides how many samples to take
  2302.             local combinedSpeed = focusExtrapolation.posVelocity.magnitude
  2303.    
  2304.             for dt = 0, min(SAMPLE_MAX_T, focusExtrapolation.rotVelocity.magnitude + maxDist/combinedSpeed), SAMPLE_DT do
  2305.                 local cfDt = focusExtrapolation.extrapolate(dt) -- Extrapolated CFrame at time dt
  2306.    
  2307.                 if queryPoint(cfDt.p, -cfDt.lookVector, dist) >= dist then
  2308.                     return false
  2309.                 end
  2310.             end
  2311.    
  2312.             debug.profileend()
  2313.         end
  2314.    
  2315.         do
  2316.             -- Test screen-space offsets from the focus for the presence of soft limits
  2317.             debug.profilebegin("testOffsets")
  2318.    
  2319.             for _, offset in ipairs(SCAN_SAMPLE_OFFSETS) do
  2320.                 local scaledOffset = offset
  2321.                 local pos = getCollisionPoint(fP, fX*scaledOffset.x + fY*scaledOffset.y)
  2322.                 if queryPoint(pos, (fP + fZ*dist - pos).Unit, dist) == inf then
  2323.                     return false
  2324.                 end
  2325.             end
  2326.    
  2327.             debug.profileend()
  2328.         end
  2329.    
  2330.         debug.profileend()
  2331.         return true
  2332.     end
  2333.    
  2334.     local function Popper(focus, targetDist, focusExtrapolation)
  2335.         debug.profilebegin("popper")
  2336.    
  2337.         subjectRoot = subjectPart and subjectPart:GetRootPart() or subjectPart
  2338.    
  2339.         local dist = targetDist
  2340.         local soft, hard = queryViewport(focus, targetDist)
  2341.         if hard < dist then
  2342.             dist = hard
  2343.         end
  2344.         if soft < dist and testPromotion(focus, targetDist, focusExtrapolation) then
  2345.             dist = soft
  2346.         end
  2347.    
  2348.         subjectRoot = nil
  2349.    
  2350.         debug.profileend()
  2351.         return dist
  2352.     end
  2353.    
  2354.     return Popper
  2355. end
  2356.  
  2357. function _ZoomController()
  2358.     local ZOOM_STIFFNESS = 4.5
  2359.     local ZOOM_DEFAULT = 12.5
  2360.     local ZOOM_ACCELERATION = 0.0375
  2361.    
  2362.     local MIN_FOCUS_DIST = 0.5
  2363.     local DIST_OPAQUE = 1
  2364.    
  2365.     local Popper = _Popper()
  2366.    
  2367.     local clamp = math.clamp
  2368.     local exp = math.exp
  2369.     local min = math.min
  2370.     local max = math.max
  2371.     local pi = math.pi
  2372.    
  2373.     local cameraMinZoomDistance, cameraMaxZoomDistance do
  2374.         local Player = game:GetService("Players").LocalPlayer
  2375.    
  2376.         local function updateBounds()
  2377.             cameraMinZoomDistance = Player.CameraMinZoomDistance
  2378.             cameraMaxZoomDistance = Player.CameraMaxZoomDistance
  2379.         end
  2380.    
  2381.         updateBounds()
  2382.    
  2383.         Player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(updateBounds)
  2384.         Player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(updateBounds)
  2385.     end
  2386.    
  2387.     local ConstrainedSpring = {} do
  2388.         ConstrainedSpring.__index = ConstrainedSpring
  2389.    
  2390.         function ConstrainedSpring.new(freq, x, minValue, maxValue)
  2391.             x = clamp(x, minValue, maxValue)
  2392.             return setmetatable({
  2393.                 freq = freq, -- Undamped frequency (Hz)
  2394.                 x = x, -- Current position
  2395.                 v = 0, -- Current velocity
  2396.                 minValue = minValue, -- Minimum bound
  2397.                 maxValue = maxValue, -- Maximum bound
  2398.                 goal = x, -- Goal position
  2399.             }, ConstrainedSpring)
  2400.         end
  2401.    
  2402.         function ConstrainedSpring:Step(dt)
  2403.             local freq = self.freq*2*pi -- Convert from Hz to rad/s
  2404.             local x = self.x
  2405.             local v = self.v
  2406.             local minValue = self.minValue
  2407.             local maxValue = self.maxValue
  2408.             local goal = self.goal
  2409.    
  2410.             -- Solve the spring ODE for position and velocity after time t, assuming critical damping:
  2411.             --   2*f*x'[t] + x''[t] = f^2*(g - x[t])
  2412.             -- Knowns are x[0] and x'[0].
  2413.             -- Solve for x[t] and x'[t].
  2414.    
  2415.             local offset = goal - x
  2416.             local step = freq*dt
  2417.             local decay = exp(-step)
  2418.    
  2419.             local x1 = goal + (v*dt - offset*(step + 1))*decay
  2420.             local v1 = ((offset*freq - v)*step + v)*decay
  2421.    
  2422.             -- Constrain
  2423.             if x1 < minValue then
  2424.                 x1 = minValue
  2425.                 v1 = 0
  2426.             elseif x1 > maxValue then
  2427.                 x1 = maxValue
  2428.                 v1 = 0
  2429.             end
  2430.    
  2431.             self.x = x1
  2432.             self.v = v1
  2433.    
  2434.             return x1
  2435.         end
  2436.     end
  2437.    
  2438.     local zoomSpring = ConstrainedSpring.new(ZOOM_STIFFNESS, ZOOM_DEFAULT, MIN_FOCUS_DIST, cameraMaxZoomDistance)
  2439.    
  2440.     local function stepTargetZoom(z, dz, zoomMin, zoomMax)
  2441.         z = clamp(z + dz*(1 + z*ZOOM_ACCELERATION), zoomMin, zoomMax)
  2442.         if z < DIST_OPAQUE then
  2443.             z = dz <= 0 and zoomMin or DIST_OPAQUE
  2444.         end
  2445.         return z
  2446.     end
  2447.    
  2448.     local zoomDelta = 0
  2449.    
  2450.     local Zoom = {} do
  2451.         function Zoom.Update(renderDt, focus, extrapolation)
  2452.             local poppedZoom = math.huge
  2453.    
  2454.             if zoomSpring.goal > DIST_OPAQUE then
  2455.                 -- Make a pessimistic estimate of zoom distance for this step without accounting for poppercam
  2456.                 local maxPossibleZoom = max(
  2457.                     zoomSpring.x,
  2458.                     stepTargetZoom(zoomSpring.goal, zoomDelta, cameraMinZoomDistance, cameraMaxZoomDistance)
  2459.                 )
  2460.    
  2461.                 -- Run the Popper algorithm on the feasible zoom range, [MIN_FOCUS_DIST, maxPossibleZoom]
  2462.                 poppedZoom = Popper(
  2463.                     focus*CFrame.new(0, 0, MIN_FOCUS_DIST),
  2464.                     maxPossibleZoom - MIN_FOCUS_DIST,
  2465.                     extrapolation
  2466.                 ) + MIN_FOCUS_DIST
  2467.             end
  2468.    
  2469.             zoomSpring.minValue = MIN_FOCUS_DIST
  2470.             zoomSpring.maxValue = min(cameraMaxZoomDistance, poppedZoom)
  2471.    
  2472.             return zoomSpring:Step(renderDt)
  2473.         end
  2474.    
  2475.         function Zoom.SetZoomParameters(targetZoom, newZoomDelta)
  2476.             zoomSpring.goal = targetZoom
  2477.             zoomDelta = newZoomDelta
  2478.         end
  2479.     end
  2480.    
  2481.     return Zoom
  2482. end
  2483.  
  2484. function _MouseLockController()
  2485.     --[[ Constants ]]--
  2486.     local DEFAULT_MOUSE_LOCK_CURSOR = "rbxasset://textures/MouseLockedCursor.png"
  2487.    
  2488.     local CONTEXT_ACTION_NAME = "MouseLockSwitchAction"
  2489.     local MOUSELOCK_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
  2490.    
  2491.     --[[ Services ]]--
  2492.     local PlayersService = game:GetService("Players")
  2493.     local ContextActionService = game:GetService("ContextActionService")
  2494.     local Settings = UserSettings() -- ignore warning
  2495.     local GameSettings = Settings.GameSettings
  2496.     local Mouse = PlayersService.LocalPlayer:GetMouse()
  2497.    
  2498.     --[[ The Module ]]--
  2499.     local MouseLockController = {}
  2500.     MouseLockController.__index = MouseLockController
  2501.    
  2502.     function MouseLockController.new()
  2503.         local self = setmetatable({}, MouseLockController)
  2504.    
  2505.         self.isMouseLocked = false
  2506.         self.savedMouseCursor = nil
  2507.         self.boundKeys = {Enum.KeyCode.LeftShift, Enum.KeyCode.RightShift} -- defaults
  2508.    
  2509.         self.mouseLockToggledEvent = Instance.new("BindableEvent")
  2510.    
  2511.         local boundKeysObj = script:FindFirstChild("BoundKeys")
  2512.         if (not boundKeysObj) or (not boundKeysObj:IsA("StringValue")) then
  2513.             -- If object with correct name was found, but it's not a StringValue, destroy and replace
  2514.             if boundKeysObj then
  2515.                 boundKeysObj:Destroy()
  2516.             end
  2517.    
  2518.             boundKeysObj = Instance.new("StringValue")
  2519.             boundKeysObj.Name = "BoundKeys"
  2520.             boundKeysObj.Value = "LeftShift,RightShift"
  2521.             boundKeysObj.Parent = script
  2522.         end
  2523.    
  2524.         if boundKeysObj then
  2525.             boundKeysObj.Changed:Connect(function(value)
  2526.                 self:OnBoundKeysObjectChanged(value)
  2527.             end)
  2528.             self:OnBoundKeysObjectChanged(boundKeysObj.Value) -- Initial setup call
  2529.         end
  2530.    
  2531.         -- Watch for changes to user's ControlMode and ComputerMovementMode settings and update the feature availability accordingly
  2532.         GameSettings.Changed:Connect(function(property)
  2533.             if property == "ControlMode" or property == "ComputerMovementMode" then
  2534.                 self:UpdateMouseLockAvailability()
  2535.             end
  2536.         end)
  2537.    
  2538.         -- Watch for changes to DevEnableMouseLock and update the feature availability accordingly
  2539.         PlayersService.LocalPlayer:GetPropertyChangedSignal("DevEnableMouseLock"):Connect(function()
  2540.             self:UpdateMouseLockAvailability()
  2541.         end)
  2542.    
  2543.         -- Watch for changes to DevEnableMouseLock and update the feature availability accordingly
  2544.         PlayersService.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(function()
  2545.             self:UpdateMouseLockAvailability()
  2546.         end)
  2547.    
  2548.         self:UpdateMouseLockAvailability()
  2549.    
  2550.         return self
  2551.     end
  2552.    
  2553.     function MouseLockController:GetIsMouseLocked()
  2554.         return self.isMouseLocked
  2555.     end
  2556.    
  2557.     function MouseLockController:GetBindableToggleEvent()
  2558.         return self.mouseLockToggledEvent.Event
  2559.     end
  2560.    
  2561.     function MouseLockController:GetMouseLockOffset()
  2562.         local offsetValueObj = script:FindFirstChild("CameraOffset")
  2563.         if offsetValueObj and offsetValueObj:IsA("Vector3Value") then
  2564.             return offsetValueObj.Value
  2565.         else
  2566.             -- If CameraOffset object was found but not correct type, destroy
  2567.             if offsetValueObj then
  2568.                 offsetValueObj:Destroy()
  2569.             end
  2570.             offsetValueObj = Instance.new("Vector3Value")
  2571.             offsetValueObj.Name = "CameraOffset"
  2572.             offsetValueObj.Value = Vector3.new(1.75,0,0) -- Legacy Default Value
  2573.             offsetValueObj.Parent = script
  2574.         end
  2575.    
  2576.         if offsetValueObj and offsetValueObj.Value then
  2577.             return offsetValueObj.Value
  2578.         end
  2579.    
  2580.         return Vector3.new(1.75,0,0)
  2581.     end
  2582.    
  2583.     function MouseLockController:UpdateMouseLockAvailability()
  2584.         local devAllowsMouseLock = PlayersService.LocalPlayer.DevEnableMouseLock
  2585.         local devMovementModeIsScriptable = PlayersService.LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.Scriptable
  2586.         local userHasMouseLockModeEnabled = GameSettings.ControlMode == Enum.ControlMode.MouseLockSwitch
  2587.         local userHasClickToMoveEnabled =  GameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove
  2588.         local MouseLockAvailable = devAllowsMouseLock and userHasMouseLockModeEnabled and not userHasClickToMoveEnabled and not devMovementModeIsScriptable
  2589.    
  2590.         if MouseLockAvailable~=self.enabled then
  2591.             self:EnableMouseLock(MouseLockAvailable)
  2592.         end
  2593.     end
  2594.    
  2595.     function MouseLockController:OnBoundKeysObjectChanged(newValue)
  2596.         self.boundKeys = {} -- Overriding defaults, note: possibly with nothing at all if boundKeysObj.Value is "" or contains invalid values
  2597.         for token in string.gmatch(newValue,"[^%s,]+") do
  2598.             for _, keyEnum in pairs(Enum.KeyCode:GetEnumItems()) do
  2599.                 if token == keyEnum.Name then
  2600.                     self.boundKeys[#self.boundKeys+1] = keyEnum
  2601.                     break
  2602.                 end
  2603.             end
  2604.         end
  2605.         self:UnbindContextActions()
  2606.         self:BindContextActions()
  2607.     end
  2608.    
  2609.     --[[ Local Functions ]]--
  2610.     function MouseLockController:OnMouseLockToggled()
  2611.         self.isMouseLocked = not self.isMouseLocked
  2612.    
  2613.         if self.isMouseLocked then
  2614.             local cursorImageValueObj = script:FindFirstChild("CursorImage")
  2615.             if cursorImageValueObj and cursorImageValueObj:IsA("StringValue") and cursorImageValueObj.Value then
  2616.                 self.savedMouseCursor = Mouse.Icon
  2617.                 Mouse.Icon = cursorImageValueObj.Value
  2618.             else
  2619.                 if cursorImageValueObj then
  2620.                     cursorImageValueObj:Destroy()
  2621.                 end
  2622.                 cursorImageValueObj = Instance.new("StringValue")
  2623.                 cursorImageValueObj.Name = "CursorImage"
  2624.                 cursorImageValueObj.Value = DEFAULT_MOUSE_LOCK_CURSOR
  2625.                 cursorImageValueObj.Parent = script
  2626.                 self.savedMouseCursor = Mouse.Icon
  2627.                 Mouse.Icon = DEFAULT_MOUSE_LOCK_CURSOR
  2628.             end
  2629.         else
  2630.             if self.savedMouseCursor then
  2631.                 Mouse.Icon = self.savedMouseCursor
  2632.                 self.savedMouseCursor = nil
  2633.             end
  2634.         end
  2635.    
  2636.         self.mouseLockToggledEvent:Fire()
  2637.     end
  2638.    
  2639.     function MouseLockController:DoMouseLockSwitch(name, state, input)
  2640.         if state == Enum.UserInputState.Begin then
  2641.             self:OnMouseLockToggled()
  2642.             return Enum.ContextActionResult.Sink
  2643.         end
  2644.         return Enum.ContextActionResult.Pass
  2645.     end
  2646.    
  2647.     function MouseLockController:BindContextActions()
  2648.         ContextActionService:BindActionAtPriority(CONTEXT_ACTION_NAME, function(name, state, input)
  2649.             return self:DoMouseLockSwitch(name, state, input)
  2650.         end, false, MOUSELOCK_ACTION_PRIORITY, unpack(self.boundKeys))
  2651.     end
  2652.    
  2653.     function MouseLockController:UnbindContextActions()
  2654.         ContextActionService:UnbindAction(CONTEXT_ACTION_NAME)
  2655.     end
  2656.    
  2657.     function MouseLockController:IsMouseLocked()
  2658.         return self.enabled and self.isMouseLocked
  2659.     end
  2660.    
  2661.     function MouseLockController:EnableMouseLock(enable)
  2662.         if enable ~= self.enabled then
  2663.    
  2664.             self.enabled = enable
  2665.    
  2666.             if self.enabled then
  2667.                 -- Enabling the mode
  2668.                 self:BindContextActions()
  2669.             else
  2670.                 -- Disabling
  2671.                 -- Restore mouse cursor
  2672.                 if Mouse.Icon~="" then
  2673.                     Mouse.Icon = ""
  2674.                 end
  2675.    
  2676.                 self:UnbindContextActions()
  2677.    
  2678.                 -- If the mode is disabled while being used, fire the event to toggle it off
  2679.                 if self.isMouseLocked then
  2680.                     self.mouseLockToggledEvent:Fire()
  2681.                 end
  2682.    
  2683.                 self.isMouseLocked = false
  2684.             end
  2685.    
  2686.         end
  2687.     end
  2688.    
  2689.     return MouseLockController
  2690. end
  2691.  
  2692. function _TransparencyController()
  2693.    
  2694.     local MAX_TWEEN_RATE = 2.8 -- per second
  2695.    
  2696.     local Util = _CameraUtils()
  2697.    
  2698.     --[[ The Module ]]--
  2699.     local TransparencyController = {}
  2700.     TransparencyController.__index = TransparencyController
  2701.    
  2702.     function TransparencyController.new()
  2703.         local self = setmetatable({}, TransparencyController)
  2704.    
  2705.         self.lastUpdate = tick()
  2706.         self.transparencyDirty = false
  2707.         self.enabled = false
  2708.         self.lastTransparency = nil
  2709.    
  2710.         self.descendantAddedConn, self.descendantRemovingConn = nil, nil
  2711.         self.toolDescendantAddedConns = {}
  2712.         self.toolDescendantRemovingConns = {}
  2713.         self.cachedParts = {}
  2714.    
  2715.         return self
  2716.     end
  2717.    
  2718.    
  2719.     function TransparencyController:HasToolAncestor(object)
  2720.         if object.Parent == nil then return false end
  2721.         return object.Parent:IsA('Tool') or self:HasToolAncestor(object.Parent)
  2722.     end
  2723.    
  2724.     function TransparencyController:IsValidPartToModify(part)
  2725.         if part:IsA('BasePart') or part:IsA('Decal') then
  2726.             return not self:HasToolAncestor(part)
  2727.         end
  2728.         return false
  2729.     end
  2730.    
  2731.     function TransparencyController:CachePartsRecursive(object)
  2732.         if object then
  2733.             if self:IsValidPartToModify(object) then
  2734.                 self.cachedParts[object] = true
  2735.                 self.transparencyDirty = true
  2736.             end
  2737.             for _, child in pairs(object:GetChildren()) do
  2738.                 self:CachePartsRecursive(child)
  2739.             end
  2740.         end
  2741.     end
  2742.    
  2743.     function TransparencyController:TeardownTransparency()
  2744.         for child, _ in pairs(self.cachedParts) do
  2745.             child.LocalTransparencyModifier = 0
  2746.         end
  2747.         self.cachedParts = {}
  2748.         self.transparencyDirty = true
  2749.         self.lastTransparency = nil
  2750.    
  2751.         if self.descendantAddedConn then
  2752.             self.descendantAddedConn:disconnect()
  2753.             self.descendantAddedConn = nil
  2754.         end
  2755.         if self.descendantRemovingConn then
  2756.             self.descendantRemovingConn:disconnect()
  2757.             self.descendantRemovingConn = nil
  2758.         end
  2759.         for object, conn in pairs(self.toolDescendantAddedConns) do
  2760.             conn:Disconnect()
  2761.             self.toolDescendantAddedConns[object] = nil
  2762.         end
  2763.         for object, conn in pairs(self.toolDescendantRemovingConns) do
  2764.             conn:Disconnect()
  2765.             self.toolDescendantRemovingConns[object] = nil
  2766.         end
  2767.     end
  2768.    
  2769.     function TransparencyController:SetupTransparency(character)
  2770.         self:TeardownTransparency()
  2771.    
  2772.         if self.descendantAddedConn then self.descendantAddedConn:disconnect() end
  2773.         self.descendantAddedConn = character.DescendantAdded:Connect(function(object)
  2774.             -- This is a part we want to invisify
  2775.             if self:IsValidPartToModify(object) then
  2776.                 self.cachedParts[object] = true
  2777.                 self.transparencyDirty = true
  2778.             -- There is now a tool under the character
  2779.             elseif object:IsA('Tool') then
  2780.                 if self.toolDescendantAddedConns[object] then self.toolDescendantAddedConns[object]:Disconnect() end
  2781.                 self.toolDescendantAddedConns[object] = object.DescendantAdded:Connect(function(toolChild)
  2782.                     self.cachedParts[toolChild] = nil
  2783.                     if toolChild:IsA('BasePart') or toolChild:IsA('Decal') then
  2784.                         -- Reset the transparency
  2785.                         toolChild.LocalTransparencyModifier = 0
  2786.                     end
  2787.                 end)
  2788.                 if self.toolDescendantRemovingConns[object] then self.toolDescendantRemovingConns[object]:disconnect() end
  2789.                 self.toolDescendantRemovingConns[object] = object.DescendantRemoving:Connect(function(formerToolChild)
  2790.                     wait() -- wait for new parent
  2791.                     if character and formerToolChild and formerToolChild:IsDescendantOf(character) then
  2792.                         if self:IsValidPartToModify(formerToolChild) then
  2793.                             self.cachedParts[formerToolChild] = true
  2794.                             self.transparencyDirty = true
  2795.                         end
  2796.                     end
  2797.                 end)
  2798.             end
  2799.         end)
  2800.         if self.descendantRemovingConn then self.descendantRemovingConn:disconnect() end
  2801.         self.descendantRemovingConn = character.DescendantRemoving:connect(function(object)
  2802.             if self.cachedParts[object] then
  2803.                 self.cachedParts[object] = nil
  2804.                 -- Reset the transparency
  2805.                 object.LocalTransparencyModifier = 0
  2806.             end
  2807.         end)
  2808.         self:CachePartsRecursive(character)
  2809.     end
  2810.    
  2811.    
  2812.     function TransparencyController:Enable(enable)
  2813.         if self.enabled ~= enable then
  2814.             self.enabled = enable
  2815.             self:Update()
  2816.         end
  2817.     end
  2818.    
  2819.     function TransparencyController:SetSubject(subject)
  2820.         local character = nil
  2821.         if subject and subject:IsA("Humanoid") then
  2822.             character = subject.Parent
  2823.         end
  2824.         if subject and subject:IsA("VehicleSeat") and subject.Occupant then
  2825.             character = subject.Occupant.Parent
  2826.         end
  2827.         if character then
  2828.             self:SetupTransparency(character)
  2829.         else
  2830.             self:TeardownTransparency()
  2831.         end
  2832.     end
  2833.    
  2834.     function TransparencyController:Update()
  2835.         local instant = false
  2836.         local now = tick()
  2837.         local currentCamera = workspace.CurrentCamera
  2838.    
  2839.         if currentCamera then
  2840.             local transparency = 0
  2841.             if not self.enabled then
  2842.                 instant = true
  2843.             else
  2844.                 local distance = (currentCamera.Focus.p - currentCamera.CoordinateFrame.p).magnitude
  2845.                 transparency = (distance<2) and (1.0-(distance-0.5)/1.5) or 0 --(7 - distance) / 5
  2846.                 if transparency < 0.5 then
  2847.                     transparency = 0
  2848.                 end
  2849.    
  2850.                 if self.lastTransparency then
  2851.                     local deltaTransparency = transparency - self.lastTransparency
  2852.    
  2853.                     -- Don't tween transparency if it is instant or your character was fully invisible last frame
  2854.                     if not instant and transparency < 1 and self.lastTransparency < 0.95 then
  2855.                         local maxDelta = MAX_TWEEN_RATE * (now - self.lastUpdate)
  2856.                         deltaTransparency = math.clamp(deltaTransparency, -maxDelta, maxDelta)
  2857.                     end
  2858.                     transparency = self.lastTransparency + deltaTransparency
  2859.                 else
  2860.                     self.transparencyDirty = true
  2861.                 end
  2862.    
  2863.                 transparency = math.clamp(Util.Round(transparency, 2), 0, 1)
  2864.             end
  2865.    
  2866.             if self.transparencyDirty or self.lastTransparency ~= transparency then
  2867.                 for child, _ in pairs(self.cachedParts) do
  2868.                     child.LocalTransparencyModifier = transparency
  2869.                 end
  2870.                 self.transparencyDirty = false
  2871.                 self.lastTransparency = transparency
  2872.             end
  2873.         end
  2874.         self.lastUpdate = now
  2875.     end
  2876.    
  2877.     return TransparencyController
  2878. end
  2879.  
  2880. function _Poppercam()
  2881.     local ZoomController =  _ZoomController()
  2882.    
  2883.     local TransformExtrapolator = {} do
  2884.         TransformExtrapolator.__index = TransformExtrapolator
  2885.    
  2886.         local CF_IDENTITY = CFrame.new()
  2887.    
  2888.         local function cframeToAxis(cframe)
  2889.             local axis, angle = cframe:toAxisAngle()
  2890.             return axis*angle
  2891.         end
  2892.    
  2893.         local function axisToCFrame(axis)
  2894.             local angle = axis.magnitude
  2895.             if angle > 1e-5 then
  2896.                 return CFrame.fromAxisAngle(axis, angle)
  2897.             end
  2898.             return CF_IDENTITY
  2899.         end
  2900.    
  2901.         local function extractRotation(cf)
  2902.             local _, _, _, xx, yx, zx, xy, yy, zy, xz, yz, zz = cf:components()
  2903.             return CFrame.new(0, 0, 0, xx, yx, zx, xy, yy, zy, xz, yz, zz)
  2904.         end
  2905.    
  2906.         function TransformExtrapolator.new()
  2907.             return setmetatable({
  2908.                 lastCFrame = nil,
  2909.             }, TransformExtrapolator)
  2910.         end
  2911.    
  2912.         function TransformExtrapolator:Step(dt, currentCFrame)
  2913.             local lastCFrame = self.lastCFrame or currentCFrame
  2914.             self.lastCFrame = currentCFrame
  2915.    
  2916.             local currentPos = currentCFrame.p
  2917.             local currentRot = extractRotation(currentCFrame)
  2918.    
  2919.             local lastPos = lastCFrame.p
  2920.             local lastRot = extractRotation(lastCFrame)
  2921.    
  2922.             -- Estimate velocities from the delta between now and the last frame
  2923.             -- This estimation can be a little noisy.
  2924.             local dp = (currentPos - lastPos)/dt
  2925.             local dr = cframeToAxis(currentRot*lastRot:inverse())/dt
  2926.    
  2927.             local function extrapolate(t)
  2928.                 local p = dp*t + currentPos
  2929.                 local r = axisToCFrame(dr*t)*currentRot
  2930.                 return r + p
  2931.             end
  2932.    
  2933.             return {
  2934.                 extrapolate = extrapolate,
  2935.                 posVelocity = dp,
  2936.                 rotVelocity = dr,
  2937.             }
  2938.         end
  2939.    
  2940.         function TransformExtrapolator:Reset()
  2941.             self.lastCFrame = nil
  2942.         end
  2943.     end
  2944.    
  2945.     --[[ The Module ]]--
  2946.     local BaseOcclusion = _BaseOcclusion()
  2947.     local Poppercam = setmetatable({}, BaseOcclusion)
  2948.     Poppercam.__index = Poppercam
  2949.    
  2950.     function Poppercam.new()
  2951.         local self = setmetatable(BaseOcclusion.new(), Poppercam)
  2952.         self.focusExtrapolator = TransformExtrapolator.new()
  2953.         return self
  2954.     end
  2955.    
  2956.     function Poppercam:GetOcclusionMode()
  2957.         return Enum.DevCameraOcclusionMode.Zoom
  2958.     end
  2959.    
  2960.     function Poppercam:Enable(enable)
  2961.         self.focusExtrapolator:Reset()
  2962.     end
  2963.    
  2964.     function Poppercam:Update(renderDt, desiredCameraCFrame, desiredCameraFocus, cameraController)
  2965.         local rotatedFocus = CFrame.new(desiredCameraFocus.p, desiredCameraCFrame.p)*CFrame.new(
  2966.             0, 0, 0,
  2967.             -1, 0, 0,
  2968.             0, 1, 0,
  2969.             0, 0, -1
  2970.         )
  2971.         local extrapolation = self.focusExtrapolator:Step(renderDt, rotatedFocus)
  2972.         local zoom = ZoomController.Update(renderDt, rotatedFocus, extrapolation)
  2973.         return rotatedFocus*CFrame.new(0, 0, zoom), desiredCameraFocus
  2974.     end
  2975.    
  2976.     -- Called when character is added
  2977.     function Poppercam:CharacterAdded(character, player)
  2978.     end
  2979.    
  2980.     -- Called when character is about to be removed
  2981.     function Poppercam:CharacterRemoving(character, player)
  2982.     end
  2983.    
  2984.     function Poppercam:OnCameraSubjectChanged(newSubject)
  2985.     end
  2986.    
  2987.     local ZoomController = _ZoomController()
  2988.    
  2989.     function Poppercam:Update(renderDt, desiredCameraCFrame, desiredCameraFocus, cameraController)
  2990.         local rotatedFocus = desiredCameraFocus * (desiredCameraCFrame - desiredCameraCFrame.p)
  2991.         local extrapolation = self.focusExtrapolator:Step(renderDt, rotatedFocus)
  2992.         local zoom = ZoomController.Update(renderDt, rotatedFocus, extrapolation)
  2993.         return rotatedFocus*CFrame.new(0, 0, zoom), desiredCameraFocus
  2994.     end
  2995.    
  2996.     return Poppercam
  2997. end
  2998.  
  2999. function _Invisicam()
  3000.    
  3001.     --[[ Top Level Roblox Services ]]--
  3002.     local PlayersService = game:GetService("Players")
  3003.    
  3004.     --[[ Constants ]]--
  3005.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  3006.     local USE_STACKING_TRANSPARENCY = true  -- Multiple items between the subject and camera get transparency values that add up to TARGET_TRANSPARENCY
  3007.     local TARGET_TRANSPARENCY = 0.75 -- Classic Invisicam's Value, also used by new invisicam for parts hit by head and torso rays
  3008.     local TARGET_TRANSPARENCY_PERIPHERAL = 0.5 -- Used by new SMART_CIRCLE mode for items not hit by head and torso rays
  3009.    
  3010.     local MODE = {
  3011.         --CUSTOM = 1,       -- Retired, unused
  3012.         LIMBS = 2,          -- Track limbs
  3013.         MOVEMENT = 3,       -- Track movement
  3014.         CORNERS = 4,        -- Char model corners
  3015.         CIRCLE1 = 5,        -- Circle of casts around character
  3016.         CIRCLE2 = 6,        -- Circle of casts around character, camera relative
  3017.         LIMBMOVE = 7,       -- LIMBS mode + MOVEMENT mode
  3018.         SMART_CIRCLE = 8,   -- More sample points on and around character
  3019.         CHAR_OUTLINE = 9,   -- Dynamic outline around the character
  3020.     }
  3021.    
  3022.     local LIMB_TRACKING_SET = {
  3023.         -- Body parts common to R15 and R6
  3024.         ['Head'] = true,
  3025.    
  3026.         -- Body parts unique to R6
  3027.         ['Left Arm'] = true,
  3028.         ['Right Arm'] = true,
  3029.         ['Left Leg'] = true,
  3030.         ['Right Leg'] = true,
  3031.    
  3032.         -- Body parts unique to R15
  3033.         ['LeftLowerArm'] = true,
  3034.         ['RightLowerArm'] = true,
  3035.         ['LeftUpperLeg'] = true,
  3036.         ['RightUpperLeg'] = true
  3037.     }
  3038.    
  3039.     local CORNER_FACTORS = {
  3040.         Vector3.new(1,1,-1),
  3041.         Vector3.new(1,-1,-1),
  3042.         Vector3.new(-1,-1,-1),
  3043.         Vector3.new(-1,1,-1)
  3044.     }
  3045.    
  3046.     local CIRCLE_CASTS = 10
  3047.     local MOVE_CASTS = 3
  3048.     local SMART_CIRCLE_CASTS = 24
  3049.     local SMART_CIRCLE_INCREMENT = 2.0 * math.pi / SMART_CIRCLE_CASTS
  3050.     local CHAR_OUTLINE_CASTS = 24
  3051.    
  3052.     -- Used to sanitize user-supplied functions
  3053.     local function AssertTypes(param, ...)
  3054.         local allowedTypes = {}
  3055.         local typeString = ''
  3056.         for _, typeName in pairs({...}) do
  3057.             allowedTypes[typeName] = true
  3058.             typeString = typeString .. (typeString == '' and '' or ' or ') .. typeName
  3059.         end
  3060.         local theType = type(param)
  3061.         assert(allowedTypes[theType], typeString .. " type expected, got: " .. theType)
  3062.     end
  3063.    
  3064.     -- Helper function for Determinant of 3x3, not in CameraUtils for performance reasons
  3065.     local function Det3x3(a,b,c,d,e,f,g,h,i)
  3066.         return (a*(e*i-f*h)-b*(d*i-f*g)+c*(d*h-e*g))
  3067.     end
  3068.    
  3069.     -- Smart Circle mode needs the intersection of 2 rays that are known to be in the same plane
  3070.     -- because they are generated from cross products with a common vector. This function is computing
  3071.     -- that intersection, but it's actually the general solution for the point halfway between where
  3072.     -- two skew lines come nearest to each other, which is more forgiving.
  3073.     local function RayIntersection(p0, v0, p1, v1)
  3074.         local v2 = v0:Cross(v1)
  3075.         local d1 = p1.x - p0.x
  3076.         local d2 = p1.y - p0.y
  3077.         local d3 = p1.z - p0.z
  3078.         local denom = Det3x3(v0.x,-v1.x,v2.x,v0.y,-v1.y,v2.y,v0.z,-v1.z,v2.z)
  3079.    
  3080.         if (denom == 0) then
  3081.             return ZERO_VECTOR3 -- No solution (rays are parallel)
  3082.         end
  3083.    
  3084.         local t0 = Det3x3(d1,-v1.x,v2.x,d2,-v1.y,v2.y,d3,-v1.z,v2.z) / denom
  3085.         local t1 = Det3x3(v0.x,d1,v2.x,v0.y,d2,v2.y,v0.z,d3,v2.z) / denom
  3086.         local s0 = p0 + t0 * v0
  3087.         local s1 = p1 + t1 * v1
  3088.         local s = s0 + 0.5 * ( s1 - s0 )
  3089.    
  3090.         -- 0.25 studs is a threshold for deciding if the rays are
  3091.         -- close enough to be considered intersecting, found through testing
  3092.         if (s1-s0).Magnitude < 0.25 then
  3093.             return s
  3094.         else
  3095.             return ZERO_VECTOR3
  3096.         end
  3097.     end
  3098.    
  3099.    
  3100.    
  3101.     --[[ The Module ]]--
  3102.     local BaseOcclusion = _BaseOcclusion()
  3103.     local Invisicam = setmetatable({}, BaseOcclusion)
  3104.     Invisicam.__index = Invisicam
  3105.    
  3106.     function Invisicam.new()
  3107.         local self = setmetatable(BaseOcclusion.new(), Invisicam)
  3108.    
  3109.         self.char = nil
  3110.         self.humanoidRootPart = nil
  3111.         self.torsoPart = nil
  3112.         self.headPart = nil
  3113.    
  3114.         self.childAddedConn = nil
  3115.         self.childRemovedConn = nil
  3116.    
  3117.         self.behaviors = {}     -- Map of modes to behavior fns
  3118.         self.behaviors[MODE.LIMBS] = self.LimbBehavior
  3119.         self.behaviors[MODE.MOVEMENT] = self.MoveBehavior
  3120.         self.behaviors[MODE.CORNERS] = self.CornerBehavior
  3121.         self.behaviors[MODE.CIRCLE1] = self.CircleBehavior
  3122.         self.behaviors[MODE.CIRCLE2] = self.CircleBehavior
  3123.         self.behaviors[MODE.LIMBMOVE] = self.LimbMoveBehavior
  3124.         self.behaviors[MODE.SMART_CIRCLE] = self.SmartCircleBehavior
  3125.         self.behaviors[MODE.CHAR_OUTLINE] = self.CharacterOutlineBehavior
  3126.    
  3127.         self.mode = MODE.SMART_CIRCLE
  3128.         self.behaviorFunction = self.SmartCircleBehavior
  3129.    
  3130.         self.savedHits = {}     -- Objects currently being faded in/out
  3131.         self.trackedLimbs = {}  -- Used in limb-tracking casting modes
  3132.    
  3133.         self.camera = game.Workspace.CurrentCamera
  3134.    
  3135.         self.enabled = false
  3136.         return self
  3137.     end
  3138.    
  3139.     function Invisicam:Enable(enable)
  3140.         self.enabled = enable
  3141.    
  3142.         if not enable then
  3143.             self:Cleanup()
  3144.         end
  3145.     end
  3146.    
  3147.     function Invisicam:GetOcclusionMode()
  3148.         return Enum.DevCameraOcclusionMode.Invisicam
  3149.     end
  3150.    
  3151.     --[[ Module functions ]]--
  3152.     function Invisicam:LimbBehavior(castPoints)
  3153.         for limb, _ in pairs(self.trackedLimbs) do
  3154.             castPoints[#castPoints + 1] = limb.Position
  3155.         end
  3156.     end
  3157.    
  3158.     function Invisicam:MoveBehavior(castPoints)
  3159.         for i = 1, MOVE_CASTS do
  3160.             local position, velocity = self.humanoidRootPart.Position, self.humanoidRootPart.Velocity
  3161.             local horizontalSpeed = Vector3.new(velocity.X, 0, velocity.Z).Magnitude / 2
  3162.             local offsetVector = (i - 1) * self.humanoidRootPart.CFrame.lookVector * horizontalSpeed
  3163.             castPoints[#castPoints + 1] = position + offsetVector
  3164.         end
  3165.     end
  3166.    
  3167.     function Invisicam:CornerBehavior(castPoints)
  3168.         local cframe = self.humanoidRootPart.CFrame
  3169.         local centerPoint = cframe.p
  3170.         local rotation = cframe - centerPoint
  3171.         local halfSize = self.char:GetExtentsSize() / 2 --NOTE: Doesn't update w/ limb animations
  3172.         castPoints[#castPoints + 1] = centerPoint
  3173.         for i = 1, #CORNER_FACTORS do
  3174.             castPoints[#castPoints + 1] = centerPoint + (rotation * (halfSize * CORNER_FACTORS[i]))
  3175.         end
  3176.     end
  3177.    
  3178.     function Invisicam:CircleBehavior(castPoints)
  3179.         local cframe
  3180.         if self.mode == MODE.CIRCLE1 then
  3181.             cframe = self.humanoidRootPart.CFrame
  3182.         else
  3183.             local camCFrame = self.camera.CoordinateFrame
  3184.             cframe = camCFrame - camCFrame.p + self.humanoidRootPart.Position
  3185.         end
  3186.         castPoints[#castPoints + 1] = cframe.p
  3187.         for i = 0, CIRCLE_CASTS - 1 do
  3188.             local angle = (2 * math.pi / CIRCLE_CASTS) * i
  3189.             local offset = 3 * Vector3.new(math.cos(angle), math.sin(angle), 0)
  3190.             castPoints[#castPoints + 1] = cframe * offset
  3191.         end
  3192.     end
  3193.    
  3194.     function Invisicam:LimbMoveBehavior(castPoints)
  3195.         self:LimbBehavior(castPoints)
  3196.         self:MoveBehavior(castPoints)
  3197.     end
  3198.    
  3199.     function Invisicam:CharacterOutlineBehavior(castPoints)
  3200.         local torsoUp = self.torsoPart.CFrame.upVector.unit
  3201.         local torsoRight = self.torsoPart.CFrame.rightVector.unit
  3202.    
  3203.         -- Torso cross of points for interior coverage
  3204.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p
  3205.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoUp
  3206.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoUp
  3207.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoRight
  3208.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoRight
  3209.         if self.headPart then
  3210.             castPoints[#castPoints + 1] = self.headPart.CFrame.p
  3211.         end
  3212.    
  3213.         local cframe = CFrame.new(ZERO_VECTOR3,Vector3.new(self.camera.CoordinateFrame.lookVector.X,0,self.camera.CoordinateFrame.lookVector.Z))
  3214.         local centerPoint = (self.torsoPart and self.torsoPart.Position or self.humanoidRootPart.Position)
  3215.    
  3216.         local partsWhitelist = {self.torsoPart}
  3217.         if self.headPart then
  3218.             partsWhitelist[#partsWhitelist + 1] = self.headPart
  3219.         end
  3220.    
  3221.         for i = 1, CHAR_OUTLINE_CASTS do
  3222.             local angle = (2 * math.pi * i / CHAR_OUTLINE_CASTS)
  3223.             local offset = cframe * (3 * Vector3.new(math.cos(angle), math.sin(angle), 0))
  3224.    
  3225.             offset = Vector3.new(offset.X, math.max(offset.Y, -2.25), offset.Z)
  3226.    
  3227.             local ray = Ray.new(centerPoint + offset, -3 * offset)
  3228.             local hit, hitPoint = game.Workspace:FindPartOnRayWithWhitelist(ray, partsWhitelist, false, false)
  3229.    
  3230.             if hit then
  3231.                 -- Use hit point as the cast point, but nudge it slightly inside the character so that bumping up against
  3232.                 -- walls is less likely to cause a transparency glitch
  3233.                 castPoints[#castPoints + 1] = hitPoint + 0.2 * (centerPoint - hitPoint).unit
  3234.             end
  3235.         end
  3236.     end
  3237.    
  3238.     function Invisicam:SmartCircleBehavior(castPoints)
  3239.         local torsoUp = self.torsoPart.CFrame.upVector.unit
  3240.         local torsoRight = self.torsoPart.CFrame.rightVector.unit
  3241.    
  3242.         -- SMART_CIRCLE mode includes rays to head and 5 to the torso.
  3243.         -- Hands, arms, legs and feet are not included since they
  3244.         -- are not canCollide and can therefore go inside of parts
  3245.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p
  3246.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoUp
  3247.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoUp
  3248.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoRight
  3249.         castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoRight
  3250.         if self.headPart then
  3251.             castPoints[#castPoints + 1] = self.headPart.CFrame.p
  3252.         end
  3253.    
  3254.         local cameraOrientation = self.camera.CFrame - self.camera.CFrame.p
  3255.         local torsoPoint = Vector3.new(0,0.5,0) + (self.torsoPart and self.torsoPart.Position or self.humanoidRootPart.Position)
  3256.         local radius = 2.5
  3257.    
  3258.         -- This loop first calculates points in a circle of radius 2.5 around the torso of the character, in the
  3259.         -- plane orthogonal to the camera's lookVector. Each point is then raycast to, to determine if it is within
  3260.         -- the free space surrounding the player (not inside anything). Two iterations are done to adjust points that
  3261.         -- are inside parts, to try to move them to valid locations that are still on their camera ray, so that the
  3262.         -- circle remains circular from the camera's perspective, but does not cast rays into walls or parts that are
  3263.         -- behind, below or beside the character and not really obstructing view of the character. This minimizes
  3264.         -- the undesirable situation where the character walks up to an exterior wall and it is made invisible even
  3265.         -- though it is behind the character.
  3266.         for i = 1, SMART_CIRCLE_CASTS do
  3267.             local angle = SMART_CIRCLE_INCREMENT * i - 0.5 * math.pi
  3268.             local offset = radius * Vector3.new(math.cos(angle), math.sin(angle), 0)
  3269.             local circlePoint = torsoPoint + cameraOrientation * offset
  3270.    
  3271.             -- Vector from camera to point on the circle being tested
  3272.             local vp = circlePoint - self.camera.CFrame.p
  3273.    
  3274.             local ray = Ray.new(torsoPoint, circlePoint - torsoPoint)
  3275.             local hit, hp, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
  3276.             local castPoint = circlePoint
  3277.    
  3278.             if hit then
  3279.                 local hprime = hp + 0.1 * hitNormal.unit -- Slightly offset hit point from the hit surface
  3280.                 local v0 = hprime - torsoPoint -- Vector from torso to offset hit point
  3281.    
  3282.                 local perp = (v0:Cross(vp)).unit
  3283.    
  3284.                 -- Vector from the offset hit point, along the hit surface
  3285.                 local v1 = (perp:Cross(hitNormal)).unit
  3286.    
  3287.                 -- Vector from camera to offset hit
  3288.                 local vprime = (hprime - self.camera.CFrame.p).unit
  3289.    
  3290.                 -- This dot product checks to see if the vector along the hit surface would hit the correct
  3291.                 -- side of the invisicam cone, or if it would cross the camera look vector and hit the wrong side
  3292.                 if ( v0.unit:Dot(-v1) < v0.unit:Dot(vprime)) then
  3293.                     castPoint = RayIntersection(hprime, v1, circlePoint, vp)
  3294.    
  3295.                     if castPoint.Magnitude > 0 then
  3296.                         local ray = Ray.new(hprime, castPoint - hprime)
  3297.                         local hit, hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
  3298.    
  3299.                         if hit then
  3300.                             local hprime2 = hitPoint + 0.1 * hitNormal.unit
  3301.                             castPoint = hprime2
  3302.                         end
  3303.                     else
  3304.                         castPoint = hprime
  3305.                     end
  3306.                 else
  3307.                     castPoint = hprime
  3308.                 end
  3309.    
  3310.                 local ray = Ray.new(torsoPoint, (castPoint - torsoPoint))
  3311.                 local hit, hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
  3312.    
  3313.                 if hit then
  3314.                     local castPoint2 = hitPoint - 0.1 * (castPoint - torsoPoint).unit
  3315.                     castPoint = castPoint2
  3316.                 end
  3317.             end
  3318.    
  3319.             castPoints[#castPoints + 1] = castPoint
  3320.         end
  3321.     end
  3322.    
  3323.     function Invisicam:CheckTorsoReference()
  3324.         if self.char then
  3325.             self.torsoPart = self.char:FindFirstChild("Torso")
  3326.             if not self.torsoPart then
  3327.                 self.torsoPart = self.char:FindFirstChild("UpperTorso")
  3328.                 if not self.torsoPart then
  3329.                     self.torsoPart = self.char:FindFirstChild("HumanoidRootPart")
  3330.                 end
  3331.             end
  3332.    
  3333.             self.headPart = self.char:FindFirstChild("Head")
  3334.         end
  3335.     end
  3336.    
  3337.     function Invisicam:CharacterAdded(char, player)
  3338.         -- We only want the LocalPlayer's character
  3339.         if player~=PlayersService.LocalPlayer then return end
  3340.    
  3341.         if self.childAddedConn then
  3342.             self.childAddedConn:Disconnect()
  3343.             self.childAddedConn = nil
  3344.         end
  3345.         if self.childRemovedConn then
  3346.             self.childRemovedConn:Disconnect()
  3347.             self.childRemovedConn = nil
  3348.         end
  3349.    
  3350.         self.char = char
  3351.    
  3352.         self.trackedLimbs = {}
  3353.         local function childAdded(child)
  3354.             if child:IsA("BasePart") then
  3355.                 if LIMB_TRACKING_SET[child.Name] then
  3356.                     self.trackedLimbs[child] = true
  3357.                 end
  3358.    
  3359.                 if child.Name == "Torso" or child.Name == "UpperTorso" then
  3360.                     self.torsoPart = child
  3361.                 end
  3362.    
  3363.                 if child.Name == "Head" then
  3364.                     self.headPart = child
  3365.                 end
  3366.             end
  3367.         end
  3368.    
  3369.         local function childRemoved(child)
  3370.             self.trackedLimbs[child] = nil
  3371.    
  3372.             -- If removed/replaced part is 'Torso' or 'UpperTorso' double check that we still have a TorsoPart to use
  3373.             self:CheckTorsoReference()
  3374.         end
  3375.    
  3376.         self.childAddedConn = char.ChildAdded:Connect(childAdded)
  3377.         self.childRemovedConn = char.ChildRemoved:Connect(childRemoved)
  3378.         for _, child in pairs(self.char:GetChildren()) do
  3379.             childAdded(child)
  3380.         end
  3381.     end
  3382.    
  3383.     function Invisicam:SetMode(newMode)
  3384.         AssertTypes(newMode, 'number')
  3385.         for _, modeNum in pairs(MODE) do
  3386.             if modeNum == newMode then
  3387.                 self.mode = newMode
  3388.                 self.behaviorFunction = self.behaviors[self.mode]
  3389.                 return
  3390.             end
  3391.         end
  3392.         error("Invalid mode number")
  3393.     end
  3394.    
  3395.     function Invisicam:GetObscuredParts()
  3396.         return self.savedHits
  3397.     end
  3398.    
  3399.     -- Want to turn off Invisicam? Be sure to call this after.
  3400.     function Invisicam:Cleanup()
  3401.         for hit, originalFade in pairs(self.savedHits) do
  3402.             hit.LocalTransparencyModifier = originalFade
  3403.         end
  3404.     end
  3405.    
  3406.     function Invisicam:Update(dt, desiredCameraCFrame, desiredCameraFocus)
  3407.         -- Bail if there is no Character
  3408.         if not self.enabled or not self.char then
  3409.             return desiredCameraCFrame, desiredCameraFocus
  3410.         end
  3411.    
  3412.         self.camera = game.Workspace.CurrentCamera
  3413.    
  3414.         -- TODO: Move this to a GetHumanoidRootPart helper, probably combine with CheckTorsoReference
  3415.         -- Make sure we still have a HumanoidRootPart
  3416.         if not self.humanoidRootPart then
  3417.             local humanoid = self.char:FindFirstChildOfClass("Humanoid")
  3418.             if humanoid and humanoid.RootPart then
  3419.                 self.humanoidRootPart = humanoid.RootPart
  3420.             else
  3421.                 -- Not set up with Humanoid? Try and see if there's one in the Character at all:
  3422.                 self.humanoidRootPart = self.char:FindFirstChild("HumanoidRootPart")
  3423.                 if not self.humanoidRootPart then
  3424.                     -- Bail out, since we're relying on HumanoidRootPart existing
  3425.                     return desiredCameraCFrame, desiredCameraFocus
  3426.                 end
  3427.             end
  3428.    
  3429.             -- TODO: Replace this with something more sensible
  3430.             local ancestryChangedConn
  3431.             ancestryChangedConn = self.humanoidRootPart.AncestryChanged:Connect(function(child, parent)
  3432.                 if child == self.humanoidRootPart and not parent then
  3433.                     self.humanoidRootPart = nil
  3434.                     if ancestryChangedConn and ancestryChangedConn.Connected then
  3435.                         ancestryChangedConn:Disconnect()
  3436.                         ancestryChangedConn = nil
  3437.                     end
  3438.                 end
  3439.             end)
  3440.         end
  3441.    
  3442.         if not self.torsoPart then
  3443.             self:CheckTorsoReference()
  3444.             if not self.torsoPart then
  3445.                 -- Bail out, since we're relying on Torso existing, should never happen since we fall back to using HumanoidRootPart as torso
  3446.                 return desiredCameraCFrame, desiredCameraFocus
  3447.             end
  3448.         end
  3449.    
  3450.         -- Make a list of world points to raycast to
  3451.         local castPoints = {}
  3452.         self.behaviorFunction(self, castPoints)
  3453.    
  3454.         -- Cast to get a list of objects between the camera and the cast points
  3455.         local currentHits = {}
  3456.         local ignoreList = {self.char}
  3457.         local function add(hit)
  3458.             currentHits[hit] = true
  3459.             if not self.savedHits[hit] then
  3460.                 self.savedHits[hit] = hit.LocalTransparencyModifier
  3461.             end
  3462.         end
  3463.    
  3464.         local hitParts
  3465.         local hitPartCount = 0
  3466.    
  3467.         -- Hash table to treat head-ray-hit parts differently than the rest of the hit parts hit by other rays
  3468.         -- head/torso ray hit parts will be more transparent than peripheral parts when USE_STACKING_TRANSPARENCY is enabled
  3469.         local headTorsoRayHitParts = {}
  3470.    
  3471.         local perPartTransparencyHeadTorsoHits = TARGET_TRANSPARENCY
  3472.         local perPartTransparencyOtherHits = TARGET_TRANSPARENCY
  3473.    
  3474.         if USE_STACKING_TRANSPARENCY then
  3475.    
  3476.             -- This first call uses head and torso rays to find out how many parts are stacked up
  3477.             -- for the purpose of calculating required per-part transparency
  3478.             local headPoint = self.headPart and self.headPart.CFrame.p or castPoints[1]
  3479.             local torsoPoint = self.torsoPart and self.torsoPart.CFrame.p or castPoints[2]
  3480.             hitParts = self.camera:GetPartsObscuringTarget({headPoint, torsoPoint}, ignoreList)
  3481.    
  3482.             -- Count how many things the sample rays passed through, including decals. This should only
  3483.             -- count decals facing the camera, but GetPartsObscuringTarget does not return surface normals,
  3484.             -- so my compromise for now is to just let any decal increase the part count by 1. Only one
  3485.             -- decal per part will be considered.
  3486.             for i = 1, #hitParts do
  3487.                 local hitPart = hitParts[i]
  3488.                 hitPartCount = hitPartCount + 1 -- count the part itself
  3489.                 headTorsoRayHitParts[hitPart] = true
  3490.                 for _, child in pairs(hitPart:GetChildren()) do
  3491.                     if child:IsA('Decal') or child:IsA('Texture') then
  3492.                         hitPartCount = hitPartCount + 1 -- count first decal hit, then break
  3493.                         break
  3494.                     end
  3495.                 end
  3496.             end
  3497.    
  3498.             if (hitPartCount > 0) then
  3499.                 perPartTransparencyHeadTorsoHits = math.pow( ((0.5 * TARGET_TRANSPARENCY) + (0.5 * TARGET_TRANSPARENCY / hitPartCount)), 1 / hitPartCount )
  3500.                 perPartTransparencyOtherHits = math.pow( ((0.5 * TARGET_TRANSPARENCY_PERIPHERAL) + (0.5 * TARGET_TRANSPARENCY_PERIPHERAL / hitPartCount)), 1 / hitPartCount )
  3501.             end
  3502.         end
  3503.    
  3504.         -- Now get all the parts hit by all the rays
  3505.         hitParts = self.camera:GetPartsObscuringTarget(castPoints, ignoreList)
  3506.    
  3507.         local partTargetTransparency = {}
  3508.    
  3509.         -- Include decals and textures
  3510.         for i = 1, #hitParts do
  3511.             local hitPart = hitParts[i]
  3512.    
  3513.             partTargetTransparency[hitPart] =headTorsoRayHitParts[hitPart] and perPartTransparencyHeadTorsoHits or perPartTransparencyOtherHits
  3514.    
  3515.             -- If the part is not already as transparent or more transparent than what invisicam requires, add it to the list of
  3516.             -- parts to be modified by invisicam
  3517.             if hitPart.Transparency < partTargetTransparency[hitPart] then
  3518.                 add(hitPart)
  3519.             end
  3520.    
  3521.             -- Check all decals and textures on the part
  3522.             for _, child in pairs(hitPart:GetChildren()) do
  3523.                 if child:IsA('Decal') or child:IsA('Texture') then
  3524.                     if (child.Transparency < partTargetTransparency[hitPart]) then
  3525.                         partTargetTransparency[child] = partTargetTransparency[hitPart]
  3526.                         add(child)
  3527.                     end
  3528.                 end
  3529.             end
  3530.         end
  3531.    
  3532.         -- Invisibilize objects that are in the way, restore those that aren't anymore
  3533.         for hitPart, originalLTM in pairs(self.savedHits) do
  3534.             if currentHits[hitPart] then
  3535.                 -- LocalTransparencyModifier gets whatever value is required to print the part's total transparency to equal perPartTransparency
  3536.                 hitPart.LocalTransparencyModifier = (hitPart.Transparency < 1) and ((partTargetTransparency[hitPart] - hitPart.Transparency) / (1.0 - hitPart.Transparency)) or 0
  3537.             else -- Restore original pre-invisicam value of LTM
  3538.                 hitPart.LocalTransparencyModifier = originalLTM
  3539.                 self.savedHits[hitPart] = nil
  3540.             end
  3541.         end
  3542.    
  3543.         -- Invisicam does not change the camera values
  3544.         return desiredCameraCFrame, desiredCameraFocus
  3545.     end
  3546.    
  3547.     return Invisicam
  3548. end
  3549.  
  3550. function _LegacyCamera()
  3551.    
  3552.     local ZERO_VECTOR2 = Vector2.new(0,0)
  3553.    
  3554.     local Util = _CameraUtils()
  3555.    
  3556.     --[[ Services ]]--
  3557.     local PlayersService = game:GetService('Players')
  3558.    
  3559.     --[[ The Module ]]--
  3560.     local BaseCamera = _BaseCamera()
  3561.     local LegacyCamera = setmetatable({}, BaseCamera)
  3562.     LegacyCamera.__index = LegacyCamera
  3563.    
  3564.     function LegacyCamera.new()
  3565.         local self = setmetatable(BaseCamera.new(), LegacyCamera)
  3566.    
  3567.         self.cameraType = Enum.CameraType.Fixed
  3568.         self.lastUpdate = tick()
  3569.         self.lastDistanceToSubject = nil
  3570.    
  3571.         return self
  3572.     end
  3573.    
  3574.     function LegacyCamera:GetModuleName()
  3575.         return "LegacyCamera"
  3576.     end
  3577.    
  3578.     --[[ Functions overridden from BaseCamera ]]--
  3579.     function LegacyCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
  3580.         return BaseCamera.SetCameraToSubjectDistance(self,desiredSubjectDistance)
  3581.     end
  3582.    
  3583.     function LegacyCamera:Update(dt)
  3584.    
  3585.         -- Cannot update until cameraType has been set
  3586.         if not self.cameraType then return end
  3587.    
  3588.         local now = tick()
  3589.         local timeDelta = (now - self.lastUpdate)
  3590.         local camera =  workspace.CurrentCamera
  3591.         local newCameraCFrame = camera.CFrame
  3592.         local newCameraFocus = camera.Focus
  3593.         local player = PlayersService.LocalPlayer
  3594.    
  3595.         if self.lastUpdate == nil or timeDelta > 1 then
  3596.             self.lastDistanceToSubject = nil
  3597.         end
  3598.         local subjectPosition = self:GetSubjectPosition()
  3599.    
  3600.         if self.cameraType == Enum.CameraType.Fixed then
  3601.             if self.lastUpdate then
  3602.                 -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  3603.                 local delta = math.min(0.1, now - self.lastUpdate)
  3604.                 local gamepadRotation = self:UpdateGamepad()
  3605.                 self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  3606.             end
  3607.    
  3608.             if subjectPosition and player and camera then
  3609.                 local distanceToSubject = self:GetCameraToSubjectDistance()
  3610.                 local newLookVector = self:CalculateNewLookVector()
  3611.                 self.rotateInput = ZERO_VECTOR2
  3612.    
  3613.                 newCameraFocus = camera.Focus -- Fixed camera does not change focus
  3614.                 newCameraCFrame = CFrame.new(camera.CFrame.p, camera.CFrame.p + (distanceToSubject * newLookVector))
  3615.             end
  3616.         elseif self.cameraType == Enum.CameraType.Attach then
  3617.             if subjectPosition and camera then
  3618.                 local distanceToSubject = self:GetCameraToSubjectDistance()
  3619.                 local humanoid = self:GetHumanoid()
  3620.                 if self.lastUpdate and humanoid and humanoid.RootPart then
  3621.    
  3622.                     -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  3623.                     local delta = math.min(0.1, now - self.lastUpdate)
  3624.                     local gamepadRotation = self:UpdateGamepad()
  3625.                     self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  3626.    
  3627.                     local forwardVector = humanoid.RootPart.CFrame.lookVector
  3628.    
  3629.                     local y = Util.GetAngleBetweenXZVectors(forwardVector, self:GetCameraLookVector())
  3630.                     if Util.IsFinite(y) then
  3631.                         -- Preserve vertical rotation from user input
  3632.                         self.rotateInput = Vector2.new(y, self.rotateInput.Y)
  3633.                     end
  3634.                 end
  3635.    
  3636.                 local newLookVector = self:CalculateNewLookVector()
  3637.                 self.rotateInput = ZERO_VECTOR2
  3638.    
  3639.                 newCameraFocus = CFrame.new(subjectPosition)
  3640.                 newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)
  3641.             end
  3642.         elseif self.cameraType == Enum.CameraType.Watch then
  3643.             if subjectPosition and player and camera then
  3644.                 local cameraLook = nil
  3645.    
  3646.                 local humanoid = self:GetHumanoid()
  3647.                 if humanoid and humanoid.RootPart then
  3648.                     local diffVector = subjectPosition - camera.CFrame.p
  3649.                     cameraLook = diffVector.unit
  3650.    
  3651.                     if self.lastDistanceToSubject and self.lastDistanceToSubject == self:GetCameraToSubjectDistance() then
  3652.                         -- Don't clobber the zoom if they zoomed the camera
  3653.                         local newDistanceToSubject = diffVector.magnitude
  3654.                         self:SetCameraToSubjectDistance(newDistanceToSubject)
  3655.                     end
  3656.                 end
  3657.    
  3658.                 local distanceToSubject = self:GetCameraToSubjectDistance()
  3659.                 local newLookVector = self:CalculateNewLookVector(cameraLook)
  3660.                 self.rotateInput = ZERO_VECTOR2
  3661.    
  3662.                 newCameraFocus = CFrame.new(subjectPosition)
  3663.                 newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)
  3664.    
  3665.                 self.lastDistanceToSubject = distanceToSubject
  3666.             end
  3667.         else
  3668.             -- Unsupported type, return current values unchanged
  3669.             return camera.CFrame, camera.Focus
  3670.         end
  3671.    
  3672.         self.lastUpdate = now
  3673.         return newCameraCFrame, newCameraFocus
  3674.     end
  3675.    
  3676.     return LegacyCamera
  3677. end
  3678.  
  3679. function _OrbitalCamera()
  3680.    
  3681.     -- Local private variables and constants
  3682.     local UNIT_Z = Vector3.new(0,0,1)
  3683.     local X1_Y0_Z1 = Vector3.new(1,0,1) --Note: not a unit vector, used for projecting onto XZ plane
  3684.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  3685.     local ZERO_VECTOR2 = Vector2.new(0,0)
  3686.     local TAU = 2 * math.pi
  3687.    
  3688.     --[[ Gamepad Support ]]--
  3689.     local THUMBSTICK_DEADZONE = 0.2
  3690.    
  3691.     -- Do not edit these values, they are not the developer-set limits, they are limits
  3692.     -- to the values the camera system equations can correctly handle
  3693.     local MIN_ALLOWED_ELEVATION_DEG = -80
  3694.     local MAX_ALLOWED_ELEVATION_DEG = 80
  3695.    
  3696.     local externalProperties = {}
  3697.     externalProperties["InitialDistance"]  = 25
  3698.     externalProperties["MinDistance"]      = 10
  3699.     externalProperties["MaxDistance"]      = 100
  3700.     externalProperties["InitialElevation"] = 35
  3701.     externalProperties["MinElevation"]     = 35
  3702.     externalProperties["MaxElevation"]     = 35
  3703.     externalProperties["ReferenceAzimuth"] = -45    -- Angle around the Y axis where the camera starts. -45 offsets the camera in the -X and +Z directions equally
  3704.     externalProperties["CWAzimuthTravel"]  = 90 -- How many degrees the camera is allowed to rotate from the reference position, CW as seen from above
  3705.     externalProperties["CCWAzimuthTravel"] = 90 -- How many degrees the camera is allowed to rotate from the reference position, CCW as seen from above
  3706.     externalProperties["UseAzimuthLimits"] = false -- Full rotation around Y axis available by default
  3707.    
  3708.     local Util = _CameraUtils()
  3709.    
  3710.     --[[ Services ]]--
  3711.     local PlayersService = game:GetService('Players')
  3712.     local VRService = game:GetService("VRService")
  3713.    
  3714.     --[[ The Module ]]--
  3715.     local BaseCamera = _BaseCamera()
  3716.     local OrbitalCamera = setmetatable({}, BaseCamera)
  3717.     OrbitalCamera.__index = OrbitalCamera
  3718.    
  3719.    
  3720.     function OrbitalCamera.new()
  3721.         local self = setmetatable(BaseCamera.new(), OrbitalCamera)
  3722.    
  3723.         self.lastUpdate = tick()
  3724.    
  3725.         -- OrbitalCamera-specific members
  3726.         self.changedSignalConnections = {}
  3727.         self.refAzimuthRad = nil
  3728.         self.curAzimuthRad = nil
  3729.         self.minAzimuthAbsoluteRad = nil
  3730.         self.maxAzimuthAbsoluteRad = nil
  3731.         self.useAzimuthLimits = nil
  3732.         self.curElevationRad = nil
  3733.         self.minElevationRad = nil
  3734.         self.maxElevationRad = nil
  3735.         self.curDistance = nil
  3736.         self.minDistance = nil
  3737.         self.maxDistance = nil
  3738.    
  3739.         -- Gamepad
  3740.         self.r3ButtonDown = false
  3741.         self.l3ButtonDown = false
  3742.         self.gamepadDollySpeedMultiplier = 1
  3743.    
  3744.         self.lastUserPanCamera = tick()
  3745.    
  3746.         self.externalProperties = {}
  3747.         self.externalProperties["InitialDistance"]  = 25
  3748.         self.externalProperties["MinDistance"]      = 10
  3749.         self.externalProperties["MaxDistance"]      = 100
  3750.         self.externalProperties["InitialElevation"]     = 35
  3751.         self.externalProperties["MinElevation"]         = 35
  3752.         self.externalProperties["MaxElevation"]         = 35
  3753.         self.externalProperties["ReferenceAzimuth"]     = -45   -- Angle around the Y axis where the camera starts. -45 offsets the camera in the -X and +Z directions equally
  3754.         self.externalProperties["CWAzimuthTravel"]  = 90    -- How many degrees the camera is allowed to rotate from the reference position, CW as seen from above
  3755.         self.externalProperties["CCWAzimuthTravel"]     = 90    -- How many degrees the camera is allowed to rotate from the reference position, CCW as seen from above
  3756.         self.externalProperties["UseAzimuthLimits"]     = false -- Full rotation around Y axis available by default
  3757.         self:LoadNumberValueParameters()
  3758.    
  3759.         return self
  3760.     end
  3761.    
  3762.     function OrbitalCamera:LoadOrCreateNumberValueParameter(name, valueType, updateFunction)
  3763.         local valueObj = script:FindFirstChild(name)
  3764.    
  3765.         if valueObj and valueObj:isA(valueType) then
  3766.             -- Value object exists and is the correct type, use its value
  3767.             self.externalProperties[name] = valueObj.Value
  3768.         elseif self.externalProperties[name] ~= nil then
  3769.             -- Create missing (or replace incorrectly-typed) valueObject with default value
  3770.             valueObj = Instance.new(valueType)
  3771.             valueObj.Name = name
  3772.             valueObj.Parent = script
  3773.             valueObj.Value = self.externalProperties[name]
  3774.         else
  3775.             print("externalProperties table has no entry for ",name)
  3776.             return
  3777.         end
  3778.    
  3779.         if updateFunction then
  3780.             if self.changedSignalConnections[name] then
  3781.                 self.changedSignalConnections[name]:Disconnect()
  3782.             end
  3783.             self.changedSignalConnections[name] = valueObj.Changed:Connect(function(newValue)
  3784.                 self.externalProperties[name] = newValue
  3785.                 updateFunction(self)
  3786.             end)
  3787.         end
  3788.     end
  3789.    
  3790.     function OrbitalCamera:SetAndBoundsCheckAzimuthValues()
  3791.         self.minAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) - math.abs(math.rad(self.externalProperties["CWAzimuthTravel"]))
  3792.         self.maxAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) + math.abs(math.rad(self.externalProperties["CCWAzimuthTravel"]))
  3793.         self.useAzimuthLimits = self.externalProperties["UseAzimuthLimits"]
  3794.         if self.useAzimuthLimits then
  3795.             self.curAzimuthRad = math.max(self.curAzimuthRad, self.minAzimuthAbsoluteRad)
  3796.             self.curAzimuthRad = math.min(self.curAzimuthRad, self.maxAzimuthAbsoluteRad)
  3797.         end
  3798.     end
  3799.    
  3800.     function OrbitalCamera:SetAndBoundsCheckElevationValues()
  3801.         -- These degree values are the direct user input values. It is deliberate that they are
  3802.         -- ranged checked only against the extremes, and not against each other. Any time one
  3803.         -- is changed, both of the internal values in radians are recalculated. This allows for
  3804.         -- A developer to change the values in any order and for the end results to be that the
  3805.         -- internal values adjust to match intent as best as possible.
  3806.         local minElevationDeg = math.max(self.externalProperties["MinElevation"], MIN_ALLOWED_ELEVATION_DEG)
  3807.         local maxElevationDeg = math.min(self.externalProperties["MaxElevation"], MAX_ALLOWED_ELEVATION_DEG)
  3808.    
  3809.         -- Set internal values in radians
  3810.         self.minElevationRad = math.rad(math.min(minElevationDeg, maxElevationDeg))
  3811.         self.maxElevationRad = math.rad(math.max(minElevationDeg, maxElevationDeg))
  3812.         self.curElevationRad = math.max(self.curElevationRad, self.minElevationRad)
  3813.         self.curElevationRad = math.min(self.curElevationRad, self.maxElevationRad)
  3814.     end
  3815.    
  3816.     function OrbitalCamera:SetAndBoundsCheckDistanceValues()
  3817.         self.minDistance = self.externalProperties["MinDistance"]
  3818.         self.maxDistance = self.externalProperties["MaxDistance"]
  3819.         self.curDistance = math.max(self.curDistance, self.minDistance)
  3820.         self.curDistance = math.min(self.curDistance, self.maxDistance)
  3821.     end
  3822.    
  3823.     -- This loads from, or lazily creates, NumberValue objects for exposed parameters
  3824.     function OrbitalCamera:LoadNumberValueParameters()
  3825.         -- These initial values do not require change listeners since they are read only once
  3826.         self:LoadOrCreateNumberValueParameter("InitialElevation", "NumberValue", nil)
  3827.         self:LoadOrCreateNumberValueParameter("InitialDistance", "NumberValue", nil)
  3828.    
  3829.         -- Note: ReferenceAzimuth is also used as an initial value, but needs a change listener because it is used in the calculation of the limits
  3830.         self:LoadOrCreateNumberValueParameter("ReferenceAzimuth", "NumberValue", self.SetAndBoundsCheckAzimuthValue)
  3831.         self:LoadOrCreateNumberValueParameter("CWAzimuthTravel", "NumberValue", self.SetAndBoundsCheckAzimuthValues)
  3832.         self:LoadOrCreateNumberValueParameter("CCWAzimuthTravel", "NumberValue", self.SetAndBoundsCheckAzimuthValues)
  3833.         self:LoadOrCreateNumberValueParameter("MinElevation", "NumberValue", self.SetAndBoundsCheckElevationValues)
  3834.         self:LoadOrCreateNumberValueParameter("MaxElevation", "NumberValue", self.SetAndBoundsCheckElevationValues)
  3835.         self:LoadOrCreateNumberValueParameter("MinDistance", "NumberValue", self.SetAndBoundsCheckDistanceValues)
  3836.         self:LoadOrCreateNumberValueParameter("MaxDistance", "NumberValue", self.SetAndBoundsCheckDistanceValues)
  3837.         self:LoadOrCreateNumberValueParameter("UseAzimuthLimits", "BoolValue", self.SetAndBoundsCheckAzimuthValues)
  3838.    
  3839.         -- Internal values set (in radians, from degrees), plus sanitization
  3840.         self.curAzimuthRad = math.rad(self.externalProperties["ReferenceAzimuth"])
  3841.         self.curElevationRad = math.rad(self.externalProperties["InitialElevation"])
  3842.         self.curDistance = self.externalProperties["InitialDistance"]
  3843.    
  3844.         self:SetAndBoundsCheckAzimuthValues()
  3845.         self:SetAndBoundsCheckElevationValues()
  3846.         self:SetAndBoundsCheckDistanceValues()
  3847.     end
  3848.    
  3849.     function OrbitalCamera:GetModuleName()
  3850.         return "OrbitalCamera"
  3851.     end
  3852.    
  3853.     function OrbitalCamera:SetInitialOrientation(humanoid)
  3854.         if not humanoid or not humanoid.RootPart then
  3855.             warn("OrbitalCamera could not set initial orientation due to missing humanoid")
  3856.             return
  3857.         end
  3858.         local newDesiredLook = (humanoid.RootPart.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
  3859.         local horizontalShift = Util.GetAngleBetweenXZVectors(newDesiredLook, self:GetCameraLookVector())
  3860.         local vertShift = math.asin(self:GetCameraLookVector().y) - math.asin(newDesiredLook.y)
  3861.         if not Util.IsFinite(horizontalShift) then
  3862.             horizontalShift = 0
  3863.         end
  3864.         if not Util.IsFinite(vertShift) then
  3865.             vertShift = 0
  3866.         end
  3867.         self.rotateInput = Vector2.new(horizontalShift, vertShift)
  3868.     end
  3869.    
  3870.     --[[ Functions of BaseCamera that are overridden by OrbitalCamera ]]--
  3871.     function OrbitalCamera:GetCameraToSubjectDistance()
  3872.         return self.curDistance
  3873.     end
  3874.    
  3875.     function OrbitalCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
  3876.         print("OrbitalCamera SetCameraToSubjectDistance ",desiredSubjectDistance)
  3877.         local player = PlayersService.LocalPlayer
  3878.         if player then
  3879.             self.currentSubjectDistance = math.clamp(desiredSubjectDistance, self.minDistance, self.maxDistance)
  3880.    
  3881.             -- OrbitalCamera is not allowed to go into the first-person range
  3882.             self.currentSubjectDistance = math.max(self.currentSubjectDistance, self.FIRST_PERSON_DISTANCE_THRESHOLD)
  3883.         end
  3884.         self.inFirstPerson = false
  3885.         self:UpdateMouseBehavior()
  3886.         return self.currentSubjectDistance
  3887.     end
  3888.    
  3889.     function OrbitalCamera:CalculateNewLookVector(suppliedLookVector, xyRotateVector)
  3890.         local currLookVector = suppliedLookVector or self:GetCameraLookVector()
  3891.         local currPitchAngle = math.asin(currLookVector.y)
  3892.         local yTheta = math.clamp(xyRotateVector.y, currPitchAngle - math.rad(MAX_ALLOWED_ELEVATION_DEG), currPitchAngle - math.rad(MIN_ALLOWED_ELEVATION_DEG))
  3893.         local constrainedRotateInput = Vector2.new(xyRotateVector.x, yTheta)
  3894.         local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
  3895.         local newLookVector = (CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)).lookVector
  3896.         return newLookVector
  3897.     end
  3898.    
  3899.     function OrbitalCamera:GetGamepadPan(name, state, input)
  3900.         if input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
  3901.             if self.r3ButtonDown or self.l3ButtonDown then
  3902.             -- R3 or L3 Thumbstick is depressed, right stick controls dolly in/out
  3903.                 if (input.Position.Y > THUMBSTICK_DEADZONE) then
  3904.                     self.gamepadDollySpeedMultiplier = 0.96
  3905.                 elseif (input.Position.Y < -THUMBSTICK_DEADZONE) then
  3906.                     self.gamepadDollySpeedMultiplier = 1.04
  3907.                 else
  3908.                     self.gamepadDollySpeedMultiplier = 1.00
  3909.                 end
  3910.             else
  3911.                 if state == Enum.UserInputState.Cancel then
  3912.                     self.gamepadPanningCamera = ZERO_VECTOR2
  3913.                     return
  3914.                 end
  3915.    
  3916.                 local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
  3917.                 if inputVector.magnitude > THUMBSTICK_DEADZONE then
  3918.                     self.gamepadPanningCamera = Vector2.new(input.Position.X, -input.Position.Y)
  3919.                 else
  3920.                     self.gamepadPanningCamera = ZERO_VECTOR2
  3921.                 end
  3922.             end
  3923.             return Enum.ContextActionResult.Sink
  3924.         end
  3925.         return Enum.ContextActionResult.Pass
  3926.     end
  3927.    
  3928.     function OrbitalCamera:DoGamepadZoom(name, state, input)
  3929.         if input.UserInputType == self.activeGamepad and (input.KeyCode == Enum.KeyCode.ButtonR3 or input.KeyCode == Enum.KeyCode.ButtonL3) then
  3930.             if (state == Enum.UserInputState.Begin) then
  3931.                 self.r3ButtonDown = input.KeyCode == Enum.KeyCode.ButtonR3
  3932.                 self.l3ButtonDown = input.KeyCode == Enum.KeyCode.ButtonL3
  3933.             elseif (state == Enum.UserInputState.End) then
  3934.                 if (input.KeyCode == Enum.KeyCode.ButtonR3) then
  3935.                     self.r3ButtonDown = false
  3936.                 elseif (input.KeyCode == Enum.KeyCode.ButtonL3) then
  3937.                     self.l3ButtonDown = false
  3938.                 end
  3939.                 if (not self.r3ButtonDown) and (not self.l3ButtonDown) then
  3940.                     self.gamepadDollySpeedMultiplier = 1.00
  3941.                 end
  3942.             end
  3943.             return Enum.ContextActionResult.Sink
  3944.         end
  3945.         return Enum.ContextActionResult.Pass
  3946.     end
  3947.    
  3948.     function OrbitalCamera:BindGamepadInputActions()
  3949.         self:BindAction("OrbitalCamGamepadPan", function(name, state, input) return self:GetGamepadPan(name, state, input) end,
  3950.             false, Enum.KeyCode.Thumbstick2)
  3951.         self:BindAction("OrbitalCamGamepadZoom", function(name, state, input) return self:DoGamepadZoom(name, state, input) end,
  3952.             false, Enum.KeyCode.ButtonR3, Enum.KeyCode.ButtonL3)
  3953.     end
  3954.    
  3955.    
  3956.     -- [[ Update ]]--
  3957.     function OrbitalCamera:Update(dt)
  3958.         local now = tick()
  3959.         local timeDelta = (now - self.lastUpdate)
  3960.         local userPanningTheCamera = (self.UserPanningTheCamera == true)
  3961.         local camera =  workspace.CurrentCamera
  3962.         local newCameraCFrame = camera.CFrame
  3963.         local newCameraFocus = camera.Focus
  3964.         local player = PlayersService.LocalPlayer
  3965.         local cameraSubject = camera and camera.CameraSubject
  3966.         local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
  3967.         local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
  3968.    
  3969.         if self.lastUpdate == nil or timeDelta > 1 then
  3970.             self.lastCameraTransform = nil
  3971.         end
  3972.    
  3973.         if self.lastUpdate then
  3974.             local gamepadRotation = self:UpdateGamepad()
  3975.    
  3976.             if self:ShouldUseVRRotation() then
  3977.                 self.RotateInput = self.RotateInput + self:GetVRRotationInput()
  3978.             else
  3979.                 -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  3980.                 local delta = math.min(0.1, timeDelta)
  3981.    
  3982.                 if gamepadRotation ~= ZERO_VECTOR2 then
  3983.                     userPanningTheCamera = true
  3984.                     self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  3985.                 end
  3986.    
  3987.                 local angle = 0
  3988.                 if not (isInVehicle or isOnASkateboard) then
  3989.                     angle = angle + (self.TurningLeft and -120 or 0)
  3990.                     angle = angle + (self.TurningRight and 120 or 0)
  3991.                 end
  3992.    
  3993.                 if angle ~= 0 then
  3994.                     self.rotateInput = self.rotateInput +  Vector2.new(math.rad(angle * delta), 0)
  3995.                     userPanningTheCamera = true
  3996.                 end
  3997.             end
  3998.         end
  3999.    
  4000.         -- Reset tween speed if user is panning
  4001.         if userPanningTheCamera then
  4002.             self.lastUserPanCamera = tick()
  4003.         end
  4004.    
  4005.         local subjectPosition = self:GetSubjectPosition()
  4006.    
  4007.         if subjectPosition and player and camera then
  4008.    
  4009.             -- Process any dollying being done by gamepad
  4010.             -- TODO: Move this
  4011.             if self.gamepadDollySpeedMultiplier ~= 1 then
  4012.                 self:SetCameraToSubjectDistance(self.currentSubjectDistance * self.gamepadDollySpeedMultiplier)
  4013.             end
  4014.    
  4015.             local VREnabled = VRService.VREnabled
  4016.             newCameraFocus = VREnabled and self:GetVRFocus(subjectPosition, timeDelta) or CFrame.new(subjectPosition)
  4017.    
  4018.             local cameraFocusP = newCameraFocus.p
  4019.             if VREnabled and not self:IsInFirstPerson() then
  4020.                 local cameraHeight = self:GetCameraHeight()
  4021.                 local vecToSubject = (subjectPosition - camera.CFrame.p)
  4022.                 local distToSubject = vecToSubject.magnitude
  4023.    
  4024.                 -- Only move the camera if it exceeded a maximum distance to the subject in VR
  4025.                 if distToSubject > self.currentSubjectDistance or self.rotateInput.x ~= 0 then
  4026.                     local desiredDist = math.min(distToSubject, self.currentSubjectDistance)
  4027.    
  4028.                     -- Note that CalculateNewLookVector is overridden from BaseCamera
  4029.                     vecToSubject = self:CalculateNewLookVector(vecToSubject.unit * X1_Y0_Z1, Vector2.new(self.rotateInput.x, 0)) * desiredDist
  4030.    
  4031.                     local newPos = cameraFocusP - vecToSubject
  4032.                     local desiredLookDir = camera.CFrame.lookVector
  4033.                     if self.rotateInput.x ~= 0 then
  4034.                         desiredLookDir = vecToSubject
  4035.                     end
  4036.                     local lookAt = Vector3.new(newPos.x + desiredLookDir.x, newPos.y, newPos.z + desiredLookDir.z)
  4037.                     self.RotateInput = ZERO_VECTOR2
  4038.    
  4039.                     newCameraCFrame = CFrame.new(newPos, lookAt) + Vector3.new(0, cameraHeight, 0)
  4040.                 end
  4041.             else
  4042.                 -- self.RotateInput is a Vector2 of mouse movement deltas since last update
  4043.                 self.curAzimuthRad = self.curAzimuthRad - self.rotateInput.x
  4044.    
  4045.                 if self.useAzimuthLimits then
  4046.                     self.curAzimuthRad = math.clamp(self.curAzimuthRad, self.minAzimuthAbsoluteRad, self.maxAzimuthAbsoluteRad)
  4047.                 else
  4048.                     self.curAzimuthRad = (self.curAzimuthRad ~= 0) and (math.sign(self.curAzimuthRad) * (math.abs(self.curAzimuthRad) % TAU)) or 0
  4049.                 end
  4050.    
  4051.                 self.curElevationRad = math.clamp(self.curElevationRad + self.rotateInput.y, self.minElevationRad, self.maxElevationRad)
  4052.    
  4053.                 local cameraPosVector = self.currentSubjectDistance * ( CFrame.fromEulerAnglesYXZ( -self.curElevationRad, self.curAzimuthRad, 0 ) * UNIT_Z )
  4054.                 local camPos = subjectPosition + cameraPosVector
  4055.    
  4056.                 newCameraCFrame = CFrame.new(camPos, subjectPosition)
  4057.    
  4058.                 self.rotateInput = ZERO_VECTOR2
  4059.             end
  4060.    
  4061.             self.lastCameraTransform = newCameraCFrame
  4062.             self.lastCameraFocus = newCameraFocus
  4063.             if (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
  4064.                 self.lastSubjectCFrame = cameraSubject.CFrame
  4065.             else
  4066.                 self.lastSubjectCFrame = nil
  4067.             end
  4068.         end
  4069.    
  4070.         self.lastUpdate = now
  4071.         return newCameraCFrame, newCameraFocus
  4072.     end
  4073.    
  4074.     return OrbitalCamera
  4075. end
  4076.  
  4077. function _ClassicCamera()
  4078.    
  4079.     -- Local private variables and constants
  4080.     local ZERO_VECTOR2 = Vector2.new(0,0)
  4081.    
  4082.     local tweenAcceleration = math.rad(220)     --Radians/Second^2
  4083.     local tweenSpeed = math.rad(0)              --Radians/Second
  4084.     local tweenMaxSpeed = math.rad(250)         --Radians/Second
  4085.     local TIME_BEFORE_AUTO_ROTATE = 2.0         --Seconds, used when auto-aligning camera with vehicles
  4086.    
  4087.     local INITIAL_CAMERA_ANGLE = CFrame.fromOrientation(math.rad(-15), 0, 0)
  4088.    
  4089.     local FFlagUserCameraToggle do
  4090.         local success, result = pcall(function()
  4091.             return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  4092.         end)
  4093.         FFlagUserCameraToggle = success and result
  4094.     end
  4095.    
  4096.     --[[ Services ]]--
  4097.     local PlayersService = game:GetService('Players')
  4098.     local VRService = game:GetService("VRService")
  4099.    
  4100.     local CameraInput = _CameraInput()
  4101.     local Util = _CameraUtils()
  4102.    
  4103.     --[[ The Module ]]--
  4104.     local BaseCamera = _BaseCamera()
  4105.     local ClassicCamera = setmetatable({}, BaseCamera)
  4106.     ClassicCamera.__index = ClassicCamera
  4107.    
  4108.     function ClassicCamera.new()
  4109.         local self = setmetatable(BaseCamera.new(), ClassicCamera)
  4110.    
  4111.         self.isFollowCamera = false
  4112.         self.isCameraToggle = false
  4113.         self.lastUpdate = tick()
  4114.         self.cameraToggleSpring = Util.Spring.new(5, 0)
  4115.    
  4116.         return self
  4117.     end
  4118.    
  4119.     function ClassicCamera:GetCameraToggleOffset(dt)
  4120.         assert(FFlagUserCameraToggle)
  4121.    
  4122.         if self.isCameraToggle then
  4123.             local zoom = self.currentSubjectDistance
  4124.    
  4125.             if CameraInput.getTogglePan() then
  4126.                 self.cameraToggleSpring.goal = math.clamp(Util.map(zoom, 0.5, self.FIRST_PERSON_DISTANCE_THRESHOLD, 0, 1), 0, 1)
  4127.             else
  4128.                 self.cameraToggleSpring.goal = 0
  4129.             end
  4130.    
  4131.             local distanceOffset = math.clamp(Util.map(zoom, 0.5, 64, 0, 1), 0, 1) + 1
  4132.             return Vector3.new(0, self.cameraToggleSpring:step(dt)*distanceOffset, 0)
  4133.         end
  4134.    
  4135.         return Vector3.new()
  4136.     end
  4137.    
  4138.     -- Movement mode standardized to Enum.ComputerCameraMovementMode values
  4139.     function ClassicCamera:SetCameraMovementMode(cameraMovementMode)
  4140.         BaseCamera.SetCameraMovementMode(self, cameraMovementMode)
  4141.    
  4142.         self.isFollowCamera = cameraMovementMode == Enum.ComputerCameraMovementMode.Follow
  4143.         self.isCameraToggle = cameraMovementMode == Enum.ComputerCameraMovementMode.CameraToggle
  4144.     end
  4145.    
  4146.     function ClassicCamera:Update()
  4147.         local now = tick()
  4148.         local timeDelta = now - self.lastUpdate
  4149.    
  4150.         local camera = workspace.CurrentCamera
  4151.         local newCameraCFrame = camera.CFrame
  4152.         local newCameraFocus = camera.Focus
  4153.    
  4154.         local overrideCameraLookVector = nil
  4155.         if self.resetCameraAngle then
  4156.             local rootPart = self:GetHumanoidRootPart()
  4157.             if rootPart then
  4158.                 overrideCameraLookVector = (rootPart.CFrame * INITIAL_CAMERA_ANGLE).lookVector
  4159.             else
  4160.                 overrideCameraLookVector = INITIAL_CAMERA_ANGLE.lookVector
  4161.             end
  4162.             self.resetCameraAngle = false
  4163.         end
  4164.    
  4165.         local player = PlayersService.LocalPlayer
  4166.         local humanoid = self:GetHumanoid()
  4167.         local cameraSubject = camera.CameraSubject
  4168.         local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
  4169.         local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
  4170.         local isClimbing = humanoid and humanoid:GetState() == Enum.HumanoidStateType.Climbing
  4171.    
  4172.         if self.lastUpdate == nil or timeDelta > 1 then
  4173.             self.lastCameraTransform = nil
  4174.         end
  4175.    
  4176.         if self.lastUpdate then
  4177.             local gamepadRotation = self:UpdateGamepad()
  4178.    
  4179.             if self:ShouldUseVRRotation() then
  4180.                 self.rotateInput = self.rotateInput + self:GetVRRotationInput()
  4181.             else
  4182.                 -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  4183.                 local delta = math.min(0.1, timeDelta)
  4184.    
  4185.                 if gamepadRotation ~= ZERO_VECTOR2 then
  4186.                     self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  4187.                 end
  4188.    
  4189.                 local angle = 0
  4190.                 if not (isInVehicle or isOnASkateboard) then
  4191.                     angle = angle + (self.turningLeft and -120 or 0)
  4192.                     angle = angle + (self.turningRight and 120 or 0)
  4193.                 end
  4194.    
  4195.                 if angle ~= 0 then
  4196.                     self.rotateInput = self.rotateInput +  Vector2.new(math.rad(angle * delta), 0)
  4197.                 end
  4198.             end
  4199.         end
  4200.    
  4201.         local cameraHeight = self:GetCameraHeight()
  4202.    
  4203.         -- Reset tween speed if user is panning
  4204.         if self.userPanningTheCamera then
  4205.             tweenSpeed = 0
  4206.             self.lastUserPanCamera = tick()
  4207.         end
  4208.    
  4209.         local userRecentlyPannedCamera = now - self.lastUserPanCamera < TIME_BEFORE_AUTO_ROTATE
  4210.         local subjectPosition = self:GetSubjectPosition()
  4211.    
  4212.         if subjectPosition and player and camera then
  4213.             local zoom = self:GetCameraToSubjectDistance()
  4214.             if zoom < 0.5 then
  4215.                 zoom = 0.5
  4216.             end
  4217.    
  4218.             if self:GetIsMouseLocked() and not self:IsInFirstPerson() then
  4219.                 -- We need to use the right vector of the camera after rotation, not before
  4220.                 local newLookCFrame = self:CalculateNewLookCFrame(overrideCameraLookVector)
  4221.    
  4222.                 local offset = self:GetMouseLockOffset()
  4223.                 local cameraRelativeOffset = offset.X * newLookCFrame.rightVector + offset.Y * newLookCFrame.upVector + offset.Z * newLookCFrame.lookVector
  4224.    
  4225.                 --offset can be NAN, NAN, NAN if newLookVector has only y component
  4226.                 if Util.IsFiniteVector3(cameraRelativeOffset) then
  4227.                     subjectPosition = subjectPosition + cameraRelativeOffset
  4228.                 end
  4229.             else
  4230.                 if not self.userPanningTheCamera and self.lastCameraTransform then
  4231.    
  4232.                     local isInFirstPerson = self:IsInFirstPerson()
  4233.    
  4234.                     if (isInVehicle or isOnASkateboard or (self.isFollowCamera and isClimbing)) and self.lastUpdate and humanoid and humanoid.Torso then
  4235.                         if isInFirstPerson then
  4236.                             if self.lastSubjectCFrame and (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
  4237.                                 local y = -Util.GetAngleBetweenXZVectors(self.lastSubjectCFrame.lookVector, cameraSubject.CFrame.lookVector)
  4238.                                 if Util.IsFinite(y) then
  4239.                                     self.rotateInput = self.rotateInput + Vector2.new(y, 0)
  4240.                                 end
  4241.                                 tweenSpeed = 0
  4242.                             end
  4243.                         elseif not userRecentlyPannedCamera then
  4244.                             local forwardVector = humanoid.Torso.CFrame.lookVector
  4245.                             if isOnASkateboard then
  4246.                                 forwardVector = cameraSubject.CFrame.lookVector
  4247.                             end
  4248.    
  4249.                             tweenSpeed = math.clamp(tweenSpeed + tweenAcceleration * timeDelta, 0, tweenMaxSpeed)
  4250.    
  4251.                             local percent = math.clamp(tweenSpeed * timeDelta, 0, 1)
  4252.                             if self:IsInFirstPerson() and not (self.isFollowCamera and self.isClimbing) then
  4253.                                 percent = 1
  4254.                             end
  4255.    
  4256.                             local y = Util.GetAngleBetweenXZVectors(forwardVector, self:GetCameraLookVector())
  4257.                             if Util.IsFinite(y) and math.abs(y) > 0.0001 then
  4258.                                 self.rotateInput = self.rotateInput + Vector2.new(y * percent, 0)
  4259.                             end
  4260.                         end
  4261.    
  4262.                     elseif self.isFollowCamera and (not (isInFirstPerson or userRecentlyPannedCamera) and not VRService.VREnabled) then
  4263.                         -- Logic that was unique to the old FollowCamera module
  4264.                         local lastVec = -(self.lastCameraTransform.p - subjectPosition)
  4265.    
  4266.                         local y = Util.GetAngleBetweenXZVectors(lastVec, self:GetCameraLookVector())
  4267.    
  4268.                         -- This cutoff is to decide if the humanoid's angle of movement,
  4269.                         -- relative to the camera's look vector, is enough that
  4270.                         -- we want the camera to be following them. The point is to provide
  4271.                         -- a sizable dead zone to allow more precise forward movements.
  4272.                         local thetaCutoff = 0.4
  4273.    
  4274.                         -- Check for NaNs
  4275.                         if Util.IsFinite(y) and math.abs(y) > 0.0001 and math.abs(y) > thetaCutoff * timeDelta then
  4276.                             self.rotateInput = self.rotateInput + Vector2.new(y, 0)
  4277.                         end
  4278.                     end
  4279.                 end
  4280.             end
  4281.    
  4282.             if not self.isFollowCamera then
  4283.                 local VREnabled = VRService.VREnabled
  4284.    
  4285.                 if VREnabled then
  4286.                     newCameraFocus = self:GetVRFocus(subjectPosition, timeDelta)
  4287.                 else
  4288.                     newCameraFocus = CFrame.new(subjectPosition)
  4289.                 end
  4290.    
  4291.                 local cameraFocusP = newCameraFocus.p
  4292.                 if VREnabled and not self:IsInFirstPerson() then
  4293.                     local vecToSubject = (subjectPosition - camera.CFrame.p)
  4294.                     local distToSubject = vecToSubject.magnitude
  4295.    
  4296.                     -- Only move the camera if it exceeded a maximum distance to the subject in VR
  4297.                     if distToSubject > zoom or self.rotateInput.x ~= 0 then
  4298.                         local desiredDist = math.min(distToSubject, zoom)
  4299.                         vecToSubject = self:CalculateNewLookVectorVR() * desiredDist
  4300.                         local newPos = cameraFocusP - vecToSubject
  4301.                         local desiredLookDir = camera.CFrame.lookVector
  4302.                         if self.rotateInput.x ~= 0 then
  4303.                             desiredLookDir = vecToSubject
  4304.                         end
  4305.                         local lookAt = Vector3.new(newPos.x + desiredLookDir.x, newPos.y, newPos.z + desiredLookDir.z)
  4306.                         self.rotateInput = ZERO_VECTOR2
  4307.    
  4308.                         newCameraCFrame = CFrame.new(newPos, lookAt) + Vector3.new(0, cameraHeight, 0)
  4309.                     end
  4310.                 else
  4311.                     local newLookVector = self:CalculateNewLookVector(overrideCameraLookVector)
  4312.                     self.rotateInput = ZERO_VECTOR2
  4313.                     newCameraCFrame = CFrame.new(cameraFocusP - (zoom * newLookVector), cameraFocusP)
  4314.                 end
  4315.             else -- is FollowCamera
  4316.                 local newLookVector = self:CalculateNewLookVector(overrideCameraLookVector)
  4317.                 self.rotateInput = ZERO_VECTOR2
  4318.    
  4319.                 if VRService.VREnabled then
  4320.                     newCameraFocus = self:GetVRFocus(subjectPosition, timeDelta)
  4321.                 else
  4322.                     newCameraFocus = CFrame.new(subjectPosition)
  4323.                 end
  4324.                 newCameraCFrame = CFrame.new(newCameraFocus.p - (zoom * newLookVector), newCameraFocus.p) + Vector3.new(0, cameraHeight, 0)
  4325.             end
  4326.    
  4327.             if FFlagUserCameraToggle then
  4328.                 local toggleOffset = self:GetCameraToggleOffset(timeDelta)
  4329.                 newCameraFocus = newCameraFocus + toggleOffset
  4330.                 newCameraCFrame = newCameraCFrame + toggleOffset
  4331.             end
  4332.    
  4333.             self.lastCameraTransform = newCameraCFrame
  4334.             self.lastCameraFocus = newCameraFocus
  4335.             if (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
  4336.                 self.lastSubjectCFrame = cameraSubject.CFrame
  4337.             else
  4338.                 self.lastSubjectCFrame = nil
  4339.             end
  4340.         end
  4341.    
  4342.         self.lastUpdate = now
  4343.         return newCameraCFrame, newCameraFocus
  4344.     end
  4345.    
  4346.     function ClassicCamera:EnterFirstPerson()
  4347.         self.inFirstPerson = true
  4348.         self:UpdateMouseBehavior()
  4349.     end
  4350.    
  4351.     function ClassicCamera:LeaveFirstPerson()
  4352.         self.inFirstPerson = false
  4353.         self:UpdateMouseBehavior()
  4354.     end
  4355.    
  4356.     return ClassicCamera
  4357. end
  4358.  
  4359. function _CameraUtils()
  4360.  
  4361.     local CameraUtils = {}
  4362.    
  4363.     local FFlagUserCameraToggle do
  4364.         local success, result = pcall(function()
  4365.             return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  4366.         end)
  4367.         FFlagUserCameraToggle = success and result
  4368.     end
  4369.    
  4370.     local function round(num)
  4371.         return math.floor(num + 0.5)
  4372.     end
  4373.    
  4374.     -- Critically damped spring class for fluid motion effects
  4375.     local Spring = {} do
  4376.         Spring.__index = Spring
  4377.    
  4378.         -- Initialize to a given undamped frequency and default position
  4379.         function Spring.new(freq, pos)
  4380.             return setmetatable({
  4381.                 freq = freq,
  4382.                 goal = pos,
  4383.                 pos = pos,
  4384.                 vel = 0,
  4385.             }, Spring)
  4386.         end
  4387.    
  4388.         -- Advance the spring simulation by `dt` seconds
  4389.         function Spring:step(dt)
  4390.             local f = self.freq*2*math.pi
  4391.             local g = self.goal
  4392.             local p0 = self.pos
  4393.             local v0 = self.vel
  4394.    
  4395.             local offset = p0 - g
  4396.             local decay = math.exp(-f*dt)
  4397.    
  4398.             local p1 = (offset*(1 + f*dt) + v0*dt)*decay + g
  4399.             local v1 = (v0*(1 - f*dt) - offset*(f*f*dt))*decay
  4400.    
  4401.             self.pos = p1
  4402.             self.vel = v1
  4403.    
  4404.             return p1
  4405.         end
  4406.     end
  4407.    
  4408.     CameraUtils.Spring = Spring
  4409.    
  4410.     -- map a value from one range to another
  4411.     function CameraUtils.map(x, inMin, inMax, outMin, outMax)
  4412.         return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
  4413.     end
  4414.    
  4415.     -- From TransparencyController
  4416.     function CameraUtils.Round(num, places)
  4417.         local decimalPivot = 10^places
  4418.         return math.floor(num * decimalPivot + 0.5) / decimalPivot
  4419.     end
  4420.    
  4421.     function CameraUtils.IsFinite(val)
  4422.         return val == val and val ~= math.huge and val ~= -math.huge
  4423.     end
  4424.    
  4425.     function CameraUtils.IsFiniteVector3(vec3)
  4426.         return CameraUtils.IsFinite(vec3.X) and CameraUtils.IsFinite(vec3.Y) and CameraUtils.IsFinite(vec3.Z)
  4427.     end
  4428.    
  4429.     -- Legacy implementation renamed
  4430.     function CameraUtils.GetAngleBetweenXZVectors(v1, v2)
  4431.         return math.atan2(v2.X*v1.Z-v2.Z*v1.X, v2.X*v1.X+v2.Z*v1.Z)
  4432.     end
  4433.    
  4434.     function  CameraUtils.RotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
  4435.         if camLook.Magnitude > 0 then
  4436.             camLook = camLook.unit
  4437.             local currAngle = math.atan2(camLook.z, camLook.x)
  4438.             local newAngle = round((math.atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
  4439.             return newAngle - currAngle
  4440.         end
  4441.         return 0
  4442.     end
  4443.    
  4444.     -- K is a tunable parameter that changes the shape of the S-curve
  4445.     -- the larger K is the more straight/linear the curve gets
  4446.     local k = 0.35
  4447.     local lowerK = 0.8
  4448.     local function SCurveTranform(t)
  4449.         t = math.clamp(t, -1, 1)
  4450.         if t >= 0 then
  4451.             return (k*t) / (k - t + 1)
  4452.         end
  4453.         return -((lowerK*-t) / (lowerK + t + 1))
  4454.     end
  4455.    
  4456.     local DEADZONE = 0.1
  4457.     local function toSCurveSpace(t)
  4458.         return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
  4459.     end
  4460.    
  4461.     local function fromSCurveSpace(t)
  4462.         return t/2 + 0.5
  4463.     end
  4464.    
  4465.     function CameraUtils.GamepadLinearToCurve(thumbstickPosition)
  4466.         local function onAxis(axisValue)
  4467.             local sign = 1
  4468.             if axisValue < 0 then
  4469.                 sign = -1
  4470.             end
  4471.             local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math.abs(axisValue))))
  4472.             point = point * sign
  4473.             return math.clamp(point, -1, 1)
  4474.         end
  4475.         return Vector2.new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
  4476.     end
  4477.    
  4478.     -- This function converts 4 different, redundant enumeration types to one standard so the values can be compared
  4479.     function CameraUtils.ConvertCameraModeEnumToStandard(enumValue)
  4480.         if enumValue == Enum.TouchCameraMovementMode.Default then
  4481.             return Enum.ComputerCameraMovementMode.Follow
  4482.         end
  4483.    
  4484.         if enumValue == Enum.ComputerCameraMovementMode.Default then
  4485.             return Enum.ComputerCameraMovementMode.Classic
  4486.         end
  4487.    
  4488.         if enumValue == Enum.TouchCameraMovementMode.Classic or
  4489.             enumValue == Enum.DevTouchCameraMovementMode.Classic or
  4490.             enumValue == Enum.DevComputerCameraMovementMode.Classic or
  4491.             enumValue == Enum.ComputerCameraMovementMode.Classic then
  4492.             return Enum.ComputerCameraMovementMode.Classic
  4493.         end
  4494.    
  4495.         if enumValue == Enum.TouchCameraMovementMode.Follow or
  4496.             enumValue == Enum.DevTouchCameraMovementMode.Follow or
  4497.             enumValue == Enum.DevComputerCameraMovementMode.Follow or
  4498.             enumValue == Enum.ComputerCameraMovementMode.Follow then
  4499.             return Enum.ComputerCameraMovementMode.Follow
  4500.         end
  4501.    
  4502.         if enumValue == Enum.TouchCameraMovementMode.Orbital or
  4503.             enumValue == Enum.DevTouchCameraMovementMode.Orbital or
  4504.             enumValue == Enum.DevComputerCameraMovementMode.Orbital or
  4505.             enumValue == Enum.ComputerCameraMovementMode.Orbital then
  4506.             return Enum.ComputerCameraMovementMode.Orbital
  4507.         end
  4508.    
  4509.         if FFlagUserCameraToggle then
  4510.             if enumValue == Enum.ComputerCameraMovementMode.CameraToggle or
  4511.                 enumValue == Enum.DevComputerCameraMovementMode.CameraToggle then
  4512.                 return Enum.ComputerCameraMovementMode.CameraToggle
  4513.             end
  4514.         end
  4515.    
  4516.         -- Note: Only the Dev versions of the Enums have UserChoice as an option
  4517.         if enumValue == Enum.DevTouchCameraMovementMode.UserChoice or
  4518.             enumValue == Enum.DevComputerCameraMovementMode.UserChoice then
  4519.             return Enum.DevComputerCameraMovementMode.UserChoice
  4520.         end
  4521.    
  4522.         -- For any unmapped options return Classic camera
  4523.         return Enum.ComputerCameraMovementMode.Classic
  4524.     end
  4525.    
  4526.     return CameraUtils
  4527. end
  4528.  
  4529. function _CameraModule()
  4530.     local CameraModule = {}
  4531.     CameraModule.__index = CameraModule
  4532.    
  4533.     local FFlagUserCameraToggle do
  4534.         local success, result = pcall(function()
  4535.             return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  4536.         end)
  4537.         FFlagUserCameraToggle = success and result
  4538.     end
  4539.    
  4540.     local FFlagUserRemoveTheCameraApi do
  4541.         local success, result = pcall(function()
  4542.             return UserSettings():IsUserFeatureEnabled("UserRemoveTheCameraApi")
  4543.         end)
  4544.         FFlagUserRemoveTheCameraApi = success and result
  4545.     end
  4546.    
  4547.     -- NOTICE: Player property names do not all match their StarterPlayer equivalents,
  4548.     -- with the differences noted in the comments on the right
  4549.     local PLAYER_CAMERA_PROPERTIES =
  4550.     {
  4551.         "CameraMinZoomDistance",
  4552.         "CameraMaxZoomDistance",
  4553.         "CameraMode",
  4554.         "DevCameraOcclusionMode",
  4555.         "DevComputerCameraMode",            -- Corresponds to StarterPlayer.DevComputerCameraMovementMode
  4556.         "DevTouchCameraMode",               -- Corresponds to StarterPlayer.DevTouchCameraMovementMode
  4557.    
  4558.         -- Character movement mode
  4559.         "DevComputerMovementMode",
  4560.         "DevTouchMovementMode",
  4561.         "DevEnableMouseLock",               -- Corresponds to StarterPlayer.EnableMouseLockOption
  4562.     }
  4563.    
  4564.     local USER_GAME_SETTINGS_PROPERTIES =
  4565.     {
  4566.         "ComputerCameraMovementMode",
  4567.         "ComputerMovementMode",
  4568.         "ControlMode",
  4569.         "GamepadCameraSensitivity",
  4570.         "MouseSensitivity",
  4571.         "RotationType",
  4572.         "TouchCameraMovementMode",
  4573.         "TouchMovementMode",
  4574.     }
  4575.    
  4576.     --[[ Roblox Services ]]--
  4577.     local Players = game:GetService("Players")
  4578.     local RunService = game:GetService("RunService")
  4579.     local UserInputService = game:GetService("UserInputService")
  4580.     local UserGameSettings = UserSettings():GetService("UserGameSettings")
  4581.    
  4582.     -- Camera math utility library
  4583.     local CameraUtils = _CameraUtils()
  4584.    
  4585.     -- Load Roblox Camera Controller Modules
  4586.     local ClassicCamera = _ClassicCamera()
  4587.     local OrbitalCamera = _OrbitalCamera()
  4588.     local LegacyCamera = _LegacyCamera()
  4589.    
  4590.     -- Load Roblox Occlusion Modules
  4591.     local Invisicam = _Invisicam()
  4592.     local Poppercam = _Poppercam()
  4593.    
  4594.     -- Load the near-field character transparency controller and the mouse lock "shift lock" controller
  4595.     local TransparencyController = _TransparencyController()
  4596.     local MouseLockController = _MouseLockController()
  4597.    
  4598.     -- Table of camera controllers that have been instantiated. They are instantiated as they are used.
  4599.     local instantiatedCameraControllers = {}
  4600.     local instantiatedOcclusionModules = {}
  4601.    
  4602.     -- Management of which options appear on the Roblox User Settings screen
  4603.     do
  4604.         local PlayerScripts = Players.LocalPlayer:WaitForChild("PlayerScripts")
  4605.    
  4606.         PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Default)
  4607.         PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Follow)
  4608.         PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Classic)
  4609.    
  4610.         PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Default)
  4611.         PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Follow)
  4612.         PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Classic)
  4613.         if FFlagUserCameraToggle then
  4614.             PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.CameraToggle)
  4615.         end
  4616.     end
  4617.    
  4618.     CameraModule.FFlagUserCameraToggle = FFlagUserCameraToggle
  4619.    
  4620.    
  4621.     function CameraModule.new()
  4622.         local self = setmetatable({},CameraModule)
  4623.    
  4624.         -- Current active controller instances
  4625.         self.activeCameraController = nil
  4626.         self.activeOcclusionModule = nil
  4627.         self.activeTransparencyController = nil
  4628.         self.activeMouseLockController = nil
  4629.    
  4630.         self.currentComputerCameraMovementMode = nil
  4631.    
  4632.         -- Connections to events
  4633.         self.cameraSubjectChangedConn = nil
  4634.         self.cameraTypeChangedConn = nil
  4635.    
  4636.         -- Adds CharacterAdded and CharacterRemoving event handlers for all current players
  4637.         for _,player in pairs(Players:GetPlayers()) do
  4638.             self:OnPlayerAdded(player)
  4639.         end
  4640.    
  4641.         -- Adds CharacterAdded and CharacterRemoving event handlers for all players who join in the future
  4642.         Players.PlayerAdded:Connect(function(player)
  4643.             self:OnPlayerAdded(player)
  4644.         end)
  4645.    
  4646.         self.activeTransparencyController = TransparencyController.new()
  4647.         self.activeTransparencyController:Enable(true)
  4648.    
  4649.         if not UserInputService.TouchEnabled then
  4650.             self.activeMouseLockController = MouseLockController.new()
  4651.             local toggleEvent = self.activeMouseLockController:GetBindableToggleEvent()
  4652.             if toggleEvent then
  4653.                 toggleEvent:Connect(function()
  4654.                     self:OnMouseLockToggled()
  4655.                 end)
  4656.             end
  4657.         end
  4658.    
  4659.         self:ActivateCameraController(self:GetCameraControlChoice())
  4660.         self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
  4661.         self:OnCurrentCameraChanged() -- Does initializations and makes first camera controller
  4662.         RunService:BindToRenderStep("cameraRenderUpdate", Enum.RenderPriority.Camera.Value, function(dt) self:Update(dt) end)
  4663.    
  4664.         -- Connect listeners to camera-related properties
  4665.         for _, propertyName in pairs(PLAYER_CAMERA_PROPERTIES) do
  4666.             Players.LocalPlayer:GetPropertyChangedSignal(propertyName):Connect(function()
  4667.                 self:OnLocalPlayerCameraPropertyChanged(propertyName)
  4668.             end)
  4669.         end
  4670.    
  4671.         for _, propertyName in pairs(USER_GAME_SETTINGS_PROPERTIES) do
  4672.             UserGameSettings:GetPropertyChangedSignal(propertyName):Connect(function()
  4673.                 self:OnUserGameSettingsPropertyChanged(propertyName)
  4674.             end)
  4675.         end
  4676.         game.Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
  4677.             self:OnCurrentCameraChanged()
  4678.         end)
  4679.    
  4680.         self.lastInputType = UserInputService:GetLastInputType()
  4681.         UserInputService.LastInputTypeChanged:Connect(function(newLastInputType)
  4682.             self.lastInputType = newLastInputType
  4683.         end)
  4684.    
  4685.         return self
  4686.     end
  4687.    
  4688.     function CameraModule:GetCameraMovementModeFromSettings()
  4689.         local cameraMode = Players.LocalPlayer.CameraMode
  4690.    
  4691.         -- Lock First Person trumps all other settings and forces ClassicCamera
  4692.         if cameraMode == Enum.CameraMode.LockFirstPerson then
  4693.             return CameraUtils.ConvertCameraModeEnumToStandard(Enum.ComputerCameraMovementMode.Classic)
  4694.         end
  4695.    
  4696.         local devMode, userMode
  4697.         if UserInputService.TouchEnabled then
  4698.             devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevTouchCameraMode)
  4699.             userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.TouchCameraMovementMode)
  4700.         else
  4701.             devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevComputerCameraMode)
  4702.             userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
  4703.         end
  4704.    
  4705.         if devMode == Enum.DevComputerCameraMovementMode.UserChoice then
  4706.             -- Developer is allowing user choice, so user setting is respected
  4707.             return userMode
  4708.         end
  4709.    
  4710.         return devMode
  4711.     end
  4712.    
  4713.     function CameraModule:ActivateOcclusionModule( occlusionMode )
  4714.         local newModuleCreator
  4715.         if occlusionMode == Enum.DevCameraOcclusionMode.Zoom then
  4716.             newModuleCreator = Poppercam
  4717.         elseif occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
  4718.             newModuleCreator = Invisicam
  4719.         else
  4720.             warn("CameraScript ActivateOcclusionModule called with unsupported mode")
  4721.             return
  4722.         end
  4723.    
  4724.         -- First check to see if there is actually a change. If the module being requested is already
  4725.         -- the currently-active solution then just make sure it's enabled and exit early
  4726.         if self.activeOcclusionModule and self.activeOcclusionModule:GetOcclusionMode() == occlusionMode then
  4727.             if not self.activeOcclusionModule:GetEnabled() then
  4728.                 self.activeOcclusionModule:Enable(true)
  4729.             end
  4730.             return
  4731.         end
  4732.    
  4733.         -- Save a reference to the current active module (may be nil) so that we can disable it if
  4734.         -- we are successful in activating its replacement
  4735.         local prevOcclusionModule = self.activeOcclusionModule
  4736.    
  4737.         -- If there is no active module, see if the one we need has already been instantiated
  4738.         self.activeOcclusionModule = instantiatedOcclusionModules[newModuleCreator]
  4739.    
  4740.         -- If the module was not already instantiated and selected above, instantiate it
  4741.         if not self.activeOcclusionModule then
  4742.             self.activeOcclusionModule = newModuleCreator.new()
  4743.             if self.activeOcclusionModule then
  4744.                 instantiatedOcclusionModules[newModuleCreator] = self.activeOcclusionModule
  4745.             end
  4746.         end
  4747.    
  4748.         -- If we were successful in either selecting or instantiating the module,
  4749.         -- enable it if it's not already the currently-active enabled module
  4750.         if self.activeOcclusionModule then
  4751.             local newModuleOcclusionMode = self.activeOcclusionModule:GetOcclusionMode()
  4752.             -- Sanity check that the module we selected or instantiated actually supports the desired occlusionMode
  4753.             if newModuleOcclusionMode ~= occlusionMode then
  4754.                 warn("CameraScript ActivateOcclusionModule mismatch: ",self.activeOcclusionModule:GetOcclusionMode(),"~=",occlusionMode)
  4755.             end
  4756.    
  4757.             -- Deactivate current module if there is one
  4758.             if prevOcclusionModule then
  4759.                 -- Sanity check that current module is not being replaced by itself (that should have been handled above)
  4760.                 if prevOcclusionModule ~= self.activeOcclusionModule then
  4761.                     prevOcclusionModule:Enable(false)
  4762.                 else
  4763.                     warn("CameraScript ActivateOcclusionModule failure to detect already running correct module")
  4764.                 end
  4765.             end
  4766.    
  4767.             -- Occlusion modules need to be initialized with information about characters and cameraSubject
  4768.             -- Invisicam needs the LocalPlayer's character
  4769.             -- Poppercam needs all player characters and the camera subject
  4770.             if occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
  4771.                 -- Optimization to only send Invisicam what we know it needs
  4772.                 if Players.LocalPlayer.Character then
  4773.                     self.activeOcclusionModule:CharacterAdded(Players.LocalPlayer.Character, Players.LocalPlayer )
  4774.                 end
  4775.             else
  4776.                 -- When Poppercam is enabled, we send it all existing player characters for its raycast ignore list
  4777.                 for _, player in pairs(Players:GetPlayers()) do
  4778.                     if player and player.Character then
  4779.                         self.activeOcclusionModule:CharacterAdded(player.Character, player)
  4780.                     end
  4781.                 end
  4782.                 self.activeOcclusionModule:OnCameraSubjectChanged(game.Workspace.CurrentCamera.CameraSubject)
  4783.             end
  4784.    
  4785.             -- Activate new choice
  4786.             self.activeOcclusionModule:Enable(true)
  4787.         end
  4788.     end
  4789.    
  4790.     -- When supplied, legacyCameraType is used and cameraMovementMode is ignored (should be nil anyways)
  4791.     -- Next, if userCameraCreator is passed in, that is used as the cameraCreator
  4792.     function CameraModule:ActivateCameraController(cameraMovementMode, legacyCameraType)
  4793.         local newCameraCreator = nil
  4794.    
  4795.         if legacyCameraType~=nil then
  4796.             --[[
  4797.                 This function has been passed a CameraType enum value. Some of these map to the use of
  4798.                 the LegacyCamera module, the value "Custom" will be translated to a movementMode enum
  4799.                 value based on Dev and User settings, and "Scriptable" will disable the camera controller.
  4800.             --]]
  4801.    
  4802.             if legacyCameraType == Enum.CameraType.Scriptable then
  4803.                 if self.activeCameraController then
  4804.                     self.activeCameraController:Enable(false)
  4805.                     self.activeCameraController = nil
  4806.                     return
  4807.                 end
  4808.             elseif legacyCameraType == Enum.CameraType.Custom then
  4809.                 cameraMovementMode = self:GetCameraMovementModeFromSettings()
  4810.    
  4811.             elseif legacyCameraType == Enum.CameraType.Track then
  4812.                 -- Note: The TrackCamera module was basically an older, less fully-featured
  4813.                 -- version of ClassicCamera, no longer actively maintained, but it is re-implemented in
  4814.                 -- case a game was dependent on its lack of ClassicCamera's extra functionality.
  4815.                 cameraMovementMode = Enum.ComputerCameraMovementMode.Classic
  4816.    
  4817.             elseif legacyCameraType == Enum.CameraType.Follow then
  4818.                 cameraMovementMode = Enum.ComputerCameraMovementMode.Follow
  4819.    
  4820.             elseif legacyCameraType == Enum.CameraType.Orbital then
  4821.                 cameraMovementMode = Enum.ComputerCameraMovementMode.Orbital
  4822.    
  4823.             elseif legacyCameraType == Enum.CameraType.Attach or
  4824.                    legacyCameraType == Enum.CameraType.Watch or
  4825.                    legacyCameraType == Enum.CameraType.Fixed then
  4826.                 newCameraCreator = LegacyCamera
  4827.             else
  4828.                 warn("CameraScript encountered an unhandled Camera.CameraType value: ",legacyCameraType)
  4829.             end
  4830.         end
  4831.    
  4832.         if not newCameraCreator then
  4833.             if cameraMovementMode == Enum.ComputerCameraMovementMode.Classic or
  4834.                 cameraMovementMode == Enum.ComputerCameraMovementMode.Follow or
  4835.                 cameraMovementMode == Enum.ComputerCameraMovementMode.Default or
  4836.                 (FFlagUserCameraToggle and cameraMovementMode == Enum.ComputerCameraMovementMode.CameraToggle) then
  4837.                 newCameraCreator = ClassicCamera
  4838.             elseif cameraMovementMode == Enum.ComputerCameraMovementMode.Orbital then
  4839.                 newCameraCreator = OrbitalCamera
  4840.             else
  4841.                 warn("ActivateCameraController did not select a module.")
  4842.                 return
  4843.             end
  4844.         end
  4845.    
  4846.         -- Create the camera control module we need if it does not already exist in instantiatedCameraControllers
  4847.         local newCameraController
  4848.         if not instantiatedCameraControllers[newCameraCreator] then
  4849.             newCameraController = newCameraCreator.new()
  4850.             instantiatedCameraControllers[newCameraCreator] = newCameraController
  4851.         else
  4852.             newCameraController = instantiatedCameraControllers[newCameraCreator]
  4853.         end
  4854.    
  4855.         -- If there is a controller active and it's not the one we need, disable it,
  4856.         -- if it is the one we need, make sure it's enabled
  4857.         if self.activeCameraController then
  4858.             if self.activeCameraController ~= newCameraController then
  4859.                 self.activeCameraController:Enable(false)
  4860.                 self.activeCameraController = newCameraController
  4861.                 self.activeCameraController:Enable(true)
  4862.             elseif not self.activeCameraController:GetEnabled() then
  4863.                 self.activeCameraController:Enable(true)
  4864.             end
  4865.         elseif newCameraController ~= nil then
  4866.             self.activeCameraController = newCameraController
  4867.             self.activeCameraController:Enable(true)
  4868.         end
  4869.    
  4870.         if self.activeCameraController then
  4871.             if cameraMovementMode~=nil then
  4872.                 self.activeCameraController:SetCameraMovementMode(cameraMovementMode)
  4873.             elseif legacyCameraType~=nil then
  4874.                 -- Note that this is only called when legacyCameraType is not a type that
  4875.                 -- was convertible to a ComputerCameraMovementMode value, i.e. really only applies to LegacyCamera
  4876.                 self.activeCameraController:SetCameraType(legacyCameraType)
  4877.             end
  4878.         end
  4879.     end
  4880.    
  4881.     -- Note: The active transparency controller could be made to listen for this event itself.
  4882.     function CameraModule:OnCameraSubjectChanged()
  4883.         if self.activeTransparencyController then
  4884.             self.activeTransparencyController:SetSubject(game.Workspace.CurrentCamera.CameraSubject)
  4885.         end
  4886.    
  4887.         if self.activeOcclusionModule then
  4888.             self.activeOcclusionModule:OnCameraSubjectChanged(game.Workspace.CurrentCamera.CameraSubject)
  4889.         end
  4890.     end
  4891.    
  4892.     function CameraModule:OnCameraTypeChanged(newCameraType)
  4893.         if newCameraType == Enum.CameraType.Scriptable then
  4894.             if UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then
  4895.                 UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  4896.             end
  4897.         end
  4898.    
  4899.         -- Forward the change to ActivateCameraController to handle
  4900.         self:ActivateCameraController(nil, newCameraType)
  4901.     end
  4902.    
  4903.     -- Note: Called whenever workspace.CurrentCamera changes, but also on initialization of this script
  4904.     function CameraModule:OnCurrentCameraChanged()
  4905.         local currentCamera = game.Workspace.CurrentCamera
  4906.         if not currentCamera then return end
  4907.    
  4908.         if self.cameraSubjectChangedConn then
  4909.             self.cameraSubjectChangedConn:Disconnect()
  4910.         end
  4911.    
  4912.         if self.cameraTypeChangedConn then
  4913.             self.cameraTypeChangedConn:Disconnect()
  4914.         end
  4915.    
  4916.         self.cameraSubjectChangedConn = currentCamera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
  4917.             self:OnCameraSubjectChanged(currentCamera.CameraSubject)
  4918.         end)
  4919.    
  4920.         self.cameraTypeChangedConn = currentCamera:GetPropertyChangedSignal("CameraType"):Connect(function()
  4921.             self:OnCameraTypeChanged(currentCamera.CameraType)
  4922.         end)
  4923.    
  4924.         self:OnCameraSubjectChanged(currentCamera.CameraSubject)
  4925.         self:OnCameraTypeChanged(currentCamera.CameraType)
  4926.     end
  4927.    
  4928.     function CameraModule:OnLocalPlayerCameraPropertyChanged(propertyName)
  4929.         if propertyName == "CameraMode" then
  4930.             -- CameraMode is only used to turn on/off forcing the player into first person view. The
  4931.             -- Note: The case "Classic" is used for all other views and does not correspond only to the ClassicCamera module
  4932.             if Players.LocalPlayer.CameraMode == Enum.CameraMode.LockFirstPerson then
  4933.                 -- Locked in first person, use ClassicCamera which supports this
  4934.                 if not self.activeCameraController or self.activeCameraController:GetModuleName() ~= "ClassicCamera" then
  4935.                     self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(Enum.DevComputerCameraMovementMode.Classic))
  4936.                 end
  4937.    
  4938.                 if self.activeCameraController then
  4939.                     self.activeCameraController:UpdateForDistancePropertyChange()
  4940.                 end
  4941.             elseif Players.LocalPlayer.CameraMode == Enum.CameraMode.Classic then
  4942.                 -- Not locked in first person view
  4943.                 local cameraMovementMode =self: GetCameraMovementModeFromSettings()
  4944.                 self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
  4945.             else
  4946.                 warn("Unhandled value for property player.CameraMode: ",Players.LocalPlayer.CameraMode)
  4947.             end
  4948.    
  4949.         elseif propertyName == "DevComputerCameraMode" or
  4950.                propertyName == "DevTouchCameraMode" then
  4951.             local cameraMovementMode = self:GetCameraMovementModeFromSettings()
  4952.             self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
  4953.    
  4954.         elseif propertyName == "DevCameraOcclusionMode" then
  4955.             self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
  4956.    
  4957.         elseif propertyName == "CameraMinZoomDistance" or propertyName == "CameraMaxZoomDistance" then
  4958.             if self.activeCameraController then
  4959.                 self.activeCameraController:UpdateForDistancePropertyChange()
  4960.             end
  4961.         elseif propertyName == "DevTouchMovementMode" then
  4962.         elseif propertyName == "DevComputerMovementMode" then
  4963.         elseif propertyName == "DevEnableMouseLock" then
  4964.             -- This is the enabling/disabling of "Shift Lock" mode, not LockFirstPerson (which is a CameraMode)
  4965.             -- Note: Enabling and disabling of MouseLock mode is normally only a publish-time choice made via
  4966.             -- the corresponding EnableMouseLockOption checkbox of StarterPlayer, and this script does not have
  4967.             -- support for changing the availability of MouseLock at runtime (this would require listening to
  4968.             -- Player.DevEnableMouseLock changes)
  4969.         end
  4970.     end
  4971.    
  4972.     function CameraModule:OnUserGameSettingsPropertyChanged(propertyName)
  4973.         if propertyName ==  "ComputerCameraMovementMode" then
  4974.             local cameraMovementMode = self:GetCameraMovementModeFromSettings()
  4975.             self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
  4976.         end
  4977.     end
  4978.    
  4979.     --[[
  4980.         Main RenderStep Update. The camera controller and occlusion module both have opportunities
  4981.         to set and modify (respectively) the CFrame and Focus before it is set once on CurrentCamera.
  4982.         The camera and occlusion modules should only return CFrames, not set the CFrame property of
  4983.         CurrentCamera directly.
  4984.     --]]
  4985.     function CameraModule:Update(dt)
  4986.         if self.activeCameraController then
  4987.             if FFlagUserCameraToggle then
  4988.                 self.activeCameraController:UpdateMouseBehavior()
  4989.             end
  4990.    
  4991.             local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
  4992.             self.activeCameraController:ApplyVRTransform()
  4993.             if self.activeOcclusionModule then
  4994.                 newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
  4995.             end
  4996.    
  4997.             -- Here is where the new CFrame and Focus are set for this render frame
  4998.             game.Workspace.CurrentCamera.CFrame = newCameraCFrame
  4999.             game.Workspace.CurrentCamera.Focus = newCameraFocus
  5000.    
  5001.             -- Update to character local transparency as needed based on camera-to-subject distance
  5002.             if self.activeTransparencyController then
  5003.                 self.activeTransparencyController:Update()
  5004.             end
  5005.         end
  5006.     end
  5007.    
  5008.     -- Formerly getCurrentCameraMode, this function resolves developer and user camera control settings to
  5009.     -- decide which camera control module should be instantiated. The old method of converting redundant enum types
  5010.     function CameraModule:GetCameraControlChoice()
  5011.         local player = Players.LocalPlayer
  5012.    
  5013.         if player then
  5014.             if self.lastInputType == Enum.UserInputType.Touch or UserInputService.TouchEnabled then
  5015.                 -- Touch
  5016.                 if player.DevTouchCameraMode == Enum.DevTouchCameraMovementMode.UserChoice then
  5017.                     return CameraUtils.ConvertCameraModeEnumToStandard( UserGameSettings.TouchCameraMovementMode )
  5018.                 else
  5019.                     return CameraUtils.ConvertCameraModeEnumToStandard( player.DevTouchCameraMode )
  5020.                 end
  5021.             else
  5022.                 -- Computer
  5023.                 if player.DevComputerCameraMode == Enum.DevComputerCameraMovementMode.UserChoice then
  5024.                     local computerMovementMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
  5025.                     return CameraUtils.ConvertCameraModeEnumToStandard(computerMovementMode)
  5026.                 else
  5027.                     return CameraUtils.ConvertCameraModeEnumToStandard(player.DevComputerCameraMode)
  5028.                 end
  5029.             end
  5030.         end
  5031.     end
  5032.    
  5033.     function CameraModule:OnCharacterAdded(char, player)
  5034.         if self.activeOcclusionModule then
  5035.             self.activeOcclusionModule:CharacterAdded(char, player)
  5036.         end
  5037.     end
  5038.    
  5039.     function CameraModule:OnCharacterRemoving(char, player)
  5040.         if self.activeOcclusionModule then
  5041.             self.activeOcclusionModule:CharacterRemoving(char, player)
  5042.         end
  5043.     end
  5044.    
  5045.     function CameraModule:OnPlayerAdded(player)
  5046.         player.CharacterAdded:Connect(function(char)
  5047.             self:OnCharacterAdded(char, player)
  5048.         end)
  5049.         player.CharacterRemoving:Connect(function(char)
  5050.             self:OnCharacterRemoving(char, player)
  5051.         end)
  5052.     end
  5053.    
  5054.     function CameraModule:OnMouseLockToggled()
  5055.         if self.activeMouseLockController then
  5056.             local mouseLocked = self.activeMouseLockController:GetIsMouseLocked()
  5057.             local mouseLockOffset = self.activeMouseLockController:GetMouseLockOffset()
  5058.             if self.activeCameraController then
  5059.                 self.activeCameraController:SetIsMouseLocked(mouseLocked)
  5060.                 self.activeCameraController:SetMouseLockOffset(mouseLockOffset)
  5061.             end
  5062.         end
  5063.     end
  5064.     --begin edit
  5065.     local Camera = CameraModule
  5066.     local IDENTITYCF = CFrame.new()
  5067.     local lastUpCFrame = IDENTITYCF
  5068.    
  5069.     Camera.UpVector = Vector3.new(0, 1, 0)
  5070.     Camera.TransitionRate = 0.15
  5071.     Camera.UpCFrame = IDENTITYCF
  5072.    
  5073.     function Camera:GetUpVector(oldUpVector)
  5074.         return oldUpVector
  5075.     end
  5076.     local function getRotationBetween(u, v, axis)
  5077.         local dot, uxv = u:Dot(v), u:Cross(v)
  5078.         if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
  5079.         return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
  5080.     end
  5081.     function Camera:CalculateUpCFrame()
  5082.         local oldUpVector = self.UpVector
  5083.         local newUpVector = self:GetUpVector(oldUpVector)
  5084.        
  5085.         local backup = game.Workspace.CurrentCamera.CFrame.RightVector
  5086.         local transitionCF = getRotationBetween(oldUpVector, newUpVector, backup)
  5087.         local vecSlerpCF = IDENTITYCF:Lerp(transitionCF, self.TransitionRate)
  5088.        
  5089.         self.UpVector = vecSlerpCF * oldUpVector
  5090.         self.UpCFrame = vecSlerpCF * self.UpCFrame
  5091.        
  5092.         lastUpCFrame = self.UpCFrame
  5093.     end
  5094.    
  5095.     function Camera:Update(dt)
  5096.         if self.activeCameraController then
  5097.             if Camera.FFlagUserCameraToggle then
  5098.                 self.activeCameraController:UpdateMouseBehavior()
  5099.             end
  5100.            
  5101.             local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
  5102.             self.activeCameraController:ApplyVRTransform()
  5103.            
  5104.             self:CalculateUpCFrame()
  5105.             self.activeCameraController:UpdateUpCFrame(self.UpCFrame)
  5106.            
  5107.             -- undo shift-lock offset
  5108.    
  5109.             local lockOffset = Vector3.new(0, 0, 0)
  5110.             if (self.activeMouseLockController and self.activeMouseLockController:GetIsMouseLocked()) then
  5111.                 lockOffset = self.activeMouseLockController:GetMouseLockOffset()
  5112.             end
  5113.            
  5114.             local offset = newCameraFocus:ToObjectSpace(newCameraCFrame)
  5115.             local camRotation = self.UpCFrame * offset
  5116.             newCameraFocus = newCameraFocus - newCameraCFrame:VectorToWorldSpace(lockOffset) + camRotation:VectorToWorldSpace(lockOffset)
  5117.             newCameraCFrame = newCameraFocus * camRotation
  5118.            
  5119.             --local offset = newCameraFocus:Inverse() * newCameraCFrame
  5120.             --newCameraCFrame = newCameraFocus * self.UpCFrame * offset
  5121.            
  5122.             if (self.activeCameraController.lastCameraTransform) then
  5123.                 self.activeCameraController.lastCameraTransform = newCameraCFrame
  5124.                 self.activeCameraController.lastCameraFocus = newCameraFocus
  5125.             end
  5126.            
  5127.             if self.activeOcclusionModule then
  5128.                 newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
  5129.             end
  5130.    
  5131.             game.Workspace.CurrentCamera.CFrame = newCameraCFrame
  5132.             game.Workspace.CurrentCamera.Focus = newCameraFocus
  5133.    
  5134.             if self.activeTransparencyController then
  5135.                 self.activeTransparencyController:Update()
  5136.             end
  5137.         end
  5138.     end
  5139.    
  5140.     function Camera:IsFirstPerson()
  5141.         if self.activeCameraController then
  5142.             return self.activeCameraController:InFirstPerson()
  5143.         end
  5144.         return false
  5145.     end
  5146.    
  5147.     function Camera:IsMouseLocked()
  5148.         if self.activeCameraController then
  5149.             return self.activeCameraController:GetIsMouseLocked()
  5150.         end
  5151.         return false
  5152.     end
  5153.     function Camera:IsToggleMode()
  5154.         if self.activeCameraController then
  5155.             return self.activeCameraController.isCameraToggle
  5156.         end
  5157.         return false
  5158.     end
  5159.     function Camera:IsCamRelative()
  5160.         return self:IsMouseLocked() or self:IsFirstPerson()
  5161.         --return self:IsToggleMode(), self:IsMouseLocked(), self:IsFirstPerson()
  5162.     end
  5163.     --
  5164.     local Utils = _CameraUtils()
  5165.     function Utils.GetAngleBetweenXZVectors(v1, v2)
  5166.         local upCFrame = lastUpCFrame
  5167.         v1 = upCFrame:VectorToObjectSpace(v1)
  5168.         v2 = upCFrame:VectorToObjectSpace(v2)
  5169.         return math.atan2(v2.X*v1.Z-v2.Z*v1.X, v2.X*v1.X+v2.Z*v1.Z)
  5170.     end
  5171.     --end edit
  5172.     local cameraModuleObject = CameraModule.new()
  5173.     local cameraApi = {}
  5174.     return cameraModuleObject
  5175. end
  5176.  
  5177. function _ClickToMoveDisplay()
  5178.     local ClickToMoveDisplay = {}
  5179.    
  5180.     local FAILURE_ANIMATION_ID = "rbxassetid://2874840706"
  5181.    
  5182.     local TrailDotIcon = "rbxasset://textures/ui/traildot.png"
  5183.     local EndWaypointIcon = "rbxasset://textures/ui/waypoint.png"
  5184.    
  5185.     local WaypointsAlwaysOnTop = false
  5186.    
  5187.     local WAYPOINT_INCLUDE_FACTOR = 2
  5188.     local LAST_DOT_DISTANCE = 3
  5189.    
  5190.     local WAYPOINT_BILLBOARD_SIZE = UDim2.new(0, 1.68 * 25, 0, 2 * 25)
  5191.    
  5192.     local ENDWAYPOINT_SIZE_OFFSET_MIN = Vector2.new(0, 0.5)
  5193.     local ENDWAYPOINT_SIZE_OFFSET_MAX = Vector2.new(0, 1)
  5194.    
  5195.     local FAIL_WAYPOINT_SIZE_OFFSET_CENTER = Vector2.new(0, 0.5)
  5196.     local FAIL_WAYPOINT_SIZE_OFFSET_LEFT = Vector2.new(0.1, 0.5)
  5197.     local FAIL_WAYPOINT_SIZE_OFFSET_RIGHT = Vector2.new(-0.1, 0.5)
  5198.    
  5199.     local FAILURE_TWEEN_LENGTH = 0.125
  5200.     local FAILURE_TWEEN_COUNT = 4
  5201.    
  5202.     local TWEEN_WAYPOINT_THRESHOLD = 5
  5203.    
  5204.     local TRAIL_DOT_PARENT_NAME = "ClickToMoveDisplay"
  5205.    
  5206.     local TrailDotSize = Vector2.new(1.5, 1.5)
  5207.    
  5208.     local TRAIL_DOT_MIN_SCALE = 1
  5209.     local TRAIL_DOT_MIN_DISTANCE = 10
  5210.     local TRAIL_DOT_MAX_SCALE = 2.5
  5211.     local TRAIL_DOT_MAX_DISTANCE = 100
  5212.    
  5213.     local PlayersService = game:GetService("Players")
  5214.     local TweenService = game:GetService("TweenService")
  5215.     local RunService = game:GetService("RunService")
  5216.     local Workspace = game:GetService("Workspace")
  5217.    
  5218.     local LocalPlayer = PlayersService.LocalPlayer
  5219.    
  5220.     local function CreateWaypointTemplates()
  5221.         local TrailDotTemplate = Instance.new("Part")
  5222.         TrailDotTemplate.Size = Vector3.new(1, 1, 1)
  5223.         TrailDotTemplate.Anchored = true
  5224.         TrailDotTemplate.CanCollide = false
  5225.         TrailDotTemplate.Name = "TrailDot"
  5226.         TrailDotTemplate.Transparency = 1
  5227.         local TrailDotImage = Instance.new("ImageHandleAdornment")
  5228.         TrailDotImage.Name = "TrailDotImage"
  5229.         TrailDotImage.Size = TrailDotSize
  5230.         TrailDotImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
  5231.         TrailDotImage.AlwaysOnTop = WaypointsAlwaysOnTop
  5232.         TrailDotImage.Image = TrailDotIcon
  5233.         TrailDotImage.Adornee = TrailDotTemplate
  5234.         TrailDotImage.Parent = TrailDotTemplate
  5235.    
  5236.         local EndWaypointTemplate = Instance.new("Part")
  5237.         EndWaypointTemplate.Size = Vector3.new(2, 2, 2)
  5238.         EndWaypointTemplate.Anchored = true
  5239.         EndWaypointTemplate.CanCollide = false
  5240.         EndWaypointTemplate.Name = "EndWaypoint"
  5241.         EndWaypointTemplate.Transparency = 1
  5242.         local EndWaypointImage = Instance.new("ImageHandleAdornment")
  5243.         EndWaypointImage.Name = "TrailDotImage"
  5244.         EndWaypointImage.Size = TrailDotSize
  5245.         EndWaypointImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
  5246.         EndWaypointImage.AlwaysOnTop = WaypointsAlwaysOnTop
  5247.         EndWaypointImage.Image = TrailDotIcon
  5248.         EndWaypointImage.Adornee = EndWaypointTemplate
  5249.         EndWaypointImage.Parent = EndWaypointTemplate
  5250.         local EndWaypointBillboard = Instance.new("BillboardGui")
  5251.         EndWaypointBillboard.Name = "EndWaypointBillboard"
  5252.         EndWaypointBillboard.Size = WAYPOINT_BILLBOARD_SIZE
  5253.         EndWaypointBillboard.LightInfluence = 0
  5254.         EndWaypointBillboard.SizeOffset = ENDWAYPOINT_SIZE_OFFSET_MIN
  5255.         EndWaypointBillboard.AlwaysOnTop = true
  5256.         EndWaypointBillboard.Adornee = EndWaypointTemplate
  5257.         EndWaypointBillboard.Parent = EndWaypointTemplate
  5258.         local EndWaypointImageLabel = Instance.new("ImageLabel")
  5259.         EndWaypointImageLabel.Image = EndWaypointIcon
  5260.         EndWaypointImageLabel.BackgroundTransparency = 1
  5261.         EndWaypointImageLabel.Size = UDim2.new(1, 0, 1, 0)
  5262.         EndWaypointImageLabel.Parent = EndWaypointBillboard
  5263.    
  5264.    
  5265.         local FailureWaypointTemplate = Instance.new("Part")
  5266.         FailureWaypointTemplate.Size = Vector3.new(2, 2, 2)
  5267.         FailureWaypointTemplate.Anchored = true
  5268.         FailureWaypointTemplate.CanCollide = false
  5269.         FailureWaypointTemplate.Name = "FailureWaypoint"
  5270.         FailureWaypointTemplate.Transparency = 1
  5271.         local FailureWaypointImage = Instance.new("ImageHandleAdornment")
  5272.         FailureWaypointImage.Name = "TrailDotImage"
  5273.         FailureWaypointImage.Size = TrailDotSize
  5274.         FailureWaypointImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
  5275.         FailureWaypointImage.AlwaysOnTop = WaypointsAlwaysOnTop
  5276.         FailureWaypointImage.Image = TrailDotIcon
  5277.         FailureWaypointImage.Adornee = FailureWaypointTemplate
  5278.         FailureWaypointImage.Parent = FailureWaypointTemplate
  5279.         local FailureWaypointBillboard = Instance.new("BillboardGui")
  5280.         FailureWaypointBillboard.Name = "FailureWaypointBillboard"
  5281.         FailureWaypointBillboard.Size = WAYPOINT_BILLBOARD_SIZE
  5282.         FailureWaypointBillboard.LightInfluence = 0
  5283.         FailureWaypointBillboard.SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_CENTER
  5284.         FailureWaypointBillboard.AlwaysOnTop = true
  5285.         FailureWaypointBillboard.Adornee = FailureWaypointTemplate
  5286.         FailureWaypointBillboard.Parent = FailureWaypointTemplate
  5287.         local FailureWaypointFrame = Instance.new("Frame")
  5288.         FailureWaypointFrame.BackgroundTransparency = 1
  5289.         FailureWaypointFrame.Size = UDim2.new(0, 0, 0, 0)
  5290.         FailureWaypointFrame.Position = UDim2.new(0.5, 0, 1, 0)
  5291.         FailureWaypointFrame.Parent = FailureWaypointBillboard
  5292.         local FailureWaypointImageLabel = Instance.new("ImageLabel")
  5293.         FailureWaypointImageLabel.Image = EndWaypointIcon
  5294.         FailureWaypointImageLabel.BackgroundTransparency = 1
  5295.         FailureWaypointImageLabel.Position = UDim2.new(
  5296.             0, -WAYPOINT_BILLBOARD_SIZE.X.Offset/2, 0, -WAYPOINT_BILLBOARD_SIZE.Y.Offset
  5297.         )
  5298.         FailureWaypointImageLabel.Size = WAYPOINT_BILLBOARD_SIZE
  5299.         FailureWaypointImageLabel.Parent = FailureWaypointFrame
  5300.    
  5301.         return TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate
  5302.     end
  5303.    
  5304.     local TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5305.    
  5306.     local function getTrailDotParent()
  5307.         local camera = Workspace.CurrentCamera
  5308.         local trailParent = camera:FindFirstChild(TRAIL_DOT_PARENT_NAME)
  5309.         if not trailParent then
  5310.             trailParent = Instance.new("Model")
  5311.             trailParent.Name = TRAIL_DOT_PARENT_NAME
  5312.             trailParent.Parent = camera
  5313.         end
  5314.         return trailParent
  5315.     end
  5316.    
  5317.     local function placePathWaypoint(waypointModel, position)
  5318.         local ray = Ray.new(position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
  5319.         local hitPart, hitPoint, hitNormal = Workspace:FindPartOnRayWithIgnoreList(
  5320.             ray,
  5321.             { Workspace.CurrentCamera, LocalPlayer.Character }
  5322.         )
  5323.         if hitPart then
  5324.             waypointModel.CFrame = CFrame.new(hitPoint, hitPoint + hitNormal)
  5325.             waypointModel.Parent = getTrailDotParent()
  5326.         end
  5327.     end
  5328.    
  5329.     local TrailDot = {}
  5330.     TrailDot.__index = TrailDot
  5331.    
  5332.     function TrailDot:Destroy()
  5333.         self.DisplayModel:Destroy()
  5334.     end
  5335.    
  5336.     function TrailDot:NewDisplayModel(position)
  5337.         local newDisplayModel = TrailDotTemplate:Clone()
  5338.         placePathWaypoint(newDisplayModel, position)
  5339.         return newDisplayModel
  5340.     end
  5341.    
  5342.     function TrailDot.new(position, closestWaypoint)
  5343.         local self = setmetatable({}, TrailDot)
  5344.    
  5345.         self.DisplayModel = self:NewDisplayModel(position)
  5346.         self.ClosestWayPoint = closestWaypoint
  5347.    
  5348.         return self
  5349.     end
  5350.    
  5351.     local EndWaypoint = {}
  5352.     EndWaypoint.__index = EndWaypoint
  5353.    
  5354.     function EndWaypoint:Destroy()
  5355.         self.Destroyed = true
  5356.         self.Tween:Cancel()
  5357.         self.DisplayModel:Destroy()
  5358.     end
  5359.    
  5360.     function EndWaypoint:NewDisplayModel(position)
  5361.         local newDisplayModel = EndWaypointTemplate:Clone()
  5362.         placePathWaypoint(newDisplayModel, position)
  5363.         return newDisplayModel
  5364.     end
  5365.    
  5366.     function EndWaypoint:CreateTween()
  5367.         local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, -1, true)
  5368.         local tween = TweenService:Create(
  5369.             self.DisplayModel.EndWaypointBillboard,
  5370.             tweenInfo,
  5371.             { SizeOffset = ENDWAYPOINT_SIZE_OFFSET_MAX }
  5372.         )
  5373.         tween:Play()
  5374.         return tween
  5375.     end
  5376.    
  5377.     function EndWaypoint:TweenInFrom(originalPosition)
  5378.         local currentPositon = self.DisplayModel.Position
  5379.         local studsOffset = originalPosition - currentPositon
  5380.         self.DisplayModel.EndWaypointBillboard.StudsOffset = Vector3.new(0, studsOffset.Y, 0)
  5381.         local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
  5382.         local tween = TweenService:Create(
  5383.             self.DisplayModel.EndWaypointBillboard,
  5384.             tweenInfo,
  5385.             { StudsOffset = Vector3.new(0, 0, 0) }
  5386.         )
  5387.         tween:Play()
  5388.         return tween
  5389.     end
  5390.    
  5391.     function EndWaypoint.new(position, closestWaypoint, originalPosition)
  5392.         local self = setmetatable({}, EndWaypoint)
  5393.    
  5394.         self.DisplayModel = self:NewDisplayModel(position)
  5395.         self.Destroyed = false
  5396.         if originalPosition and (originalPosition - position).magnitude > TWEEN_WAYPOINT_THRESHOLD then
  5397.             self.Tween = self:TweenInFrom(originalPosition)
  5398.             coroutine.wrap(function()
  5399.                 self.Tween.Completed:Wait()
  5400.                 if not self.Destroyed then
  5401.                     self.Tween = self:CreateTween()
  5402.                 end
  5403.             end)()
  5404.         else
  5405.             self.Tween = self:CreateTween()
  5406.         end
  5407.         self.ClosestWayPoint = closestWaypoint
  5408.    
  5409.         return self
  5410.     end
  5411.    
  5412.     local FailureWaypoint = {}
  5413.     FailureWaypoint.__index = FailureWaypoint
  5414.    
  5415.     function FailureWaypoint:Hide()
  5416.         self.DisplayModel.Parent = nil
  5417.     end
  5418.    
  5419.     function FailureWaypoint:Destroy()
  5420.         self.DisplayModel:Destroy()
  5421.     end
  5422.    
  5423.     function FailureWaypoint:NewDisplayModel(position)
  5424.         local newDisplayModel = FailureWaypointTemplate:Clone()
  5425.         placePathWaypoint(newDisplayModel, position)
  5426.         local ray = Ray.new(position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
  5427.         local hitPart, hitPoint, hitNormal = Workspace:FindPartOnRayWithIgnoreList(
  5428.             ray, { Workspace.CurrentCamera, LocalPlayer.Character }
  5429.         )
  5430.         if hitPart then
  5431.             newDisplayModel.CFrame = CFrame.new(hitPoint, hitPoint + hitNormal)
  5432.             newDisplayModel.Parent = getTrailDotParent()
  5433.         end
  5434.         return newDisplayModel
  5435.     end
  5436.    
  5437.     function FailureWaypoint:RunFailureTween()
  5438.         wait(FAILURE_TWEEN_LENGTH) -- Delay one tween length betfore starting tweening
  5439.         -- Tween out from center
  5440.         local tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH/2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
  5441.         local tweenLeft = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
  5442.             { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_LEFT })
  5443.         tweenLeft:Play()
  5444.    
  5445.         local tweenLeftRoation = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
  5446.             { Rotation = 10 })
  5447.         tweenLeftRoation:Play()
  5448.    
  5449.         tweenLeft.Completed:wait()
  5450.    
  5451.         -- Tween back and forth
  5452.         tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH, Enum.EasingStyle.Sine, Enum.EasingDirection.Out,
  5453.             FAILURE_TWEEN_COUNT - 1, true)
  5454.         local tweenSideToSide = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
  5455.             { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_RIGHT})
  5456.         tweenSideToSide:Play()
  5457.    
  5458.         -- Tween flash dark and roate left and right
  5459.         tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH, Enum.EasingStyle.Sine, Enum.EasingDirection.Out,
  5460.             FAILURE_TWEEN_COUNT - 1, true)
  5461.         local tweenFlash = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame.ImageLabel, tweenInfo,
  5462.             { ImageColor3 = Color3.new(0.75, 0.75, 0.75)})
  5463.         tweenFlash:Play()
  5464.    
  5465.         local tweenRotate = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
  5466.             { Rotation = -10 })
  5467.         tweenRotate:Play()
  5468.    
  5469.         tweenSideToSide.Completed:wait()
  5470.    
  5471.         -- Tween back to center
  5472.         tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH/2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
  5473.         local tweenCenter = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
  5474.             { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_CENTER })
  5475.         tweenCenter:Play()
  5476.    
  5477.         local tweenRoation = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
  5478.             { Rotation = 0 })
  5479.         tweenRoation:Play()
  5480.    
  5481.         tweenCenter.Completed:wait()
  5482.    
  5483.         wait(FAILURE_TWEEN_LENGTH) -- Delay one tween length betfore removing
  5484.     end
  5485.    
  5486.     function FailureWaypoint.new(position)
  5487.         local self = setmetatable({}, FailureWaypoint)
  5488.    
  5489.         self.DisplayModel = self:NewDisplayModel(position)
  5490.    
  5491.         return self
  5492.     end
  5493.    
  5494.     local failureAnimation = Instance.new("Animation")
  5495.     failureAnimation.AnimationId = FAILURE_ANIMATION_ID
  5496.    
  5497.     local lastHumanoid = nil
  5498.     local lastFailureAnimationTrack = nil
  5499.    
  5500.     local function getFailureAnimationTrack(myHumanoid)
  5501.         if myHumanoid == lastHumanoid then
  5502.             return lastFailureAnimationTrack
  5503.         end
  5504.         lastFailureAnimationTrack = myHumanoid:LoadAnimation(failureAnimation)
  5505.         lastFailureAnimationTrack.Priority = Enum.AnimationPriority.Action
  5506.         lastFailureAnimationTrack.Looped = false
  5507.         return lastFailureAnimationTrack
  5508.     end
  5509.    
  5510.     local function findPlayerHumanoid()
  5511.         local character = LocalPlayer.Character
  5512.         if character then
  5513.             return character:FindFirstChildOfClass("Humanoid")
  5514.         end
  5515.     end
  5516.    
  5517.     local function createTrailDots(wayPoints, originalEndWaypoint)
  5518.         local newTrailDots = {}
  5519.         local count = 1
  5520.         for i = 1, #wayPoints - 1 do
  5521.             local closeToEnd = (wayPoints[i].Position - wayPoints[#wayPoints].Position).magnitude < LAST_DOT_DISTANCE
  5522.             local includeWaypoint = i % WAYPOINT_INCLUDE_FACTOR == 0 and not closeToEnd
  5523.             if includeWaypoint then
  5524.                 local trailDot = TrailDot.new(wayPoints[i].Position, i)
  5525.                 newTrailDots[count] = trailDot
  5526.                 count = count + 1
  5527.             end
  5528.         end
  5529.    
  5530.         local newEndWaypoint = EndWaypoint.new(wayPoints[#wayPoints].Position, #wayPoints, originalEndWaypoint)
  5531.         table.insert(newTrailDots, newEndWaypoint)
  5532.    
  5533.         local reversedTrailDots = {}
  5534.         count = 1
  5535.         for i = #newTrailDots, 1, -1 do
  5536.             reversedTrailDots[count] = newTrailDots[i]
  5537.             count = count + 1
  5538.         end
  5539.         return reversedTrailDots
  5540.     end
  5541.    
  5542.     local function getTrailDotScale(distanceToCamera, defaultSize)
  5543.         local rangeLength = TRAIL_DOT_MAX_DISTANCE - TRAIL_DOT_MIN_DISTANCE
  5544.         local inRangePoint = math.clamp(distanceToCamera - TRAIL_DOT_MIN_DISTANCE, 0, rangeLength)/rangeLength
  5545.         local scale = TRAIL_DOT_MIN_SCALE + (TRAIL_DOT_MAX_SCALE - TRAIL_DOT_MIN_SCALE)*inRangePoint
  5546.         return defaultSize * scale
  5547.     end
  5548.    
  5549.     local createPathCount = 0
  5550.     -- originalEndWaypoint is optional, causes the waypoint to tween from that position.
  5551.     function ClickToMoveDisplay.CreatePathDisplay(wayPoints, originalEndWaypoint)
  5552.         createPathCount = createPathCount + 1
  5553.         local trailDots = createTrailDots(wayPoints, originalEndWaypoint)
  5554.    
  5555.         local function removePathBeforePoint(wayPointNumber)
  5556.             -- kill all trailDots before and at wayPointNumber
  5557.             for i = #trailDots, 1, -1 do
  5558.                 local trailDot = trailDots[i]
  5559.                 if trailDot.ClosestWayPoint <= wayPointNumber then
  5560.                     trailDot:Destroy()
  5561.                     trailDots[i] = nil
  5562.                 else
  5563.                     break
  5564.                 end
  5565.             end
  5566.         end
  5567.    
  5568.         local reiszeTrailDotsUpdateName = "ClickToMoveResizeTrail" ..createPathCount
  5569.         local function resizeTrailDots()
  5570.             if #trailDots == 0 then
  5571.                 RunService:UnbindFromRenderStep(reiszeTrailDotsUpdateName)
  5572.                 return
  5573.             end
  5574.             local cameraPos = Workspace.CurrentCamera.CFrame.p
  5575.             for i = 1, #trailDots do
  5576.                 local trailDotImage = trailDots[i].DisplayModel:FindFirstChild("TrailDotImage")
  5577.                 if trailDotImage then
  5578.                     local distanceToCamera = (trailDots[i].DisplayModel.Position - cameraPos).magnitude
  5579.                     trailDotImage.Size = getTrailDotScale(distanceToCamera, TrailDotSize)
  5580.                 end
  5581.             end
  5582.         end
  5583.         RunService:BindToRenderStep(reiszeTrailDotsUpdateName, Enum.RenderPriority.Camera.Value - 1, resizeTrailDots)
  5584.    
  5585.         local function removePath()
  5586.             removePathBeforePoint(#wayPoints)
  5587.         end
  5588.    
  5589.         return removePath, removePathBeforePoint
  5590.     end
  5591.    
  5592.     local lastFailureWaypoint = nil
  5593.     function ClickToMoveDisplay.DisplayFailureWaypoint(position)
  5594.         if lastFailureWaypoint then
  5595.             lastFailureWaypoint:Hide()
  5596.         end
  5597.         local failureWaypoint = FailureWaypoint.new(position)
  5598.         lastFailureWaypoint = failureWaypoint
  5599.         coroutine.wrap(function()
  5600.             failureWaypoint:RunFailureTween()
  5601.             failureWaypoint:Destroy()
  5602.             failureWaypoint = nil
  5603.         end)()
  5604.     end
  5605.    
  5606.     function ClickToMoveDisplay.CreateEndWaypoint(position)
  5607.         return EndWaypoint.new(position)
  5608.     end
  5609.    
  5610.     function ClickToMoveDisplay.PlayFailureAnimation()
  5611.         local myHumanoid = findPlayerHumanoid()
  5612.         if myHumanoid then
  5613.             local animationTrack = getFailureAnimationTrack(myHumanoid)
  5614.             animationTrack:Play()
  5615.         end
  5616.     end
  5617.    
  5618.     function ClickToMoveDisplay.CancelFailureAnimation()
  5619.         if lastFailureAnimationTrack ~= nil and lastFailureAnimationTrack.IsPlaying then
  5620.             lastFailureAnimationTrack:Stop()
  5621.         end
  5622.     end
  5623.    
  5624.     function ClickToMoveDisplay.SetWaypointTexture(texture)
  5625.         TrailDotIcon = texture
  5626.         TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5627.     end
  5628.    
  5629.     function ClickToMoveDisplay.GetWaypointTexture()
  5630.         return TrailDotIcon
  5631.     end
  5632.    
  5633.     function ClickToMoveDisplay.SetWaypointRadius(radius)
  5634.         TrailDotSize = Vector2.new(radius, radius)
  5635.         TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5636.     end
  5637.    
  5638.     function ClickToMoveDisplay.GetWaypointRadius()
  5639.         return TrailDotSize.X
  5640.     end
  5641.    
  5642.     function ClickToMoveDisplay.SetEndWaypointTexture(texture)
  5643.         EndWaypointIcon = texture
  5644.         TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5645.     end
  5646.    
  5647.     function ClickToMoveDisplay.GetEndWaypointTexture()
  5648.         return EndWaypointIcon
  5649.     end
  5650.    
  5651.     function ClickToMoveDisplay.SetWaypointsAlwaysOnTop(alwaysOnTop)
  5652.         WaypointsAlwaysOnTop = alwaysOnTop
  5653.         TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5654.     end
  5655.    
  5656.     function ClickToMoveDisplay.GetWaypointsAlwaysOnTop()
  5657.         return WaypointsAlwaysOnTop
  5658.     end
  5659.    
  5660.     return ClickToMoveDisplay
  5661. end
  5662.  
  5663. function _BaseCharacterController()
  5664.  
  5665.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  5666.    
  5667.     --[[ The Module ]]--
  5668.     local BaseCharacterController = {}
  5669.     BaseCharacterController.__index = BaseCharacterController
  5670.    
  5671.     function BaseCharacterController.new()
  5672.         local self = setmetatable({}, BaseCharacterController)
  5673.         self.enabled = false
  5674.         self.moveVector = ZERO_VECTOR3
  5675.         self.moveVectorIsCameraRelative = true
  5676.         self.isJumping = false
  5677.         return self
  5678.     end
  5679.    
  5680.     function BaseCharacterController:OnRenderStepped(dt)
  5681.         -- By default, nothing to do
  5682.     end
  5683.    
  5684.     function BaseCharacterController:GetMoveVector()
  5685.         return self.moveVector
  5686.     end
  5687.    
  5688.     function BaseCharacterController:IsMoveVectorCameraRelative()
  5689.         return self.moveVectorIsCameraRelative
  5690.     end
  5691.    
  5692.     function BaseCharacterController:GetIsJumping()
  5693.         return self.isJumping
  5694.     end
  5695.    
  5696.     -- Override in derived classes to set self.enabled and return boolean indicating
  5697.     -- whether Enable/Disable was successful. Return true if controller is already in the requested state.
  5698.     function BaseCharacterController:Enable(enable)
  5699.         error("BaseCharacterController:Enable must be overridden in derived classes and should not be called.")
  5700.         return false
  5701.     end
  5702.    
  5703.     return BaseCharacterController
  5704. end
  5705.  
  5706. function _VehicleController()
  5707.     local ContextActionService = game:GetService("ContextActionService")
  5708.    
  5709.     --[[ Constants ]]--
  5710.     -- Set this to true if you want to instead use the triggers for the throttle
  5711.     local useTriggersForThrottle = true
  5712.     -- Also set this to true if you want the thumbstick to not affect throttle, only triggers when a gamepad is conected
  5713.     local onlyTriggersForThrottle = false
  5714.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  5715.    
  5716.     local AUTO_PILOT_DEFAULT_MAX_STEERING_ANGLE = 35
  5717.    
  5718.    
  5719.     -- Note that VehicleController does not derive from BaseCharacterController, it is a special case
  5720.     local VehicleController = {}
  5721.     VehicleController.__index = VehicleController
  5722.    
  5723.     function VehicleController.new(CONTROL_ACTION_PRIORITY)
  5724.         local self = setmetatable({}, VehicleController)
  5725.    
  5726.         self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
  5727.    
  5728.         self.enabled = false
  5729.         self.vehicleSeat = nil
  5730.         self.throttle = 0
  5731.         self.steer = 0
  5732.    
  5733.         self.acceleration = 0
  5734.         self.decceleration = 0
  5735.         self.turningRight = 0
  5736.         self.turningLeft = 0
  5737.    
  5738.         self.vehicleMoveVector = ZERO_VECTOR3
  5739.    
  5740.         self.autoPilot = {}
  5741.         self.autoPilot.MaxSpeed = 0
  5742.         self.autoPilot.MaxSteeringAngle = 0
  5743.    
  5744.         return self
  5745.     end
  5746.    
  5747.     function VehicleController:BindContextActions()
  5748.         if useTriggersForThrottle then
  5749.             ContextActionService:BindActionAtPriority("throttleAccel", (function(actionName, inputState, inputObject)
  5750.                 self:OnThrottleAccel(actionName, inputState, inputObject)
  5751.                 return Enum.ContextActionResult.Pass
  5752.             end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonR2)
  5753.             ContextActionService:BindActionAtPriority("throttleDeccel", (function(actionName, inputState, inputObject)
  5754.                 self:OnThrottleDeccel(actionName, inputState, inputObject)
  5755.                 return Enum.ContextActionResult.Pass
  5756.             end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonL2)
  5757.         end
  5758.         ContextActionService:BindActionAtPriority("arrowSteerRight", (function(actionName, inputState, inputObject)
  5759.             self:OnSteerRight(actionName, inputState, inputObject)
  5760.             return Enum.ContextActionResult.Pass
  5761.         end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Right)
  5762.         ContextActionService:BindActionAtPriority("arrowSteerLeft", (function(actionName, inputState, inputObject)
  5763.             self:OnSteerLeft(actionName, inputState, inputObject)
  5764.             return Enum.ContextActionResult.Pass
  5765.         end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Left)
  5766.     end
  5767.    
  5768.     function VehicleController:Enable(enable, vehicleSeat)
  5769.         if enable == self.enabled and vehicleSeat == self.vehicleSeat then
  5770.             return
  5771.         end
  5772.    
  5773.         self.enabled = enable
  5774.         self.vehicleMoveVector = ZERO_VECTOR3
  5775.    
  5776.         if enable then
  5777.             if vehicleSeat then
  5778.                 self.vehicleSeat = vehicleSeat
  5779.    
  5780.                 self:SetupAutoPilot()
  5781.                 self:BindContextActions()
  5782.             end
  5783.         else
  5784.             if useTriggersForThrottle then
  5785.                 ContextActionService:UnbindAction("throttleAccel")
  5786.                 ContextActionService:UnbindAction("throttleDeccel")
  5787.             end
  5788.             ContextActionService:UnbindAction("arrowSteerRight")
  5789.             ContextActionService:UnbindAction("arrowSteerLeft")
  5790.             self.vehicleSeat = nil
  5791.         end
  5792.     end
  5793.    
  5794.     function VehicleController:OnThrottleAccel(actionName, inputState, inputObject)
  5795.         if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  5796.             self.acceleration = 0
  5797.         else
  5798.             self.acceleration = -1
  5799.         end
  5800.         self.throttle = self.acceleration + self.decceleration
  5801.     end
  5802.    
  5803.     function VehicleController:OnThrottleDeccel(actionName, inputState, inputObject)
  5804.         if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  5805.             self.decceleration = 0
  5806.         else
  5807.             self.decceleration = 1
  5808.         end
  5809.         self.throttle = self.acceleration + self.decceleration
  5810.     end
  5811.    
  5812.     function VehicleController:OnSteerRight(actionName, inputState, inputObject)
  5813.         if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  5814.             self.turningRight = 0
  5815.         else
  5816.             self.turningRight = 1
  5817.         end
  5818.         self.steer = self.turningRight + self.turningLeft
  5819.     end
  5820.    
  5821.     function VehicleController:OnSteerLeft(actionName, inputState, inputObject)
  5822.         if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  5823.             self.turningLeft = 0
  5824.         else
  5825.             self.turningLeft = -1
  5826.         end
  5827.         self.steer = self.turningRight + self.turningLeft
  5828.     end
  5829.    
  5830.     -- Call this from a function bound to Renderstep with Input Priority
  5831.     function VehicleController:Update(moveVector, cameraRelative, usingGamepad)
  5832.         if self.vehicleSeat then
  5833.             if cameraRelative then
  5834.                 -- This is the default steering mode
  5835.                 moveVector = moveVector + Vector3.new(self.steer, 0, self.throttle)
  5836.                 if usingGamepad and onlyTriggersForThrottle and useTriggersForThrottle then
  5837.                     self.vehicleSeat.ThrottleFloat = -self.throttle
  5838.                 else
  5839.                     self.vehicleSeat.ThrottleFloat = -moveVector.Z
  5840.                 end
  5841.                 self.vehicleSeat.SteerFloat = moveVector.X
  5842.    
  5843.                 return moveVector, true
  5844.             else
  5845.                 -- This is the path following mode
  5846.                 local localMoveVector = self.vehicleSeat.Occupant.RootPart.CFrame:VectorToObjectSpace(moveVector)
  5847.    
  5848.                 self.vehicleSeat.ThrottleFloat = self:ComputeThrottle(localMoveVector)
  5849.                 self.vehicleSeat.SteerFloat = self:ComputeSteer(localMoveVector)
  5850.    
  5851.                 return ZERO_VECTOR3, true
  5852.             end
  5853.         end
  5854.         return moveVector, false
  5855.     end
  5856.    
  5857.     function VehicleController:ComputeThrottle(localMoveVector)
  5858.         if localMoveVector ~= ZERO_VECTOR3 then
  5859.             local throttle = -localMoveVector.Z
  5860.             return throttle
  5861.         else
  5862.             return 0.0
  5863.         end
  5864.     end
  5865.    
  5866.     function VehicleController:ComputeSteer(localMoveVector)
  5867.         if localMoveVector ~= ZERO_VECTOR3 then
  5868.             local steerAngle = -math.atan2(-localMoveVector.x, -localMoveVector.z) * (180 / math.pi)
  5869.             return steerAngle / self.autoPilot.MaxSteeringAngle
  5870.         else
  5871.             return 0.0
  5872.         end
  5873.     end
  5874.    
  5875.     function VehicleController:SetupAutoPilot()
  5876.         -- Setup default
  5877.         self.autoPilot.MaxSpeed = self.vehicleSeat.MaxSpeed
  5878.         self.autoPilot.MaxSteeringAngle = AUTO_PILOT_DEFAULT_MAX_STEERING_ANGLE
  5879.    
  5880.         -- VehicleSeat should have a MaxSteeringAngle as well.
  5881.         -- Or we could look for a child "AutoPilotConfigModule" to find these values
  5882.         -- Or allow developer to set them through the API as like the CLickToMove customization API
  5883.     end
  5884.    
  5885.     return VehicleController
  5886. end
  5887.  
  5888. function _TouchJump()
  5889.    
  5890.     local Players = game:GetService("Players")
  5891.     local GuiService = game:GetService("GuiService")
  5892.    
  5893.     --[[ Constants ]]--
  5894.     local TOUCH_CONTROL_SHEET = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
  5895.    
  5896.     --[[ The Module ]]--
  5897.     local BaseCharacterController = _BaseCharacterController()
  5898.     local TouchJump = setmetatable({}, BaseCharacterController)
  5899.     TouchJump.__index = TouchJump
  5900.    
  5901.     function TouchJump.new()
  5902.         local self = setmetatable(BaseCharacterController.new(), TouchJump)
  5903.    
  5904.         self.parentUIFrame = nil
  5905.         self.jumpButton = nil
  5906.         self.characterAddedConn = nil
  5907.         self.humanoidStateEnabledChangedConn = nil
  5908.         self.humanoidJumpPowerConn = nil
  5909.         self.humanoidParentConn = nil
  5910.         self.externallyEnabled = false
  5911.         self.jumpPower = 0
  5912.         self.jumpStateEnabled = true
  5913.         self.isJumping = false
  5914.         self.humanoid = nil -- saved reference because property change connections are made using it
  5915.    
  5916.         return self
  5917.     end
  5918.    
  5919.     function TouchJump:EnableButton(enable)
  5920.         if enable then
  5921.             if not self.jumpButton then
  5922.                 self:Create()
  5923.             end
  5924.             local humanoid = Players.LocalPlayer.Character and Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
  5925.             if humanoid and self.externallyEnabled then
  5926.                 if self.externallyEnabled then
  5927.                     if humanoid.JumpPower > 0 then
  5928.                         self.jumpButton.Visible = true
  5929.                     end
  5930.                 end
  5931.             end
  5932.         else
  5933.             self.jumpButton.Visible = false
  5934.             self.isJumping = false
  5935.             self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
  5936.         end
  5937.     end
  5938.    
  5939.     function TouchJump:UpdateEnabled()
  5940.         if self.jumpPower > 0 and self.jumpStateEnabled then
  5941.             self:EnableButton(true)
  5942.         else
  5943.             self:EnableButton(false)
  5944.         end
  5945.     end
  5946.    
  5947.     function TouchJump:HumanoidChanged(prop)
  5948.         local humanoid = Players.LocalPlayer.Character and Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
  5949.         if humanoid then
  5950.             if prop == "JumpPower" then
  5951.                 self.jumpPower =  humanoid.JumpPower
  5952.                 self:UpdateEnabled()
  5953.             elseif prop == "Parent" then
  5954.                 if not humanoid.Parent then
  5955.                     self.humanoidChangeConn:Disconnect()
  5956.                 end
  5957.             end
  5958.         end
  5959.     end
  5960.    
  5961.     function TouchJump:HumanoidStateEnabledChanged(state, isEnabled)
  5962.         if state == Enum.HumanoidStateType.Jumping then
  5963.             self.jumpStateEnabled = isEnabled
  5964.             self:UpdateEnabled()
  5965.         end
  5966.     end
  5967.    
  5968.     function TouchJump:CharacterAdded(char)
  5969.         if self.humanoidChangeConn then
  5970.             self.humanoidChangeConn:Disconnect()
  5971.             self.humanoidChangeConn = nil
  5972.         end
  5973.    
  5974.         self.humanoid = char:FindFirstChildOfClass("Humanoid")
  5975.         while not self.humanoid do
  5976.             char.ChildAdded:wait()
  5977.             self.humanoid = char:FindFirstChildOfClass("Humanoid")
  5978.         end
  5979.    
  5980.         self.humanoidJumpPowerConn = self.humanoid:GetPropertyChangedSignal("JumpPower"):Connect(function()
  5981.             self.jumpPower =  self.humanoid.JumpPower
  5982.             self:UpdateEnabled()
  5983.         end)
  5984.    
  5985.         self.humanoidParentConn = self.humanoid:GetPropertyChangedSignal("Parent"):Connect(function()
  5986.             if not self.humanoid.Parent then
  5987.                 self.humanoidJumpPowerConn:Disconnect()
  5988.                 self.humanoidJumpPowerConn = nil
  5989.                 self.humanoidParentConn:Disconnect()
  5990.                 self.humanoidParentConn = nil
  5991.             end
  5992.         end)
  5993.    
  5994.         self.humanoidStateEnabledChangedConn = self.humanoid.StateEnabledChanged:Connect(function(state, enabled)
  5995.             self:HumanoidStateEnabledChanged(state, enabled)
  5996.         end)
  5997.    
  5998.         self.jumpPower = self.humanoid.JumpPower
  5999.         self.jumpStateEnabled = self.humanoid:GetStateEnabled(Enum.HumanoidStateType.Jumping)
  6000.         self:UpdateEnabled()
  6001.     end
  6002.    
  6003.     function TouchJump:SetupCharacterAddedFunction()
  6004.         self.characterAddedConn = Players.LocalPlayer.CharacterAdded:Connect(function(char)
  6005.             self:CharacterAdded(char)
  6006.         end)
  6007.         if Players.LocalPlayer.Character then
  6008.             self:CharacterAdded(Players.LocalPlayer.Character)
  6009.         end
  6010.     end
  6011.    
  6012.     function TouchJump:Enable(enable, parentFrame)
  6013.         if parentFrame then
  6014.             self.parentUIFrame = parentFrame
  6015.         end
  6016.         self.externallyEnabled = enable
  6017.         self:EnableButton(enable)
  6018.     end
  6019.    
  6020.     function TouchJump:Create()
  6021.         if not self.parentUIFrame then
  6022.             return
  6023.         end
  6024.    
  6025.         if self.jumpButton then
  6026.             self.jumpButton:Destroy()
  6027.             self.jumpButton = nil
  6028.         end
  6029.    
  6030.         local minAxis = math.min(self.parentUIFrame.AbsoluteSize.x, self.parentUIFrame.AbsoluteSize.y)
  6031.         local isSmallScreen = minAxis <= 500
  6032.         local jumpButtonSize = isSmallScreen and 70 or 120
  6033.    
  6034.         self.jumpButton = Instance.new("ImageButton")
  6035.         self.jumpButton.Name = "JumpButton"
  6036.         self.jumpButton.Visible = false
  6037.         self.jumpButton.BackgroundTransparency = 1
  6038.         self.jumpButton.Image = TOUCH_CONTROL_SHEET
  6039.         self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
  6040.         self.jumpButton.ImageRectSize = Vector2.new(144, 144)
  6041.         self.jumpButton.Size = UDim2.new(0, jumpButtonSize, 0, jumpButtonSize)
  6042.    
  6043.         self.jumpButton.Position = isSmallScreen and UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize - 20) or
  6044.             UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize * 1.75)
  6045.    
  6046.         local touchObject = nil
  6047.         self.jumpButton.InputBegan:connect(function(inputObject)
  6048.             --A touch that starts elsewhere on the screen will be sent to a frame's InputBegan event
  6049.             --if it moves over the frame. So we check that this is actually a new touch (inputObject.UserInputState ~= Enum.UserInputState.Begin)
  6050.             if touchObject or inputObject.UserInputType ~= Enum.UserInputType.Touch
  6051.                 or inputObject.UserInputState ~= Enum.UserInputState.Begin then
  6052.                 return
  6053.             end
  6054.    
  6055.             touchObject = inputObject
  6056.             self.jumpButton.ImageRectOffset = Vector2.new(146, 146)
  6057.             self.isJumping = true
  6058.         end)
  6059.    
  6060.         local OnInputEnded = function()
  6061.             touchObject = nil
  6062.             self.isJumping = false
  6063.             self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
  6064.         end
  6065.    
  6066.         self.jumpButton.InputEnded:connect(function(inputObject)
  6067.             if inputObject == touchObject then
  6068.                 OnInputEnded()
  6069.             end
  6070.         end)
  6071.    
  6072.         GuiService.MenuOpened:connect(function()
  6073.             if touchObject then
  6074.                 OnInputEnded()
  6075.             end
  6076.         end)
  6077.    
  6078.         if not self.characterAddedConn then
  6079.             self:SetupCharacterAddedFunction()
  6080.         end
  6081.    
  6082.         self.jumpButton.Parent = self.parentUIFrame
  6083.     end
  6084.    
  6085.     return TouchJump
  6086. end
  6087.  
  6088. function _ClickToMoveController()
  6089.     --[[ Roblox Services ]]--
  6090.     local UserInputService = game:GetService("UserInputService")
  6091.     local PathfindingService = game:GetService("PathfindingService")
  6092.     local Players = game:GetService("Players")
  6093.     local DebrisService = game:GetService('Debris')
  6094.     local StarterGui = game:GetService("StarterGui")
  6095.     local Workspace = game:GetService("Workspace")
  6096.     local CollectionService = game:GetService("CollectionService")
  6097.     local GuiService = game:GetService("GuiService")
  6098.    
  6099.     --[[ Configuration ]]
  6100.     local ShowPath = true
  6101.     local PlayFailureAnimation = true
  6102.     local UseDirectPath = false
  6103.     local UseDirectPathForVehicle = true
  6104.     local AgentSizeIncreaseFactor = 1.0
  6105.     local UnreachableWaypointTimeout = 8
  6106.    
  6107.     --[[ Constants ]]--
  6108.     local movementKeys = {
  6109.         [Enum.KeyCode.W] = true;
  6110.         [Enum.KeyCode.A] = true;
  6111.         [Enum.KeyCode.S] = true;
  6112.         [Enum.KeyCode.D] = true;
  6113.         [Enum.KeyCode.Up] = true;
  6114.         [Enum.KeyCode.Down] = true;
  6115.     }
  6116.    
  6117.     local FFlagUserNavigationClickToMoveSkipPassedWaypointsSuccess, FFlagUserNavigationClickToMoveSkipPassedWaypointsResult = pcall(function() return UserSettings():IsUserFeatureEnabled("UserNavigationClickToMoveSkipPassedWaypoints") end)
  6118.     local FFlagUserNavigationClickToMoveSkipPassedWaypoints = FFlagUserNavigationClickToMoveSkipPassedWaypointsSuccess and FFlagUserNavigationClickToMoveSkipPassedWaypointsResult
  6119.    
  6120.     local Player = Players.LocalPlayer
  6121.    
  6122.     local ClickToMoveDisplay = _ClickToMoveDisplay()
  6123.    
  6124.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  6125.     local ALMOST_ZERO = 0.000001
  6126.    
  6127.    
  6128.     --------------------------UTIL LIBRARY-------------------------------
  6129.     local Utility = {}
  6130.     do
  6131.         local function FindCharacterAncestor(part)
  6132.             if part then
  6133.                 local humanoid = part:FindFirstChildOfClass("Humanoid")
  6134.                 if humanoid then
  6135.                     return part, humanoid
  6136.                 else
  6137.                     return FindCharacterAncestor(part.Parent)
  6138.                 end
  6139.             end
  6140.         end
  6141.         Utility.FindCharacterAncestor = FindCharacterAncestor
  6142.    
  6143.         local function Raycast(ray, ignoreNonCollidable, ignoreList)
  6144.             ignoreList = ignoreList or {}
  6145.             local hitPart, hitPos, hitNorm, hitMat = Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
  6146.             if hitPart then
  6147.                 if ignoreNonCollidable and hitPart.CanCollide == false then
  6148.                     -- We always include character parts so a user can click on another character
  6149.                     -- to walk to them.
  6150.                     local _, humanoid = FindCharacterAncestor(hitPart)
  6151.                     if humanoid == nil then
  6152.                         table.insert(ignoreList, hitPart)
  6153.                         return Raycast(ray, ignoreNonCollidable, ignoreList)
  6154.                     end
  6155.                 end
  6156.                 return hitPart, hitPos, hitNorm, hitMat
  6157.             end
  6158.             return nil, nil
  6159.         end
  6160.         Utility.Raycast = Raycast
  6161.     end
  6162.    
  6163.     local humanoidCache = {}
  6164.     local function findPlayerHumanoid(player)
  6165.         local character = player and player.Character
  6166.         if character then
  6167.             local resultHumanoid = humanoidCache[player]
  6168.             if resultHumanoid and resultHumanoid.Parent == character then
  6169.                 return resultHumanoid
  6170.             else
  6171.                 humanoidCache[player] = nil -- Bust Old Cache
  6172.                 local humanoid = character:FindFirstChildOfClass("Humanoid")
  6173.                 if humanoid then
  6174.                     humanoidCache[player] = humanoid
  6175.                 end
  6176.                 return humanoid
  6177.             end
  6178.         end
  6179.     end
  6180.    
  6181.     --------------------------CHARACTER CONTROL-------------------------------
  6182.     local CurrentIgnoreList
  6183.     local CurrentIgnoreTag = nil
  6184.    
  6185.     local TaggedInstanceAddedConnection = nil
  6186.     local TaggedInstanceRemovedConnection = nil
  6187.    
  6188.     local function GetCharacter()
  6189.         return Player and Player.Character
  6190.     end
  6191.    
  6192.     local function UpdateIgnoreTag(newIgnoreTag)
  6193.         if newIgnoreTag == CurrentIgnoreTag then
  6194.             return
  6195.         end
  6196.         if TaggedInstanceAddedConnection then
  6197.             TaggedInstanceAddedConnection:Disconnect()
  6198.             TaggedInstanceAddedConnection = nil
  6199.         end
  6200.         if TaggedInstanceRemovedConnection then
  6201.             TaggedInstanceRemovedConnection:Disconnect()
  6202.             TaggedInstanceRemovedConnection = nil
  6203.         end
  6204.         CurrentIgnoreTag = newIgnoreTag
  6205.         CurrentIgnoreList = {GetCharacter()}
  6206.         if CurrentIgnoreTag ~= nil then
  6207.             local ignoreParts = CollectionService:GetTagged(CurrentIgnoreTag)
  6208.             for _, ignorePart in ipairs(ignoreParts) do
  6209.                 table.insert(CurrentIgnoreList, ignorePart)
  6210.             end
  6211.             TaggedInstanceAddedConnection = CollectionService:GetInstanceAddedSignal(
  6212.                 CurrentIgnoreTag):Connect(function(ignorePart)
  6213.                 table.insert(CurrentIgnoreList, ignorePart)
  6214.             end)
  6215.             TaggedInstanceRemovedConnection = CollectionService:GetInstanceRemovedSignal(
  6216.                 CurrentIgnoreTag):Connect(function(ignorePart)
  6217.                 for i = 1, #CurrentIgnoreList do
  6218.                     if CurrentIgnoreList[i] == ignorePart then
  6219.                         CurrentIgnoreList[i] = CurrentIgnoreList[#CurrentIgnoreList]
  6220.                         table.remove(CurrentIgnoreList)
  6221.                         break
  6222.                     end
  6223.                 end
  6224.             end)
  6225.         end
  6226.     end
  6227.    
  6228.     local function getIgnoreList()
  6229.         if CurrentIgnoreList then
  6230.             return CurrentIgnoreList
  6231.         end
  6232.         CurrentIgnoreList = {}
  6233.         table.insert(CurrentIgnoreList, GetCharacter())
  6234.         return CurrentIgnoreList
  6235.     end
  6236.    
  6237.     -----------------------------------PATHER--------------------------------------
  6238.    
  6239.     local function Pather(endPoint, surfaceNormal, overrideUseDirectPath)
  6240.         local this = {}
  6241.    
  6242.         local directPathForHumanoid
  6243.         local directPathForVehicle
  6244.         if overrideUseDirectPath ~= nil then
  6245.             directPathForHumanoid = overrideUseDirectPath
  6246.             directPathForVehicle = overrideUseDirectPath
  6247.         else
  6248.             directPathForHumanoid = UseDirectPath
  6249.             directPathForVehicle = UseDirectPathForVehicle
  6250.         end
  6251.    
  6252.         this.Cancelled = false
  6253.         this.Started = false
  6254.    
  6255.         this.Finished = Instance.new("BindableEvent")
  6256.         this.PathFailed = Instance.new("BindableEvent")
  6257.    
  6258.         this.PathComputing = false
  6259.         this.PathComputed = false
  6260.    
  6261.         this.OriginalTargetPoint = endPoint
  6262.         this.TargetPoint = endPoint
  6263.         this.TargetSurfaceNormal = surfaceNormal
  6264.    
  6265.         this.DiedConn = nil
  6266.         this.SeatedConn = nil
  6267.         this.BlockedConn = nil
  6268.         this.TeleportedConn = nil
  6269.    
  6270.         this.CurrentPoint = 0
  6271.    
  6272.         this.HumanoidOffsetFromPath = ZERO_VECTOR3
  6273.    
  6274.         this.CurrentWaypointPosition = nil
  6275.         this.CurrentWaypointPlaneNormal = ZERO_VECTOR3
  6276.         this.CurrentWaypointPlaneDistance = 0
  6277.         this.CurrentWaypointNeedsJump = false;
  6278.    
  6279.         this.CurrentHumanoidPosition = ZERO_VECTOR3
  6280.         this.CurrentHumanoidVelocity = 0
  6281.    
  6282.         this.NextActionMoveDirection = ZERO_VECTOR3
  6283.         this.NextActionJump = false
  6284.    
  6285.         this.Timeout = 0
  6286.    
  6287.         this.Humanoid = findPlayerHumanoid(Player)
  6288.         this.OriginPoint = nil
  6289.         this.AgentCanFollowPath = false
  6290.         this.DirectPath = false
  6291.         this.DirectPathRiseFirst = false
  6292.    
  6293.         local rootPart = this.Humanoid and this.Humanoid.RootPart
  6294.         if rootPart then
  6295.             -- Setup origin
  6296.             this.OriginPoint = rootPart.CFrame.p
  6297.    
  6298.             -- Setup agent
  6299.             local agentRadius = 2
  6300.             local agentHeight = 5
  6301.             local agentCanJump = true
  6302.    
  6303.             local seat = this.Humanoid.SeatPart
  6304.             if seat and seat:IsA("VehicleSeat") then
  6305.                 -- Humanoid is seated on a vehicle
  6306.                 local vehicle = seat:FindFirstAncestorOfClass("Model")
  6307.                 if vehicle then
  6308.                     -- Make sure the PrimaryPart is set to the vehicle seat while we compute the extends.
  6309.                     local tempPrimaryPart = vehicle.PrimaryPart
  6310.                     vehicle.PrimaryPart = seat
  6311.    
  6312.                     -- For now, only direct path
  6313.                     if directPathForVehicle then
  6314.                         local extents = vehicle:GetExtentsSize()
  6315.                         agentRadius = AgentSizeIncreaseFactor * 0.5 * math.sqrt(extents.X * extents.X + extents.Z * extents.Z)
  6316.                         agentHeight = AgentSizeIncreaseFactor * extents.Y
  6317.                         agentCanJump = false
  6318.                         this.AgentCanFollowPath = true
  6319.                         this.DirectPath = directPathForVehicle
  6320.                     end
  6321.    
  6322.                     -- Reset PrimaryPart
  6323.                     vehicle.PrimaryPart = tempPrimaryPart
  6324.                 end
  6325.             else
  6326.                 local extents = GetCharacter():GetExtentsSize()
  6327.                 agentRadius = AgentSizeIncreaseFactor * 0.5 * math.sqrt(extents.X * extents.X + extents.Z * extents.Z)
  6328.                 agentHeight = AgentSizeIncreaseFactor * extents.Y
  6329.                 agentCanJump = (this.Humanoid.JumpPower > 0)
  6330.                 this.AgentCanFollowPath = true
  6331.                 this.DirectPath = directPathForHumanoid
  6332.                 this.DirectPathRiseFirst = this.Humanoid.Sit
  6333.             end
  6334.    
  6335.             -- Build path object
  6336.             this.pathResult = PathfindingService:CreatePath({AgentRadius = agentRadius, AgentHeight = agentHeight, AgentCanJump = agentCanJump})
  6337.         end
  6338.    
  6339.         function this:Cleanup()
  6340.             if this.stopTraverseFunc then
  6341.                 this.stopTraverseFunc()
  6342.                 this.stopTraverseFunc = nil
  6343.             end
  6344.    
  6345.             if this.MoveToConn then
  6346.                 this.MoveToConn:Disconnect()
  6347.                 this.MoveToConn = nil
  6348.             end
  6349.    
  6350.             if this.BlockedConn then
  6351.                 this.BlockedConn:Disconnect()
  6352.                 this.BlockedConn = nil
  6353.             end
  6354.    
  6355.             if this.DiedConn then
  6356.                 this.DiedConn:Disconnect()
  6357.                 this.DiedConn = nil
  6358.             end
  6359.    
  6360.             if this.SeatedConn then
  6361.                 this.SeatedConn:Disconnect()
  6362.                 this.SeatedConn = nil
  6363.             end
  6364.    
  6365.             if this.TeleportedConn then
  6366.                 this.TeleportedConn:Disconnect()
  6367.                 this.TeleportedConn = nil
  6368.             end
  6369.    
  6370.             this.Started = false
  6371.         end
  6372.    
  6373.         function this:Cancel()
  6374.             this.Cancelled = true
  6375.             this:Cleanup()
  6376.         end
  6377.    
  6378.         function this:IsActive()
  6379.             return this.AgentCanFollowPath and this.Started and not this.Cancelled
  6380.         end
  6381.    
  6382.         function this:OnPathInterrupted()
  6383.             -- Stop moving
  6384.             this.Cancelled = true
  6385.             this:OnPointReached(false)
  6386.         end
  6387.    
  6388.         function this:ComputePath()
  6389.             if this.OriginPoint then
  6390.                 if this.PathComputed or this.PathComputing then return end
  6391.                 this.PathComputing = true
  6392.                 if this.AgentCanFollowPath then
  6393.                     if this.DirectPath then
  6394.                         this.pointList = {
  6395.                             PathWaypoint.new(this.OriginPoint, Enum.PathWaypointAction.Walk),
  6396.                             PathWaypoint.new(this.TargetPoint, this.DirectPathRiseFirst and Enum.PathWaypointAction.Jump or Enum.PathWaypointAction.Walk)
  6397.                         }
  6398.                         this.PathComputed = true
  6399.                     else
  6400.                         this.pathResult:ComputeAsync(this.OriginPoint, this.TargetPoint)
  6401.                         this.pointList = this.pathResult:GetWaypoints()
  6402.                         this.BlockedConn = this.pathResult.Blocked:Connect(function(blockedIdx) this:OnPathBlocked(blockedIdx) end)
  6403.                         this.PathComputed = this.pathResult.Status == Enum.PathStatus.Success
  6404.                     end
  6405.                 end
  6406.                 this.PathComputing = false
  6407.             end
  6408.         end
  6409.    
  6410.         function this:IsValidPath()
  6411.             this:ComputePath()
  6412.             return this.PathComputed and this.AgentCanFollowPath
  6413.         end
  6414.    
  6415.         this.Recomputing = false
  6416.         function this:OnPathBlocked(blockedWaypointIdx)
  6417.             local pathBlocked = blockedWaypointIdx >= this.CurrentPoint
  6418.             if not pathBlocked or this.Recomputing then
  6419.                 return
  6420.             end
  6421.    
  6422.             this.Recomputing = true
  6423.    
  6424.             if this.stopTraverseFunc then
  6425.                 this.stopTraverseFunc()
  6426.                 this.stopTraverseFunc = nil
  6427.             end
  6428.    
  6429.             this.OriginPoint = this.Humanoid.RootPart.CFrame.p
  6430.    
  6431.             this.pathResult:ComputeAsync(this.OriginPoint, this.TargetPoint)
  6432.             this.pointList = this.pathResult:GetWaypoints()
  6433.             if #this.pointList > 0 then
  6434.                 this.HumanoidOffsetFromPath = this.pointList[1].Position - this.OriginPoint
  6435.             end
  6436.             this.PathComputed = this.pathResult.Status == Enum.PathStatus.Success
  6437.    
  6438.             if ShowPath then
  6439.                 this.stopTraverseFunc, this.setPointFunc = ClickToMoveDisplay.CreatePathDisplay(this.pointList)
  6440.             end
  6441.             if this.PathComputed then
  6442.                 this.CurrentPoint = 1 -- The first waypoint is always the start location. Skip it.
  6443.                 this:OnPointReached(true) -- Move to first point
  6444.             else
  6445.                 this.PathFailed:Fire()
  6446.                 this:Cleanup()
  6447.             end
  6448.    
  6449.             this.Recomputing = false
  6450.         end
  6451.    
  6452.         function this:OnRenderStepped(dt)
  6453.             if this.Started and not this.Cancelled then
  6454.                 -- Check for Timeout (if a waypoint is not reached within the delay, we fail)
  6455.                 this.Timeout = this.Timeout + dt
  6456.                 if this.Timeout > UnreachableWaypointTimeout then
  6457.                     this:OnPointReached(false)
  6458.                     return
  6459.                 end
  6460.    
  6461.                 -- Get Humanoid position and velocity
  6462.                 this.CurrentHumanoidPosition = this.Humanoid.RootPart.Position + this.HumanoidOffsetFromPath
  6463.                 this.CurrentHumanoidVelocity = this.Humanoid.RootPart.Velocity
  6464.    
  6465.                 -- Check if it has reached some waypoints
  6466.                 while this.Started and this:IsCurrentWaypointReached() do
  6467.                     this:OnPointReached(true)
  6468.                 end
  6469.    
  6470.                 -- If still started, update actions
  6471.                 if this.Started then
  6472.                     -- Move action
  6473.                     this.NextActionMoveDirection = this.CurrentWaypointPosition - this.CurrentHumanoidPosition
  6474.                     if this.NextActionMoveDirection.Magnitude > ALMOST_ZERO then
  6475.                         this.NextActionMoveDirection = this.NextActionMoveDirection.Unit
  6476.                     else
  6477.                         this.NextActionMoveDirection = ZERO_VECTOR3
  6478.                     end
  6479.                     -- Jump action
  6480.                     if this.CurrentWaypointNeedsJump then
  6481.                         this.NextActionJump = true
  6482.                         this.CurrentWaypointNeedsJump = false   -- Request jump only once
  6483.                     else
  6484.                         this.NextActionJump = false
  6485.                     end
  6486.                 end
  6487.             end
  6488.         end
  6489.    
  6490.         function this:IsCurrentWaypointReached()
  6491.             local reached = false
  6492.    
  6493.             -- Check we do have a plane, if not, we consider the waypoint reached
  6494.             if this.CurrentWaypointPlaneNormal ~= ZERO_VECTOR3 then
  6495.                 -- Compute distance of Humanoid from destination plane
  6496.                 local dist = this.CurrentWaypointPlaneNormal:Dot(this.CurrentHumanoidPosition) - this.CurrentWaypointPlaneDistance
  6497.                 -- Compute the component of the Humanoid velocity that is towards the plane
  6498.                 local velocity = -this.CurrentWaypointPlaneNormal:Dot(this.CurrentHumanoidVelocity)
  6499.                 -- Compute the threshold from the destination plane based on Humanoid velocity
  6500.                 local threshold = math.max(1.0, 0.0625 * velocity)
  6501.                 -- 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
  6502.                 reached = dist < threshold
  6503.             else
  6504.                 reached = true
  6505.             end
  6506.    
  6507.             if reached then
  6508.                 this.CurrentWaypointPosition = nil
  6509.                 this.CurrentWaypointPlaneNormal = ZERO_VECTOR3
  6510.                 this.CurrentWaypointPlaneDistance = 0
  6511.             end
  6512.    
  6513.             return reached
  6514.         end
  6515.    
  6516.         function this:OnPointReached(reached)
  6517.    
  6518.             if reached and not this.Cancelled then
  6519.                 -- First, destroyed the current displayed waypoint
  6520.                 if this.setPointFunc then
  6521.                     this.setPointFunc(this.CurrentPoint)
  6522.                 end
  6523.    
  6524.                 local nextWaypointIdx = this.CurrentPoint + 1
  6525.    
  6526.                 if nextWaypointIdx > #this.pointList then
  6527.                     -- End of path reached
  6528.                     if this.stopTraverseFunc then
  6529.                         this.stopTraverseFunc()
  6530.                     end
  6531.                     this.Finished:Fire()
  6532.                     this:Cleanup()
  6533.                 else
  6534.                     local currentWaypoint = this.pointList[this.CurrentPoint]
  6535.                     local nextWaypoint = this.pointList[nextWaypointIdx]
  6536.    
  6537.                     -- If airborne, only allow to keep moving
  6538.                     -- if nextWaypoint.Action ~= Jump, or path mantains a direction
  6539.                     -- Otherwise, wait until the humanoid gets to the ground
  6540.                     local currentState = this.Humanoid:GetState()
  6541.                     local isInAir = currentState == Enum.HumanoidStateType.FallingDown
  6542.                         or currentState == Enum.HumanoidStateType.Freefall
  6543.                         or currentState == Enum.HumanoidStateType.Jumping
  6544.    
  6545.                     if isInAir then
  6546.                         local shouldWaitForGround = nextWaypoint.Action == Enum.PathWaypointAction.Jump
  6547.                         if not shouldWaitForGround and this.CurrentPoint > 1 then
  6548.                             local prevWaypoint = this.pointList[this.CurrentPoint - 1]
  6549.    
  6550.                             local prevDir = currentWaypoint.Position - prevWaypoint.Position
  6551.                             local currDir = nextWaypoint.Position - currentWaypoint.Position
  6552.    
  6553.                             local prevDirXZ = Vector2.new(prevDir.x, prevDir.z).Unit
  6554.                             local currDirXZ = Vector2.new(currDir.x, currDir.z).Unit
  6555.    
  6556.                             local THRESHOLD_COS = 0.996 -- ~cos(5 degrees)
  6557.                             shouldWaitForGround = prevDirXZ:Dot(currDirXZ) < THRESHOLD_COS
  6558.                         end
  6559.    
  6560.                         if shouldWaitForGround then
  6561.                             this.Humanoid.FreeFalling:Wait()
  6562.    
  6563.                             -- Give time to the humanoid's state to change
  6564.                             -- Otherwise, the jump flag in Humanoid
  6565.                             -- will be reset by the state change
  6566.                             wait(0.1)
  6567.                         end
  6568.                     end
  6569.    
  6570.                     -- Move to the next point
  6571.                     if FFlagUserNavigationClickToMoveSkipPassedWaypoints then
  6572.                         this:MoveToNextWayPoint(currentWaypoint, nextWaypoint, nextWaypointIdx)
  6573.                     else
  6574.                         if this.setPointFunc then
  6575.                             this.setPointFunc(nextWaypointIdx)
  6576.                         end
  6577.                         if nextWaypoint.Action == Enum.PathWaypointAction.Jump then
  6578.                             this.Humanoid.Jump = true
  6579.                         end
  6580.                         this.Humanoid:MoveTo(nextWaypoint.Position)
  6581.    
  6582.                         this.CurrentPoint = nextWaypointIdx
  6583.                     end
  6584.                 end
  6585.             else
  6586.                 this.PathFailed:Fire()
  6587.                 this:Cleanup()
  6588.             end
  6589.         end
  6590.    
  6591.         function this:MoveToNextWayPoint(currentWaypoint, nextWaypoint, nextWaypointIdx)
  6592.             -- Build next destination plane
  6593.             -- (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))
  6594.             -- (plane location is at next waypoint)
  6595.             this.CurrentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
  6596.             this.CurrentWaypointPlaneNormal = Vector3.new(this.CurrentWaypointPlaneNormal.X, 0, this.CurrentWaypointPlaneNormal.Z)
  6597.             if this.CurrentWaypointPlaneNormal.Magnitude > ALMOST_ZERO then
  6598.                 this.CurrentWaypointPlaneNormal = this.CurrentWaypointPlaneNormal.Unit
  6599.                 this.CurrentWaypointPlaneDistance = this.CurrentWaypointPlaneNormal:Dot(nextWaypoint.Position)
  6600.             else
  6601.                 -- Next waypoint is the same as current waypoint so no plane
  6602.                 this.CurrentWaypointPlaneNormal = ZERO_VECTOR3
  6603.                 this.CurrentWaypointPlaneDistance = 0
  6604.             end
  6605.    
  6606.             -- Should we jump
  6607.             this.CurrentWaypointNeedsJump = nextWaypoint.Action == Enum.PathWaypointAction.Jump;
  6608.    
  6609.             -- Remember next waypoint position
  6610.             this.CurrentWaypointPosition = nextWaypoint.Position
  6611.    
  6612.             -- Move to next point
  6613.             this.CurrentPoint = nextWaypointIdx
  6614.    
  6615.             -- Finally reset Timeout
  6616.             this.Timeout = 0
  6617.         end
  6618.    
  6619.         function this:Start(overrideShowPath)
  6620.             if not this.AgentCanFollowPath then
  6621.                 this.PathFailed:Fire()
  6622.                 return
  6623.             end
  6624.    
  6625.             if this.Started then return end
  6626.             this.Started = true
  6627.    
  6628.             ClickToMoveDisplay.CancelFailureAnimation()
  6629.    
  6630.             if ShowPath then
  6631.                 if overrideShowPath == nil or overrideShowPath then
  6632.                     this.stopTraverseFunc, this.setPointFunc = ClickToMoveDisplay.CreatePathDisplay(this.pointList, this.OriginalTargetPoint)
  6633.                 end
  6634.             end
  6635.    
  6636.             if #this.pointList > 0 then
  6637.                 -- Determine the humanoid offset from the path's first point
  6638.                 -- Offset of the first waypoint from the path's origin point
  6639.                 this.HumanoidOffsetFromPath = Vector3.new(0, this.pointList[1].Position.Y - this.OriginPoint.Y, 0)
  6640.    
  6641.                 -- As well as its current position and velocity
  6642.                 this.CurrentHumanoidPosition = this.Humanoid.RootPart.Position + this.HumanoidOffsetFromPath
  6643.                 this.CurrentHumanoidVelocity = this.Humanoid.RootPart.Velocity
  6644.    
  6645.                 -- Connect to events
  6646.                 this.SeatedConn = this.Humanoid.Seated:Connect(function(isSeated, seat) this:OnPathInterrupted() end)
  6647.                 this.DiedConn = this.Humanoid.Died:Connect(function() this:OnPathInterrupted() end)
  6648.                 this.TeleportedConn = this.Humanoid.RootPart:GetPropertyChangedSignal("CFrame"):Connect(function() this:OnPathInterrupted() end)
  6649.    
  6650.                 -- Actually start
  6651.                 this.CurrentPoint = 1 -- The first waypoint is always the start location. Skip it.
  6652.                 this:OnPointReached(true) -- Move to first point
  6653.             else
  6654.                 this.PathFailed:Fire()
  6655.                 if this.stopTraverseFunc then
  6656.                     this.stopTraverseFunc()
  6657.                 end
  6658.             end
  6659.         end
  6660.    
  6661.         --We always raycast to the ground in the case that the user clicked a wall.
  6662.         local offsetPoint = this.TargetPoint + this.TargetSurfaceNormal*1.5
  6663.         local ray = Ray.new(offsetPoint, Vector3.new(0,-1,0)*50)
  6664.         local newHitPart, newHitPos = Workspace:FindPartOnRayWithIgnoreList(ray, getIgnoreList())
  6665.         if newHitPart then
  6666.             this.TargetPoint = newHitPos
  6667.         end
  6668.         this:ComputePath()
  6669.    
  6670.         return this
  6671.     end
  6672.    
  6673.     -------------------------------------------------------------------------
  6674.    
  6675.     local function CheckAlive()
  6676.         local humanoid = findPlayerHumanoid(Player)
  6677.         return humanoid ~= nil and humanoid.Health > 0
  6678.     end
  6679.    
  6680.     local function GetEquippedTool(character)
  6681.         if character ~= nil then
  6682.             for _, child in pairs(character:GetChildren()) do
  6683.                 if child:IsA('Tool') then
  6684.                     return child
  6685.                 end
  6686.             end
  6687.         end
  6688.     end
  6689.    
  6690.     local ExistingPather = nil
  6691.     local ExistingIndicator = nil
  6692.     local PathCompleteListener = nil
  6693.     local PathFailedListener = nil
  6694.    
  6695.     local function CleanupPath()
  6696.         if ExistingPather then
  6697.             ExistingPather:Cancel()
  6698.             ExistingPather = nil
  6699.         end
  6700.         if PathCompleteListener then
  6701.             PathCompleteListener:Disconnect()
  6702.             PathCompleteListener = nil
  6703.         end
  6704.         if PathFailedListener then
  6705.             PathFailedListener:Disconnect()
  6706.             PathFailedListener = nil
  6707.         end
  6708.         if ExistingIndicator then
  6709.             ExistingIndicator:Destroy()
  6710.         end
  6711.     end
  6712.    
  6713.     local function HandleMoveTo(thisPather, hitPt, hitChar, character, overrideShowPath)
  6714.         if ExistingPather then
  6715.             CleanupPath()
  6716.         end
  6717.         ExistingPather = thisPather
  6718.         thisPather:Start(overrideShowPath)
  6719.    
  6720.         PathCompleteListener = thisPather.Finished.Event:Connect(function()
  6721.             CleanupPath()
  6722.             if hitChar then
  6723.                 local currentWeapon = GetEquippedTool(character)
  6724.                 if currentWeapon then
  6725.                     currentWeapon:Activate()
  6726.                 end
  6727.             end
  6728.         end)
  6729.         PathFailedListener = thisPather.PathFailed.Event:Connect(function()
  6730.             CleanupPath()
  6731.             if overrideShowPath == nil or overrideShowPath then
  6732.                 local shouldPlayFailureAnim = PlayFailureAnimation and not (ExistingPather and ExistingPather:IsActive())
  6733.                 if shouldPlayFailureAnim then
  6734.                     ClickToMoveDisplay.PlayFailureAnimation()
  6735.                 end
  6736.                 ClickToMoveDisplay.DisplayFailureWaypoint(hitPt)
  6737.             end
  6738.         end)
  6739.     end
  6740.    
  6741.     local function ShowPathFailedFeedback(hitPt)
  6742.         if ExistingPather and ExistingPather:IsActive() then
  6743.             ExistingPather:Cancel()
  6744.         end
  6745.         if PlayFailureAnimation then
  6746.             ClickToMoveDisplay.PlayFailureAnimation()
  6747.         end
  6748.         ClickToMoveDisplay.DisplayFailureWaypoint(hitPt)
  6749.     end
  6750.    
  6751.     function OnTap(tapPositions, goToPoint, wasTouchTap)
  6752.         -- Good to remember if this is the latest tap event
  6753.         local camera = Workspace.CurrentCamera
  6754.         local character = Player.Character
  6755.    
  6756.         if not CheckAlive() then return end
  6757.    
  6758.         -- This is a path tap position
  6759.         if #tapPositions == 1 or goToPoint then
  6760.             if camera then
  6761.                 local unitRay = camera:ScreenPointToRay(tapPositions[1].x, tapPositions[1].y)
  6762.                 local ray = Ray.new(unitRay.Origin, unitRay.Direction*1000)
  6763.    
  6764.                 local myHumanoid = findPlayerHumanoid(Player)
  6765.                 local hitPart, hitPt, hitNormal = Utility.Raycast(ray, true, getIgnoreList())
  6766.    
  6767.                 local hitChar, hitHumanoid = Utility.FindCharacterAncestor(hitPart)
  6768.                 if wasTouchTap and hitHumanoid and StarterGui:GetCore("AvatarContextMenuEnabled") then
  6769.                     local clickedPlayer = Players:GetPlayerFromCharacter(hitHumanoid.Parent)
  6770.                     if clickedPlayer then
  6771.                         CleanupPath()
  6772.                         return
  6773.                     end
  6774.                 end
  6775.                 if goToPoint then
  6776.                     hitPt = goToPoint
  6777.                     hitChar = nil
  6778.                 end
  6779.                 if hitPt and character then
  6780.                     -- Clean up current path
  6781.                     CleanupPath()
  6782.                     local thisPather = Pather(hitPt, hitNormal)
  6783.                     if thisPather:IsValidPath() then
  6784.                         HandleMoveTo(thisPather, hitPt, hitChar, character)
  6785.                     else
  6786.                         -- Clean up
  6787.                         thisPather:Cleanup()
  6788.                         -- Feedback here for when we don't have a good path
  6789.                         ShowPathFailedFeedback(hitPt)
  6790.                     end
  6791.                 end
  6792.             end
  6793.         elseif #tapPositions >= 2 then
  6794.             if camera then
  6795.                 -- Do shoot
  6796.                 local currentWeapon = GetEquippedTool(character)
  6797.                 if currentWeapon then
  6798.                     currentWeapon:Activate()
  6799.                 end
  6800.             end
  6801.         end
  6802.     end
  6803.    
  6804.     local function DisconnectEvent(event)
  6805.         if event then
  6806.             event:Disconnect()
  6807.         end
  6808.     end
  6809.    
  6810.     --[[ The ClickToMove Controller Class ]]--
  6811.     local KeyboardController = _Keyboard()
  6812.     local ClickToMove = setmetatable({}, KeyboardController)
  6813.     ClickToMove.__index = ClickToMove
  6814.    
  6815.     function ClickToMove.new(CONTROL_ACTION_PRIORITY)
  6816.         local self = setmetatable(KeyboardController.new(CONTROL_ACTION_PRIORITY), ClickToMove)
  6817.    
  6818.         self.fingerTouches = {}
  6819.         self.numUnsunkTouches = 0
  6820.         -- PC simulation
  6821.         self.mouse1Down = tick()
  6822.         self.mouse1DownPos = Vector2.new()
  6823.         self.mouse2DownTime = tick()
  6824.         self.mouse2DownPos = Vector2.new()
  6825.         self.mouse2UpTime = tick()
  6826.    
  6827.         self.keyboardMoveVector = ZERO_VECTOR3
  6828.    
  6829.         self.tapConn = nil
  6830.         self.inputBeganConn = nil
  6831.         self.inputChangedConn = nil
  6832.         self.inputEndedConn = nil
  6833.         self.humanoidDiedConn = nil
  6834.         self.characterChildAddedConn = nil
  6835.         self.onCharacterAddedConn = nil
  6836.         self.characterChildRemovedConn = nil
  6837.         self.renderSteppedConn = nil
  6838.         self.menuOpenedConnection = nil
  6839.    
  6840.         self.running = false
  6841.    
  6842.         self.wasdEnabled = false
  6843.    
  6844.         return self
  6845.     end
  6846.    
  6847.     function ClickToMove:DisconnectEvents()
  6848.         DisconnectEvent(self.tapConn)
  6849.         DisconnectEvent(self.inputBeganConn)
  6850.         DisconnectEvent(self.inputChangedConn)
  6851.         DisconnectEvent(self.inputEndedConn)
  6852.         DisconnectEvent(self.humanoidDiedConn)
  6853.         DisconnectEvent(self.characterChildAddedConn)
  6854.         DisconnectEvent(self.onCharacterAddedConn)
  6855.         DisconnectEvent(self.renderSteppedConn)
  6856.         DisconnectEvent(self.characterChildRemovedConn)
  6857.         DisconnectEvent(self.menuOpenedConnection)
  6858.     end
  6859.    
  6860.     function ClickToMove:OnTouchBegan(input, processed)
  6861.         if self.fingerTouches[input] == nil and not processed then
  6862.             self.numUnsunkTouches = self.numUnsunkTouches + 1
  6863.         end
  6864.         self.fingerTouches[input] = processed
  6865.     end
  6866.    
  6867.     function ClickToMove:OnTouchChanged(input, processed)
  6868.         if self.fingerTouches[input] == nil then
  6869.             self.fingerTouches[input] = processed
  6870.             if not processed then
  6871.                 self.numUnsunkTouches = self.numUnsunkTouches + 1
  6872.             end
  6873.         end
  6874.     end
  6875.    
  6876.     function ClickToMove:OnTouchEnded(input, processed)
  6877.         if self.fingerTouches[input] ~= nil and self.fingerTouches[input] == false then
  6878.             self.numUnsunkTouches = self.numUnsunkTouches - 1
  6879.         end
  6880.         self.fingerTouches[input] = nil
  6881.     end
  6882.    
  6883.    
  6884.     function ClickToMove:OnCharacterAdded(character)
  6885.         self:DisconnectEvents()
  6886.    
  6887.         self.inputBeganConn = UserInputService.InputBegan:Connect(function(input, processed)
  6888.             if input.UserInputType == Enum.UserInputType.Touch then
  6889.                 self:OnTouchBegan(input, processed)
  6890.             end
  6891.    
  6892.             -- Cancel path when you use the keyboard controls if wasd is enabled.
  6893.             if self.wasdEnabled and processed == false and input.UserInputType == Enum.UserInputType.Keyboard
  6894.                 and movementKeys[input.KeyCode] then
  6895.                 CleanupPath()
  6896.                 ClickToMoveDisplay.CancelFailureAnimation()
  6897.             end
  6898.             if input.UserInputType == Enum.UserInputType.MouseButton1 then
  6899.                 self.mouse1DownTime = tick()
  6900.                 self.mouse1DownPos = input.Position
  6901.             end
  6902.             if input.UserInputType == Enum.UserInputType.MouseButton2 then
  6903.                 self.mouse2DownTime = tick()
  6904.                 self.mouse2DownPos = input.Position
  6905.             end
  6906.         end)
  6907.    
  6908.         self.inputChangedConn = UserInputService.InputChanged:Connect(function(input, processed)
  6909.             if input.UserInputType == Enum.UserInputType.Touch then
  6910.                 self:OnTouchChanged(input, processed)
  6911.             end
  6912.         end)
  6913.    
  6914.         self.inputEndedConn = UserInputService.InputEnded:Connect(function(input, processed)
  6915.             if input.UserInputType == Enum.UserInputType.Touch then
  6916.                 self:OnTouchEnded(input, processed)
  6917.             end
  6918.    
  6919.             if input.UserInputType == Enum.UserInputType.MouseButton2 then
  6920.                 self.mouse2UpTime = tick()
  6921.                 local currPos = input.Position
  6922.                 -- We allow click to move during path following or if there is no keyboard movement
  6923.                 local allowed = ExistingPather or self.keyboardMoveVector.Magnitude <= 0
  6924.                 if self.mouse2UpTime - self.mouse2DownTime < 0.25 and (currPos - self.mouse2DownPos).magnitude < 5 and allowed then
  6925.                     local positions = {currPos}
  6926.                     OnTap(positions)
  6927.                 end
  6928.             end
  6929.         end)
  6930.    
  6931.         self.tapConn = UserInputService.TouchTap:Connect(function(touchPositions, processed)
  6932.             if not processed then
  6933.                 OnTap(touchPositions, nil, true)
  6934.             end
  6935.         end)
  6936.    
  6937.         self.menuOpenedConnection = GuiService.MenuOpened:Connect(function()
  6938.             CleanupPath()
  6939.         end)
  6940.    
  6941.         local function OnCharacterChildAdded(child)
  6942.             if UserInputService.TouchEnabled then
  6943.                 if child:IsA('Tool') then
  6944.                     child.ManualActivationOnly = true
  6945.                 end
  6946.             end
  6947.             if child:IsA('Humanoid') then
  6948.                 DisconnectEvent(self.humanoidDiedConn)
  6949.                 self.humanoidDiedConn = child.Died:Connect(function()
  6950.                     if ExistingIndicator then
  6951.                         DebrisService:AddItem(ExistingIndicator.Model, 1)
  6952.                     end
  6953.                 end)
  6954.             end
  6955.         end
  6956.    
  6957.         self.characterChildAddedConn = character.ChildAdded:Connect(function(child)
  6958.             OnCharacterChildAdded(child)
  6959.         end)
  6960.         self.characterChildRemovedConn = character.ChildRemoved:Connect(function(child)
  6961.             if UserInputService.TouchEnabled then
  6962.                 if child:IsA('Tool') then
  6963.                     child.ManualActivationOnly = false
  6964.                 end
  6965.             end
  6966.         end)
  6967.         for _, child in pairs(character:GetChildren()) do
  6968.             OnCharacterChildAdded(child)
  6969.         end
  6970.     end
  6971.    
  6972.     function ClickToMove:Start()
  6973.         self:Enable(true)
  6974.     end
  6975.    
  6976.     function ClickToMove:Stop()
  6977.         self:Enable(false)
  6978.     end
  6979.    
  6980.     function ClickToMove:CleanupPath()
  6981.         CleanupPath()
  6982.     end
  6983.    
  6984.     function ClickToMove:Enable(enable, enableWASD, touchJumpController)
  6985.         if enable then
  6986.             if not self.running then
  6987.                 if Player.Character then -- retro-listen
  6988.                     self:OnCharacterAdded(Player.Character)
  6989.                 end
  6990.                 self.onCharacterAddedConn = Player.CharacterAdded:Connect(function(char)
  6991.                     self:OnCharacterAdded(char)
  6992.                 end)
  6993.                 self.running = true
  6994.             end
  6995.             self.touchJumpController = touchJumpController
  6996.             if self.touchJumpController then
  6997.                 self.touchJumpController:Enable(self.jumpEnabled)
  6998.             end
  6999.         else
  7000.             if self.running then
  7001.                 self:DisconnectEvents()
  7002.                 CleanupPath()
  7003.                 -- Restore tool activation on shutdown
  7004.                 if UserInputService.TouchEnabled then
  7005.                     local character = Player.Character
  7006.                     if character then
  7007.                         for _, child in pairs(character:GetChildren()) do
  7008.                             if child:IsA('Tool') then
  7009.                                 child.ManualActivationOnly = false
  7010.                             end
  7011.                         end
  7012.                     end
  7013.                 end
  7014.                 self.running = false
  7015.             end
  7016.             if self.touchJumpController and not self.jumpEnabled then
  7017.                 self.touchJumpController:Enable(true)
  7018.             end
  7019.             self.touchJumpController = nil
  7020.         end
  7021.    
  7022.         -- Extension for initializing Keyboard input as this class now derives from Keyboard
  7023.         if UserInputService.KeyboardEnabled and enable ~= self.enabled then
  7024.    
  7025.             self.forwardValue  = 0
  7026.             self.backwardValue = 0
  7027.             self.leftValue = 0
  7028.             self.rightValue = 0
  7029.    
  7030.             self.moveVector = ZERO_VECTOR3
  7031.    
  7032.             if enable then
  7033.                 self:BindContextActions()
  7034.                 self:ConnectFocusEventListeners()
  7035.             else
  7036.                 self:UnbindContextActions()
  7037.                 self:DisconnectFocusEventListeners()
  7038.             end
  7039.         end
  7040.    
  7041.         self.wasdEnabled = enable and enableWASD or false
  7042.         self.enabled = enable
  7043.     end
  7044.    
  7045.     function ClickToMove:OnRenderStepped(dt)
  7046.         -- Reset jump
  7047.         self.isJumping = false
  7048.    
  7049.         -- Handle Pather
  7050.         if ExistingPather then
  7051.             -- Let the Pather update
  7052.             ExistingPather:OnRenderStepped(dt)
  7053.    
  7054.             -- If we still have a Pather, set the resulting actions
  7055.             if ExistingPather then
  7056.                 -- Setup move (NOT relative to camera)
  7057.                 self.moveVector = ExistingPather.NextActionMoveDirection
  7058.                 self.moveVectorIsCameraRelative = false
  7059.    
  7060.                 -- Setup jump (but do NOT prevent the base Keayboard class from requesting jumps as well)
  7061.                 if ExistingPather.NextActionJump then
  7062.                     self.isJumping = true
  7063.                 end
  7064.             else
  7065.                 self.moveVector = self.keyboardMoveVector
  7066.                 self.moveVectorIsCameraRelative = true
  7067.             end
  7068.         else
  7069.             self.moveVector = self.keyboardMoveVector
  7070.             self.moveVectorIsCameraRelative = true
  7071.         end
  7072.    
  7073.         -- Handle Keyboard's jump
  7074.         if self.jumpRequested then
  7075.             self.isJumping = true
  7076.         end
  7077.     end
  7078.    
  7079.     -- Overrides Keyboard:UpdateMovement(inputState) to conditionally consider self.wasdEnabled and let OnRenderStepped handle the movement
  7080.     function ClickToMove:UpdateMovement(inputState)
  7081.         if inputState == Enum.UserInputState.Cancel then
  7082.             self.keyboardMoveVector = ZERO_VECTOR3
  7083.         elseif self.wasdEnabled then
  7084.             self.keyboardMoveVector = Vector3.new(self.leftValue + self.rightValue, 0, self.forwardValue + self.backwardValue)
  7085.         end
  7086.     end
  7087.    
  7088.     -- Overrides Keyboard:UpdateJump() because jump is handled in OnRenderStepped
  7089.     function ClickToMove:UpdateJump()
  7090.         -- Nothing to do (handled in OnRenderStepped)
  7091.     end
  7092.    
  7093.     --Public developer facing functions
  7094.     function ClickToMove:SetShowPath(value)
  7095.         ShowPath = value
  7096.     end
  7097.    
  7098.     function ClickToMove:GetShowPath()
  7099.         return ShowPath
  7100.     end
  7101.    
  7102.     function ClickToMove:SetWaypointTexture(texture)
  7103.         ClickToMoveDisplay.SetWaypointTexture(texture)
  7104.     end
  7105.    
  7106.     function ClickToMove:GetWaypointTexture()
  7107.         return ClickToMoveDisplay.GetWaypointTexture()
  7108.     end
  7109.    
  7110.     function ClickToMove:SetWaypointRadius(radius)
  7111.         ClickToMoveDisplay.SetWaypointRadius(radius)
  7112.     end
  7113.    
  7114.     function ClickToMove:GetWaypointRadius()
  7115.         return ClickToMoveDisplay.GetWaypointRadius()
  7116.     end
  7117.    
  7118.     function ClickToMove:SetEndWaypointTexture(texture)
  7119.         ClickToMoveDisplay.SetEndWaypointTexture(texture)
  7120.     end
  7121.    
  7122.     function ClickToMove:GetEndWaypointTexture()
  7123.         return ClickToMoveDisplay.GetEndWaypointTexture()
  7124.     end
  7125.    
  7126.     function ClickToMove:SetWaypointsAlwaysOnTop(alwaysOnTop)
  7127.         ClickToMoveDisplay.SetWaypointsAlwaysOnTop(alwaysOnTop)
  7128.     end
  7129.    
  7130.     function ClickToMove:GetWaypointsAlwaysOnTop()
  7131.         return ClickToMoveDisplay.GetWaypointsAlwaysOnTop()
  7132.     end
  7133.    
  7134.     function ClickToMove:SetFailureAnimationEnabled(enabled)
  7135.         PlayFailureAnimation = enabled
  7136.     end
  7137.    
  7138.     function ClickToMove:GetFailureAnimationEnabled()
  7139.         return PlayFailureAnimation
  7140.     end
  7141.    
  7142.     function ClickToMove:SetIgnoredPartsTag(tag)
  7143.         UpdateIgnoreTag(tag)
  7144.     end
  7145.    
  7146.     function ClickToMove:GetIgnoredPartsTag()
  7147.         return CurrentIgnoreTag
  7148.     end
  7149.    
  7150.     function ClickToMove:SetUseDirectPath(directPath)
  7151.         UseDirectPath = directPath
  7152.     end
  7153.    
  7154.     function ClickToMove:GetUseDirectPath()
  7155.         return UseDirectPath
  7156.     end
  7157.    
  7158.     function ClickToMove:SetAgentSizeIncreaseFactor(increaseFactorPercent)
  7159.         AgentSizeIncreaseFactor = 1.0 + (increaseFactorPercent / 100.0)
  7160.     end
  7161.    
  7162.     function ClickToMove:GetAgentSizeIncreaseFactor()
  7163.         return (AgentSizeIncreaseFactor - 1.0) * 100.0
  7164.     end
  7165.    
  7166.     function ClickToMove:SetUnreachableWaypointTimeout(timeoutInSec)
  7167.         UnreachableWaypointTimeout = timeoutInSec
  7168.     end
  7169.    
  7170.     function ClickToMove:GetUnreachableWaypointTimeout()
  7171.         return UnreachableWaypointTimeout
  7172.     end
  7173.    
  7174.     function ClickToMove:SetUserJumpEnabled(jumpEnabled)
  7175.         self.jumpEnabled = jumpEnabled
  7176.         if self.touchJumpController then
  7177.             self.touchJumpController:Enable(jumpEnabled)
  7178.         end
  7179.     end
  7180.    
  7181.     function ClickToMove:GetUserJumpEnabled()
  7182.         return self.jumpEnabled
  7183.     end
  7184.    
  7185.     function ClickToMove:MoveTo(position, showPath, useDirectPath)
  7186.         local character = Player.Character
  7187.         if character == nil then
  7188.             return false
  7189.         end
  7190.         local thisPather = Pather(position, Vector3.new(0, 1, 0), useDirectPath)
  7191.         if thisPather and thisPather:IsValidPath() then
  7192.             HandleMoveTo(thisPather, position, nil, character, showPath)
  7193.             return true
  7194.         end
  7195.         return false
  7196.     end
  7197.    
  7198.     return ClickToMove
  7199. end
  7200.  
  7201. function _TouchThumbstick()
  7202.     local Players = game:GetService("Players")
  7203.     local GuiService = game:GetService("GuiService")
  7204.     local UserInputService = game:GetService("UserInputService")
  7205.     --[[ Constants ]]--
  7206.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  7207.     local TOUCH_CONTROL_SHEET = "rbxasset://textures/ui/TouchControlsSheet.png"
  7208.     --[[ The Module ]]--
  7209.     local BaseCharacterController = _BaseCharacterController()
  7210.     local TouchThumbstick = setmetatable({}, BaseCharacterController)
  7211.     TouchThumbstick.__index = TouchThumbstick
  7212.     function TouchThumbstick.new()
  7213.         local self = setmetatable(BaseCharacterController.new(), TouchThumbstick)
  7214.        
  7215.         self.isFollowStick = false
  7216.        
  7217.         self.thumbstickFrame = nil
  7218.         self.moveTouchObject = nil
  7219.         self.onTouchMovedConn = nil
  7220.         self.onTouchEndedConn = nil
  7221.         self.screenPos = nil
  7222.         self.stickImage = nil
  7223.         self.thumbstickSize = nil -- Float
  7224.        
  7225.         return self
  7226.     end
  7227.     function TouchThumbstick:Enable(enable, uiParentFrame)
  7228.         if enable == nil then return false end          -- If nil, return false (invalid argument)
  7229.         enable = enable and true or false               -- Force anything non-nil to boolean before comparison
  7230.         if self.enabled == enable then return true end  -- If no state change, return true indicating already in requested state
  7231.        
  7232.         self.moveVector = ZERO_VECTOR3
  7233.         self.isJumping = false
  7234.        
  7235.         if enable then
  7236.             -- Enable
  7237.             if not self.thumbstickFrame then
  7238.                 self:Create(uiParentFrame)
  7239.             end
  7240.             self.thumbstickFrame.Visible = true
  7241.         else
  7242.             -- Disable
  7243.             self.thumbstickFrame.Visible = false
  7244.             self:OnInputEnded()
  7245.         end
  7246.         self.enabled = enable
  7247.     end
  7248.     function TouchThumbstick:OnInputEnded()
  7249.         self.thumbstickFrame.Position = self.screenPos
  7250.         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)
  7251.        
  7252.         self.moveVector = ZERO_VECTOR3
  7253.         self.isJumping = false
  7254.         self.thumbstickFrame.Position = self.screenPos
  7255.         self.moveTouchObject = nil
  7256.     end
  7257.     function TouchThumbstick:Create(parentFrame)
  7258.        
  7259.         if self.thumbstickFrame then
  7260.             self.thumbstickFrame:Destroy()
  7261.             self.thumbstickFrame = nil
  7262.             if self.onTouchMovedConn then
  7263.                 self.onTouchMovedConn:Disconnect()
  7264.                 self.onTouchMovedConn = nil
  7265.             end
  7266.             if self.onTouchEndedConn then
  7267.                 self.onTouchEndedConn:Disconnect()
  7268.                 self.onTouchEndedConn = nil
  7269.             end
  7270.         end
  7271.        
  7272.         local minAxis = math.min(parentFrame.AbsoluteSize.x, parentFrame.AbsoluteSize.y)
  7273.         local isSmallScreen = minAxis <= 500
  7274.         self.thumbstickSize = isSmallScreen and 70 or 120
  7275.         self.screenPos = isSmallScreen and UDim2.new(0, (self.thumbstickSize/2) - 10, 1, -self.thumbstickSize - 20) or
  7276.             UDim2.new(0, self.thumbstickSize/2, 1, -self.thumbstickSize * 1.75)
  7277.            
  7278.         self.thumbstickFrame = Instance.new("Frame")
  7279.         self.thumbstickFrame.Name = "ThumbstickFrame"
  7280.         self.thumbstickFrame.Active = true
  7281.         self.thumbstickFrame.Visible = false
  7282.         self.thumbstickFrame.Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize)
  7283.         self.thumbstickFrame.Position = self.screenPos
  7284.         self.thumbstickFrame.BackgroundTransparency = 1
  7285.        
  7286.         local outerImage = Instance.new("ImageLabel")
  7287.         outerImage.Name = "OuterImage"
  7288.         outerImage.Image = TOUCH_CONTROL_SHEET
  7289.         outerImage.ImageRectOffset = Vector2.new()
  7290.         outerImage.ImageRectSize = Vector2.new(220, 220)
  7291.         outerImage.BackgroundTransparency = 1
  7292.         outerImage.Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize)
  7293.         outerImage.Position = UDim2.new(0, 0, 0, 0)
  7294.         outerImage.Parent = self.thumbstickFrame
  7295.        
  7296.         self.stickImage = Instance.new("ImageLabel")
  7297.         self.stickImage.Name = "StickImage"
  7298.         self.stickImage.Image = TOUCH_CONTROL_SHEET
  7299.         self.stickImage.ImageRectOffset = Vector2.new(220, 0)
  7300.         self.stickImage.ImageRectSize = Vector2.new(111, 111)
  7301.         self.stickImage.BackgroundTransparency = 1
  7302.         self.stickImage.Size = UDim2.new(0, self.thumbstickSize/2, 0, self.thumbstickSize/2)
  7303.         self.stickImage.Position = UDim2.new(0, self.thumbstickSize/2 - self.thumbstickSize/4, 0, self.thumbstickSize/2 - self.thumbstickSize/4)
  7304.         self.stickImage.ZIndex = 2
  7305.         self.stickImage.Parent = self.thumbstickFrame
  7306.        
  7307.         local centerPosition = nil
  7308.         local deadZone = 0.05
  7309.        
  7310.         local function DoMove(direction)
  7311.            
  7312.             local currentMoveVector = direction / (self.thumbstickSize/2)
  7313.            
  7314.             -- Scaled Radial Dead Zone
  7315.             local inputAxisMagnitude = currentMoveVector.magnitude
  7316.             if inputAxisMagnitude < deadZone then
  7317.                 currentMoveVector = Vector3.new()
  7318.             else
  7319.                 currentMoveVector = currentMoveVector.unit * ((inputAxisMagnitude - deadZone) / (1 - deadZone))
  7320.                 -- NOTE: Making currentMoveVector a unit vector will cause the player to instantly go max speed
  7321.                 -- must check for zero length vector is using unit
  7322.                 currentMoveVector = Vector3.new(currentMoveVector.x, 0, currentMoveVector.y)
  7323.             end
  7324.            
  7325.             self.moveVector = currentMoveVector
  7326.         end
  7327.        
  7328.         local function MoveStick(pos)
  7329.             local relativePosition = Vector2.new(pos.x - centerPosition.x, pos.y - centerPosition.y)
  7330.             local length = relativePosition.magnitude
  7331.             local maxLength = self.thumbstickFrame.AbsoluteSize.x/2
  7332.             if self.isFollowStick and length > maxLength then
  7333.                 local offset = relativePosition.unit * maxLength
  7334.                 self.thumbstickFrame.Position = UDim2.new(
  7335.                     0, pos.x - self.thumbstickFrame.AbsoluteSize.x/2 - offset.x,
  7336.                     0, pos.y - self.thumbstickFrame.AbsoluteSize.y/2 - offset.y)
  7337.             else
  7338.                 length = math.min(length, maxLength)
  7339.                 relativePosition = relativePosition.unit * length
  7340.             end
  7341.             self.stickImage.Position = UDim2.new(0, relativePosition.x + self.stickImage.AbsoluteSize.x/2, 0, relativePosition.y + self.stickImage.AbsoluteSize.y/2)
  7342.         end
  7343.        
  7344.         -- input connections
  7345.         self.thumbstickFrame.InputBegan:Connect(function(inputObject)
  7346.             --A touch that starts elsewhere on the screen will be sent to a frame's InputBegan event
  7347.             --if it moves over the frame. So we check that this is actually a new touch (inputObject.UserInputState ~= Enum.UserInputState.Begin)
  7348.             if self.moveTouchObject or inputObject.UserInputType ~= Enum.UserInputType.Touch
  7349.                 or inputObject.UserInputState ~= Enum.UserInputState.Begin then
  7350.                 return
  7351.             end
  7352.            
  7353.             self.moveTouchObject = inputObject
  7354.             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)
  7355.             centerPosition = Vector2.new(self.thumbstickFrame.AbsolutePosition.x + self.thumbstickFrame.AbsoluteSize.x/2,
  7356.                 self.thumbstickFrame.AbsolutePosition.y + self.thumbstickFrame.AbsoluteSize.y/2)
  7357.             local direction = Vector2.new(inputObject.Position.x - centerPosition.x, inputObject.Position.y - centerPosition.y)
  7358.         end)
  7359.        
  7360.         self.onTouchMovedConn = UserInputService.TouchMoved:Connect(function(inputObject, isProcessed)
  7361.             if inputObject == self.moveTouchObject then
  7362.                 centerPosition = Vector2.new(self.thumbstickFrame.AbsolutePosition.x + self.thumbstickFrame.AbsoluteSize.x/2,
  7363.                     self.thumbstickFrame.AbsolutePosition.y + self.thumbstickFrame.AbsoluteSize.y/2)
  7364.                 local direction = Vector2.new(inputObject.Position.x - centerPosition.x, inputObject.Position.y - centerPosition.y)
  7365.                 DoMove(direction)
  7366.                 MoveStick(inputObject.Position)
  7367.             end
  7368.         end)
  7369.        
  7370.         self.onTouchEndedConn = UserInputService.TouchEnded:Connect(function(inputObject, isProcessed)
  7371.             if inputObject == self.moveTouchObject then
  7372.                 self:OnInputEnded()
  7373.             end
  7374.         end)
  7375.        
  7376.         GuiService.MenuOpened:Connect(function()
  7377.             if self.moveTouchObject then
  7378.                 self:OnInputEnded()
  7379.             end
  7380.         end)   
  7381.        
  7382.         self.thumbstickFrame.Parent = parentFrame
  7383.     end
  7384.     return TouchThumbstick
  7385. end
  7386.  
  7387. function _DynamicThumbstick()
  7388.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  7389.     local TOUCH_CONTROLS_SHEET = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
  7390.    
  7391.     local DYNAMIC_THUMBSTICK_ACTION_NAME = "DynamicThumbstickAction"
  7392.     local DYNAMIC_THUMBSTICK_ACTION_PRIORITY = Enum.ContextActionPriority.High.Value
  7393.    
  7394.     local MIDDLE_TRANSPARENCIES = {
  7395.         1 - 0.89,
  7396.         1 - 0.70,
  7397.         1 - 0.60,
  7398.         1 - 0.50,
  7399.         1 - 0.40,
  7400.         1 - 0.30,
  7401.         1 - 0.25
  7402.     }
  7403.     local NUM_MIDDLE_IMAGES = #MIDDLE_TRANSPARENCIES
  7404.    
  7405.     local FADE_IN_OUT_BACKGROUND = true
  7406.     local FADE_IN_OUT_MAX_ALPHA = 0.35
  7407.    
  7408.     local FADE_IN_OUT_HALF_DURATION_DEFAULT = 0.3
  7409.     local FADE_IN_OUT_BALANCE_DEFAULT = 0.5
  7410.     local ThumbstickFadeTweenInfo = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
  7411.    
  7412.     local Players = game:GetService("Players")
  7413.     local GuiService = game:GetService("GuiService")
  7414.     local UserInputService = game:GetService("UserInputService")
  7415.     local ContextActionService = game:GetService("ContextActionService")
  7416.     local RunService = game:GetService("RunService")
  7417.     local TweenService = game:GetService("TweenService")
  7418.    
  7419.     local LocalPlayer = Players.LocalPlayer
  7420.     if not LocalPlayer then
  7421.         Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  7422.         LocalPlayer = Players.LocalPlayer
  7423.     end
  7424.    
  7425.     --[[ The Module ]]--
  7426.     local BaseCharacterController = _BaseCharacterController()
  7427.     local DynamicThumbstick = setmetatable({}, BaseCharacterController)
  7428.     DynamicThumbstick.__index = DynamicThumbstick
  7429.    
  7430.     function DynamicThumbstick.new()
  7431.         local self = setmetatable(BaseCharacterController.new(), DynamicThumbstick)
  7432.    
  7433.         self.moveTouchObject = nil
  7434.         self.moveTouchLockedIn = false
  7435.         self.moveTouchFirstChanged = false
  7436.         self.moveTouchStartPosition = nil
  7437.    
  7438.         self.startImage = nil
  7439.         self.endImage = nil
  7440.         self.middleImages = {}
  7441.    
  7442.         self.startImageFadeTween = nil
  7443.         self.endImageFadeTween = nil
  7444.         self.middleImageFadeTweens = {}
  7445.    
  7446.         self.isFirstTouch = true
  7447.    
  7448.         self.thumbstickFrame = nil
  7449.    
  7450.         self.onRenderSteppedConn = nil
  7451.    
  7452.         self.fadeInAndOutBalance = FADE_IN_OUT_BALANCE_DEFAULT
  7453.         self.fadeInAndOutHalfDuration = FADE_IN_OUT_HALF_DURATION_DEFAULT
  7454.         self.hasFadedBackgroundInPortrait = false
  7455.         self.hasFadedBackgroundInLandscape = false
  7456.    
  7457.         self.tweenInAlphaStart = nil
  7458.         self.tweenOutAlphaStart = nil
  7459.    
  7460.         return self
  7461.     end
  7462.    
  7463.     -- Note: Overrides base class GetIsJumping with get-and-clear behavior to do a single jump
  7464.     -- rather than sustained jumping. This is only to preserve the current behavior through the refactor.
  7465.     function DynamicThumbstick:GetIsJumping()
  7466.         local wasJumping = self.isJumping
  7467.         self.isJumping = false
  7468.         return wasJumping
  7469.     end
  7470.    
  7471.     function DynamicThumbstick:Enable(enable, uiParentFrame)
  7472.         if enable == nil then return false end          -- If nil, return false (invalid argument)
  7473.         enable = enable and true or false               -- Force anything non-nil to boolean before comparison
  7474.         if self.enabled == enable then return true end  -- If no state change, return true indicating already in requested state
  7475.    
  7476.         if enable then
  7477.             -- Enable
  7478.             if not self.thumbstickFrame then
  7479.                 self:Create(uiParentFrame)
  7480.             end
  7481.    
  7482.             self:BindContextActions()
  7483.         else
  7484.             ContextActionService:UnbindAction(DYNAMIC_THUMBSTICK_ACTION_NAME)
  7485.             -- Disable
  7486.             self:OnInputEnded() -- Cleanup
  7487.         end
  7488.    
  7489.         self.enabled = enable
  7490.         self.thumbstickFrame.Visible = enable
  7491.     end
  7492.    
  7493.     -- Was called OnMoveTouchEnded in previous version
  7494.     function DynamicThumbstick:OnInputEnded()
  7495.         self.moveTouchObject = nil
  7496.         self.moveVector = ZERO_VECTOR3
  7497.         self:FadeThumbstick(false)
  7498.     end
  7499.    
  7500.     function DynamicThumbstick:FadeThumbstick(visible)
  7501.         if not visible and self.moveTouchObject then
  7502.             return
  7503.         end
  7504.         if self.isFirstTouch then return end
  7505.    
  7506.         if self.startImageFadeTween then
  7507.             self.startImageFadeTween:Cancel()
  7508.         end
  7509.         if self.endImageFadeTween then
  7510.             self.endImageFadeTween:Cancel()
  7511.         end
  7512.         for i = 1, #self.middleImages do
  7513.             if self.middleImageFadeTweens[i] then
  7514.                 self.middleImageFadeTweens[i]:Cancel()
  7515.             end
  7516.         end
  7517.    
  7518.         if visible then
  7519.             self.startImageFadeTween = TweenService:Create(self.startImage, ThumbstickFadeTweenInfo, { ImageTransparency = 0 })
  7520.             self.startImageFadeTween:Play()
  7521.    
  7522.             self.endImageFadeTween = TweenService:Create(self.endImage, ThumbstickFadeTweenInfo, { ImageTransparency = 0.2 })
  7523.             self.endImageFadeTween:Play()
  7524.    
  7525.             for i = 1, #self.middleImages do
  7526.                 self.middleImageFadeTweens[i] = TweenService:Create(self.middleImages[i], ThumbstickFadeTweenInfo, { ImageTransparency = MIDDLE_TRANSPARENCIES[i] })
  7527.                 self.middleImageFadeTweens[i]:Play()
  7528.             end
  7529.         else
  7530.             self.startImageFadeTween = TweenService:Create(self.startImage, ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
  7531.             self.startImageFadeTween:Play()
  7532.    
  7533.             self.endImageFadeTween = TweenService:Create(self.endImage, ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
  7534.             self.endImageFadeTween:Play()
  7535.    
  7536.             for i = 1, #self.middleImages do
  7537.                 self.middleImageFadeTweens[i] = TweenService:Create(self.middleImages[i], ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
  7538.                 self.middleImageFadeTweens[i]:Play()
  7539.             end
  7540.         end
  7541.     end
  7542.    
  7543.     function DynamicThumbstick:FadeThumbstickFrame(fadeDuration, fadeRatio)
  7544.         self.fadeInAndOutHalfDuration = fadeDuration * 0.5
  7545.         self.fadeInAndOutBalance = fadeRatio
  7546.         self.tweenInAlphaStart = tick()
  7547.     end
  7548.    
  7549.     function DynamicThumbstick:InputInFrame(inputObject)
  7550.         local frameCornerTopLeft = self.thumbstickFrame.AbsolutePosition
  7551.         local frameCornerBottomRight = frameCornerTopLeft + self.thumbstickFrame.AbsoluteSize
  7552.         local inputPosition = inputObject.Position
  7553.         if inputPosition.X >= frameCornerTopLeft.X and inputPosition.Y >= frameCornerTopLeft.Y then
  7554.             if inputPosition.X <= frameCornerBottomRight.X and inputPosition.Y <= frameCornerBottomRight.Y then
  7555.                 return true
  7556.             end
  7557.         end
  7558.         return false
  7559.     end
  7560.    
  7561.     function DynamicThumbstick:DoFadeInBackground()
  7562.         local playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
  7563.         local hasFadedBackgroundInOrientation = false
  7564.    
  7565.         -- only fade in/out the background once per orientation
  7566.         if playerGui then
  7567.             if playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeLeft or
  7568.                 playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeRight then
  7569.                     hasFadedBackgroundInOrientation = self.hasFadedBackgroundInLandscape
  7570.                     self.hasFadedBackgroundInLandscape = true
  7571.             elseif playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.Portrait then
  7572.                     hasFadedBackgroundInOrientation = self.hasFadedBackgroundInPortrait
  7573.                     self.hasFadedBackgroundInPortrait = true
  7574.             end
  7575.         end
  7576.    
  7577.         if not hasFadedBackgroundInOrientation then
  7578.             self.fadeInAndOutHalfDuration = FADE_IN_OUT_HALF_DURATION_DEFAULT
  7579.             self.fadeInAndOutBalance = FADE_IN_OUT_BALANCE_DEFAULT
  7580.             self.tweenInAlphaStart = tick()
  7581.         end
  7582.     end
  7583.    
  7584.     function DynamicThumbstick:DoMove(direction)
  7585.         local currentMoveVector = direction
  7586.    
  7587.         -- Scaled Radial Dead Zone
  7588.         local inputAxisMagnitude = currentMoveVector.magnitude
  7589.         if inputAxisMagnitude < self.radiusOfDeadZone then
  7590.             currentMoveVector = ZERO_VECTOR3
  7591.         else
  7592.             currentMoveVector = currentMoveVector.unit*(
  7593.                 1 - math.max(0, (self.radiusOfMaxSpeed - currentMoveVector.magnitude)/self.radiusOfMaxSpeed)
  7594.             )
  7595.             currentMoveVector = Vector3.new(currentMoveVector.x, 0, currentMoveVector.y)
  7596.         end
  7597.    
  7598.         self.moveVector = currentMoveVector
  7599.     end
  7600.    
  7601.    
  7602.     function DynamicThumbstick:LayoutMiddleImages(startPos, endPos)
  7603.         local startDist = (self.thumbstickSize / 2) + self.middleSize
  7604.         local vector = endPos - startPos
  7605.         local distAvailable = vector.magnitude - (self.thumbstickRingSize / 2) - self.middleSize
  7606.         local direction = vector.unit
  7607.    
  7608.         local distNeeded = self.middleSpacing * NUM_MIDDLE_IMAGES
  7609.         local spacing = self.middleSpacing
  7610.    
  7611.         if distNeeded < distAvailable then
  7612.             spacing = distAvailable / NUM_MIDDLE_IMAGES
  7613.         end
  7614.    
  7615.         for i = 1, NUM_MIDDLE_IMAGES do
  7616.             local image = self.middleImages[i]
  7617.             local distWithout = startDist + (spacing * (i - 2))
  7618.             local currentDist = startDist + (spacing * (i - 1))
  7619.    
  7620.             if distWithout < distAvailable then
  7621.                 local pos = endPos - direction * currentDist
  7622.                 local exposedFraction = math.clamp(1 - ((currentDist - distAvailable) / spacing), 0, 1)
  7623.    
  7624.                 image.Visible = true
  7625.                 image.Position = UDim2.new(0, pos.X, 0, pos.Y)
  7626.                 image.Size = UDim2.new(0, self.middleSize * exposedFraction, 0, self.middleSize * exposedFraction)
  7627.             else
  7628.                 image.Visible = false
  7629.             end
  7630.         end
  7631.     end
  7632.    
  7633.     function DynamicThumbstick:MoveStick(pos)
  7634.         local vector2StartPosition = Vector2.new(self.moveTouchStartPosition.X, self.moveTouchStartPosition.Y)
  7635.         local startPos = vector2StartPosition - self.thumbstickFrame.AbsolutePosition
  7636.         local endPos = Vector2.new(pos.X, pos.Y) - self.thumbstickFrame.AbsolutePosition
  7637.         self.endImage.Position = UDim2.new(0, endPos.X, 0, endPos.Y)
  7638.         self:LayoutMiddleImages(startPos, endPos)
  7639.     end
  7640.    
  7641.     function DynamicThumbstick:BindContextActions()
  7642.         local function inputBegan(inputObject)
  7643.             if self.moveTouchObject then
  7644.                 return Enum.ContextActionResult.Pass
  7645.             end
  7646.    
  7647.             if not self:InputInFrame(inputObject) then
  7648.                 return Enum.ContextActionResult.Pass
  7649.             end
  7650.    
  7651.             if self.isFirstTouch then
  7652.                 self.isFirstTouch = false
  7653.                 local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out,0,false,0)
  7654.                 TweenService:Create(self.startImage, tweenInfo, {Size = UDim2.new(0, 0, 0, 0)}):Play()
  7655.                 TweenService:Create(
  7656.                     self.endImage,
  7657.                     tweenInfo,
  7658.                     {Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize), ImageColor3 = Color3.new(0,0,0)}
  7659.                 ):Play()
  7660.             end
  7661.    
  7662.             self.moveTouchLockedIn = false
  7663.             self.moveTouchObject = inputObject
  7664.             self.moveTouchStartPosition = inputObject.Position
  7665.             self.moveTouchFirstChanged = true
  7666.    
  7667.             if FADE_IN_OUT_BACKGROUND then
  7668.                 self:DoFadeInBackground()
  7669.             end
  7670.    
  7671.             return Enum.ContextActionResult.Pass
  7672.         end
  7673.    
  7674.         local function inputChanged(inputObject)
  7675.             if inputObject == self.moveTouchObject then
  7676.                 if self.moveTouchFirstChanged then
  7677.                     self.moveTouchFirstChanged = false
  7678.    
  7679.                     local startPosVec2 = Vector2.new(
  7680.                         inputObject.Position.X - self.thumbstickFrame.AbsolutePosition.X,
  7681.                         inputObject.Position.Y - self.thumbstickFrame.AbsolutePosition.Y
  7682.                     )
  7683.                     self.startImage.Visible = true
  7684.                     self.startImage.Position = UDim2.new(0, startPosVec2.X, 0, startPosVec2.Y)
  7685.                     self.endImage.Visible = true
  7686.                     self.endImage.Position = self.startImage.Position
  7687.    
  7688.                     self:FadeThumbstick(true)
  7689.                     self:MoveStick(inputObject.Position)
  7690.                 end
  7691.    
  7692.                 self.moveTouchLockedIn = true
  7693.    
  7694.                 local direction = Vector2.new(
  7695.                     inputObject.Position.x - self.moveTouchStartPosition.x,
  7696.                     inputObject.Position.y - self.moveTouchStartPosition.y
  7697.                 )
  7698.                 if math.abs(direction.x) > 0 or math.abs(direction.y) > 0 then
  7699.                     self:DoMove(direction)
  7700.                     self:MoveStick(inputObject.Position)
  7701.                 end
  7702.                 return Enum.ContextActionResult.Sink
  7703.             end
  7704.             return Enum.ContextActionResult.Pass
  7705.         end
  7706.    
  7707.         local function inputEnded(inputObject)
  7708.             if inputObject == self.moveTouchObject then
  7709.                 self:OnInputEnded()
  7710.                 if self.moveTouchLockedIn then
  7711.                     return Enum.ContextActionResult.Sink
  7712.                 end
  7713.             end
  7714.             return Enum.ContextActionResult.Pass
  7715.         end
  7716.    
  7717.         local function handleInput(actionName, inputState, inputObject)
  7718.             if inputState == Enum.UserInputState.Begin then
  7719.                 return inputBegan(inputObject)
  7720.             elseif inputState == Enum.UserInputState.Change then
  7721.                 return inputChanged(inputObject)
  7722.             elseif inputState == Enum.UserInputState.End then
  7723.                 return inputEnded(inputObject)
  7724.             elseif inputState == Enum.UserInputState.Cancel then
  7725.                 self:OnInputEnded()
  7726.             end
  7727.         end
  7728.    
  7729.         ContextActionService:BindActionAtPriority(
  7730.             DYNAMIC_THUMBSTICK_ACTION_NAME,
  7731.             handleInput,
  7732.             false,
  7733.             DYNAMIC_THUMBSTICK_ACTION_PRIORITY,
  7734.             Enum.UserInputType.Touch)
  7735.     end
  7736.    
  7737.     function DynamicThumbstick:Create(parentFrame)
  7738.         if self.thumbstickFrame then
  7739.             self.thumbstickFrame:Destroy()
  7740.             self.thumbstickFrame = nil
  7741.             if self.onRenderSteppedConn then
  7742.                 self.onRenderSteppedConn:Disconnect()
  7743.                 self.onRenderSteppedConn = nil
  7744.             end
  7745.         end
  7746.    
  7747.         self.thumbstickSize = 45
  7748.         self.thumbstickRingSize = 20
  7749.         self.middleSize = 10
  7750.         self.middleSpacing = self.middleSize + 4
  7751.         self.radiusOfDeadZone = 2
  7752.         self.radiusOfMaxSpeed = 20
  7753.    
  7754.         local screenSize = parentFrame.AbsoluteSize
  7755.         local isBigScreen = math.min(screenSize.x, screenSize.y) > 500
  7756.         if isBigScreen then
  7757.             self.thumbstickSize = self.thumbstickSize * 2
  7758.             self.thumbstickRingSize = self.thumbstickRingSize * 2
  7759.             self.middleSize = self.middleSize * 2
  7760.             self.middleSpacing = self.middleSpacing * 2
  7761.             self.radiusOfDeadZone = self.radiusOfDeadZone * 2
  7762.             self.radiusOfMaxSpeed = self.radiusOfMaxSpeed * 2
  7763.         end
  7764.    
  7765.         local function layoutThumbstickFrame(portraitMode)
  7766.             if portraitMode then
  7767.                 self.thumbstickFrame.Size = UDim2.new(1, 0, 0.4, 0)
  7768.                 self.thumbstickFrame.Position = UDim2.new(0, 0, 0.6, 0)
  7769.             else
  7770.                 self.thumbstickFrame.Size = UDim2.new(0.4, 0, 2/3, 0)
  7771.                 self.thumbstickFrame.Position = UDim2.new(0, 0, 1/3, 0)
  7772.             end
  7773.         end
  7774.    
  7775.         self.thumbstickFrame = Instance.new("Frame")
  7776.         self.thumbstickFrame.BorderSizePixel = 0
  7777.         self.thumbstickFrame.Name = "DynamicThumbstickFrame"
  7778.         self.thumbstickFrame.Visible = false
  7779.         self.thumbstickFrame.BackgroundTransparency = 1.0
  7780.         self.thumbstickFrame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
  7781.         self.thumbstickFrame.Active = false
  7782.         layoutThumbstickFrame(false)
  7783.    
  7784.         self.startImage = Instance.new("ImageLabel")
  7785.         self.startImage.Name = "ThumbstickStart"
  7786.         self.startImage.Visible = true
  7787.         self.startImage.BackgroundTransparency = 1
  7788.         self.startImage.Image = TOUCH_CONTROLS_SHEET
  7789.         self.startImage.ImageRectOffset = Vector2.new(1,1)
  7790.         self.startImage.ImageRectSize = Vector2.new(144, 144)
  7791.         self.startImage.ImageColor3 = Color3.new(0, 0, 0)
  7792.         self.startImage.AnchorPoint = Vector2.new(0.5, 0.5)
  7793.         self.startImage.Position = UDim2.new(0, self.thumbstickRingSize * 3.3, 1, -self.thumbstickRingSize  * 2.8)
  7794.         self.startImage.Size = UDim2.new(0, self.thumbstickRingSize  * 3.7, 0, self.thumbstickRingSize  * 3.7)
  7795.         self.startImage.ZIndex = 10
  7796.         self.startImage.Parent = self.thumbstickFrame
  7797.    
  7798.         self.endImage = Instance.new("ImageLabel")
  7799.         self.endImage.Name = "ThumbstickEnd"
  7800.         self.endImage.Visible = true
  7801.         self.endImage.BackgroundTransparency = 1
  7802.         self.endImage.Image = TOUCH_CONTROLS_SHEET
  7803.         self.endImage.ImageRectOffset = Vector2.new(1,1)
  7804.         self.endImage.ImageRectSize =  Vector2.new(144, 144)
  7805.         self.endImage.AnchorPoint = Vector2.new(0.5, 0.5)
  7806.         self.endImage.Position = self.startImage.Position
  7807.         self.endImage.Size = UDim2.new(0, self.thumbstickSize * 0.8, 0, self.thumbstickSize * 0.8)
  7808.         self.endImage.ZIndex = 10
  7809.         self.endImage.Parent = self.thumbstickFrame
  7810.    
  7811.         for i = 1, NUM_MIDDLE_IMAGES do
  7812.             self.middleImages[i] = Instance.new("ImageLabel")
  7813.             self.middleImages[i].Name = "ThumbstickMiddle"
  7814.             self.middleImages[i].Visible = false
  7815.             self.middleImages[i].BackgroundTransparency = 1
  7816.             self.middleImages[i].Image = TOUCH_CONTROLS_SHEET
  7817.             self.middleImages[i].ImageRectOffset = Vector2.new(1,1)
  7818.             self.middleImages[i].ImageRectSize = Vector2.new(144, 144)
  7819.             self.middleImages[i].ImageTransparency = MIDDLE_TRANSPARENCIES[i]
  7820.             self.middleImages[i].AnchorPoint = Vector2.new(0.5, 0.5)
  7821.             self.middleImages[i].ZIndex = 9
  7822.             self.middleImages[i].Parent = self.thumbstickFrame
  7823.         end
  7824.    
  7825.         local CameraChangedConn = nil
  7826.         local function onCurrentCameraChanged()
  7827.             if CameraChangedConn then
  7828.                 CameraChangedConn:Disconnect()
  7829.                 CameraChangedConn = nil
  7830.             end
  7831.             local newCamera = workspace.CurrentCamera
  7832.             if newCamera then
  7833.                 local function onViewportSizeChanged()
  7834.                     local size = newCamera.ViewportSize
  7835.                     local portraitMode = size.X < size.Y
  7836.                     layoutThumbstickFrame(portraitMode)
  7837.                 end
  7838.                 CameraChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(onViewportSizeChanged)
  7839.                 onViewportSizeChanged()
  7840.             end
  7841.         end
  7842.         workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onCurrentCameraChanged)
  7843.         if workspace.CurrentCamera then
  7844.             onCurrentCameraChanged()
  7845.         end
  7846.    
  7847.         self.moveTouchStartPosition = nil
  7848.    
  7849.         self.startImageFadeTween = nil
  7850.         self.endImageFadeTween = nil
  7851.         self.middleImageFadeTweens = {}
  7852.    
  7853.         self.onRenderSteppedConn = RunService.RenderStepped:Connect(function()
  7854.             if self.tweenInAlphaStart ~= nil then
  7855.                 local delta = tick() - self.tweenInAlphaStart
  7856.                 local fadeInTime = (self.fadeInAndOutHalfDuration * 2 * self.fadeInAndOutBalance)
  7857.                 self.thumbstickFrame.BackgroundTransparency = 1 - FADE_IN_OUT_MAX_ALPHA*math.min(delta/fadeInTime, 1)
  7858.                 if delta > fadeInTime then
  7859.                     self.tweenOutAlphaStart = tick()
  7860.                     self.tweenInAlphaStart = nil
  7861.                 end
  7862.             elseif self.tweenOutAlphaStart ~= nil then
  7863.                 local delta = tick() - self.tweenOutAlphaStart
  7864.                 local fadeOutTime = (self.fadeInAndOutHalfDuration * 2) - (self.fadeInAndOutHalfDuration * 2 * self.fadeInAndOutBalance)
  7865.                 self.thumbstickFrame.BackgroundTransparency = 1 - FADE_IN_OUT_MAX_ALPHA + FADE_IN_OUT_MAX_ALPHA*math.min(delta/fadeOutTime, 1)
  7866.                 if delta > fadeOutTime  then
  7867.                     self.tweenOutAlphaStart = nil
  7868.                 end
  7869.             end
  7870.         end)
  7871.    
  7872.         self.onTouchEndedConn = UserInputService.TouchEnded:connect(function(inputObject)
  7873.             if inputObject == self.moveTouchObject then
  7874.                 self:OnInputEnded()
  7875.             end
  7876.         end)
  7877.    
  7878.         GuiService.MenuOpened:connect(function()
  7879.             if self.moveTouchObject then
  7880.                 self:OnInputEnded()
  7881.             end
  7882.         end)
  7883.    
  7884.         local playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
  7885.         while not playerGui do
  7886.             LocalPlayer.ChildAdded:wait()
  7887.             playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
  7888.         end
  7889.    
  7890.         local playerGuiChangedConn = nil
  7891.         local originalScreenOrientationWasLandscape =   playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeLeft or
  7892.                                                         playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeRight
  7893.    
  7894.         local function longShowBackground()
  7895.             self.fadeInAndOutHalfDuration = 2.5
  7896.             self.fadeInAndOutBalance = 0.05
  7897.             self.tweenInAlphaStart = tick()
  7898.         end
  7899.    
  7900.         playerGuiChangedConn = playerGui:GetPropertyChangedSignal("CurrentScreenOrientation"):Connect(function()
  7901.             if (originalScreenOrientationWasLandscape and playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.Portrait) or
  7902.                 (not originalScreenOrientationWasLandscape and playerGui.CurrentScreenOrientation ~= Enum.ScreenOrientation.Portrait) then
  7903.    
  7904.                 playerGuiChangedConn:disconnect()
  7905.                 longShowBackground()
  7906.    
  7907.                 if originalScreenOrientationWasLandscape then
  7908.                     self.hasFadedBackgroundInPortrait = true
  7909.                 else
  7910.                     self.hasFadedBackgroundInLandscape = true
  7911.                 end
  7912.             end
  7913.         end)
  7914.    
  7915.         self.thumbstickFrame.Parent = parentFrame
  7916.    
  7917.         if game:IsLoaded() then
  7918.             longShowBackground()
  7919.         else
  7920.             coroutine.wrap(function()
  7921.                 game.Loaded:Wait()
  7922.                 longShowBackground()
  7923.             end)()
  7924.         end
  7925.     end
  7926.    
  7927.     return DynamicThumbstick
  7928. end
  7929.  
  7930. function _Gamepad()
  7931.     local UserInputService = game:GetService("UserInputService")
  7932.     local ContextActionService = game:GetService("ContextActionService")
  7933.    
  7934.     --[[ Constants ]]--
  7935.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  7936.     local NONE = Enum.UserInputType.None
  7937.     local thumbstickDeadzone = 0.2
  7938.    
  7939.     --[[ The Module ]]--
  7940.     local BaseCharacterController = _BaseCharacterController()
  7941.     local Gamepad = setmetatable({}, BaseCharacterController)
  7942.     Gamepad.__index = Gamepad
  7943.    
  7944.     function Gamepad.new(CONTROL_ACTION_PRIORITY)
  7945.         local self = setmetatable(BaseCharacterController.new(), Gamepad)
  7946.    
  7947.         self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
  7948.    
  7949.         self.forwardValue  = 0
  7950.         self.backwardValue = 0
  7951.         self.leftValue = 0
  7952.         self.rightValue = 0
  7953.    
  7954.         self.activeGamepad = NONE   -- Enum.UserInputType.Gamepad1, 2, 3...
  7955.         self.gamepadConnectedConn = nil
  7956.         self.gamepadDisconnectedConn = nil
  7957.         return self
  7958.     end
  7959.    
  7960.     function Gamepad:Enable(enable)
  7961.         if not UserInputService.GamepadEnabled then
  7962.             return false
  7963.         end
  7964.    
  7965.         if enable == self.enabled then
  7966.             -- Module is already in the state being requested. True is returned here since the module will be in the state
  7967.             -- expected by the code that follows the Enable() call. This makes more sense than returning false to indicate
  7968.             -- no action was necessary. False indicates failure to be in requested/expected state.
  7969.             return true
  7970.         end
  7971.    
  7972.         self.forwardValue  = 0
  7973.         self.backwardValue = 0
  7974.         self.leftValue = 0
  7975.         self.rightValue = 0
  7976.         self.moveVector = ZERO_VECTOR3
  7977.         self.isJumping = false
  7978.    
  7979.         if enable then
  7980.             self.activeGamepad = self:GetHighestPriorityGamepad()
  7981.             if self.activeGamepad ~= NONE then
  7982.                 self:BindContextActions()
  7983.                 self:ConnectGamepadConnectionListeners()
  7984.             else
  7985.                 -- No connected gamepads, failure to enable
  7986.                 return false
  7987.             end
  7988.         else
  7989.             self:UnbindContextActions()
  7990.             self:DisconnectGamepadConnectionListeners()
  7991.             self.activeGamepad = NONE
  7992.         end
  7993.    
  7994.         self.enabled = enable
  7995.         return true
  7996.     end
  7997.    
  7998.     -- This function selects the lowest number gamepad from the currently-connected gamepad
  7999.     -- and sets it as the active gamepad
  8000.     function Gamepad:GetHighestPriorityGamepad()
  8001.         local connectedGamepads = UserInputService:GetConnectedGamepads()
  8002.         local bestGamepad = NONE -- Note that this value is higher than all valid gamepad values
  8003.         for _, gamepad in pairs(connectedGamepads) do
  8004.             if gamepad.Value < bestGamepad.Value then
  8005.                 bestGamepad = gamepad
  8006.             end
  8007.         end
  8008.         return bestGamepad
  8009.     end
  8010.    
  8011.     function Gamepad:BindContextActions()
  8012.    
  8013.         if self.activeGamepad == NONE then
  8014.             -- There must be an active gamepad to set up bindings
  8015.             return false
  8016.         end
  8017.    
  8018.         local handleJumpAction = function(actionName, inputState, inputObject)
  8019.             self.isJumping = (inputState == Enum.UserInputState.Begin)
  8020.             return Enum.ContextActionResult.Sink
  8021.         end
  8022.    
  8023.         local handleThumbstickInput = function(actionName, inputState, inputObject)
  8024.    
  8025.             if inputState == Enum.UserInputState.Cancel then
  8026.                 self.moveVector = ZERO_VECTOR3
  8027.                 return Enum.ContextActionResult.Sink
  8028.             end
  8029.    
  8030.             if self.activeGamepad ~= inputObject.UserInputType then
  8031.                 return Enum.ContextActionResult.Pass
  8032.             end
  8033.             if inputObject.KeyCode ~= Enum.KeyCode.Thumbstick1 then return end
  8034.    
  8035.             if inputObject.Position.magnitude > thumbstickDeadzone then
  8036.                 self.moveVector  =  Vector3.new(inputObject.Position.X, 0, -inputObject.Position.Y)
  8037.             else
  8038.                 self.moveVector = ZERO_VECTOR3
  8039.             end
  8040.             return Enum.ContextActionResult.Sink
  8041.         end
  8042.    
  8043.         ContextActionService:BindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8044.         ContextActionService:BindActionAtPriority("jumpAction", handleJumpAction, false,
  8045.             self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonA)
  8046.         ContextActionService:BindActionAtPriority("moveThumbstick", handleThumbstickInput, false,
  8047.             self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Thumbstick1)
  8048.    
  8049.         return true
  8050.     end
  8051.    
  8052.     function Gamepad:UnbindContextActions()
  8053.         if self.activeGamepad ~= NONE then
  8054.             ContextActionService:UnbindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8055.         end
  8056.         ContextActionService:UnbindAction("moveThumbstick")
  8057.         ContextActionService:UnbindAction("jumpAction")
  8058.     end
  8059.    
  8060.     function Gamepad:OnNewGamepadConnected()
  8061.         -- A new gamepad has been connected.
  8062.         local bestGamepad = self:GetHighestPriorityGamepad()
  8063.    
  8064.         if bestGamepad == self.activeGamepad then
  8065.             -- A new gamepad was connected, but our active gamepad is not changing
  8066.             return
  8067.         end
  8068.    
  8069.         if bestGamepad == NONE then
  8070.             -- There should be an active gamepad when GamepadConnected fires, so this should not
  8071.             -- normally be hit. If there is no active gamepad, unbind actions but leave
  8072.             -- the module enabled and continue to listen for a new gamepad connection.
  8073.             warn("Gamepad:OnNewGamepadConnected found no connected gamepads")
  8074.             self:UnbindContextActions()
  8075.             return
  8076.         end
  8077.    
  8078.         if self.activeGamepad ~= NONE then
  8079.             -- Switching from one active gamepad to another
  8080.             self:UnbindContextActions()
  8081.         end
  8082.    
  8083.         self.activeGamepad = bestGamepad
  8084.         self:BindContextActions()
  8085.     end
  8086.    
  8087.     function Gamepad:OnCurrentGamepadDisconnected()
  8088.         if self.activeGamepad ~= NONE then
  8089.             ContextActionService:UnbindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8090.         end
  8091.    
  8092.         local bestGamepad = self:GetHighestPriorityGamepad()
  8093.    
  8094.         if self.activeGamepad ~= NONE and bestGamepad == self.activeGamepad then
  8095.             warn("Gamepad:OnCurrentGamepadDisconnected found the supposedly disconnected gamepad in connectedGamepads.")
  8096.             self:UnbindContextActions()
  8097.             self.activeGamepad = NONE
  8098.             return
  8099.         end
  8100.    
  8101.         if bestGamepad == NONE then
  8102.             -- No active gamepad, unbinding actions but leaving gamepad connection listener active
  8103.             self:UnbindContextActions()
  8104.             self.activeGamepad = NONE
  8105.         else
  8106.             -- Set new gamepad as active and bind to tool activation
  8107.             self.activeGamepad = bestGamepad
  8108.             ContextActionService:BindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8109.         end
  8110.     end
  8111.    
  8112.     function Gamepad:ConnectGamepadConnectionListeners()
  8113.         self.gamepadConnectedConn = UserInputService.GamepadConnected:Connect(function(gamepadEnum)
  8114.             self:OnNewGamepadConnected()
  8115.         end)
  8116.    
  8117.         self.gamepadDisconnectedConn = UserInputService.GamepadDisconnected:Connect(function(gamepadEnum)
  8118.             if self.activeGamepad == gamepadEnum then
  8119.                 self:OnCurrentGamepadDisconnected()
  8120.             end
  8121.         end)
  8122.    
  8123.     end
  8124.    
  8125.     function Gamepad:DisconnectGamepadConnectionListeners()
  8126.         if self.gamepadConnectedConn then
  8127.             self.gamepadConnectedConn:Disconnect()
  8128.             self.gamepadConnectedConn = nil
  8129.         end
  8130.    
  8131.         if self.gamepadDisconnectedConn then
  8132.             self.gamepadDisconnectedConn:Disconnect()
  8133.             self.gamepadDisconnectedConn = nil
  8134.         end
  8135.     end
  8136.    
  8137.     return Gamepad
  8138. end
  8139.  
  8140. function _Keyboard()
  8141.    
  8142.     --[[ Roblox Services ]]--
  8143.     local UserInputService = game:GetService("UserInputService")
  8144.     local ContextActionService = game:GetService("ContextActionService")
  8145.    
  8146.     --[[ Constants ]]--
  8147.     local ZERO_VECTOR3 = Vector3.new(0,0,0)
  8148.    
  8149.     --[[ The Module ]]--
  8150.     local BaseCharacterController = _BaseCharacterController()
  8151.     local Keyboard = setmetatable({}, BaseCharacterController)
  8152.     Keyboard.__index = Keyboard
  8153.    
  8154.     function Keyboard.new(CONTROL_ACTION_PRIORITY)
  8155.         local self = setmetatable(BaseCharacterController.new(), Keyboard)
  8156.    
  8157.         self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
  8158.    
  8159.         self.textFocusReleasedConn = nil
  8160.         self.textFocusGainedConn = nil
  8161.         self.windowFocusReleasedConn = nil
  8162.    
  8163.         self.forwardValue  = 0
  8164.         self.backwardValue = 0
  8165.         self.leftValue = 0
  8166.         self.rightValue = 0
  8167.    
  8168.         self.jumpEnabled = true
  8169.    
  8170.         return self
  8171.     end
  8172.    
  8173.     function Keyboard:Enable(enable)
  8174.         if not UserInputService.KeyboardEnabled then
  8175.             return false
  8176.         end
  8177.    
  8178.         if enable == self.enabled then
  8179.             -- Module is already in the state being requested. True is returned here since the module will be in the state
  8180.             -- expected by the code that follows the Enable() call. This makes more sense than returning false to indicate
  8181.             -- no action was necessary. False indicates failure to be in requested/expected state.
  8182.             return true
  8183.         end
  8184.    
  8185.         self.forwardValue  = 0
  8186.         self.backwardValue = 0
  8187.         self.leftValue = 0
  8188.         self.rightValue = 0
  8189.         self.moveVector = ZERO_VECTOR3
  8190.         self.jumpRequested = false
  8191.         self:UpdateJump()
  8192.    
  8193.         if enable then
  8194.             self:BindContextActions()
  8195.             self:ConnectFocusEventListeners()
  8196.         else
  8197.             self:UnbindContextActions()
  8198.             self:DisconnectFocusEventListeners()
  8199.         end
  8200.    
  8201.         self.enabled = enable
  8202.         return true
  8203.     end
  8204.    
  8205.     function Keyboard:UpdateMovement(inputState)
  8206.         if inputState == Enum.UserInputState.Cancel then
  8207.             self.moveVector = ZERO_VECTOR3
  8208.         else
  8209.             self.moveVector = Vector3.new(self.leftValue + self.rightValue, 0, self.forwardValue + self.backwardValue)
  8210.         end
  8211.     end
  8212.    
  8213.     function Keyboard:UpdateJump()
  8214.         self.isJumping = self.jumpRequested
  8215.     end
  8216.    
  8217.     function Keyboard:BindContextActions()
  8218.    
  8219.         -- Note: In the previous version of this code, the movement values were not zeroed-out on UserInputState. Cancel, now they are,
  8220.         -- which fixes them from getting stuck on.
  8221.         -- We return ContextActionResult.Pass here for legacy reasons.
  8222.         -- Many games rely on gameProcessedEvent being false on UserInputService.InputBegan for these control actions.
  8223.         local handleMoveForward = function(actionName, inputState, inputObject)
  8224.             self.forwardValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
  8225.             self:UpdateMovement(inputState)
  8226.             return Enum.ContextActionResult.Pass
  8227.         end
  8228.    
  8229.         local handleMoveBackward = function(actionName, inputState, inputObject)
  8230.             self.backwardValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
  8231.             self:UpdateMovement(inputState)
  8232.             return Enum.ContextActionResult.Pass
  8233.         end
  8234.    
  8235.         local handleMoveLeft = function(actionName, inputState, inputObject)
  8236.             self.leftValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
  8237.             self:UpdateMovement(inputState)
  8238.             return Enum.ContextActionResult.Pass
  8239.         end
  8240.    
  8241.         local handleMoveRight = function(actionName, inputState, inputObject)
  8242.             self.rightValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
  8243.             self:UpdateMovement(inputState)
  8244.             return Enum.ContextActionResult.Pass
  8245.         end
  8246.    
  8247.         local handleJumpAction = function(actionName, inputState, inputObject)
  8248.             self.jumpRequested = self.jumpEnabled and (inputState == Enum.UserInputState.Begin)
  8249.             self:UpdateJump()
  8250.             return Enum.ContextActionResult.Pass
  8251.         end
  8252.    
  8253.         -- TODO: Revert to KeyCode bindings so that in the future the abstraction layer from actual keys to
  8254.         -- movement direction is done in Lua
  8255.         ContextActionService:BindActionAtPriority("moveForwardAction", handleMoveForward, false,
  8256.             self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterForward)
  8257.         ContextActionService:BindActionAtPriority("moveBackwardAction", handleMoveBackward, false,
  8258.             self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterBackward)
  8259.         ContextActionService:BindActionAtPriority("moveLeftAction", handleMoveLeft, false,
  8260.             self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterLeft)
  8261.         ContextActionService:BindActionAtPriority("moveRightAction", handleMoveRight, false,
  8262.             self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterRight)
  8263.         ContextActionService:BindActionAtPriority("jumpAction", handleJumpAction, false,
  8264.             self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterJump)
  8265.     end
  8266.    
  8267.     function Keyboard:UnbindContextActions()
  8268.         ContextActionService:UnbindAction("moveForwardAction")
  8269.         ContextActionService:UnbindAction("moveBackwardAction")
  8270.         ContextActionService:UnbindAction("moveLeftAction")
  8271.         ContextActionService:UnbindAction("moveRightAction")
  8272.         ContextActionService:UnbindAction("jumpAction")
  8273.     end
  8274.    
  8275.     function Keyboard:ConnectFocusEventListeners()
  8276.         local function onFocusReleased()
  8277.             self.moveVector = ZERO_VECTOR3
  8278.             self.forwardValue  = 0
  8279.             self.backwardValue = 0
  8280.             self.leftValue = 0
  8281.             self.rightValue = 0
  8282.             self.jumpRequested = false
  8283.             self:UpdateJump()
  8284.         end
  8285.    
  8286.         local function onTextFocusGained(textboxFocused)
  8287.             self.jumpRequested = false
  8288.             self:UpdateJump()
  8289.         end
  8290.    
  8291.         self.textFocusReleasedConn = UserInputService.TextBoxFocusReleased:Connect(onFocusReleased)
  8292.         self.textFocusGainedConn = UserInputService.TextBoxFocused:Connect(onTextFocusGained)
  8293.         self.windowFocusReleasedConn = UserInputService.WindowFocused:Connect(onFocusReleased)
  8294.     end
  8295.    
  8296.     function Keyboard:DisconnectFocusEventListeners()
  8297.         if self.textFocusReleasedCon then
  8298.             self.textFocusReleasedCon:Disconnect()
  8299.             self.textFocusReleasedCon = nil
  8300.         end
  8301.         if self.textFocusGainedConn then
  8302.             self.textFocusGainedConn:Disconnect()
  8303.             self.textFocusGainedConn = nil
  8304.         end
  8305.         if self.windowFocusReleasedConn then
  8306.             self.windowFocusReleasedConn:Disconnect()
  8307.             self.windowFocusReleasedConn = nil
  8308.         end
  8309.     end
  8310.    
  8311.     return Keyboard
  8312. end
  8313.  
  8314. function _ControlModule()
  8315.     local ControlModule = {}
  8316.     ControlModule.__index = ControlModule
  8317.    
  8318.     --[[ Roblox Services ]]--
  8319.     local Players = game:GetService("Players")
  8320.     local RunService = game:GetService("RunService")
  8321.     local UserInputService = game:GetService("UserInputService")
  8322.     local Workspace = game:GetService("Workspace")
  8323.     local UserGameSettings = UserSettings():GetService("UserGameSettings")
  8324.    
  8325.     -- Roblox User Input Control Modules - each returns a new() constructor function used to create controllers as needed
  8326.     local Keyboard = _Keyboard()
  8327.     local Gamepad = _Gamepad()
  8328.     local DynamicThumbstick = _DynamicThumbstick()
  8329.    
  8330.     local FFlagUserMakeThumbstickDynamic do
  8331.         local success, value = pcall(function()
  8332.             return UserSettings():IsUserFeatureEnabled("UserMakeThumbstickDynamic")
  8333.         end)
  8334.         FFlagUserMakeThumbstickDynamic = success and value
  8335.     end
  8336.    
  8337.     local TouchThumbstick = FFlagUserMakeThumbstickDynamic and DynamicThumbstick or _TouchThumbstick()
  8338.    
  8339.     -- These controllers handle only walk/run movement, jumping is handled by the
  8340.     -- TouchJump controller if any of these are active
  8341.     local ClickToMove = _ClickToMoveController()
  8342.     local TouchJump = _TouchJump()
  8343.    
  8344.     local VehicleController = _VehicleController()
  8345.    
  8346.     local CONTROL_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
  8347.    
  8348.     -- Mapping from movement mode and lastInputType enum values to control modules to avoid huge if elseif switching
  8349.     local movementEnumToModuleMap = {
  8350.         [Enum.TouchMovementMode.DPad] = DynamicThumbstick,
  8351.         [Enum.DevTouchMovementMode.DPad] = DynamicThumbstick,
  8352.         [Enum.TouchMovementMode.Thumbpad] = DynamicThumbstick,
  8353.         [Enum.DevTouchMovementMode.Thumbpad] = DynamicThumbstick,
  8354.         [Enum.TouchMovementMode.Thumbstick] = TouchThumbstick,
  8355.         [Enum.DevTouchMovementMode.Thumbstick] = TouchThumbstick,
  8356.         [Enum.TouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
  8357.         [Enum.DevTouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
  8358.         [Enum.TouchMovementMode.ClickToMove] = ClickToMove,
  8359.         [Enum.DevTouchMovementMode.ClickToMove] = ClickToMove,
  8360.    
  8361.         -- Current default
  8362.         [Enum.TouchMovementMode.Default] = DynamicThumbstick,
  8363.    
  8364.         [Enum.ComputerMovementMode.Default] = Keyboard,
  8365.         [Enum.ComputerMovementMode.KeyboardMouse] = Keyboard,
  8366.         [Enum.DevComputerMovementMode.KeyboardMouse] = Keyboard,
  8367.         [Enum.DevComputerMovementMode.Scriptable] = nil,
  8368.         [Enum.ComputerMovementMode.ClickToMove] = ClickToMove,
  8369.         [Enum.DevComputerMovementMode.ClickToMove] = ClickToMove,
  8370.     }
  8371.    
  8372.     -- Keyboard controller is really keyboard and mouse controller
  8373.     local computerInputTypeToModuleMap = {
  8374.         [Enum.UserInputType.Keyboard] = Keyboard,
  8375.         [Enum.UserInputType.MouseButton1] = Keyboard,
  8376.         [Enum.UserInputType.MouseButton2] = Keyboard,
  8377.         [Enum.UserInputType.MouseButton3] = Keyboard,
  8378.         [Enum.UserInputType.MouseWheel] = Keyboard,
  8379.         [Enum.UserInputType.MouseMovement] = Keyboard,
  8380.         [Enum.UserInputType.Gamepad1] = Gamepad,
  8381.         [Enum.UserInputType.Gamepad2] = Gamepad,
  8382.         [Enum.UserInputType.Gamepad3] = Gamepad,
  8383.         [Enum.UserInputType.Gamepad4] = Gamepad,
  8384.     }
  8385.    
  8386.     local lastInputType
  8387.    
  8388.     function ControlModule.new()
  8389.         local self = setmetatable({},ControlModule)
  8390.    
  8391.         -- The Modules above are used to construct controller instances as-needed, and this
  8392.         -- table is a map from Module to the instance created from it
  8393.         self.controllers = {}
  8394.    
  8395.         self.activeControlModule = nil  -- Used to prevent unnecessarily expensive checks on each input event
  8396.         self.activeController = nil
  8397.         self.touchJumpController = nil
  8398.         self.moveFunction = Players.LocalPlayer.Move
  8399.         self.humanoid = nil
  8400.         self.lastInputType = Enum.UserInputType.None
  8401.    
  8402.         -- For Roblox self.vehicleController
  8403.         self.humanoidSeatedConn = nil
  8404.         self.vehicleController = nil
  8405.    
  8406.         self.touchControlFrame = nil
  8407.    
  8408.         self.vehicleController = VehicleController.new(CONTROL_ACTION_PRIORITY)
  8409.    
  8410.         Players.LocalPlayer.CharacterAdded:Connect(function(char) self:OnCharacterAdded(char) end)
  8411.         Players.LocalPlayer.CharacterRemoving:Connect(function(char) self:OnCharacterRemoving(char) end)
  8412.         if Players.LocalPlayer.Character then
  8413.             self:OnCharacterAdded(Players.LocalPlayer.Character)
  8414.         end
  8415.    
  8416.         RunService:BindToRenderStep("ControlScriptRenderstep", Enum.RenderPriority.Input.Value, function(dt)
  8417.             self:OnRenderStepped(dt)
  8418.         end)
  8419.    
  8420.         UserInputService.LastInputTypeChanged:Connect(function(newLastInputType)
  8421.             self:OnLastInputTypeChanged(newLastInputType)
  8422.         end)
  8423.    
  8424.    
  8425.         UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
  8426.             self:OnTouchMovementModeChange()
  8427.         end)
  8428.         Players.LocalPlayer:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
  8429.             self:OnTouchMovementModeChange()
  8430.         end)
  8431.    
  8432.         UserGameSettings:GetPropertyChangedSignal("ComputerMovementMode"):Connect(function()
  8433.             self:OnComputerMovementModeChange()
  8434.         end)
  8435.         Players.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(function()
  8436.             self:OnComputerMovementModeChange()
  8437.         end)
  8438.    
  8439.         --[[ Touch Device UI ]]--
  8440.         self.playerGui = nil
  8441.         self.touchGui = nil
  8442.         self.playerGuiAddedConn = nil
  8443.    
  8444.         if UserInputService.TouchEnabled then
  8445.             self.playerGui = Players.LocalPlayer:FindFirstChildOfClass("PlayerGui")
  8446.             if self.playerGui then
  8447.                 self:CreateTouchGuiContainer()
  8448.                 self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
  8449.             else
  8450.                 self.playerGuiAddedConn = Players.LocalPlayer.ChildAdded:Connect(function(child)
  8451.                     if child:IsA("PlayerGui") then
  8452.                         self.playerGui = child
  8453.                         self:CreateTouchGuiContainer()
  8454.                         self.playerGuiAddedConn:Disconnect()
  8455.                         self.playerGuiAddedConn = nil
  8456.                         self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
  8457.                     end
  8458.                 end)
  8459.             end
  8460.         else
  8461.             self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
  8462.         end
  8463.    
  8464.         return self
  8465.     end
  8466.    
  8467.     -- Convenience function so that calling code does not have to first get the activeController
  8468.     -- and then call GetMoveVector on it. When there is no active controller, this function returns
  8469.     -- nil so that this case can be distinguished from no current movement (which returns zero vector).
  8470.     function ControlModule:GetMoveVector()
  8471.         if self.activeController then
  8472.             return self.activeController:GetMoveVector()
  8473.         end
  8474.         return Vector3.new(0,0,0)
  8475.     end
  8476.    
  8477.     function ControlModule:GetActiveController()
  8478.         return self.activeController
  8479.     end
  8480.    
  8481.     function ControlModule:EnableActiveControlModule()
  8482.         if self.activeControlModule == ClickToMove then
  8483.             -- For ClickToMove, when it is the player's choice, we also enable the full keyboard controls.
  8484.             -- When the developer is forcing click to move, the most keyboard controls (WASD) are not available, only jump.
  8485.             self.activeController:Enable(
  8486.                 true,
  8487.                 Players.LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.UserChoice,
  8488.                 self.touchJumpController
  8489.             )
  8490.         elseif self.touchControlFrame then
  8491.             self.activeController:Enable(true, self.touchControlFrame)
  8492.         else
  8493.             self.activeController:Enable(true)
  8494.         end
  8495.     end
  8496.    
  8497.     function ControlModule:Enable(enable)
  8498.         if not self.activeController then
  8499.             return
  8500.         end
  8501.    
  8502.         if enable == nil then
  8503.             enable = true
  8504.         end
  8505.         if enable then
  8506.             self:EnableActiveControlModule()
  8507.         else
  8508.             self:Disable()
  8509.         end
  8510.     end
  8511.    
  8512.     -- For those who prefer distinct functions
  8513.     function ControlModule:Disable()
  8514.         if self.activeController then
  8515.             self.activeController:Enable(false)
  8516.    
  8517.             if self.moveFunction then
  8518.                 self.moveFunction(Players.LocalPlayer, Vector3.new(0,0,0), true)
  8519.             end
  8520.         end
  8521.     end
  8522.    
  8523.    
  8524.     -- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
  8525.     function ControlModule:SelectComputerMovementModule()
  8526.         if not (UserInputService.KeyboardEnabled or UserInputService.GamepadEnabled) then
  8527.             return nil, false
  8528.         end
  8529.    
  8530.         local computerModule
  8531.         local DevMovementMode = Players.LocalPlayer.DevComputerMovementMode
  8532.    
  8533.         if DevMovementMode == Enum.DevComputerMovementMode.UserChoice then
  8534.             computerModule = computerInputTypeToModuleMap[lastInputType]
  8535.             if UserGameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove and computerModule == Keyboard then
  8536.                 -- User has ClickToMove set in Settings, prefer ClickToMove controller for keyboard and mouse lastInputTypes
  8537.                 computerModule = ClickToMove
  8538.             end
  8539.         else
  8540.             -- Developer has selected a mode that must be used.
  8541.             computerModule = movementEnumToModuleMap[DevMovementMode]
  8542.    
  8543.             -- computerModule is expected to be nil here only when developer has selected Scriptable
  8544.             if (not computerModule) and DevMovementMode ~= Enum.DevComputerMovementMode.Scriptable then
  8545.                 warn("No character control module is associated with DevComputerMovementMode ", DevMovementMode)
  8546.             end
  8547.         end
  8548.    
  8549.         if computerModule then
  8550.             return computerModule, true
  8551.         elseif DevMovementMode == Enum.DevComputerMovementMode.Scriptable then
  8552.             -- Special case where nil is returned and we actually want to set self.activeController to nil for Scriptable
  8553.             return nil, true
  8554.         else
  8555.             -- This case is for when computerModule is nil because of an error and no suitable control module could
  8556.             -- be found.
  8557.             return nil, false
  8558.         end
  8559.     end
  8560.    
  8561.     -- Choose current Touch control module based on settings (user, dev)
  8562.     -- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
  8563.     function ControlModule:SelectTouchModule()
  8564.         if not UserInputService.TouchEnabled then
  8565.             return nil, false
  8566.         end
  8567.         local touchModule
  8568.         local DevMovementMode = Players.LocalPlayer.DevTouchMovementMode
  8569.         if DevMovementMode == Enum.DevTouchMovementMode.UserChoice then
  8570.             touchModule = movementEnumToModuleMap[UserGameSettings.TouchMovementMode]
  8571.         elseif DevMovementMode == Enum.DevTouchMovementMode.Scriptable then
  8572.             return nil, true
  8573.         else
  8574.             touchModule = movementEnumToModuleMap[DevMovementMode]
  8575.         end
  8576.         return touchModule, true
  8577.     end
  8578.    
  8579.     local function calculateRawMoveVector(humanoid, cameraRelativeMoveVector)
  8580.         local camera = Workspace.CurrentCamera
  8581.         if not camera then
  8582.             return cameraRelativeMoveVector
  8583.         end
  8584.    
  8585.         if humanoid:GetState() == Enum.HumanoidStateType.Swimming then
  8586.             return camera.CFrame:VectorToWorldSpace(cameraRelativeMoveVector)
  8587.         end
  8588.    
  8589.         local c, s
  8590.         local _, _, _, R00, R01, R02, _, _, R12, _, _, R22 = camera.CFrame:GetComponents()
  8591.         if R12 < 1 and R12 > -1 then
  8592.             -- X and Z components from back vector.
  8593.             c = R22
  8594.             s = R02
  8595.         else
  8596.             -- In this case the camera is looking straight up or straight down.
  8597.             -- Use X components from right and up vectors.
  8598.             c = R00
  8599.             s = -R01*math.sign(R12)
  8600.         end
  8601.         local norm = math.sqrt(c*c + s*s)
  8602.         return Vector3.new(
  8603.             (c*cameraRelativeMoveVector.x + s*cameraRelativeMoveVector.z)/norm,
  8604.             0,
  8605.             (c*cameraRelativeMoveVector.z - s*cameraRelativeMoveVector.x)/norm
  8606.         )
  8607.     end
  8608.    
  8609.     function ControlModule:OnRenderStepped(dt)
  8610.         if self.activeController and self.activeController.enabled and self.humanoid then
  8611.             -- Give the controller a chance to adjust its state
  8612.             self.activeController:OnRenderStepped(dt)
  8613.    
  8614.             -- Now retrieve info from the controller
  8615.             local moveVector = self.activeController:GetMoveVector()
  8616.             local cameraRelative = self.activeController:IsMoveVectorCameraRelative()
  8617.    
  8618.             local clickToMoveController = self:GetClickToMoveController()
  8619.             if self.activeController ~= clickToMoveController then
  8620.                 if moveVector.magnitude > 0 then
  8621.                     -- Clean up any developer started MoveTo path
  8622.                     clickToMoveController:CleanupPath()
  8623.                 else
  8624.                     -- Get move vector for developer started MoveTo
  8625.                     clickToMoveController:OnRenderStepped(dt)
  8626.                     moveVector = clickToMoveController:GetMoveVector()
  8627.                     cameraRelative = clickToMoveController:IsMoveVectorCameraRelative()
  8628.                 end
  8629.             end
  8630.    
  8631.             -- Are we driving a vehicle ?
  8632.             local vehicleConsumedInput = false
  8633.             if self.vehicleController then
  8634.                 moveVector, vehicleConsumedInput = self.vehicleController:Update(moveVector, cameraRelative, self.activeControlModule==Gamepad)
  8635.             end
  8636.    
  8637.             -- If not, move the player
  8638.             -- Verification of vehicleConsumedInput is commented out to preserve legacy behavior,
  8639.             -- in case some game relies on Humanoid.MoveDirection still being set while in a VehicleSeat
  8640.             --if not vehicleConsumedInput then
  8641.                 if cameraRelative then
  8642.                     moveVector = calculateRawMoveVector(self.humanoid, moveVector)
  8643.                 end
  8644.                 self.moveFunction(Players.LocalPlayer, moveVector, false)
  8645.             --end
  8646.    
  8647.             -- And make them jump if needed
  8648.             self.humanoid.Jump = self.activeController:GetIsJumping() or (self.touchJumpController and self.touchJumpController:GetIsJumping())
  8649.         end
  8650.     end
  8651.    
  8652.     function ControlModule:OnHumanoidSeated(active, currentSeatPart)
  8653.         if active then
  8654.             if currentSeatPart and currentSeatPart:IsA("VehicleSeat") then
  8655.                 if not self.vehicleController then
  8656.                     self.vehicleController = self.vehicleController.new(CONTROL_ACTION_PRIORITY)
  8657.                 end
  8658.                 self.vehicleController:Enable(true, currentSeatPart)
  8659.             end
  8660.         else
  8661.             if self.vehicleController then
  8662.                 self.vehicleController:Enable(false, currentSeatPart)
  8663.             end
  8664.         end
  8665.     end
  8666.    
  8667.     function ControlModule:OnCharacterAdded(char)
  8668.         self.humanoid = char:FindFirstChildOfClass("Humanoid")
  8669.         while not self.humanoid do
  8670.             char.ChildAdded:wait()
  8671.             self.humanoid = char:FindFirstChildOfClass("Humanoid")
  8672.         end
  8673.    
  8674.         if self.touchGui then
  8675.             self.touchGui.Enabled = true
  8676.         end
  8677.    
  8678.         if self.humanoidSeatedConn then
  8679.             self.humanoidSeatedConn:Disconnect()
  8680.             self.humanoidSeatedConn = nil
  8681.         end
  8682.         self.humanoidSeatedConn = self.humanoid.Seated:Connect(function(active, currentSeatPart)
  8683.             self:OnHumanoidSeated(active, currentSeatPart)
  8684.         end)
  8685.     end
  8686.    
  8687.     function ControlModule:OnCharacterRemoving(char)
  8688.         self.humanoid = nil
  8689.    
  8690.         if self.touchGui then
  8691.             self.touchGui.Enabled = false
  8692.         end
  8693.     end
  8694.    
  8695.     -- Helper function to lazily instantiate a controller if it does not yet exist,
  8696.     -- disable the active controller if it is different from the on being switched to,
  8697.     -- and then enable the requested controller. The argument to this function must be
  8698.     -- a reference to one of the control modules, i.e. Keyboard, Gamepad, etc.
  8699.     function ControlModule:SwitchToController(controlModule)
  8700.         if not controlModule then
  8701.             if self.activeController then
  8702.                 self.activeController:Enable(false)
  8703.             end
  8704.             self.activeController = nil
  8705.             self.activeControlModule = nil
  8706.         else
  8707.             if not self.controllers[controlModule] then
  8708.                 self.controllers[controlModule] = controlModule.new(CONTROL_ACTION_PRIORITY)
  8709.             end
  8710.    
  8711.             if self.activeController ~= self.controllers[controlModule] then
  8712.                 if self.activeController then
  8713.                     self.activeController:Enable(false)
  8714.                 end
  8715.                 self.activeController = self.controllers[controlModule]
  8716.                 self.activeControlModule = controlModule -- Only used to check if controller switch is necessary
  8717.    
  8718.                 if self.touchControlFrame and (self.activeControlModule == ClickToMove
  8719.                             or self.activeControlModule == TouchThumbstick
  8720.                             or self.activeControlModule == DynamicThumbstick) then
  8721.                     if not self.controllers[TouchJump] then
  8722.                         self.controllers[TouchJump] = TouchJump.new()
  8723.                     end
  8724.                     self.touchJumpController = self.controllers[TouchJump]
  8725.                     self.touchJumpController:Enable(true, self.touchControlFrame)
  8726.                 else
  8727.                     if self.touchJumpController then
  8728.                         self.touchJumpController:Enable(false)
  8729.                     end
  8730.                 end
  8731.    
  8732.                 self:EnableActiveControlModule()
  8733.             end
  8734.         end
  8735.     end
  8736.    
  8737.     function ControlModule:OnLastInputTypeChanged(newLastInputType)
  8738.         if lastInputType == newLastInputType then
  8739.             warn("LastInputType Change listener called with current type.")
  8740.         end
  8741.         lastInputType = newLastInputType
  8742.    
  8743.         if lastInputType == Enum.UserInputType.Touch then
  8744.             -- TODO: Check if touch module already active
  8745.             local touchModule, success = self:SelectTouchModule()
  8746.             if success then
  8747.                 while not self.touchControlFrame do
  8748.                     wait()
  8749.                 end
  8750.                 self:SwitchToController(touchModule)
  8751.             end
  8752.         elseif computerInputTypeToModuleMap[lastInputType] ~= nil then
  8753.             local computerModule = self:SelectComputerMovementModule()
  8754.             if computerModule then
  8755.                 self:SwitchToController(computerModule)
  8756.             end
  8757.         end
  8758.     end
  8759.    
  8760.     -- Called when any relevant values of GameSettings or LocalPlayer change, forcing re-evalulation of
  8761.     -- current control scheme
  8762.     function ControlModule:OnComputerMovementModeChange()
  8763.         local controlModule, success =  self:SelectComputerMovementModule()
  8764.         if success then
  8765.             self:SwitchToController(controlModule)
  8766.         end
  8767.     end
  8768.    
  8769.     function ControlModule:OnTouchMovementModeChange()
  8770.         local touchModule, success = self:SelectTouchModule()
  8771.         if success then
  8772.             while not self.touchControlFrame do
  8773.                 wait()
  8774.             end
  8775.             self:SwitchToController(touchModule)
  8776.         end
  8777.     end
  8778.    
  8779.     function ControlModule:CreateTouchGuiContainer()
  8780.         if self.touchGui then self.touchGui:Destroy() end
  8781.    
  8782.         -- Container for all touch device guis
  8783.         self.touchGui = Instance.new("ScreenGui")
  8784.         self.touchGui.Name = "TouchGui"
  8785.         self.touchGui.ResetOnSpawn = false
  8786.         self.touchGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
  8787.         self.touchGui.Enabled = self.humanoid ~= nil
  8788.    
  8789.         self.touchControlFrame = Instance.new("Frame")
  8790.         self.touchControlFrame.Name = "TouchControlFrame"
  8791.         self.touchControlFrame.Size = UDim2.new(1, 0, 1, 0)
  8792.         self.touchControlFrame.BackgroundTransparency = 1
  8793.         self.touchControlFrame.Parent = self.touchGui
  8794.    
  8795.         self.touchGui.Parent = self.playerGui
  8796.     end
  8797.    
  8798.     function ControlModule:GetClickToMoveController()
  8799.         if not self.controllers[ClickToMove] then
  8800.             self.controllers[ClickToMove] = ClickToMove.new(CONTROL_ACTION_PRIORITY)
  8801.         end
  8802.         return self.controllers[ClickToMove]
  8803.     end
  8804.    
  8805.     function ControlModule:IsJumping()
  8806.         if self.activeController then
  8807.             return self.activeController:GetIsJumping() or (self.touchJumpController and self.touchJumpController:GetIsJumping())
  8808.         end
  8809.         return false
  8810.     end
  8811.    
  8812.     return ControlModule.new()
  8813. end
  8814.  
  8815. function _PlayerModule()
  8816.     local PlayerModule = {}
  8817.     PlayerModule.__index = PlayerModule
  8818.     function PlayerModule.new()
  8819.         local self = setmetatable({},PlayerModule)
  8820.         self.cameras = _CameraModule()
  8821.         self.controls = _ControlModule()
  8822.         return self
  8823.     end
  8824.     function PlayerModule:GetCameras()
  8825.         return self.cameras
  8826.     end
  8827.     function PlayerModule:GetControls()
  8828.         return self.controls
  8829.     end
  8830.     function PlayerModule:GetClickToMoveController()
  8831.         return self.controls:GetClickToMoveController()
  8832.     end
  8833.     return PlayerModule.new()
  8834. end
  8835.  
  8836. function _sounds()
  8837.    
  8838.     local SetState = Instance.new("BindableEvent",script)
  8839.    
  8840.     local Players = game:GetService("Players")
  8841.     local RunService = game:GetService("RunService")
  8842.    
  8843.     local SOUND_DATA = {
  8844.         Climbing = {
  8845.             SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
  8846.             Looped = true,
  8847.         },
  8848.         Died = {
  8849.             SoundId = "rbxasset://sounds/uuhhh.mp3",
  8850.         },
  8851.         FreeFalling = {
  8852.             SoundId = "rbxasset://sounds/action_falling.mp3",
  8853.             Looped = true,
  8854.         },
  8855.         GettingUp = {
  8856.             SoundId = "rbxasset://sounds/action_get_up.mp3",
  8857.         },
  8858.         Jumping = {
  8859.             SoundId = "rbxasset://sounds/action_jump.mp3",
  8860.         },
  8861.         Landing = {
  8862.             SoundId = "rbxasset://sounds/action_jump_land.mp3",
  8863.         },
  8864.         Running = {
  8865.             SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
  8866.             Looped = true,
  8867.             Pitch = 1.85,
  8868.         },
  8869.         Splash = {
  8870.             SoundId = "rbxasset://sounds/impact_water.mp3",
  8871.         },
  8872.         Swimming = {
  8873.             SoundId = "rbxasset://sounds/action_swim.mp3",
  8874.             Looped = true,
  8875.             Pitch = 1.6,
  8876.         },
  8877.     }
  8878.    
  8879.      -- wait for the first of the passed signals to fire
  8880.     local function waitForFirst(...)
  8881.         local shunt = Instance.new("BindableEvent")
  8882.         local slots = {...}
  8883.    
  8884.         local function fire(...)
  8885.             for i = 1, #slots do
  8886.                 slots[i]:Disconnect()
  8887.             end
  8888.    
  8889.             return shunt:Fire(...)
  8890.         end
  8891.    
  8892.         for i = 1, #slots do
  8893.             slots[i] = slots[i]:Connect(fire)
  8894.         end
  8895.    
  8896.         return shunt.Event:Wait()
  8897.     end
  8898.    
  8899.     -- map a value from one range to another
  8900.     local function map(x, inMin, inMax, outMin, outMax)
  8901.         return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
  8902.     end
  8903.    
  8904.     local function playSound(sound)
  8905.         sound.TimePosition = 0
  8906.         sound.Playing = true
  8907.     end
  8908.    
  8909.     local function stopSound(sound)
  8910.         sound.Playing = false
  8911.         sound.TimePosition = 0
  8912.     end
  8913.    
  8914.     local function shallowCopy(t)
  8915.         local out = {}
  8916.         for k, v in pairs(t) do
  8917.             out[k] = v
  8918.         end
  8919.         return out
  8920.     end
  8921.    
  8922.     local function initializeSoundSystem(player, humanoid, rootPart)
  8923.         local sounds = {}
  8924.    
  8925.         -- initialize sounds
  8926.         for name, props in pairs(SOUND_DATA) do
  8927.             local sound = Instance.new("Sound")
  8928.             sound.Name = name
  8929.    
  8930.             -- set default values
  8931.             sound.Archivable = false
  8932.             sound.EmitterSize = 5
  8933.             sound.MaxDistance = 150
  8934.             sound.Volume = 0.65
  8935.    
  8936.             for propName, propValue in pairs(props) do
  8937.                 sound[propName] = propValue
  8938.             end
  8939.    
  8940.             sound.Parent = rootPart
  8941.             sounds[name] = sound
  8942.         end
  8943.    
  8944.         local playingLoopedSounds = {}
  8945.    
  8946.         local function stopPlayingLoopedSounds(except)
  8947.             for sound in pairs(shallowCopy(playingLoopedSounds)) do
  8948.                 if sound ~= except then
  8949.                     sound.Playing = false
  8950.                     playingLoopedSounds[sound] = nil
  8951.                 end
  8952.             end
  8953.         end
  8954.    
  8955.         -- state transition callbacks
  8956.         local stateTransitions = {
  8957.             [Enum.HumanoidStateType.FallingDown] = function()
  8958.                 stopPlayingLoopedSounds()
  8959.             end,
  8960.    
  8961.             [Enum.HumanoidStateType.GettingUp] = function()
  8962.                 stopPlayingLoopedSounds()
  8963.                 playSound(sounds.GettingUp)
  8964.             end,
  8965.    
  8966.             [Enum.HumanoidStateType.Jumping] = function()
  8967.                 stopPlayingLoopedSounds()
  8968.                 playSound(sounds.Jumping)
  8969.             end,
  8970.    
  8971.             [Enum.HumanoidStateType.Swimming] = function()
  8972.                 local verticalSpeed = math.abs(rootPart.Velocity.Y)
  8973.                 if verticalSpeed > 0.1 then
  8974.                     sounds.Splash.Volume = math.clamp(map(verticalSpeed, 100, 350, 0.28, 1), 0, 1)
  8975.                     playSound(sounds.Splash)
  8976.                 end
  8977.                 stopPlayingLoopedSounds(sounds.Swimming)
  8978.                 sounds.Swimming.Playing = true
  8979.                 playingLoopedSounds[sounds.Swimming] = true
  8980.             end,
  8981.    
  8982.             [Enum.HumanoidStateType.Freefall] = function()
  8983.                 sounds.FreeFalling.Volume = 0
  8984.                 stopPlayingLoopedSounds(sounds.FreeFalling)
  8985.                 playingLoopedSounds[sounds.FreeFalling] = true
  8986.             end,
  8987.    
  8988.             [Enum.HumanoidStateType.Landed] = function()
  8989.                 stopPlayingLoopedSounds()
  8990.                 local verticalSpeed = math.abs(rootPart.Velocity.Y)
  8991.                 if verticalSpeed > 75 then
  8992.                     sounds.Landing.Volume = math.clamp(map(verticalSpeed, 50, 100, 0, 1), 0, 1)
  8993.                     playSound(sounds.Landing)
  8994.                 end
  8995.             end,
  8996.    
  8997.             [Enum.HumanoidStateType.Running] = function()
  8998.                 stopPlayingLoopedSounds(sounds.Running)
  8999.                 sounds.Running.Playing = true
  9000.                 playingLoopedSounds[sounds.Running] = true
  9001.             end,
  9002.    
  9003.             [Enum.HumanoidStateType.Climbing] = function()
  9004.                 local sound = sounds.Climbing
  9005.                 if math.abs(rootPart.Velocity.Y) > 0.1 then
  9006.                     sound.Playing = true
  9007.                     stopPlayingLoopedSounds(sound)
  9008.                 else
  9009.                     stopPlayingLoopedSounds()
  9010.                 end
  9011.                 playingLoopedSounds[sound] = true
  9012.             end,
  9013.    
  9014.             [Enum.HumanoidStateType.Seated] = function()
  9015.                 stopPlayingLoopedSounds()
  9016.             end,
  9017.    
  9018.             [Enum.HumanoidStateType.Dead] = function()
  9019.                 stopPlayingLoopedSounds()
  9020.                 playSound(sounds.Died)
  9021.             end,
  9022.         }
  9023.    
  9024.         -- updaters for looped sounds
  9025.         local loopedSoundUpdaters = {
  9026.             [sounds.Climbing] = function(dt, sound, vel)
  9027.                 sound.Playing = vel.Magnitude > 0.1
  9028.             end,
  9029.    
  9030.             [sounds.FreeFalling] = function(dt, sound, vel)
  9031.                 if vel.Magnitude > 75 then
  9032.                     sound.Volume = math.clamp(sound.Volume + 0.9*dt, 0, 1)
  9033.                 else
  9034.                     sound.Volume = 0
  9035.                 end
  9036.             end,
  9037.    
  9038.             [sounds.Running] = function(dt, sound, vel)
  9039.                 sound.Playing = vel.Magnitude > 0.5 and humanoid.MoveDirection.Magnitude > 0.5
  9040.             end,
  9041.         }
  9042.    
  9043.         -- state substitutions to avoid duplicating entries in the state table
  9044.         local stateRemap = {
  9045.             [Enum.HumanoidStateType.RunningNoPhysics] = Enum.HumanoidStateType.Running,
  9046.         }
  9047.    
  9048.         local activeState = stateRemap[humanoid:GetState()] or humanoid:GetState()
  9049.         local activeConnections = {}
  9050.    
  9051.         local stateChangedConn = humanoid.StateChanged:Connect(function(_, state)
  9052.             state = stateRemap[state] or state
  9053.    
  9054.             if state ~= activeState then
  9055.                 local transitionFunc = stateTransitions[state]
  9056.    
  9057.                 if transitionFunc then
  9058.                     transitionFunc()
  9059.                 end
  9060.    
  9061.                 activeState = state
  9062.             end
  9063.         end)
  9064.        
  9065.         local customStateChangedConn = SetState.Event:Connect(function(state)
  9066.             state = stateRemap[state] or state
  9067.    
  9068.             if state ~= activeState then
  9069.                 local transitionFunc = stateTransitions[state]
  9070.    
  9071.                 if transitionFunc then
  9072.                     transitionFunc()
  9073.                 end
  9074.    
  9075.                 activeState = state
  9076.             end
  9077.         end)
  9078.    
  9079.         local steppedConn = RunService.Stepped:Connect(function(_, worldDt)
  9080.             -- update looped sounds on stepped
  9081.             for sound in pairs(playingLoopedSounds) do
  9082.                 local updater = loopedSoundUpdaters[sound]
  9083.    
  9084.                 if updater then
  9085.                     updater(worldDt, sound, rootPart.Velocity)
  9086.                 end
  9087.             end
  9088.         end)
  9089.    
  9090.         local humanoidAncestryChangedConn
  9091.         local rootPartAncestryChangedConn
  9092.         local characterAddedConn
  9093.    
  9094.         local function terminate()
  9095.             stateChangedConn:Disconnect()
  9096.             customStateChangedConn:Disconnect()
  9097.             steppedConn:Disconnect()
  9098.             humanoidAncestryChangedConn:Disconnect()
  9099.             rootPartAncestryChangedConn:Disconnect()
  9100.             characterAddedConn:Disconnect()
  9101.         end
  9102.    
  9103.         humanoidAncestryChangedConn = humanoid.AncestryChanged:Connect(function(_, parent)
  9104.             if not parent then
  9105.                 terminate()
  9106.             end
  9107.         end)
  9108.    
  9109.         rootPartAncestryChangedConn = rootPart.AncestryChanged:Connect(function(_, parent)
  9110.             if not parent then
  9111.                 terminate()
  9112.             end
  9113.         end)
  9114.    
  9115.         characterAddedConn = player.CharacterAdded:Connect(terminate)
  9116.     end
  9117.    
  9118.     local function playerAdded(player)
  9119.         local function characterAdded(character)
  9120.             -- Avoiding memory leaks in the face of Character/Humanoid/RootPart lifetime has a few complications:
  9121.             -- * character deparenting is a Remove instead of a Destroy, so signals are not cleaned up automatically.
  9122.             -- ** must use a waitForFirst on everything and listen for hierarchy changes.
  9123.             -- * the character might not be in the dm by the time CharacterAdded fires
  9124.             -- ** constantly check consistency with player.Character and abort if CharacterAdded is fired again
  9125.             -- * Humanoid may not exist immediately, and by the time it's inserted the character might be deparented.
  9126.             -- * RootPart probably won't exist immediately.
  9127.             -- ** by the time RootPart is inserted and Humanoid.RootPart is set, the character or the humanoid might be deparented.
  9128.    
  9129.             if not character.Parent then
  9130.                 waitForFirst(character.AncestryChanged, player.CharacterAdded)
  9131.             end
  9132.    
  9133.             if player.Character ~= character or not character.Parent then
  9134.                 return
  9135.             end
  9136.    
  9137.             local humanoid = character:FindFirstChildOfClass("Humanoid")
  9138.             while character:IsDescendantOf(game) and not humanoid do
  9139.                 waitForFirst(character.ChildAdded, character.AncestryChanged, player.CharacterAdded)
  9140.                 humanoid = character:FindFirstChildOfClass("Humanoid")
  9141.             end
  9142.    
  9143.             if player.Character ~= character or not character:IsDescendantOf(game) then
  9144.                 return
  9145.             end
  9146.    
  9147.             -- must rely on HumanoidRootPart naming because Humanoid.RootPart does not fire changed signals
  9148.             local rootPart = character:FindFirstChild("HumanoidRootPart")
  9149.             while character:IsDescendantOf(game) and not rootPart do
  9150.                 waitForFirst(character.ChildAdded, character.AncestryChanged, humanoid.AncestryChanged, player.CharacterAdded)
  9151.                 rootPart = character:FindFirstChild("HumanoidRootPart")
  9152.             end
  9153.    
  9154.             if rootPart and humanoid:IsDescendantOf(game) and character:IsDescendantOf(game) and player.Character == character then
  9155.                 initializeSoundSystem(player, humanoid, rootPart)
  9156.             end
  9157.         end
  9158.    
  9159.         if player.Character then
  9160.             characterAdded(player.Character)
  9161.         end
  9162.         player.CharacterAdded:Connect(characterAdded)
  9163.     end
  9164.    
  9165.     Players.PlayerAdded:Connect(playerAdded)
  9166.     for _, player in ipairs(Players:GetPlayers()) do
  9167.         playerAdded(player)
  9168.     end
  9169.     return SetState
  9170. end
  9171.  
  9172. function _StateTracker()
  9173.     local EPSILON = 0.1
  9174.    
  9175.     local SPEED = {
  9176.         ["onRunning"] = true,
  9177.         ["onClimbing"] = true
  9178.     }
  9179.    
  9180.     local INAIR = {
  9181.         ["onFreeFall"] = true,
  9182.         ["onJumping"] = true
  9183.     }
  9184.    
  9185.     local STATEMAP = {
  9186.         ["onRunning"] = Enum.HumanoidStateType.Running,
  9187.         ["onJumping"] = Enum.HumanoidStateType.Jumping,
  9188.         ["onFreeFall"] = Enum.HumanoidStateType.Freefall
  9189.     }
  9190.    
  9191.     local StateTracker = {}
  9192.     StateTracker.__index = StateTracker
  9193.    
  9194.     function StateTracker.new(humanoid, soundState)
  9195.         local self = setmetatable({}, StateTracker)
  9196.        
  9197.         self.Humanoid = humanoid
  9198.         self.HRP = humanoid.RootPart
  9199.        
  9200.         self.Speed = 0
  9201.         self.State = "onRunning"
  9202.         self.Jumped = false
  9203.         self.JumpTick = tick()
  9204.        
  9205.         self.SoundState = soundState
  9206.        
  9207.         self._ChangedEvent = Instance.new("BindableEvent")
  9208.         self.Changed = self._ChangedEvent.Event
  9209.        
  9210.         return self
  9211.     end
  9212.    
  9213.     function StateTracker:Destroy()
  9214.         self._ChangedEvent:Destroy()
  9215.     end
  9216.    
  9217.     function StateTracker:RequestedJump()
  9218.         self.Jumped = true
  9219.         self.JumpTick = tick()
  9220.     end
  9221.    
  9222.     function StateTracker:OnStep(gravityUp, grounded, isMoving)
  9223.         local cVelocity = self.HRP.Velocity
  9224.         local gVelocity = cVelocity:Dot(gravityUp)
  9225.        
  9226.         local oldState, oldSpeed = self.State, self.Speed
  9227.        
  9228.         local newState
  9229.         local newSpeed = cVelocity.Magnitude
  9230.    
  9231.         if (not grounded) then
  9232.             if (gVelocity > 0) then
  9233.                 if (self.Jumped) then
  9234.                     newState = "onJumping"
  9235.                 else
  9236.                     newState = "onFreeFall"
  9237.                 end
  9238.             else
  9239.                 if (self.Jumped) then
  9240.                     self.Jumped = false
  9241.                 end
  9242.                 newState = "onFreeFall"
  9243.             end
  9244.         else
  9245.             if (self.Jumped and tick() - self.JumpTick > 0.1) then
  9246.                 self.Jumped = false
  9247.             end
  9248.             newSpeed = (cVelocity - gVelocity*gravityUp).Magnitude
  9249.             newState = "onRunning"
  9250.         end
  9251.        
  9252.         newSpeed = isMoving and newSpeed or 0
  9253.        
  9254.         if (oldState ~= newState or (SPEED[newState] and math.abs(oldSpeed - newSpeed) > EPSILON)) then
  9255.             self.State = newState
  9256.             self.Speed = newSpeed
  9257.             self.SoundState:Fire(STATEMAP[newState])
  9258.             self._ChangedEvent:Fire(self.State, self.Speed)
  9259.         end
  9260.     end
  9261.    
  9262.     return StateTracker
  9263. end
  9264. function _InitObjects()
  9265.     local model = workspace:FindFirstChild("objects") or game:GetObjects("rbxassetid://5045408489")[1]
  9266.     local SPHERE = model:WaitForChild("Sphere")
  9267.     local FLOOR = model:WaitForChild("Floor")
  9268.     local VFORCE = model:WaitForChild("VectorForce")
  9269.     local BGYRO = model:WaitForChild("BodyGyro")
  9270.     local function initObjects(self)
  9271.         local hrp = self.HRP
  9272.         local humanoid = self.Humanoid
  9273.         local sphere = SPHERE:Clone()
  9274.         sphere.Parent = self.Character
  9275.         local floor = FLOOR:Clone()
  9276.         floor.Parent = self.Character
  9277.         local isR15 = (humanoid.RigType == Enum.HumanoidRigType.R15)
  9278.         local height = isR15 and (humanoid.HipHeight + 0.05) or 2
  9279.         local weld = Instance.new("Weld")
  9280.         weld.C0 = CFrame.new(0, -height, 0.1)
  9281.         weld.Part0 = hrp
  9282.         weld.Part1 = sphere
  9283.         weld.Parent = sphere
  9284.         local weld2 = Instance.new("Weld")
  9285.         weld2.C0 = CFrame.new(0, -(height + 1.5), 0)
  9286.         weld2.Part0 = hrp
  9287.         weld2.Part1 = floor
  9288.         weld2.Parent = floor
  9289.         local gyro = BGYRO:Clone()
  9290.         gyro.CFrame = hrp.CFrame
  9291.         gyro.Parent = hrp
  9292.         local vForce = VFORCE:Clone()
  9293.         vForce.Attachment0 = isR15 and hrp:WaitForChild("RootRigAttachment") or hrp:WaitForChild("RootAttachment")
  9294.         vForce.Parent = hrp
  9295.         return sphere, gyro, vForce, floor
  9296.     end
  9297.     return initObjects
  9298. end
  9299. local plr = game.Players.LocalPlayer
  9300. local ms = plr:GetMouse()
  9301. local char
  9302. plr.CharacterAdded:Connect(function(c)
  9303.     char = c
  9304. end)
  9305. function _R6()
  9306.     function r6()
  9307.     local Figure = char
  9308.     local Torso = Figure:WaitForChild("Torso")
  9309.     local RightShoulder = Torso:WaitForChild("Right Shoulder")
  9310.     local LeftShoulder = Torso:WaitForChild("Left Shoulder")
  9311.     local RightHip = Torso:WaitForChild("Right Hip")
  9312.     local LeftHip = Torso:WaitForChild("Left Hip")
  9313.     local Neck = Torso:WaitForChild("Neck")
  9314.     local Humanoid = Figure:WaitForChild("Humanoid")
  9315.     local pose = "Standing"
  9316.     local currentAnim = ""
  9317.     local currentAnimInstance = nil
  9318.     local currentAnimTrack = nil
  9319.     local currentAnimKeyframeHandler = nil
  9320.     local currentAnimSpeed = 1.0
  9321.     local animTable = {}
  9322.     local animNames = {
  9323.         idle =  {  
  9324.                     { id = "http://www.roblox.com/asset/?id=180435571", weight = 9 },
  9325.                     { id = "http://www.roblox.com/asset/?id=180435792", weight = 1 }
  9326.                 },
  9327.         walk =  {  
  9328.                     { id = "http://www.roblox.com/asset/?id=180426354", weight = 10 }
  9329.                 },
  9330.         run =   {
  9331.                     { id = "run.xml", weight = 10 }
  9332.                 },
  9333.         jump =  {
  9334.                     { id = "http://www.roblox.com/asset/?id=125750702", weight = 10 }
  9335.                 },
  9336.         fall =  {
  9337.                     { id = "http://www.roblox.com/asset/?id=180436148", weight = 10 }
  9338.                 },
  9339.         climb = {
  9340.                     { id = "http://www.roblox.com/asset/?id=180436334", weight = 10 }
  9341.                 },
  9342.         sit =   {
  9343.                     { id = "http://www.roblox.com/asset/?id=178130996", weight = 10 }
  9344.                 }, 
  9345.         toolnone = {
  9346.                     { id = "http://www.roblox.com/asset/?id=182393478", weight = 10 }
  9347.                 },
  9348.         toolslash = {
  9349.                     { id = "http://www.roblox.com/asset/?id=129967390", weight = 10 }
  9350.     --              { id = "slash.xml", weight = 10 }
  9351.                 },
  9352.         toollunge = {
  9353.                     { id = "http://www.roblox.com/asset/?id=129967478", weight = 10 }
  9354.                 },
  9355.         wave = {
  9356.                     { id = "http://www.roblox.com/asset/?id=128777973", weight = 10 }
  9357.                 },
  9358.         point = {
  9359.                     { id = "http://www.roblox.com/asset/?id=128853357", weight = 10 }
  9360.                 },
  9361.         dance1 = {
  9362.                     { id = "http://www.roblox.com/asset/?id=182435998", weight = 10 },
  9363.                     { id = "http://www.roblox.com/asset/?id=182491037", weight = 10 },
  9364.                     { id = "http://www.roblox.com/asset/?id=182491065", weight = 10 }
  9365.                 },
  9366.         dance2 = {
  9367.                     { id = "http://www.roblox.com/asset/?id=182436842", weight = 10 },
  9368.                     { id = "http://www.roblox.com/asset/?id=182491248", weight = 10 },
  9369.                     { id = "http://www.roblox.com/asset/?id=182491277", weight = 10 }
  9370.                 },
  9371.         dance3 = {
  9372.                     { id = "http://www.roblox.com/asset/?id=182436935", weight = 10 },
  9373.                     { id = "http://www.roblox.com/asset/?id=182491368", weight = 10 },
  9374.                     { id = "http://www.roblox.com/asset/?id=182491423", weight = 10 }
  9375.                 },
  9376.         laugh = {
  9377.                     { id = "http://www.roblox.com/asset/?id=129423131", weight = 10 }
  9378.                 },
  9379.         cheer = {
  9380.                     { id = "http://www.roblox.com/asset/?id=129423030", weight = 10 }
  9381.                 },
  9382.     }
  9383.     local dances = {"dance1", "dance2", "dance3"}
  9384.     -- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
  9385.     local emoteNames = { wave = false, point = false, dance1 = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
  9386.     function configureAnimationSet(name, fileList)
  9387.         if (animTable[name] ~= nil) then
  9388.             for _, connection in pairs(animTable[name].connections) do
  9389.                 connection:disconnect()
  9390.             end
  9391.         end
  9392.         animTable[name] = {}
  9393.         animTable[name].count = 0
  9394.         animTable[name].totalWeight = 0
  9395.         animTable[name].connections = {}
  9396.         -- check for config values
  9397.         local config = script:FindFirstChild(name)
  9398.         if (config ~= nil) then
  9399.     --      print("Loading anims " .. name)
  9400.             table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
  9401.             table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
  9402.             local idx = 1
  9403.             for _, childPart in pairs(config:GetChildren()) do
  9404.                 if (childPart:IsA("Animation")) then
  9405.                     table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
  9406.                     animTable[name][idx] = {}
  9407.                     animTable[name][idx].anim = childPart
  9408.                     local weightObject = childPart:FindFirstChild("Weight")
  9409.                     if (weightObject == nil) then
  9410.                         animTable[name][idx].weight = 1
  9411.                     else
  9412.                         animTable[name][idx].weight = weightObject.Value
  9413.                     end
  9414.                     animTable[name].count = animTable[name].count + 1
  9415.                     animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
  9416.         --          print(name .. " [" .. idx .. "] " .. animTable[name][idx].anim.AnimationId .. " (" .. animTable[name][idx].weight .. ")")
  9417.                     idx = idx + 1
  9418.                 end
  9419.             end
  9420.         end
  9421.         -- fallback to defaults
  9422.         if (animTable[name].count <= 0) then
  9423.             for idx, anim in pairs(fileList) do
  9424.                 animTable[name][idx] = {}
  9425.                 animTable[name][idx].anim = Instance.new("Animation")
  9426.                 animTable[name][idx].anim.Name = name
  9427.                 animTable[name][idx].anim.AnimationId = anim.id
  9428.                 animTable[name][idx].weight = anim.weight
  9429.                 animTable[name].count = animTable[name].count + 1
  9430.                 animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
  9431.     --          print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
  9432.             end
  9433.         end
  9434.     end
  9435.     -- Setup animation objects
  9436.     function scriptChildModified(child)
  9437.         local fileList = animNames[child.Name]
  9438.         if (fileList ~= nil) then
  9439.             configureAnimationSet(child.Name, fileList)
  9440.         end
  9441.     end
  9442.    
  9443.     script.ChildAdded:connect(scriptChildModified)
  9444.     script.ChildRemoved:connect(scriptChildModified)
  9445.    
  9446.    
  9447.     for name, fileList in pairs(animNames) do
  9448.         configureAnimationSet(name, fileList)
  9449.     end
  9450.    
  9451.     -- ANIMATION
  9452.    
  9453.     -- declarations
  9454.     local toolAnim = "None"
  9455.     local toolAnimTime = 0
  9456.    
  9457.     local jumpAnimTime = 0
  9458.     local jumpAnimDuration = 0.3
  9459.    
  9460.     local toolTransitionTime = 0.1
  9461.     local fallTransitionTime = 0.3
  9462.     local jumpMaxLimbVelocity = 0.75
  9463.    
  9464.     -- functions
  9465.    
  9466.     function stopAllAnimations()
  9467.         local oldAnim = currentAnim
  9468.    
  9469.         -- return to idle if finishing an emote
  9470.         if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
  9471.             oldAnim = "idle"
  9472.         end
  9473.    
  9474.         currentAnim = ""
  9475.         currentAnimInstance = nil
  9476.         if (currentAnimKeyframeHandler ~= nil) then
  9477.             currentAnimKeyframeHandler:disconnect()
  9478.         end
  9479.    
  9480.         if (currentAnimTrack ~= nil) then
  9481.             currentAnimTrack:Stop()
  9482.             currentAnimTrack:Destroy()
  9483.             currentAnimTrack = nil
  9484.         end
  9485.         return oldAnim
  9486.     end
  9487.    
  9488.     function setAnimationSpeed(speed)
  9489.         if speed ~= currentAnimSpeed then
  9490.             currentAnimSpeed = speed
  9491.             currentAnimTrack:AdjustSpeed(currentAnimSpeed)
  9492.         end
  9493.     end
  9494.    
  9495.     function keyFrameReachedFunc(frameName)
  9496.         if (frameName == "End") then
  9497.    
  9498.             local repeatAnim = currentAnim
  9499.             -- return to idle if finishing an emote
  9500.             if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
  9501.                 repeatAnim = "idle"
  9502.             end
  9503.            
  9504.             local animSpeed = currentAnimSpeed
  9505.             playAnimation(repeatAnim, 0.0, Humanoid)
  9506.             setAnimationSpeed(animSpeed)
  9507.         end
  9508.     end
  9509.    
  9510.     -- Preload animations
  9511.     function playAnimation(animName, transitionTime, humanoid)
  9512.            
  9513.         local roll = math.random(1, animTable[animName].totalWeight)
  9514.         local origRoll = roll
  9515.         local idx = 1
  9516.         while (roll > animTable[animName][idx].weight) do
  9517.             roll = roll - animTable[animName][idx].weight
  9518.             idx = idx + 1
  9519.         end
  9520.     --      print(animName .. " " .. idx .. " [" .. origRoll .. "]")
  9521.         local anim = animTable[animName][idx].anim
  9522.    
  9523.         -- switch animation    
  9524.         if (anim ~= currentAnimInstance) then
  9525.            
  9526.             if (currentAnimTrack ~= nil) then
  9527.                 currentAnimTrack:Stop(transitionTime)
  9528.                 currentAnimTrack:Destroy()
  9529.             end
  9530.    
  9531.             currentAnimSpeed = 1.0
  9532.        
  9533.             -- load it to the humanoid; get AnimationTrack
  9534.             currentAnimTrack = humanoid:LoadAnimation(anim)
  9535.             currentAnimTrack.Priority = Enum.AnimationPriority.Core
  9536.              
  9537.             -- play the animation
  9538.             currentAnimTrack:Play(transitionTime)
  9539.             currentAnim = animName
  9540.             currentAnimInstance = anim
  9541.    
  9542.             -- set up keyframe name triggers
  9543.             if (currentAnimKeyframeHandler ~= nil) then
  9544.                 currentAnimKeyframeHandler:disconnect()
  9545.             end
  9546.             currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
  9547.            
  9548.         end
  9549.    
  9550.     end
  9551.    
  9552.     -------------------------------------------------------------------------------------------
  9553.     -------------------------------------------------------------------------------------------
  9554.    
  9555.     local toolAnimName = ""
  9556.     local toolAnimTrack = nil
  9557.     local toolAnimInstance = nil
  9558.     local currentToolAnimKeyframeHandler = nil
  9559.    
  9560.     function toolKeyFrameReachedFunc(frameName)
  9561.         if (frameName == "End") then
  9562.     --      print("Keyframe : ".. frameName)   
  9563.             playToolAnimation(toolAnimName, 0.0, Humanoid)
  9564.         end
  9565.     end
  9566.    
  9567.    
  9568.     function playToolAnimation(animName, transitionTime, humanoid, priority)     
  9569.            
  9570.             local roll = math.random(1, animTable[animName].totalWeight)
  9571.             local origRoll = roll
  9572.             local idx = 1
  9573.             while (roll > animTable[animName][idx].weight) do
  9574.                 roll = roll - animTable[animName][idx].weight
  9575.                 idx = idx + 1
  9576.             end
  9577.     --      print(animName .. " * " .. idx .. " [" .. origRoll .. "]")
  9578.             local anim = animTable[animName][idx].anim
  9579.    
  9580.             if (toolAnimInstance ~= anim) then
  9581.                
  9582.                 if (toolAnimTrack ~= nil) then
  9583.                     toolAnimTrack:Stop()
  9584.                     toolAnimTrack:Destroy()
  9585.                     transitionTime = 0
  9586.                 end
  9587.                        
  9588.                 -- load it to the humanoid; get AnimationTrack
  9589.                 toolAnimTrack = humanoid:LoadAnimation(anim)
  9590.                 if priority then
  9591.                     toolAnimTrack.Priority = priority
  9592.                 end
  9593.                  
  9594.                 -- play the animation
  9595.                 toolAnimTrack:Play(transitionTime)
  9596.                 toolAnimName = animName
  9597.                 toolAnimInstance = anim
  9598.    
  9599.                 currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
  9600.             end
  9601.     end
  9602.    
  9603.     function stopToolAnimations()
  9604.         local oldAnim = toolAnimName
  9605.    
  9606.         if (currentToolAnimKeyframeHandler ~= nil) then
  9607.             currentToolAnimKeyframeHandler:disconnect()
  9608.         end
  9609.    
  9610.         toolAnimName = ""
  9611.         toolAnimInstance = nil
  9612.         if (toolAnimTrack ~= nil) then
  9613.             toolAnimTrack:Stop()
  9614.             toolAnimTrack:Destroy()
  9615.             toolAnimTrack = nil
  9616.         end
  9617.    
  9618.    
  9619.         return oldAnim
  9620.     end
  9621.    
  9622.     -------------------------------------------------------------------------------------------
  9623.     -------------------------------------------------------------------------------------------
  9624.    
  9625.    
  9626.     function onRunning(speed)
  9627.         if speed > 0.01 then
  9628.             playAnimation("walk", 0.1, Humanoid)
  9629.             if currentAnimInstance and currentAnimInstance.AnimationId == "http://www.roblox.com/asset/?id=180426354" then
  9630.                 setAnimationSpeed(speed / 14.5)
  9631.             end
  9632.             pose = "Running"
  9633.         else
  9634.             if emoteNames[currentAnim] == nil then
  9635.                 playAnimation("idle", 0.1, Humanoid)
  9636.                 pose = "Standing"
  9637.             end
  9638.         end
  9639.     end
  9640.    
  9641.     function onDied()
  9642.         pose = "Dead"
  9643.     end
  9644.    
  9645.     function onJumping()
  9646.         playAnimation("jump", 0.1, Humanoid)
  9647.         jumpAnimTime = jumpAnimDuration
  9648.         pose = "Jumping"
  9649.     end
  9650.    
  9651.     function onClimbing(speed)
  9652.         playAnimation("climb", 0.1, Humanoid)
  9653.         setAnimationSpeed(speed / 12.0)
  9654.         pose = "Climbing"
  9655.     end
  9656.    
  9657.     function onGettingUp()
  9658.         pose = "GettingUp"
  9659.     end
  9660.    
  9661.     function onFreeFall()
  9662.         if (jumpAnimTime <= 0) then
  9663.             playAnimation("fall", fallTransitionTime, Humanoid)
  9664.         end
  9665.         pose = "FreeFall"
  9666.     end
  9667.    
  9668.     function onFallingDown()
  9669.         pose = "FallingDown"
  9670.     end
  9671.    
  9672.     function onSeated()
  9673.         pose = "Seated"
  9674.     end
  9675.    
  9676.     function onPlatformStanding()
  9677.         pose = "PlatformStanding"
  9678.     end
  9679.    
  9680.     function onSwimming(speed)
  9681.         if speed > 0 then
  9682.             pose = "Running"
  9683.         else
  9684.             pose = "Standing"
  9685.         end
  9686.     end
  9687.    
  9688.     function getTool() 
  9689.         for _, kid in ipairs(Figure:GetChildren()) do
  9690.             if kid.className == "Tool" then return kid end
  9691.         end
  9692.         return nil
  9693.     end
  9694.    
  9695.     function getToolAnim(tool)
  9696.         for _, c in ipairs(tool:GetChildren()) do
  9697.             if c.Name == "toolanim" and c.className == "StringValue" then
  9698.                 return c
  9699.             end
  9700.         end
  9701.         return nil
  9702.     end
  9703.    
  9704.     function animateTool()
  9705.        
  9706.         if (toolAnim == "None") then
  9707.             playToolAnimation("toolnone", toolTransitionTime, Humanoid, Enum.AnimationPriority.Idle)
  9708.             return
  9709.         end
  9710.    
  9711.         if (toolAnim == "Slash") then
  9712.             playToolAnimation("toolslash", 0, Humanoid, Enum.AnimationPriority.Action)
  9713.             return
  9714.         end
  9715.    
  9716.         if (toolAnim == "Lunge") then
  9717.             playToolAnimation("toollunge", 0, Humanoid, Enum.AnimationPriority.Action)
  9718.             return
  9719.         end
  9720.     end
  9721.    
  9722.     function moveSit()
  9723.         RightShoulder.MaxVelocity = 0.15
  9724.         LeftShoulder.MaxVelocity = 0.15
  9725.         RightShoulder:SetDesiredAngle(3.14 /2)
  9726.         LeftShoulder:SetDesiredAngle(-3.14 /2)
  9727.         RightHip:SetDesiredAngle(3.14 /2)
  9728.         LeftHip:SetDesiredAngle(-3.14 /2)
  9729.     end
  9730.    
  9731.     local lastTick = 0
  9732.    
  9733.     function move(time)
  9734.         local amplitude = 1
  9735.         local frequency = 1
  9736.         local deltaTime = time - lastTick
  9737.         lastTick = time
  9738.    
  9739.         local climbFudge = 0
  9740.         local setAngles = false
  9741.    
  9742.         if (jumpAnimTime > 0) then
  9743.             jumpAnimTime = jumpAnimTime - deltaTime
  9744.         end
  9745.    
  9746.         if (pose == "FreeFall" and jumpAnimTime <= 0) then
  9747.             playAnimation("fall", fallTransitionTime, Humanoid)
  9748.         elseif (pose == "Seated") then
  9749.             playAnimation("sit", 0.5, Humanoid)
  9750.             return
  9751.         elseif (pose == "Running") then
  9752.             playAnimation("walk", 0.1, Humanoid)
  9753.         elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
  9754.     --      print("Wha " .. pose)
  9755.             stopAllAnimations()
  9756.             amplitude = 0.1
  9757.             frequency = 1
  9758.             setAngles = true
  9759.         end
  9760.    
  9761.         if (setAngles) then
  9762.             local desiredAngle = amplitude * math.sin(time * frequency)
  9763.    
  9764.             RightShoulder:SetDesiredAngle(desiredAngle + climbFudge)
  9765.             LeftShoulder:SetDesiredAngle(desiredAngle - climbFudge)
  9766.             RightHip:SetDesiredAngle(-desiredAngle)
  9767.             LeftHip:SetDesiredAngle(-desiredAngle)
  9768.         end
  9769.    
  9770.         -- Tool Animation handling
  9771.         local tool = getTool()
  9772.         if tool and tool:FindFirstChild("Handle") then
  9773.        
  9774.             local animStringValueObject = getToolAnim(tool)
  9775.    
  9776.             if animStringValueObject then
  9777.                 toolAnim = animStringValueObject.Value
  9778.                 -- message recieved, delete StringValue
  9779.                 animStringValueObject.Parent = nil
  9780.                 toolAnimTime = time + .3
  9781.             end
  9782.    
  9783.             if time > toolAnimTime then
  9784.                 toolAnimTime = 0
  9785.                 toolAnim = "None"
  9786.             end
  9787.    
  9788.             animateTool()      
  9789.         else
  9790.             stopToolAnimations()
  9791.             toolAnim = "None"
  9792.             toolAnimInstance = nil
  9793.             toolAnimTime = 0
  9794.         end
  9795.     end
  9796.    
  9797.    
  9798.     local events = {}
  9799.     local eventHum = Humanoid
  9800.    
  9801.     local function onUnhook()
  9802.         for i = 1, #events do
  9803.             events[i]:Disconnect()
  9804.         end
  9805.         events = {}
  9806.     end
  9807.    
  9808.     local function onHook()
  9809.         onUnhook()
  9810.        
  9811.         pose = eventHum.Sit and "Seated" or "Standing"
  9812.        
  9813.         events = {
  9814.             eventHum.Died:connect(onDied),
  9815.             eventHum.Running:connect(onRunning),
  9816.             eventHum.Jumping:connect(onJumping),
  9817.             eventHum.Climbing:connect(onClimbing),
  9818.             eventHum.GettingUp:connect(onGettingUp),
  9819.             eventHum.FreeFalling:connect(onFreeFall),
  9820.             eventHum.FallingDown:connect(onFallingDown),
  9821.             eventHum.Seated:connect(onSeated),
  9822.             eventHum.PlatformStanding:connect(onPlatformStanding),
  9823.             eventHum.Swimming:connect(onSwimming)
  9824.         }
  9825.     end
  9826.    
  9827.    
  9828.     onHook()
  9829.    
  9830.     -- setup emote chat hook
  9831.     game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
  9832.         local emote = ""
  9833.         if msg == "/e dance" then
  9834.             emote = dances[math.random(1, #dances)]
  9835.         elseif (string.sub(msg, 1, 3) == "/e ") then
  9836.             emote = string.sub(msg, 4)
  9837.         elseif (string.sub(msg, 1, 7) == "/emote ") then
  9838.             emote = string.sub(msg, 8)
  9839.         end
  9840.        
  9841.         if (pose == "Standing" and emoteNames[emote] ~= nil) then
  9842.             playAnimation(emote, 0.1, Humanoid)
  9843.         end
  9844.    
  9845.     end)
  9846.    
  9847.    
  9848.     -- main program
  9849.    
  9850.     -- initialize to idle
  9851.     playAnimation("idle", 0.1, Humanoid)
  9852.     pose = "Standing"
  9853.    
  9854.     spawn(function()
  9855.         while Figure.Parent ~= nil do
  9856.             local _, time = wait(0.1)
  9857.             move(time)
  9858.         end
  9859.     end)
  9860.    
  9861.     return {
  9862.         onRunning = onRunning,
  9863.         onDied = onDied,
  9864.         onJumping = onJumping,
  9865.         onClimbing = onClimbing,
  9866.         onGettingUp = onGettingUp,
  9867.         onFreeFall = onFreeFall,
  9868.         onFallingDown = onFallingDown,
  9869.         onSeated = onSeated,
  9870.         onPlatformStanding = onPlatformStanding,
  9871.         onHook = onHook,
  9872.         onUnhook = onUnhook
  9873.     }
  9874.    
  9875.     end
  9876.     return r6()
  9877. end
  9878.  
  9879. function _R15()
  9880.     local function r15()
  9881.        
  9882.     local Character = char
  9883.     local Humanoid = Character:WaitForChild("Humanoid")
  9884.     local pose = "Standing"
  9885.    
  9886.     local userNoUpdateOnLoopSuccess, userNoUpdateOnLoopValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserNoUpdateOnLoop") end)
  9887.     local userNoUpdateOnLoop = userNoUpdateOnLoopSuccess and userNoUpdateOnLoopValue
  9888.     local userAnimationSpeedDampeningSuccess, userAnimationSpeedDampeningValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserAnimationSpeedDampening") end)
  9889.     local userAnimationSpeedDampening = userAnimationSpeedDampeningSuccess and userAnimationSpeedDampeningValue
  9890.    
  9891.     local animateScriptEmoteHookFlagExists, animateScriptEmoteHookFlagEnabled = pcall(function()
  9892.         return UserSettings():IsUserFeatureEnabled("UserAnimateScriptEmoteHook")
  9893.     end)
  9894.     local FFlagAnimateScriptEmoteHook = animateScriptEmoteHookFlagExists and animateScriptEmoteHookFlagEnabled
  9895.    
  9896.     local AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
  9897.     local HumanoidHipHeight = 2
  9898.    
  9899.     local EMOTE_TRANSITION_TIME = 0.1
  9900.    
  9901.     local currentAnim = ""
  9902.     local currentAnimInstance = nil
  9903.     local currentAnimTrack = nil
  9904.     local currentAnimKeyframeHandler = nil
  9905.     local currentAnimSpeed = 1.0
  9906.    
  9907.     local runAnimTrack = nil
  9908.     local runAnimKeyframeHandler = nil
  9909.    
  9910.     local animTable = {}
  9911.     local animNames = {
  9912.         idle =  {  
  9913.                     { id = "http://www.roblox.com/asset/?id=507766666", weight = 1 },
  9914.                     { id = "http://www.roblox.com/asset/?id=507766951", weight = 1 },
  9915.                     { id = "http://www.roblox.com/asset/?id=507766388", weight = 9 }
  9916.                 },
  9917.         walk =  {  
  9918.                     { id = "http://www.roblox.com/asset/?id=507777826", weight = 10 }
  9919.                 },
  9920.         run =   {
  9921.                     { id = "http://www.roblox.com/asset/?id=507767714", weight = 10 }
  9922.                 },
  9923.         swim =  {
  9924.                     { id = "http://www.roblox.com/asset/?id=507784897", weight = 10 }
  9925.                 },
  9926.         swimidle =  {
  9927.                     { id = "http://www.roblox.com/asset/?id=507785072", weight = 10 }
  9928.                 },
  9929.         jump =  {
  9930.                     { id = "http://www.roblox.com/asset/?id=507765000", weight = 10 }
  9931.                 },
  9932.         fall =  {
  9933.                     { id = "http://www.roblox.com/asset/?id=507767968", weight = 10 }
  9934.                 },
  9935.         climb = {
  9936.                     { id = "http://www.roblox.com/asset/?id=507765644", weight = 10 }
  9937.                 },
  9938.         sit =   {
  9939.                     { id = "http://www.roblox.com/asset/?id=2506281703", weight = 10 }
  9940.                 }, 
  9941.         toolnone = {
  9942.                     { id = "http://www.roblox.com/asset/?id=507768375", weight = 10 }
  9943.                 },
  9944.         toolslash = {
  9945.                     { id = "http://www.roblox.com/asset/?id=522635514", weight = 10 }
  9946.                 },
  9947.         toollunge = {
  9948.                     { id = "http://www.roblox.com/asset/?id=522638767", weight = 10 }
  9949.                 },
  9950.         wave = {
  9951.                     { id = "http://www.roblox.com/asset/?id=507770239", weight = 10 }
  9952.                 },
  9953.         point = {
  9954.                     { id = "http://www.roblox.com/asset/?id=507770453", weight = 10 }
  9955.                 },
  9956.         dance = {
  9957.                     { id = "http://www.roblox.com/asset/?id=507771019", weight = 10 },
  9958.                     { id = "http://www.roblox.com/asset/?id=507771955", weight = 10 },
  9959.                     { id = "http://www.roblox.com/asset/?id=507772104", weight = 10 }
  9960.                 },
  9961.         dance2 = {
  9962.                     { id = "http://www.roblox.com/asset/?id=507776043", weight = 10 },
  9963.                     { id = "http://www.roblox.com/asset/?id=507776720", weight = 10 },
  9964.                     { id = "http://www.roblox.com/asset/?id=507776879", weight = 10 }
  9965.                 },
  9966.         dance3 = {
  9967.                     { id = "http://www.roblox.com/asset/?id=507777268", weight = 10 },
  9968.                     { id = "http://www.roblox.com/asset/?id=507777451", weight = 10 },
  9969.                     { id = "http://www.roblox.com/asset/?id=507777623", weight = 10 }
  9970.                 },
  9971.         laugh = {
  9972.                     { id = "http://www.roblox.com/asset/?id=507770818", weight = 10 }
  9973.                 },
  9974.         cheer = {
  9975.                     { id = "http://www.roblox.com/asset/?id=507770677", weight = 10 }
  9976.                 },
  9977.     }
  9978.    
  9979.     -- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
  9980.     local emoteNames = { wave = false, point = false, dance = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
  9981.    
  9982.     local PreloadAnimsUserFlag = false
  9983.     local PreloadedAnims = {}
  9984.     local successPreloadAnim, msgPreloadAnim = pcall(function()
  9985.         PreloadAnimsUserFlag = UserSettings():IsUserFeatureEnabled("UserPreloadAnimations")
  9986.     end)
  9987.     if not successPreloadAnim then
  9988.         PreloadAnimsUserFlag = false
  9989.     end
  9990.    
  9991.     math.randomseed(tick())
  9992.    
  9993.     function findExistingAnimationInSet(set, anim)
  9994.         if set == nil or anim == nil then
  9995.             return 0
  9996.         end
  9997.        
  9998.         for idx = 1, set.count, 1 do
  9999.             if set[idx].anim.AnimationId == anim.AnimationId then
  10000.                 return idx
  10001.             end
  10002.         end
  10003.        
  10004.         return 0
  10005.     end
  10006.    
  10007.     function configureAnimationSet(name, fileList)
  10008.         if (animTable[name] ~= nil) then
  10009.             for _, connection in pairs(animTable[name].connections) do
  10010.                 connection:disconnect()
  10011.             end
  10012.         end
  10013.         animTable[name] = {}
  10014.         animTable[name].count = 0
  10015.         animTable[name].totalWeight = 0
  10016.         animTable[name].connections = {}
  10017.    
  10018.         local allowCustomAnimations = true
  10019.    
  10020.         local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
  10021.         if not success then
  10022.             allowCustomAnimations = true
  10023.         end
  10024.    
  10025.         -- check for config values
  10026.         local config = script:FindFirstChild(name)
  10027.         if (allowCustomAnimations and config ~= nil) then
  10028.             table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
  10029.             table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
  10030.            
  10031.             local idx = 0
  10032.             for _, childPart in pairs(config:GetChildren()) do
  10033.                 if (childPart:IsA("Animation")) then
  10034.                     local newWeight = 1
  10035.                     local weightObject = childPart:FindFirstChild("Weight")
  10036.                     if (weightObject ~= nil) then
  10037.                         newWeight = weightObject.Value
  10038.                     end
  10039.                     animTable[name].count = animTable[name].count + 1
  10040.                     idx = animTable[name].count
  10041.                     animTable[name][idx] = {}
  10042.                     animTable[name][idx].anim = childPart
  10043.                     animTable[name][idx].weight = newWeight
  10044.                     animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
  10045.                     table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
  10046.                     table.insert(animTable[name].connections, childPart.ChildAdded:connect(function(property) configureAnimationSet(name, fileList) end))
  10047.                     table.insert(animTable[name].connections, childPart.ChildRemoved:connect(function(property) configureAnimationSet(name, fileList) end))
  10048.                 end
  10049.             end
  10050.         end
  10051.        
  10052.         -- fallback to defaults
  10053.         if (animTable[name].count <= 0) then
  10054.             for idx, anim in pairs(fileList) do
  10055.                 animTable[name][idx] = {}
  10056.                 animTable[name][idx].anim = Instance.new("Animation")
  10057.                 animTable[name][idx].anim.Name = name
  10058.                 animTable[name][idx].anim.AnimationId = anim.id
  10059.                 animTable[name][idx].weight = anim.weight
  10060.                 animTable[name].count = animTable[name].count + 1
  10061.                 animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
  10062.             end
  10063.         end
  10064.        
  10065.         -- preload anims
  10066.         if PreloadAnimsUserFlag then
  10067.             for i, animType in pairs(animTable) do
  10068.                 for idx = 1, animType.count, 1 do
  10069.                     if PreloadedAnims[animType[idx].anim.AnimationId] == nil then
  10070.                         Humanoid:LoadAnimation(animType[idx].anim)
  10071.                         PreloadedAnims[animType[idx].anim.AnimationId] = true
  10072.                     end            
  10073.                 end
  10074.             end
  10075.         end
  10076.     end
  10077.    
  10078.     ------------------------------------------------------------------------------------------------------------
  10079.    
  10080.     function configureAnimationSetOld(name, fileList)
  10081.         if (animTable[name] ~= nil) then
  10082.             for _, connection in pairs(animTable[name].connections) do
  10083.                 connection:disconnect()
  10084.             end
  10085.         end
  10086.         animTable[name] = {}
  10087.         animTable[name].count = 0
  10088.         animTable[name].totalWeight = 0
  10089.         animTable[name].connections = {}
  10090.    
  10091.         local allowCustomAnimations = true
  10092.    
  10093.         local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
  10094.         if not success then
  10095.             allowCustomAnimations = true
  10096.         end
  10097.    
  10098.         -- check for config values
  10099.         local config = script:FindFirstChild(name)
  10100.         if (allowCustomAnimations and config ~= nil) then
  10101.             table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
  10102.             table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
  10103.             local idx = 1
  10104.             for _, childPart in pairs(config:GetChildren()) do
  10105.                 if (childPart:IsA("Animation")) then
  10106.                     table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
  10107.                     animTable[name][idx] = {}
  10108.                     animTable[name][idx].anim = childPart
  10109.                     local weightObject = childPart:FindFirstChild("Weight")
  10110.                     if (weightObject == nil) then
  10111.                         animTable[name][idx].weight = 1
  10112.                     else
  10113.                         animTable[name][idx].weight = weightObject.Value
  10114.                     end
  10115.                     animTable[name].count = animTable[name].count + 1
  10116.                     animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
  10117.                     idx = idx + 1
  10118.                 end
  10119.             end
  10120.         end
  10121.    
  10122.         -- fallback to defaults
  10123.         if (animTable[name].count <= 0) then
  10124.             for idx, anim in pairs(fileList) do
  10125.                 animTable[name][idx] = {}
  10126.                 animTable[name][idx].anim = Instance.new("Animation")
  10127.                 animTable[name][idx].anim.Name = name
  10128.                 animTable[name][idx].anim.AnimationId = anim.id
  10129.                 animTable[name][idx].weight = anim.weight
  10130.                 animTable[name].count = animTable[name].count + 1
  10131.                 animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
  10132.                 -- print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
  10133.             end
  10134.         end
  10135.        
  10136.         -- preload anims
  10137.         if PreloadAnimsUserFlag then
  10138.             for i, animType in pairs(animTable) do
  10139.                 for idx = 1, animType.count, 1 do
  10140.                     Humanoid:LoadAnimation(animType[idx].anim)
  10141.                 end
  10142.             end
  10143.         end
  10144.     end
  10145.    
  10146.     -- Setup animation objects
  10147.     function scriptChildModified(child)
  10148.         local fileList = animNames[child.Name]
  10149.         if (fileList ~= nil) then
  10150.             configureAnimationSet(child.Name, fileList)
  10151.         end
  10152.     end
  10153.    
  10154.     script.ChildAdded:connect(scriptChildModified)
  10155.     script.ChildRemoved:connect(scriptChildModified)
  10156.    
  10157.    
  10158.     for name, fileList in pairs(animNames) do
  10159.         configureAnimationSet(name, fileList)
  10160.     end
  10161.    
  10162.     -- ANIMATION
  10163.    
  10164.     -- declarations
  10165.     local toolAnim = "None"
  10166.     local toolAnimTime = 0
  10167.    
  10168.     local jumpAnimTime = 0
  10169.     local jumpAnimDuration = 0.31
  10170.    
  10171.     local toolTransitionTime = 0.1
  10172.     local fallTransitionTime = 0.2
  10173.    
  10174.     local currentlyPlayingEmote = false
  10175.    
  10176.     -- functions
  10177.    
  10178.     function stopAllAnimations()
  10179.         local oldAnim = currentAnim
  10180.    
  10181.         -- return to idle if finishing an emote
  10182.         if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
  10183.             oldAnim = "idle"
  10184.         end
  10185.        
  10186.         if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
  10187.             oldAnim = "idle"
  10188.             currentlyPlayingEmote = false
  10189.         end
  10190.    
  10191.         currentAnim = ""
  10192.         currentAnimInstance = nil
  10193.         if (currentAnimKeyframeHandler ~= nil) then
  10194.             currentAnimKeyframeHandler:disconnect()
  10195.         end
  10196.    
  10197.         if (currentAnimTrack ~= nil) then
  10198.             currentAnimTrack:Stop()
  10199.             currentAnimTrack:Destroy()
  10200.             currentAnimTrack = nil
  10201.         end
  10202.    
  10203.         -- clean up walk if there is one
  10204.         if (runAnimKeyframeHandler ~= nil) then
  10205.             runAnimKeyframeHandler:disconnect()
  10206.         end
  10207.        
  10208.         if (runAnimTrack ~= nil) then
  10209.             runAnimTrack:Stop()
  10210.             runAnimTrack:Destroy()
  10211.             runAnimTrack = nil
  10212.         end
  10213.        
  10214.         return oldAnim
  10215.     end
  10216.    
  10217.     function getHeightScale()
  10218.         if Humanoid then
  10219.             if not Humanoid.AutomaticScalingEnabled then
  10220.                 return 1
  10221.             end
  10222.            
  10223.             local scale = Humanoid.HipHeight / HumanoidHipHeight
  10224.             if userAnimationSpeedDampening then
  10225.                 if AnimationSpeedDampeningObject == nil then
  10226.                     AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
  10227.                 end
  10228.                 if AnimationSpeedDampeningObject ~= nil then
  10229.                     scale = 1 + (Humanoid.HipHeight - HumanoidHipHeight) * AnimationSpeedDampeningObject.Value / HumanoidHipHeight
  10230.                 end
  10231.             end
  10232.             return scale
  10233.         end
  10234.         return 1
  10235.     end
  10236.    
  10237.     local smallButNotZero = 0.0001
  10238.     function setRunSpeed(speed)
  10239.         local speedScaled = speed * 1.25
  10240.         local heightScale = getHeightScale()
  10241.         local runSpeed = speedScaled / heightScale
  10242.    
  10243.         if runSpeed ~= currentAnimSpeed then
  10244.             if runSpeed < 0.33 then
  10245.                 currentAnimTrack:AdjustWeight(1.0)     
  10246.                 runAnimTrack:AdjustWeight(smallButNotZero)
  10247.             elseif runSpeed < 0.66 then
  10248.                 local weight = ((runSpeed - 0.33) / 0.33)
  10249.                 currentAnimTrack:AdjustWeight(1.0 - weight + smallButNotZero)
  10250.                 runAnimTrack:AdjustWeight(weight + smallButNotZero)
  10251.             else
  10252.                 currentAnimTrack:AdjustWeight(smallButNotZero)
  10253.                 runAnimTrack:AdjustWeight(1.0)
  10254.             end
  10255.             currentAnimSpeed = runSpeed
  10256.             runAnimTrack:AdjustSpeed(runSpeed)
  10257.             currentAnimTrack:AdjustSpeed(runSpeed)
  10258.         end
  10259.     end
  10260.    
  10261.     function setAnimationSpeed(speed)
  10262.         if currentAnim == "walk" then
  10263.                 setRunSpeed(speed)
  10264.         else
  10265.             if speed ~= currentAnimSpeed then
  10266.                 currentAnimSpeed = speed
  10267.                 currentAnimTrack:AdjustSpeed(currentAnimSpeed)
  10268.             end
  10269.         end
  10270.     end
  10271.    
  10272.     function keyFrameReachedFunc(frameName)
  10273.         if (frameName == "End") then
  10274.             if currentAnim == "walk" then
  10275.                 if userNoUpdateOnLoop == true then
  10276.                     if runAnimTrack.Looped ~= true then
  10277.                         runAnimTrack.TimePosition = 0.0
  10278.                     end
  10279.                     if currentAnimTrack.Looped ~= true then
  10280.                         currentAnimTrack.TimePosition = 0.0
  10281.                     end
  10282.                 else
  10283.                     runAnimTrack.TimePosition = 0.0
  10284.                     currentAnimTrack.TimePosition = 0.0
  10285.                 end
  10286.             else
  10287.                 local repeatAnim = currentAnim
  10288.                 -- return to idle if finishing an emote
  10289.                 if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
  10290.                     repeatAnim = "idle"
  10291.                 end
  10292.                
  10293.                 if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
  10294.                     if currentAnimTrack.Looped then
  10295.                         -- Allow the emote to loop
  10296.                         return
  10297.                     end
  10298.                    
  10299.                     repeatAnim = "idle"
  10300.                     currentlyPlayingEmote = false
  10301.                 end
  10302.                
  10303.                 local animSpeed = currentAnimSpeed
  10304.                 playAnimation(repeatAnim, 0.15, Humanoid)
  10305.                 setAnimationSpeed(animSpeed)
  10306.             end
  10307.         end
  10308.     end
  10309.    
  10310.     function rollAnimation(animName)
  10311.         local roll = math.random(1, animTable[animName].totalWeight)
  10312.         local origRoll = roll
  10313.         local idx = 1
  10314.         while (roll > animTable[animName][idx].weight) do
  10315.             roll = roll - animTable[animName][idx].weight
  10316.             idx = idx + 1
  10317.         end
  10318.         return idx
  10319.     end
  10320.    
  10321.     local function switchToAnim(anim, animName, transitionTime, humanoid)
  10322.         -- switch animation    
  10323.         if (anim ~= currentAnimInstance) then
  10324.            
  10325.             if (currentAnimTrack ~= nil) then
  10326.                 currentAnimTrack:Stop(transitionTime)
  10327.                 currentAnimTrack:Destroy()
  10328.             end
  10329.    
  10330.             if (runAnimTrack ~= nil) then
  10331.                 runAnimTrack:Stop(transitionTime)
  10332.                 runAnimTrack:Destroy()
  10333.                 if userNoUpdateOnLoop == true then
  10334.                     runAnimTrack = nil
  10335.                 end
  10336.             end
  10337.    
  10338.             currentAnimSpeed = 1.0
  10339.        
  10340.             -- load it to the humanoid; get AnimationTrack
  10341.             currentAnimTrack = humanoid:LoadAnimation(anim)
  10342.             currentAnimTrack.Priority = Enum.AnimationPriority.Core
  10343.              
  10344.             -- play the animation
  10345.             currentAnimTrack:Play(transitionTime)
  10346.             currentAnim = animName
  10347.             currentAnimInstance = anim
  10348.    
  10349.             -- set up keyframe name triggers
  10350.             if (currentAnimKeyframeHandler ~= nil) then
  10351.                 currentAnimKeyframeHandler:disconnect()
  10352.             end
  10353.             currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
  10354.            
  10355.             -- check to see if we need to blend a walk/run animation
  10356.             if animName == "walk" then
  10357.                 local runAnimName = "run"
  10358.                 local runIdx = rollAnimation(runAnimName)
  10359.    
  10360.                 runAnimTrack = humanoid:LoadAnimation(animTable[runAnimName][runIdx].anim)
  10361.                 runAnimTrack.Priority = Enum.AnimationPriority.Core
  10362.                 runAnimTrack:Play(transitionTime)      
  10363.                
  10364.                 if (runAnimKeyframeHandler ~= nil) then
  10365.                     runAnimKeyframeHandler:disconnect()
  10366.                 end
  10367.                 runAnimKeyframeHandler = runAnimTrack.KeyframeReached:connect(keyFrameReachedFunc) 
  10368.             end
  10369.         end
  10370.     end
  10371.    
  10372.     function playAnimation(animName, transitionTime, humanoid)  
  10373.         local idx = rollAnimation(animName)
  10374.         local anim = animTable[animName][idx].anim
  10375.    
  10376.         switchToAnim(anim, animName, transitionTime, humanoid)
  10377.         currentlyPlayingEmote = false
  10378.     end
  10379.    
  10380.     function playEmote(emoteAnim, transitionTime, humanoid)
  10381.         switchToAnim(emoteAnim, emoteAnim.Name, transitionTime, humanoid)
  10382.         currentlyPlayingEmote = true
  10383.     end
  10384.    
  10385.     -------------------------------------------------------------------------------------------
  10386.     -------------------------------------------------------------------------------------------
  10387.    
  10388.     local toolAnimName = ""
  10389.     local toolAnimTrack = nil
  10390.     local toolAnimInstance = nil
  10391.     local currentToolAnimKeyframeHandler = nil
  10392.    
  10393.     function toolKeyFrameReachedFunc(frameName)
  10394.         if (frameName == "End") then
  10395.             playToolAnimation(toolAnimName, 0.0, Humanoid)
  10396.         end
  10397.     end
  10398.    
  10399.    
  10400.     function playToolAnimation(animName, transitionTime, humanoid, priority)           
  10401.             local idx = rollAnimation(animName)
  10402.             local anim = animTable[animName][idx].anim
  10403.    
  10404.             if (toolAnimInstance ~= anim) then
  10405.                
  10406.                 if (toolAnimTrack ~= nil) then
  10407.                     toolAnimTrack:Stop()
  10408.                     toolAnimTrack:Destroy()
  10409.                     transitionTime = 0
  10410.                 end
  10411.                        
  10412.                 -- load it to the humanoid; get AnimationTrack
  10413.                 toolAnimTrack = humanoid:LoadAnimation(anim)
  10414.                 if priority then
  10415.                     toolAnimTrack.Priority = priority
  10416.                 end
  10417.                  
  10418.                 -- play the animation
  10419.                 toolAnimTrack:Play(transitionTime)
  10420.                 toolAnimName = animName
  10421.                 toolAnimInstance = anim
  10422.    
  10423.                 currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
  10424.             end
  10425.     end
  10426.    
  10427.     function stopToolAnimations()
  10428.         local oldAnim = toolAnimName
  10429.    
  10430.         if (currentToolAnimKeyframeHandler ~= nil) then
  10431.             currentToolAnimKeyframeHandler:disconnect()
  10432.         end
  10433.    
  10434.         toolAnimName = ""
  10435.         toolAnimInstance = nil
  10436.         if (toolAnimTrack ~= nil) then
  10437.             toolAnimTrack:Stop()
  10438.             toolAnimTrack:Destroy()
  10439.             toolAnimTrack = nil
  10440.         end
  10441.    
  10442.         return oldAnim
  10443.     end
  10444.    
  10445.     -------------------------------------------------------------------------------------------
  10446.     -------------------------------------------------------------------------------------------
  10447.     -- STATE CHANGE HANDLERS
  10448.    
  10449.     function onRunning(speed)
  10450.         if speed > 0.75 then
  10451.             local scale = 16.0
  10452.             playAnimation("walk", 0.2, Humanoid)
  10453.             setAnimationSpeed(speed / scale)
  10454.             pose = "Running"
  10455.         else
  10456.             if emoteNames[currentAnim] == nil and not currentlyPlayingEmote then
  10457.                 playAnimation("idle", 0.2, Humanoid)
  10458.                 pose = "Standing"
  10459.             end
  10460.         end
  10461.     end
  10462.    
  10463.     function onDied()
  10464.         pose = "Dead"
  10465.     end
  10466.    
  10467.     function onJumping()
  10468.         playAnimation("jump", 0.1, Humanoid)
  10469.         jumpAnimTime = jumpAnimDuration
  10470.         pose = "Jumping"
  10471.     end
  10472.    
  10473.     function onClimbing(speed)
  10474.         local scale = 5.0
  10475.         playAnimation("climb", 0.1, Humanoid)
  10476.         setAnimationSpeed(speed / scale)
  10477.         pose = "Climbing"
  10478.     end
  10479.    
  10480.     function onGettingUp()
  10481.         pose = "GettingUp"
  10482.     end
  10483.    
  10484.     function onFreeFall()
  10485.         if (jumpAnimTime <= 0) then
  10486.             playAnimation("fall", fallTransitionTime, Humanoid)
  10487.         end
  10488.         pose = "FreeFall"
  10489.     end
  10490.    
  10491.     function onFallingDown()
  10492.         pose = "FallingDown"
  10493.     end
  10494.    
  10495.     function onSeated()
  10496.         pose = "Seated"
  10497.     end
  10498.    
  10499.     function onPlatformStanding()
  10500.         pose = "PlatformStanding"
  10501.     end
  10502.    
  10503.     -------------------------------------------------------------------------------------------
  10504.     -------------------------------------------------------------------------------------------
  10505.    
  10506.     function onSwimming(speed)
  10507.         if speed > 1.00 then
  10508.             local scale = 10.0
  10509.             playAnimation("swim", 0.4, Humanoid)
  10510.             setAnimationSpeed(speed / scale)
  10511.             pose = "Swimming"
  10512.         else
  10513.             playAnimation("swimidle", 0.4, Humanoid)
  10514.             pose = "Standing"
  10515.         end
  10516.     end
  10517.    
  10518.     function animateTool()
  10519.         if (toolAnim == "None") then
  10520.             playToolAnimation("toolnone", toolTransitionTime, Humanoid, Enum.AnimationPriority.Idle)
  10521.             return
  10522.         end
  10523.    
  10524.         if (toolAnim == "Slash") then
  10525.             playToolAnimation("toolslash", 0, Humanoid, Enum.AnimationPriority.Action)
  10526.             return
  10527.         end
  10528.    
  10529.         if (toolAnim == "Lunge") then
  10530.             playToolAnimation("toollunge", 0, Humanoid, Enum.AnimationPriority.Action)
  10531.             return
  10532.         end
  10533.     end
  10534.    
  10535.     function getToolAnim(tool)
  10536.         for _, c in ipairs(tool:GetChildren()) do
  10537.             if c.Name == "toolanim" and c.className == "StringValue" then
  10538.                 return c
  10539.             end
  10540.         end
  10541.         return nil
  10542.     end
  10543.    
  10544.     local lastTick = 0
  10545.    
  10546.     function stepAnimate(currentTime)
  10547.         local amplitude = 1
  10548.         local frequency = 1
  10549.         local deltaTime = currentTime - lastTick
  10550.         lastTick = currentTime
  10551.    
  10552.         local climbFudge = 0
  10553.         local setAngles = false
  10554.    
  10555.         if (jumpAnimTime > 0) then
  10556.             jumpAnimTime = jumpAnimTime - deltaTime
  10557.         end
  10558.    
  10559.         if (pose == "FreeFall" and jumpAnimTime <= 0) then
  10560.             playAnimation("fall", fallTransitionTime, Humanoid)
  10561.         elseif (pose == "Seated") then
  10562.             playAnimation("sit", 0.5, Humanoid)
  10563.             return
  10564.         elseif (pose == "Running") then
  10565.             playAnimation("walk", 0.2, Humanoid)
  10566.         elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
  10567.             stopAllAnimations()
  10568.             amplitude = 0.1
  10569.             frequency = 1
  10570.             setAngles = true
  10571.         end
  10572.    
  10573.         -- Tool Animation handling
  10574.         local tool = Character:FindFirstChildOfClass("Tool")
  10575.         if tool and tool:FindFirstChild("Handle") then
  10576.             local animStringValueObject = getToolAnim(tool)
  10577.    
  10578.             if animStringValueObject then
  10579.                 toolAnim = animStringValueObject.Value
  10580.                 -- message recieved, delete StringValue
  10581.                 animStringValueObject.Parent = nil
  10582.                 toolAnimTime = currentTime + .3
  10583.             end
  10584.    
  10585.             if currentTime > toolAnimTime then
  10586.                 toolAnimTime = 0
  10587.                 toolAnim = "None"
  10588.             end
  10589.    
  10590.             animateTool()      
  10591.         else
  10592.             stopToolAnimations()
  10593.             toolAnim = "None"
  10594.             toolAnimInstance = nil
  10595.             toolAnimTime = 0
  10596.         end
  10597.     end
  10598.    
  10599.     -- connect events
  10600.    
  10601.     local events = {}
  10602.     local eventHum = Humanoid
  10603.    
  10604.     local function onUnhook()
  10605.         for i = 1, #events do
  10606.             events[i]:Disconnect()
  10607.         end
  10608.         events = {}
  10609.     end
  10610.    
  10611.     local function onHook()
  10612.         onUnhook()
  10613.        
  10614.         pose = eventHum.Sit and "Seated" or "Standing"
  10615.        
  10616.         events = {
  10617.             eventHum.Died:connect(onDied),
  10618.             eventHum.Running:connect(onRunning),
  10619.             eventHum.Jumping:connect(onJumping),
  10620.             eventHum.Climbing:connect(onClimbing),
  10621.             eventHum.GettingUp:connect(onGettingUp),
  10622.             eventHum.FreeFalling:connect(onFreeFall),
  10623.             eventHum.FallingDown:connect(onFallingDown),
  10624.             eventHum.Seated:connect(onSeated),
  10625.             eventHum.PlatformStanding:connect(onPlatformStanding),
  10626.             eventHum.Swimming:connect(onSwimming)
  10627.         }
  10628.     end
  10629.    
  10630.    
  10631.     onHook()
  10632.    
  10633.     -- setup emote chat hook
  10634.     game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
  10635.         local emote = ""
  10636.         if (string.sub(msg, 1, 3) == "/e ") then
  10637.             emote = string.sub(msg, 4)
  10638.         elseif (string.sub(msg, 1, 7) == "/emote ") then
  10639.             emote = string.sub(msg, 8)
  10640.         end
  10641.        
  10642.         if (pose == "Standing" and emoteNames[emote] ~= nil) then
  10643.             playAnimation(emote, EMOTE_TRANSITION_TIME, Humanoid)
  10644.         end
  10645.     end)
  10646.    
  10647.     --[[ emote bindable hook
  10648.     if FFlagAnimateScriptEmoteHook then
  10649.         script:WaitForChild("PlayEmote").OnInvoke = function(emote)
  10650.             -- Only play emotes when idling
  10651.             if pose ~= "Standing" then
  10652.                 return
  10653.             end
  10654.             if emoteNames[emote] ~= nil then
  10655.                 -- Default emotes
  10656.                 playAnimation(emote, EMOTE_TRANSITION_TIME, Humanoid)
  10657.                 return true
  10658.             elseif typeof(emote) == "Instance" and emote:IsA("Animation") then
  10659.                 -- Non-default emotes
  10660.                 playEmote(emote, EMOTE_TRANSITION_TIME, Humanoid)
  10661.                 return true
  10662.             end
  10663.             -- Return false to indicate that the emote could not be played
  10664.             return false
  10665.         end
  10666.     end
  10667.     ]]
  10668.     -- initialize to idle
  10669.     playAnimation("idle", 0.1, Humanoid)
  10670.     pose = "Standing"
  10671.     -- loop to handle timed state transitions and tool animations
  10672.     spawn(function()
  10673.         while Character.Parent ~= nil do
  10674.             local _, currentGameTime = wait(0.1)
  10675.             stepAnimate(currentGameTime)
  10676.         end
  10677.     end)
  10678.     return {
  10679.         onRunning = onRunning,
  10680.         onDied = onDied,
  10681.         onJumping = onJumping,
  10682.         onClimbing = onClimbing,
  10683.         onGettingUp = onGettingUp,
  10684.         onFreeFall = onFreeFall,
  10685.         onFallingDown = onFallingDown,
  10686.         onSeated = onSeated,
  10687.         onPlatformStanding = onPlatformStanding,
  10688.         onHook = onHook,
  10689.         onUnhook = onUnhook
  10690.     }
  10691.     end
  10692.     return r15()
  10693. end
  10694. while true do
  10695.     wait(.1)
  10696.     if plr.Character ~= nil then
  10697.         char = plr.Character
  10698.         break
  10699.     end
  10700. end
  10701. function _Controller()
  10702.     local humanoid = char:WaitForChild("Humanoid")
  10703.     local animFuncs = {}
  10704.     if (humanoid.RigType == Enum.HumanoidRigType.R6) then
  10705.         animFuncs = _R6()
  10706.     else
  10707.         animFuncs = _R15()
  10708.     end
  10709.     print("Animation succes")
  10710.     return animFuncs
  10711. end
  10712. function _AnimationHandler()
  10713. local AnimationHandler = {}
  10714. AnimationHandler.__index = AnimationHandler
  10715.  
  10716. function AnimationHandler.new(humanoid, animate)
  10717.     local self = setmetatable({}, AnimationHandler)
  10718.    
  10719.     self._AnimFuncs = _Controller()
  10720.     self.Humanoid = humanoid
  10721.    
  10722.     return self
  10723. end
  10724.  
  10725. function AnimationHandler:EnableDefault(bool)
  10726.     if (bool) then
  10727.         self._AnimFuncs.onHook()
  10728.     else
  10729.         self._AnimFuncs.onUnhook()
  10730.     end
  10731. end
  10732.  
  10733. function AnimationHandler:Run(name, ...)
  10734.     self._AnimFuncs[name](...)
  10735. end
  10736.  
  10737. return AnimationHandler
  10738. end
  10739.  
  10740. function _GravityController()
  10741.  
  10742. local ZERO = Vector3.new(0, 0, 0)
  10743. local UNIT_X = Vector3.new(1, 0, 0)
  10744. local UNIT_Y = Vector3.new(0, 1, 0)
  10745. local UNIT_Z = Vector3.new(0, 0, 1)
  10746. local VEC_XY = Vector3.new(1, 0, 1)
  10747.  
  10748. local IDENTITYCF = CFrame.new()
  10749.  
  10750. local JUMPMODIFIER = 1.2
  10751. local TRANSITION = 0.15
  10752. local WALKF = 200 / 3
  10753.  
  10754. local UIS = game:GetService("UserInputService")
  10755. local RUNSERVICE = game:GetService("RunService")
  10756.  
  10757. local InitObjects = _InitObjects()
  10758. local AnimationHandler = _AnimationHandler()
  10759. local StateTracker = _StateTracker()
  10760.  
  10761. -- Class
  10762.  
  10763. local GravityController = {}
  10764. GravityController.__index = GravityController
  10765.  
  10766. -- Private Functions
  10767.  
  10768. local function getRotationBetween(u, v, axis)
  10769.     local dot, uxv = u:Dot(v), u:Cross(v)
  10770.     if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
  10771.     return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
  10772. end
  10773.  
  10774. local function lookAt(pos, forward, up)
  10775.     local r = forward:Cross(up)
  10776.     local u = r:Cross(forward)
  10777.     return CFrame.fromMatrix(pos, r.Unit, u.Unit)
  10778. end
  10779.  
  10780. local function getMass(array)
  10781.     local mass = 0
  10782.     for _, part in next, array do
  10783.         if (part:IsA("BasePart")) then
  10784.             mass = mass + part:GetMass()
  10785.         end
  10786.     end
  10787.     return mass
  10788. end
  10789.  
  10790. -- Public Constructor
  10791. local ExecutedPlayerModule = _PlayerModule()
  10792. local ExecutedSounds = _sounds()
  10793. function GravityController.new(player)
  10794.     local self = setmetatable({}, GravityController)
  10795.  
  10796.     --[[ Camera
  10797.     local loaded = player.PlayerScripts:WaitForChild("PlayerScriptsLoader"):WaitForChild("Loaded")
  10798.     if (not loaded.Value) then
  10799.         --loaded.Changed:Wait()
  10800.     end
  10801.     ]]
  10802.     local playerModule = ExecutedPlayerModule
  10803.     self.Controls = playerModule:GetControls()
  10804.     self.Camera = playerModule:GetCameras()
  10805.    
  10806.     -- Player and character
  10807.     self.Player = player
  10808.     self.Character = player.Character
  10809.     self.Humanoid = player.Character:WaitForChild("Humanoid")
  10810.     self.HRP = player.Character:WaitForChild("HumanoidRootPart")
  10811.    
  10812.     -- Animation
  10813.     self.AnimationHandler = AnimationHandler.new(self.Humanoid, self.Character:WaitForChild("Animate"))
  10814.     self.AnimationHandler:EnableDefault(false)
  10815.     local ssss = game:GetService("Players").LocalPlayer.PlayerScripts:FindFirstChild("SetState") or Instance.new("BindableEvent",game:GetService("Players").LocalPlayer.PlayerScripts)
  10816.     local soundState = ExecutedSounds
  10817.     ssss.Name = "SetState"
  10818.    
  10819.     self.StateTracker = StateTracker.new(self.Humanoid, soundState)
  10820.     self.StateTracker.Changed:Connect(function(name, speed)
  10821.         self.AnimationHandler:Run(name, speed)
  10822.     end)
  10823.    
  10824.     -- Collider and forces
  10825.     local collider, gyro, vForce, floor = InitObjects(self)
  10826.    
  10827.     floor.Touched:Connect(function() end)
  10828.     collider.Touched:Connect(function() end)
  10829.    
  10830.     self.Collider = collider
  10831.     self.VForce = vForce
  10832.     self.Gyro = gyro
  10833.     self.Floor = floor
  10834.    
  10835.     -- Attachment to parts
  10836.     self.LastPart = workspace.Terrain
  10837.     self.LastPartCFrame = IDENTITYCF
  10838.    
  10839.     -- Gravity properties
  10840.     self.GravityUp = UNIT_Y
  10841.     self.Ignores = {self.Character}
  10842.    
  10843.     function self.Camera.GetUpVector(this, oldUpVector)
  10844.         return self.GravityUp
  10845.     end
  10846.    
  10847.     -- Events etc
  10848.     self.Humanoid.PlatformStand = true
  10849.    
  10850.     self.CharacterMass = getMass(self.Character:GetDescendants())
  10851.     self.Character.AncestryChanged:Connect(function() self.CharacterMass = getMass(self.Character:GetDescendants()) end)
  10852.    
  10853.     self.JumpCon = RUNSERVICE.RenderStepped:Connect(function(dt)
  10854.         if (self.Controls:IsJumping()) then
  10855.             self:OnJumpRequest()
  10856.         end
  10857.     end)
  10858.    
  10859.     self.DeathCon = self.Humanoid.Died:Connect(function() self:Destroy() end)
  10860.     self.SeatCon = self.Humanoid.Seated:Connect(function(active) if (active) then self:Destroy() end end)
  10861.     self.HeartCon = RUNSERVICE.Heartbeat:Connect(function(dt) self:OnHeartbeatStep(dt) end)
  10862.     RUNSERVICE:BindToRenderStep("GravityStep", Enum.RenderPriority.Input.Value + 1, function(dt) self:OnGravityStep(dt) end)
  10863.    
  10864.    
  10865.     return self
  10866. end
  10867.  
  10868. -- Public Methods
  10869.  
  10870. function GravityController:Destroy()
  10871.     self.JumpCon:Disconnect()
  10872.     self.DeathCon:Disconnect()
  10873.     self.SeatCon:Disconnect()
  10874.     self.HeartCon:Disconnect()
  10875.    
  10876.     RUNSERVICE:UnbindFromRenderStep("GravityStep")
  10877.    
  10878.     self.Collider:Destroy()
  10879.     self.VForce:Destroy()
  10880.     self.Gyro:Destroy()
  10881.     self.StateTracker:Destroy()
  10882.    
  10883.     self.Humanoid.PlatformStand = false
  10884.     self.AnimationHandler:EnableDefault(true)
  10885.    
  10886.     self.GravityUp = UNIT_Y
  10887. end
  10888.  
  10889. function GravityController:GetGravityUp(oldGravity)
  10890.     return oldGravity
  10891. end
  10892.  
  10893. function GravityController:IsGrounded(isJumpCheck)
  10894.     if (not isJumpCheck) then
  10895.         local parts = self.Floor:GetTouchingParts()
  10896.         for _, part in next, parts do
  10897.             if (not part:IsDescendantOf(self.Character)) then
  10898.                 return true
  10899.             end
  10900.         end
  10901.     else
  10902.         if (self.StateTracker.Jumped) then
  10903.             return false
  10904.         end
  10905.    
  10906.         -- 1. check we are touching something with the collider
  10907.         local valid = {}
  10908.         local parts = self.Collider:GetTouchingParts()
  10909.         for _, part in next, parts do
  10910.             if (not part:IsDescendantOf(self.Character)) then
  10911.                 table.insert(valid, part)
  10912.             end
  10913.         end
  10914.        
  10915.         if (#valid > 0) then
  10916.             -- 2. do a decently long downwards raycast
  10917.             local max = math.cos(self.Humanoid.MaxSlopeAngle)
  10918.             local ray = Ray.new(self.Collider.Position, -10 * self.GravityUp)
  10919.             local hit, pos, normal = workspace:FindPartOnRayWithWhitelist(ray, valid, true)
  10920.            
  10921.             -- 3. use slope to decide on jump
  10922.             if (hit and max <= self.GravityUp:Dot(normal)) then
  10923.                 return true
  10924.             end
  10925.         end
  10926.     end
  10927.     return false
  10928. end
  10929.  
  10930. function GravityController:OnJumpRequest()
  10931.     if (not self.StateTracker.Jumped and self:IsGrounded(true)) then
  10932.         local hrpVel = self.HRP.Velocity
  10933.         self.HRP.Velocity = hrpVel + self.GravityUp*self.Humanoid.JumpPower*JUMPMODIFIER
  10934.         self.StateTracker:RequestedJump()
  10935.     end
  10936. end
  10937.  
  10938. function GravityController:GetMoveVector()
  10939.     return self.Controls:GetMoveVector()
  10940. end
  10941.  
  10942. function GravityController:OnHeartbeatStep(dt)
  10943.     local ray = Ray.new(self.Collider.Position, -1.1*self.GravityUp)
  10944.     local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, self.Ignores)
  10945.     local lastPart = self.LastPart
  10946.    
  10947.     if (hit and lastPart and lastPart == hit) then
  10948.         local offset = self.LastPartCFrame:ToObjectSpace(self.HRP.CFrame)
  10949.         self.HRP.CFrame = hit.CFrame:ToWorldSpace(offset)
  10950.     end
  10951.    
  10952.     self.LastPart = hit
  10953.     self.LastPartCFrame = hit and hit.CFrame
  10954. end
  10955.  
  10956. function GravityController:OnGravityStep(dt)
  10957.     -- update gravity up vector
  10958.     local oldGravity = self.GravityUp
  10959.     local newGravity = self:GetGravityUp(oldGravity)
  10960.    
  10961.     local rotation = getRotationBetween(oldGravity, newGravity, workspace.CurrentCamera.CFrame.RightVector)
  10962.     rotation = IDENTITYCF:Lerp(rotation, TRANSITION)
  10963.    
  10964.     self.GravityUp = rotation * oldGravity
  10965.    
  10966.     -- get world move vector
  10967.     local camCF = workspace.CurrentCamera.CFrame
  10968.     local fDot = camCF.LookVector:Dot(newGravity)
  10969.     local cForward = math.abs(fDot) > 0.5 and -math.sign(fDot)*camCF.UpVector or camCF.LookVector
  10970.    
  10971.     local left = cForward:Cross(-newGravity).Unit
  10972.     local forward = -left:Cross(newGravity).Unit
  10973.    
  10974.     local move = self:GetMoveVector()
  10975.     local worldMove = forward*move.z - left*move.x
  10976.     worldMove = worldMove:Dot(worldMove) > 1 and worldMove.Unit or worldMove
  10977.    
  10978.     local isInputMoving = worldMove:Dot(worldMove) > 0
  10979.    
  10980.     -- get the desired character cframe
  10981.     local hrpCFLook = self.HRP.CFrame.LookVector
  10982.     local charF = hrpCFLook:Dot(forward)*forward + hrpCFLook:Dot(left)*left
  10983.     local charR = charF:Cross(newGravity).Unit
  10984.     local newCharCF = CFrame.fromMatrix(ZERO, charR, newGravity, -charF)
  10985.    
  10986.     local newCharRotation = IDENTITYCF
  10987.     if (isInputMoving) then
  10988.         newCharRotation = IDENTITYCF:Lerp(getRotationBetween(charF, worldMove, newGravity), 0.7)   
  10989.     end
  10990.    
  10991.     -- calculate forces
  10992.     local g = workspace.Gravity
  10993.     local gForce = g * self.CharacterMass * (UNIT_Y - newGravity)
  10994.    
  10995.     local cVelocity = self.HRP.Velocity
  10996.     local tVelocity = self.Humanoid.WalkSpeed * worldMove
  10997.     local gVelocity = cVelocity:Dot(newGravity)*newGravity
  10998.     local hVelocity = cVelocity - gVelocity
  10999.    
  11000.     if (hVelocity:Dot(hVelocity) < 1) then
  11001.         hVelocity = ZERO
  11002.     end
  11003.    
  11004.     local dVelocity = tVelocity - hVelocity
  11005.     local walkForceM = math.min(10000, WALKF * self.CharacterMass * dVelocity.Magnitude / (dt*60))
  11006.     local walkForce = walkForceM > 0 and dVelocity.Unit*walkForceM or ZERO
  11007.    
  11008.     -- mouse lock
  11009.     local charRotation = newCharRotation * newCharCF
  11010.    
  11011.     if (self.Camera:IsCamRelative()) then
  11012.         local lv = workspace.CurrentCamera.CFrame.LookVector
  11013.         local hlv = lv - charRotation.UpVector:Dot(lv)*charRotation.UpVector
  11014.         charRotation = lookAt(ZERO, hlv, charRotation.UpVector)
  11015.     end
  11016.    
  11017.     -- get state
  11018.     self.StateTracker:OnStep(self.GravityUp, self:IsGrounded(), isInputMoving)
  11019.  
  11020.     -- update values
  11021.     self.VForce.Force = walkForce + gForce
  11022.     self.Gyro.CFrame = charRotation
  11023. end
  11024. return GravityController
  11025. end
  11026. function _Draw3D()
  11027.     local module = {}
  11028.    
  11029.     -- Style Guide
  11030.    
  11031.     module.StyleGuide = {
  11032.         Point = {
  11033.             Thickness = 0.5;
  11034.             Color = Color3.new(0, 1, 0);
  11035.         },
  11036.        
  11037.         Line = {
  11038.             Thickness = 0.1;
  11039.             Color = Color3.new(1, 1, 0);
  11040.         },
  11041.        
  11042.         Ray = {
  11043.             Thickness = 0.1;
  11044.             Color = Color3.new(1, 0, 1);
  11045.         },
  11046.        
  11047.         Triangle = {
  11048.             Thickness = 0.05;
  11049.         };
  11050.        
  11051.         CFrame = {
  11052.             Thickness = 0.1;
  11053.             RightColor3 = Color3.new(1, 0, 0);
  11054.             UpColor3 = Color3.new(0, 1, 0);
  11055.             BackColor3 = Color3.new(0, 0, 1);
  11056.             PartProperties = {
  11057.                 Material = Enum.Material.SmoothPlastic;
  11058.             };
  11059.         }
  11060.     }
  11061.    
  11062.     -- CONSTANTS
  11063.    
  11064.     local WEDGE = Instance.new("WedgePart")
  11065.     WEDGE.Material = Enum.Material.SmoothPlastic
  11066.     WEDGE.Anchored = true
  11067.     WEDGE.CanCollide = false
  11068.    
  11069.     local PART = Instance.new("Part")
  11070.     PART.Size = Vector3.new(0.1, 0.1, 0.1)
  11071.     PART.Anchored = true
  11072.     PART.CanCollide = false
  11073.     PART.TopSurface = Enum.SurfaceType.Smooth
  11074.     PART.BottomSurface = Enum.SurfaceType.Smooth
  11075.     PART.Material = Enum.Material.SmoothPlastic
  11076.    
  11077.     -- Functions
  11078.    
  11079.     local function draw(properties, style)
  11080.         local part = PART:Clone()
  11081.         for k, v in next, properties do
  11082.             part[k] = v
  11083.         end
  11084.         if (style) then
  11085.             for k, v in next, style do
  11086.                 if (k ~= "Thickness") then
  11087.                     part[k] = v
  11088.                 end
  11089.             end
  11090.         end
  11091.         return part
  11092.     end
  11093.    
  11094.     function module.Draw(parent, properties)
  11095.         properties.Parent = parent
  11096.         return draw(properties, nil)
  11097.     end
  11098.    
  11099.     function module.Point(parent, cf_v3)
  11100.         local thickness = module.StyleGuide.Point.Thickness
  11101.         return draw({
  11102.             Size = Vector3.new(thickness, thickness, thickness);
  11103.             CFrame = (typeof(cf_v3) == "CFrame" and cf_v3 or CFrame.new(cf_v3));
  11104.             Parent = parent;
  11105.         }, module.StyleGuide.Point)
  11106.     end
  11107.    
  11108.     function module.Line(parent, a, b)
  11109.         local thickness = module.StyleGuide.Line.Thickness
  11110.         return draw({
  11111.             CFrame = CFrame.new((a + b)/2, b);
  11112.             Size = Vector3.new(thickness, thickness, (b - a).Magnitude);
  11113.             Parent = parent;
  11114.         }, module.StyleGuide.Line)
  11115.     end
  11116.    
  11117.     function module.Ray(parent, origin, direction)
  11118.         local thickness = module.StyleGuide.Ray.Thickness
  11119.         return draw({
  11120.             CFrame = CFrame.new(origin + direction/2, origin + direction);
  11121.             Size = Vector3.new(thickness, thickness, direction.Magnitude);
  11122.             Parent = parent;
  11123.         }, module.StyleGuide.Ray)
  11124.     end
  11125.    
  11126.     function module.Triangle(parent, a, b, c)
  11127.         local ab, ac, bc = b - a, c - a, c - b
  11128.         local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc)
  11129.        
  11130.         if (abd > acd and abd > bcd) then
  11131.             c, a = a, c
  11132.         elseif (acd > bcd and acd > abd) then
  11133.             a, b = b, a
  11134.         end
  11135.        
  11136.         ab, ac, bc = b - a, c - a, c - b
  11137.        
  11138.         local right = ac:Cross(ab).Unit
  11139.         local up = bc:Cross(right).Unit
  11140.         local back = bc.Unit
  11141.        
  11142.         local height = math.abs(ab:Dot(up))
  11143.         local width1 = math.abs(ab:Dot(back))
  11144.         local width2 = math.abs(ac:Dot(back))
  11145.        
  11146.         local thickness = module.StyleGuide.Triangle.Thickness
  11147.        
  11148.         local w1 = WEDGE:Clone()
  11149.         w1.Size = Vector3.new(thickness, height, width1)
  11150.         w1.CFrame = CFrame.fromMatrix((a + b)/2, right, up, back)
  11151.         w1.Parent = parent
  11152.        
  11153.         local w2 = WEDGE:Clone()
  11154.         w2.Size = Vector3.new(thickness, height, width2)
  11155.         w2.CFrame = CFrame.fromMatrix((a + c)/2, -right, up, -back)
  11156.         w2.Parent = parent
  11157.        
  11158.         for k, v in next, module.StyleGuide.Triangle do
  11159.             if (k ~= "Thickness") then
  11160.                 w1[k] = v
  11161.                 w2[k] = v
  11162.             end
  11163.         end
  11164.        
  11165.         return w1, w2
  11166.     end
  11167.    
  11168.     function module.CFrame(parent, cf)
  11169.         local origin = cf.Position
  11170.         local r = cf.RightVector
  11171.         local u = cf.UpVector
  11172.         local b = -cf.LookVector
  11173.        
  11174.         local thickness = module.StyleGuide.CFrame.Thickness
  11175.        
  11176.         local right = draw({
  11177.             CFrame = CFrame.new(origin + r/2, origin + r);
  11178.             Size = Vector3.new(thickness, thickness, r.Magnitude);
  11179.             Color = module.StyleGuide.CFrame.RightColor3;
  11180.             Parent = parent;
  11181.         }, module.StyleGuide.CFrame.PartProperties)
  11182.        
  11183.         local up = draw({
  11184.             CFrame = CFrame.new(origin + u/2, origin + u);
  11185.             Size = Vector3.new(thickness, thickness, r.Magnitude);
  11186.             Color = module.StyleGuide.CFrame.UpColor3;
  11187.             Parent = parent;
  11188.         }, module.StyleGuide.CFrame.PartProperties)
  11189.        
  11190.         local back = draw({
  11191.             CFrame = CFrame.new(origin + b/2, origin + b);
  11192.             Size = Vector3.new(thickness, thickness, u.Magnitude);
  11193.             Color = module.StyleGuide.CFrame.BackColor3;
  11194.             Parent = parent;
  11195.         }, module.StyleGuide.CFrame.PartProperties)
  11196.        
  11197.         return right, up, back
  11198.     end
  11199.    
  11200.     -- Return
  11201.    
  11202.     return module
  11203. end
  11204. function _Draw2D()
  11205.     local module = {}
  11206.    
  11207.     -- Style Guide
  11208.    
  11209.     module.StyleGuide = {
  11210.         Point = {
  11211.             BorderSizePixel = 0;
  11212.             Size = UDim2.new(0, 4, 0, 4);
  11213.             BorderColor3 = Color3.new(0, 0, 0);
  11214.             BackgroundColor3 = Color3.new(0, 1, 0);
  11215.         },
  11216.        
  11217.         Line = {
  11218.             Thickness = 1;
  11219.             BorderSizePixel = 0;
  11220.             BorderColor3 = Color3.new(0, 0, 0);
  11221.             BackgroundColor3 = Color3.new(0, 1, 0);
  11222.         },
  11223.        
  11224.         Ray = {
  11225.             Thickness = 1;
  11226.             BorderSizePixel = 0;
  11227.             BorderColor3 = Color3.new(0, 0, 0);
  11228.             BackgroundColor3 = Color3.new(0, 1, 0);
  11229.         },
  11230.        
  11231.         Triangle = {
  11232.             ImageTransparency = 0;
  11233.             ImageColor3 = Color3.new(0, 1, 0);
  11234.         }
  11235.     }
  11236.    
  11237.     -- CONSTANTS
  11238.    
  11239.     local HALF = Vector2.new(0.5, 0.5)
  11240.    
  11241.     local RIGHT = "rbxassetid://2798177521"
  11242.     local LEFT = "rbxassetid://2798177955"
  11243.    
  11244.     local IMG = Instance.new("ImageLabel")
  11245.     IMG.BackgroundTransparency = 1
  11246.     IMG.AnchorPoint = HALF
  11247.     IMG.BorderSizePixel = 0
  11248.    
  11249.     local FRAME = Instance.new("Frame")
  11250.     FRAME.BorderSizePixel = 0
  11251.     FRAME.Size = UDim2.new(0, 0, 0, 0)
  11252.     FRAME.BackgroundColor3 = Color3.new(1, 1, 1)
  11253.    
  11254.     -- Functions
  11255.    
  11256.     function draw(properties, style)
  11257.         local frame = FRAME:Clone()
  11258.         for k, v in next, properties do
  11259.             frame[k] = v
  11260.         end
  11261.         if (style) then
  11262.             for k, v in next, style do
  11263.                 if (k ~= "Thickness") then
  11264.                     frame[k] = v
  11265.                 end
  11266.             end
  11267.         end
  11268.         return frame
  11269.     end
  11270.    
  11271.     function module.Draw(parent, properties)
  11272.         properties.Parent = parent
  11273.         return draw(properties, nil)
  11274.     end
  11275.    
  11276.     function module.Point(parent, v2)
  11277.         return draw({
  11278.             AnchorPoint = HALF;
  11279.             Position = UDim2.new(0, v2.x, 0, v2.y);
  11280.             Parent = parent;
  11281.         }, module.StyleGuide.Point)
  11282.     end
  11283.    
  11284.     function module.Line(parent, a, b)
  11285.         local v = (b - a)
  11286.         local m = (a + b)/2
  11287.        
  11288.         return draw({
  11289.             AnchorPoint = HALF;
  11290.             Position = UDim2.new(0, m.x, 0, m.y);
  11291.             Size = UDim2.new(0, module.StyleGuide.Line.Thickness, 0, v.magnitude);
  11292.             Rotation = math.deg(math.atan2(v.y, v.x)) - 90;
  11293.             BackgroundColor3 = Color3.new(1, 1, 0);
  11294.             Parent = parent;
  11295.         }, module.StyleGuide.Line)
  11296.     end
  11297.    
  11298.     function module.Ray(parent, origin, direction)
  11299.         local a, b = origin, origin + direction
  11300.         local v = (b - a)
  11301.         local m = (a + b)/2
  11302.        
  11303.         return draw({
  11304.             AnchorPoint = HALF;
  11305.             Position = UDim2.new(0, m.x, 0, m.y);
  11306.             Size = UDim2.new(0, module.StyleGuide.Ray.Thickness, 0, v.magnitude);
  11307.             Rotation = math.deg(math.atan2(v.y, v.x)) - 90;
  11308.             Parent = parent;
  11309.         }, module.StyleGuide.Ray)
  11310.     end
  11311.    
  11312.     function module.Triangle(parent, a, b, c)
  11313.         local ab, ac, bc = b - a, c - a, c - b
  11314.         local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc)
  11315.        
  11316.         if (abd > acd and abd > bcd) then
  11317.             c, a = a, c
  11318.         elseif (acd > bcd and acd > abd) then
  11319.             a, b = b, a
  11320.         end
  11321.        
  11322.         ab, ac, bc = b - a, c - a, c - b
  11323.        
  11324.         local unit = bc.unit
  11325.         local height = unit:Cross(ab)
  11326.         local flip = (height >= 0)
  11327.         local theta = math.deg(math.atan2(unit.y, unit.x)) + (flip and 0 or 180)
  11328.        
  11329.         local m1 = (a + b)/2
  11330.         local m2 = (a + c)/2
  11331.        
  11332.         local w1 = IMG:Clone()
  11333.         w1.Image = flip and RIGHT or LEFT
  11334.         w1.AnchorPoint = HALF
  11335.         w1.Size = UDim2.new(0, math.abs(unit:Dot(ab)), 0, height)
  11336.         w1.Position = UDim2.new(0, m1.x, 0, m1.y)
  11337.         w1.Rotation = theta
  11338.         w1.Parent = parent
  11339.        
  11340.         local w2 = IMG:Clone()
  11341.         w2.Image = flip and LEFT or RIGHT
  11342.         w2.AnchorPoint = HALF
  11343.         w2.Size = UDim2.new(0, math.abs(unit:Dot(ac)), 0, height)
  11344.         w2.Position = UDim2.new(0, m2.x, 0, m2.y)
  11345.         w2.Rotation = theta
  11346.         w2.Parent = parent
  11347.        
  11348.         for k, v in next, module.StyleGuide.Triangle do
  11349.             w1[k] = v
  11350.             w2[k] = v
  11351.         end
  11352.        
  11353.         return w1, w2
  11354.     end
  11355.    
  11356.     -- Return
  11357.    
  11358.     return module
  11359. end
  11360. function _DrawClass()
  11361.     local Draw2DModule = _Draw2D()
  11362.     local Draw3DModule = _Draw3D()
  11363.    
  11364.     --
  11365.    
  11366.     local DrawClass = {}
  11367.     local DrawClassStorage = setmetatable({}, {__mode = "k"})
  11368.     DrawClass.__index = DrawClass
  11369.    
  11370.     function DrawClass.new(parent)
  11371.         local self = setmetatable({}, DrawClass)
  11372.        
  11373.         self.Parent = parent
  11374.         DrawClassStorage[self] = {}
  11375.        
  11376.         self.Draw3D = {}
  11377.         for key, func in next, Draw3DModule do
  11378.             self.Draw3D[key] = function(...)
  11379.                 local returns = {func(self.Parent, ...)}
  11380.                 for i = 1, #returns do
  11381.                     table.insert(DrawClassStorage[self], returns[i])
  11382.                 end
  11383.                 return unpack(returns)
  11384.             end
  11385.         end
  11386.        
  11387.         self.Draw2D = {}
  11388.         for key, func in next, Draw2DModule do
  11389.             self.Draw2D[key] = function(...)
  11390.                 local returns = {func(self.Parent, ...)}
  11391.                 for i = 1, #returns do
  11392.                     table.insert(DrawClassStorage[self], returns[i])
  11393.                 end
  11394.                 return unpack(returns)
  11395.             end
  11396.         end
  11397.        
  11398.         return self
  11399.     end
  11400.    
  11401.     --
  11402.    
  11403.     function DrawClass:Clear()
  11404.         local t = DrawClassStorage[self]
  11405.         while (#t > 0) do
  11406.             local part = table.remove(t)
  11407.             if (part) then
  11408.                 part:Destroy()
  11409.             end
  11410.         end
  11411.         DrawClassStorage[self] = {}
  11412.     end
  11413.    
  11414.     --
  11415.    
  11416.     return DrawClass
  11417. end
  11418.  
  11419.  
  11420. --END TEST
  11421.  
  11422. local PLAYERS = game:GetService("Players")
  11423.  
  11424. local GravityController = _GravityController()
  11425. local Controller = GravityController.new(PLAYERS.LocalPlayer)
  11426.  
  11427. local DrawClass = _DrawClass()
  11428.  
  11429. local PI2 = math.pi*2
  11430. local ZERO = Vector3.new(0, 0, 0)
  11431.  
  11432. local LOWER_RADIUS_OFFSET = 3
  11433. local NUM_DOWN_RAYS = 24
  11434. local ODD_DOWN_RAY_START_RADIUS = 3
  11435. local EVEN_DOWN_RAY_START_RADIUS = 2
  11436. local ODD_DOWN_RAY_END_RADIUS = 1.66666
  11437. local EVEN_DOWN_RAY_END_RADIUS = 1
  11438.  
  11439. local NUM_FEELER_RAYS = 9
  11440. local FEELER_LENGTH = 2
  11441. local FEELER_START_OFFSET = 2
  11442. local FEELER_RADIUS = 3.5
  11443. local FEELER_APEX_OFFSET = 1
  11444. local FEELER_WEIGHTING = 8
  11445.  
  11446. function GetGravityUp(self, oldGravityUp)
  11447.     local ignoreList = {}
  11448.     for i, player in next, PLAYERS:GetPlayers() do
  11449.         ignoreList[i] = player.Character
  11450.     end
  11451.    
  11452.     -- get the normal
  11453.    
  11454.     local hrpCF = self.HRP.CFrame
  11455.     local isR15 = (self.Humanoid.RigType == Enum.HumanoidRigType.R15)
  11456.    
  11457.     local origin = isR15 and hrpCF.p or hrpCF.p + 0.35*oldGravityUp
  11458.     local radialVector = math.abs(hrpCF.LookVector:Dot(oldGravityUp)) < 0.999 and hrpCF.LookVector:Cross(oldGravityUp) or hrpCF.RightVector:Cross(oldGravityUp)
  11459.    
  11460.     local centerRayLength = 25
  11461.     local centerRay = Ray.new(origin, -centerRayLength * oldGravityUp)
  11462.     local centerHit, centerHitPoint, centerHitNormal = workspace:FindPartOnRayWithIgnoreList(centerRay, ignoreList)
  11463.    
  11464.     --[[disable
  11465.     DrawClass:Clear()
  11466.     DrawClass.Draw3D.Ray(centerRay.Origin, centerRay.Direction)
  11467.     ]]
  11468.     local downHitCount = 0
  11469.     local totalHitCount = 0
  11470.     local centerRayHitCount = 0
  11471.     local evenRayHitCount = 0
  11472.     local oddRayHitCount = 0
  11473.    
  11474.     local mainDownNormal = ZERO
  11475.     if (centerHit) then
  11476.         mainDownNormal = centerHitNormal
  11477.         centerRayHitCount = 0
  11478.     end
  11479.    
  11480.     local downRaySum = ZERO
  11481.     for i = 1, NUM_DOWN_RAYS do
  11482.         local dtheta = PI2 * ((i-1)/NUM_DOWN_RAYS)
  11483.        
  11484.         local angleWeight = 0.25 + 0.75 * math.abs(math.cos(dtheta))
  11485.         local isEvenRay = (i%2 == 0)
  11486.         local startRadius = isEvenRay and EVEN_DOWN_RAY_START_RADIUS or ODD_DOWN_RAY_START_RADIUS  
  11487.         local endRadius = isEvenRay and EVEN_DOWN_RAY_END_RADIUS or ODD_DOWN_RAY_END_RADIUS
  11488.         local downRayLength = centerRayLength
  11489.        
  11490.         local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
  11491.         local dir = (LOWER_RADIUS_OFFSET * -oldGravityUp + (endRadius - startRadius) * offset)
  11492.         local ray = Ray.new(origin + startRadius * offset, downRayLength * dir.unit)
  11493.         local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
  11494.         --[[disable
  11495.         DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)
  11496.         ]]
  11497.         if (hit) then
  11498.             downRaySum = downRaySum + angleWeight * hitNormal
  11499.             downHitCount = downHitCount + 1
  11500.             if isEvenRay then
  11501.                 evenRayHitCount = evenRayHitCount + 1                  
  11502.             else
  11503.                 oddRayHitCount = oddRayHitCount + 1
  11504.             end
  11505.         end
  11506.     end
  11507.    
  11508.     local feelerHitCount = 0   
  11509.     local feelerNormalSum = ZERO
  11510.    
  11511.     for i = 1, NUM_FEELER_RAYS do
  11512.         local dtheta = 2 * math.pi * ((i-1)/NUM_FEELER_RAYS)
  11513.         local angleWeight =  0.25 + 0.75 * math.abs(math.cos(dtheta))  
  11514.         local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
  11515.         local dir = (FEELER_RADIUS * offset + LOWER_RADIUS_OFFSET * -oldGravityUp).unit
  11516.         local feelerOrigin = origin - FEELER_APEX_OFFSET * -oldGravityUp + FEELER_START_OFFSET * dir
  11517.         local ray = Ray.new(feelerOrigin, FEELER_LENGTH * dir)
  11518.         local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
  11519.         --[[disable
  11520.         DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)
  11521.         ]]
  11522.         if (hit) then
  11523.             feelerNormalSum = feelerNormalSum + FEELER_WEIGHTING * angleWeight * hitNormal --* hitDistSqInv
  11524.             feelerHitCount = feelerHitCount + 1
  11525.         end
  11526.     end
  11527.    
  11528.     if (centerRayHitCount + downHitCount + feelerHitCount > 0) then
  11529.         local normalSum = mainDownNormal + downRaySum + feelerNormalSum
  11530.         if (normalSum ~= ZERO) then
  11531.             return normalSum.unit
  11532.         end
  11533.     end
  11534.    
  11535.     return oldGravityUp
  11536. end
  11537.  
  11538. Controller.GetGravityUp = GetGravityUp
  11539.  
  11540. -- E is toggle
  11541. game:GetService("ContextActionService"):BindAction("Toggle", function(action, state, input)
  11542.     if not (state == Enum.UserInputState.Begin) then
  11543.         return
  11544.     end
  11545.    
  11546.     if (Controller) then
  11547.         Controller:Destroy()
  11548.         Controller = nil
  11549.     else
  11550.         Controller = GravityController.new(PLAYERS.LocalPlayer)
  11551.         Controller.GetGravityUp = GetGravityUp
  11552.     end
  11553. end, false, Enum.KeyCode.Z)
  11554. print("end")
Tags: lua
Add Comment
Please, Sign In to add comment