MaxproGlitcher

scritp marcher sur les mur by Max

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