zubastik_cakes

spiderman

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