Ben_Scripts343

R6.R15/Hub

Jan 27th, 2021
62
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 406.49 KB | None | 0 0
  1.  
  2. local player = game.Players.LocalPlayer
  3. player.Chatted:connect(function(cht)
  4. if cht:match(".r6 naruto run")then
  5. --R6Naruto run
  6. spawn(function()
  7. while wait() do
  8. settings().Physics.AllowSleep = false
  9. game.Players.LocalPlayer.MaximumSimulationRadius = math.huge*math.huge
  10. setsimulationradius(math.huge*math.huge,math.huge*math.huge)
  11. end
  12. end)
  13. function Align(Part1,Part0,Position,Angle,name)
  14. local AlignPos = Instance.new("AlignPosition", Part1);
  15. AlignPos.Parent.CanCollide = false;
  16. AlignPos.ApplyAtCenterOfMass = true;
  17. AlignPos.MaxForce = 67752;
  18. AlignPos.MaxVelocity = math.huge/9e110;
  19. AlignPos.ReactionForceEnabled = false;
  20. AlignPos.Responsiveness = 200;
  21. AlignPos.RigidityEnabled = false;
  22.  
  23. local AlignOrient = Instance.new("AlignOrientation", Part1);
  24. AlignOrient.MaxAngularVelocity = math.huge/9e110;
  25. AlignOrient.MaxTorque = 67752;
  26. AlignOrient.PrimaryAxisOnly = false;
  27. AlignOrient.ReactionTorqueEnabled = false;
  28. AlignOrient.Responsiveness = 200;
  29. AlignOrient.RigidityEnabled = false;
  30.  
  31. local AttachmentA=Instance.new("Attachment",Part1);
  32.  
  33. local AttachmentB=Instance.new("Attachment",Part0);
  34. AttachmentB.Orientation = Angle
  35. AttachmentB.Position = Position
  36. AttachmentB.Name = name
  37.  
  38. AlignPos.Attachment0 = AttachmentA;
  39. AlignPos.Attachment1 = AttachmentB;
  40.  
  41. AlignOrient.Attachment0 = AttachmentA;
  42. AlignOrient.Attachment1 = AttachmentB;
  43. end
  44. char = game.Players.LocalPlayer.Character
  45. char.Torso["Right Shoulder"]:Destroy();char.Torso["Left Shoulder"]:Destroy();
  46. Align(char["Right Arm"],char["Torso"],Vector3.new(1.3, 1, 0.4),Vector3.new(-90, 0, 0),"RA")
  47. Align(char["Left Arm"],char["Torso"],Vector3.new(-1, 1, 0.4),Vector3.new(-90, 0, 0),"LA")
  48. wait(0.4)
  49. char.Humanoid.WalkSpeed = 50
  50.  
  51. elseif cht:match(".r6 pp")then
  52. --R15pp
  53. spawn(function()
  54. while wait() do
  55. settings().Physics.AllowSleep = false
  56. game.Players.LocalPlayer.MaximumSimulationRadius = math.huge*math.huge
  57. setsimulationradius(math.huge*math.huge,math.huge*math.huge)
  58. end
  59. end)
  60. function Align(Part1,Part0,Position,Angle,name)
  61. local AlignPos = Instance.new("AlignPosition", Part1);
  62. AlignPos.Parent.CanCollide = false;
  63. AlignPos.ApplyAtCenterOfMass = true;
  64. AlignPos.MaxForce = 67752;
  65. AlignPos.MaxVelocity = math.huge/9e110;
  66. AlignPos.ReactionForceEnabled = false;
  67. AlignPos.Responsiveness = 200;
  68. AlignPos.RigidityEnabled = false;
  69.  
  70. local AlignOrient = Instance.new("AlignOrientation", Part1);
  71. AlignOrient.MaxAngularVelocity = math.huge/9e110;
  72. AlignOrient.MaxTorque = 67752;
  73. AlignOrient.PrimaryAxisOnly = false;
  74. AlignOrient.ReactionTorqueEnabled = false;
  75. AlignOrient.Responsiveness = 200;
  76. AlignOrient.RigidityEnabled = false;
  77.  
  78. local AttachmentA=Instance.new("Attachment",Part1);
  79.  
  80. local AttachmentB=Instance.new("Attachment",Part0);
  81. AttachmentB.Orientation = Angle
  82. AttachmentB.Position = Position
  83. AttachmentB.Name = name
  84.  
  85. AlignPos.Attachment0 = AttachmentA;
  86. AlignPos.Attachment1 = AttachmentB;
  87.  
  88. AlignOrient.Attachment0 = AttachmentA;
  89. AlignOrient.Attachment1 = AttachmentB;
  90. end
  91. char = game.Players.LocalPlayer.Character
  92. pal = char["Pal Hair"]
  93. meshy = char ["MeshPartAccessory"]
  94. cool = char["CoolBoyHair"]
  95. girl = char["LongStraightHair"]
  96. pal.Handle.AccessoryWeld:destroy();meshy.Handle.AccessoryWeld:destroy();girl.Handle.AccessoryWeld:destroy();cool.Handle.AccessoryWeld:destroy();
  97. Align(pal.Handle,char["Torso"],Vector3.new(-0.2, -1.4, -3.5),Vector3.new(0, 0, 0),"Pal")
  98. Align(cool.Handle,char["Torso"],Vector3.new(-0.2, -1.4, -2.4),Vector3.new(-90, 0, 100),"cool")
  99. Align(girl.Handle,char["Torso"],Vector3.new(-0.2, -1.4, -1.3),Vector3.new(0, 0, 90),"girly")
  100. Align(meshy.Handle,char["Torso"],Vector3.new(-0.2, -1.4, -5),Vector3.new(90, 0, 0),"meshy")
  101. wait(2)
  102. brian = char["Pal Hair"].Handle.Mesh
  103. brian:Destroy()
  104. lilie = char["MeshPartAccessory"].Handle.SpecialMesh
  105. lilie:Destroy()
  106. banda = char["LongStraightHair"].Handle.Mesh
  107. banda:Destroy()
  108. licon = char["CoolBoyHair"].Handle.Mesh
  109. licon:Destroy()
  110. wait(0.8)
  111. char.Humanoid.WalkSpeed = 30
  112.  
  113. elseif cht:match(".r15 naruto run") then
  114. --R15Naruto run
  115. spawn(function()
  116. while wait() do
  117. settings().Physics.AllowSleep = false
  118. game.Players.LocalPlayer.MaximumSimulationRadius = math.huge*math.huge
  119. setsimulationradius(math.huge*math.huge,math.huge*math.huge)
  120. end
  121. end)
  122. function Align(Part1,Part0,Position,Angle,name)
  123. local AlignPos = Instance.new("AlignPosition", Part1);
  124. AlignPos.Parent.CanCollide = false;
  125. AlignPos.ApplyAtCenterOfMass = true;
  126. AlignPos.MaxForce = 67752;
  127. AlignPos.MaxVelocity = math.huge/9e110;
  128. AlignPos.ReactionForceEnabled = false;
  129. AlignPos.Responsiveness = 200;
  130. AlignPos.RigidityEnabled = false;
  131.  
  132. local AlignOrient = Instance.new("AlignOrientation", Part1);
  133. AlignOrient.MaxAngularVelocity = math.huge/9e110;
  134. AlignOrient.MaxTorque = 67752;
  135. AlignOrient.PrimaryAxisOnly = false;
  136. AlignOrient.ReactionTorqueEnabled = false;
  137. AlignOrient.Responsiveness = 200;
  138. AlignOrient.RigidityEnabled = false;
  139.  
  140. local AttachmentA=Instance.new("Attachment",Part1);
  141.  
  142. local AttachmentB=Instance.new("Attachment",Part0);
  143. AttachmentB.Orientation = Angle
  144. AttachmentB.Position = Position
  145. AttachmentB.Name = name
  146.  
  147. AlignPos.Attachment0 = AttachmentA;
  148. AlignPos.Attachment1 = AttachmentB;
  149.  
  150. AlignOrient.Attachment0 = AttachmentA;
  151. AlignOrient.Attachment1 = AttachmentB;
  152. end
  153. char = game.Players.LocalPlayer.Character
  154. char["RightUpperArm"].RightShoulder:Destroy();char["RightLowerArm"].RightElbow:Destroy();char["LeftUpperArm"].LeftShoulder:Destroy();char["LeftLowerArm"].LeftElbow:Destroy();
  155. Align(char["RightUpperArm"],char["UpperTorso"],Vector3.new(1.3, 0.2, 0.4),Vector3.new(-90, 0, 0),"Arm")
  156. Align(char["RightLowerArm"],char["UpperTorso"],Vector3.new(1.3, 0.2, 1.2),Vector3.new(-87, 0, 0),"Elbow")
  157. Align(char["LeftUpperArm"],char["UpperTorso"],Vector3.new(-1, 0.2, 0.4),Vector3.new(-90, 0, 0),"Arm2")
  158. Align(char["LeftLowerArm"],char["UpperTorso"],Vector3.new(-1, 0.2, 1.2),Vector3.new(-87, 0, 0),"Elbow2")
  159. wait(2)
  160. brian = char["Pal Hair"].Handle.Mesh
  161. brian:Destroy()
  162. lilie = char["MeshPartAccessory"].Handle.SpecialMesh
  163. lilie:Destroy()
  164. banda = char["LongStraightHair"].Handle.Mesh
  165. banda:Destroy()
  166. licon = char["CoolBoyHair"].Handle.Mesh
  167. licon:Destroy()
  168. char.Humanoid.WalkSpeed = 30
  169.  
  170. elseif cht:match(".r15 pp") then
  171. --R15pp
  172. spawn(function()
  173. while wait() do
  174. settings().Physics.AllowSleep = false
  175. game.Players.LocalPlayer.MaximumSimulationRadius = math.huge*math.huge
  176. setsimulationradius(math.huge*math.huge,math.huge*math.huge)
  177. end
  178. end)
  179. function Align(Part1,Part0,Position,Angle,name)
  180. local AlignPos = Instance.new("AlignPosition", Part1);
  181. AlignPos.Parent.CanCollide = false;
  182. AlignPos.ApplyAtCenterOfMass = true;
  183. AlignPos.MaxForce = 67752;
  184. AlignPos.MaxVelocity = math.huge/9e110;
  185. AlignPos.ReactionForceEnabled = false;
  186. AlignPos.Responsiveness = 200;
  187. AlignPos.RigidityEnabled = false;
  188.  
  189. local AlignOrient = Instance.new("AlignOrientation", Part1);
  190. AlignOrient.MaxAngularVelocity = math.huge/9e110;
  191. AlignOrient.MaxTorque = 67752;
  192. AlignOrient.PrimaryAxisOnly = false;
  193. AlignOrient.ReactionTorqueEnabled = false;
  194. AlignOrient.Responsiveness = 200;
  195. AlignOrient.RigidityEnabled = false;
  196.  
  197. local AttachmentA=Instance.new("Attachment",Part1);
  198.  
  199. local AttachmentB=Instance.new("Attachment",Part0);
  200. AttachmentB.Orientation = Angle
  201. AttachmentB.Position = Position
  202. AttachmentB.Name = name
  203.  
  204. AlignPos.Attachment0 = AttachmentA;
  205. AlignPos.Attachment1 = AttachmentB;
  206.  
  207. AlignOrient.Attachment0 = AttachmentA;
  208. AlignOrient.Attachment1 = AttachmentB;
  209. end
  210. char = game.Players.LocalPlayer.Character
  211. pal = char["Pal Hair"]
  212. meshy = char ["MeshPartAccessory"]
  213. cool = char["CoolBoyHair"]
  214. girl = char["LongStraightHair"]
  215. pal.Handle.AccessoryWeld:destroy();meshy.Handle.AccessoryWeld:destroy();girl.Handle.AccessoryWeld:destroy();cool.Handle.AccessoryWeld:destroy();
  216. Align(pal.Handle,char["UpperTorso"],Vector3.new(-0.2, -1.4, -3.5),Vector3.new(0, 0, 0),"Pal")
  217. Align(cool.Handle,char["UpperTorso"],Vector3.new(-0.2, -1.4, -2.4),Vector3.new(-90, 0, 100),"cool")
  218. Align(girl.Handle,char["UpperTorso"],Vector3.new(-0.2, -1.4, -1.3),Vector3.new(0, 0, 90),"girly")
  219. Align(meshy.Handle,char["UpperTorso"],Vector3.new(-0.2, -1.4, -5),Vector3.new(90, 0, 0),"meshy")
  220. wait(2)
  221. brian = char["Pal Hair"].Handle.Mesh
  222. brian:Destroy()
  223. lilie = char["MeshPartAccessory"].Handle.SpecialMesh
  224. lilie:Destroy()
  225. banda = char["LongStraightHair"].Handle.Mesh
  226. banda:Destroy()
  227. licon = char["CoolBoyHair"].Handle.Mesh
  228. licon:Destroy()
  229. wait(0.8)
  230. char.Humanoid.WalkSpeed = 25
  231.  
  232. elseif cht:match(".iy") then
  233. -- IY
  234. loadstring(game:HttpGet('https://raw.githubusercontent.com/EdgeIY/infiniteyield/master/source'))()
  235.  
  236. elseif cht:match(".spiderman") then
  237. --[[
  238. local _p = game:WaitForChild("Players")
  239. local _plr = _p.ChildAdded:Wait()
  240. if _plr == _p.LocalPlayer then
  241. _plr.ChildAdded:Connect(function(cccc)
  242. if c.Name == "PlayerScriptsLoader" then
  243. c.Disabled = true
  244. end
  245. end)
  246. end
  247. ]]
  248. repeat wait()
  249. a = pcall(function()
  250. game:WaitForChild("Players").LocalPlayer:WaitForChild("PlayerScripts").ChildAdded:Connect(function(c)
  251. if c.Name == "PlayerScriptsLoader"then
  252. c.Disabled = true
  253. end
  254. end)
  255. end)
  256. if a == true then break end
  257. until true == false
  258. game:WaitForChild("Players").LocalPlayer:WaitForChild("PlayerScripts").ChildAdded:Connect(function(c)
  259. if c.Name == "PlayerScriptsLoader"then
  260. c.Disabled = true
  261. end
  262. end)
  263.  
  264.  
  265. function _CameraUI()
  266. local Players = game:GetService("Players")
  267. local TweenService = game:GetService("TweenService")
  268.  
  269. local LocalPlayer = Players.LocalPlayer
  270. if not LocalPlayer then
  271. Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  272. LocalPlayer = Players.LocalPlayer
  273. end
  274.  
  275. local function waitForChildOfClass(parent, class)
  276. local child = parent:FindFirstChildOfClass(class)
  277. while not child or child.ClassName ~= class do
  278. child = parent.ChildAdded:Wait()
  279. end
  280. return child
  281. end
  282.  
  283. local PlayerGui = waitForChildOfClass(LocalPlayer, "PlayerGui")
  284.  
  285. local TOAST_OPEN_SIZE = UDim2.new(0, 326, 0, 58)
  286. local TOAST_CLOSED_SIZE = UDim2.new(0, 80, 0, 58)
  287. local TOAST_BACKGROUND_COLOR = Color3.fromRGB(32, 32, 32)
  288. local TOAST_BACKGROUND_TRANS = 0.4
  289. local TOAST_FOREGROUND_COLOR = Color3.fromRGB(200, 200, 200)
  290. local TOAST_FOREGROUND_TRANS = 0
  291.  
  292. -- Convenient syntax for creating a tree of instanes
  293. local function create(className)
  294. return function(props)
  295. local inst = Instance.new(className)
  296. local parent = props.Parent
  297. props.Parent = nil
  298. for name, val in pairs(props) do
  299. if type(name) == "string" then
  300. inst[name] = val
  301. else
  302. val.Parent = inst
  303. end
  304. end
  305. -- Only set parent after all other properties are initialized
  306. inst.Parent = parent
  307. return inst
  308. end
  309. end
  310.  
  311. local initialized = false
  312.  
  313. local uiRoot
  314. local toast
  315. local toastIcon
  316. local toastUpperText
  317. local toastLowerText
  318.  
  319. local function initializeUI()
  320. assert(not initialized)
  321.  
  322. uiRoot = create("ScreenGui"){
  323. Name = "RbxCameraUI",
  324. AutoLocalize = false,
  325. Enabled = true,
  326. DisplayOrder = -1, -- Appears behind default developer UI
  327. IgnoreGuiInset = false,
  328. ResetOnSpawn = false,
  329. ZIndexBehavior = Enum.ZIndexBehavior.Sibling,
  330.  
  331. create("ImageLabel"){
  332. Name = "Toast",
  333. Visible = false,
  334. AnchorPoint = Vector2.new(0.5, 0),
  335. BackgroundTransparency = 1,
  336. BorderSizePixel = 0,
  337. Position = UDim2.new(0.5, 0, 0, 8),
  338. Size = TOAST_CLOSED_SIZE,
  339. Image = "rbxasset://textures/ui/Camera/CameraToast9Slice.png",
  340. ImageColor3 = TOAST_BACKGROUND_COLOR,
  341. ImageRectSize = Vector2.new(6, 6),
  342. ImageTransparency = 1,
  343. ScaleType = Enum.ScaleType.Slice,
  344. SliceCenter = Rect.new(3, 3, 3, 3),
  345. ClipsDescendants = true,
  346.  
  347. create("Frame"){
  348. Name = "IconBuffer",
  349. BackgroundTransparency = 1,
  350. BorderSizePixel = 0,
  351. Position = UDim2.new(0, 0, 0, 0),
  352. Size = UDim2.new(0, 80, 1, 0),
  353.  
  354. create("ImageLabel"){
  355. Name = "Icon",
  356. AnchorPoint = Vector2.new(0.5, 0.5),
  357. BackgroundTransparency = 1,
  358. Position = UDim2.new(0.5, 0, 0.5, 0),
  359. Size = UDim2.new(0, 48, 0, 48),
  360. ZIndex = 2,
  361. Image = "rbxasset://textures/ui/Camera/CameraToastIcon.png",
  362. ImageColor3 = TOAST_FOREGROUND_COLOR,
  363. ImageTransparency = 1,
  364. }
  365. },
  366.  
  367. create("Frame"){
  368. Name = "TextBuffer",
  369. BackgroundTransparency = 1,
  370. BorderSizePixel = 0,
  371. Position = UDim2.new(0, 80, 0, 0),
  372. Size = UDim2.new(1, -80, 1, 0),
  373. ClipsDescendants = true,
  374.  
  375. create("TextLabel"){
  376. Name = "Upper",
  377. AnchorPoint = Vector2.new(0, 1),
  378. BackgroundTransparency = 1,
  379. Position = UDim2.new(0, 0, 0.5, 0),
  380. Size = UDim2.new(1, 0, 0, 19),
  381. Font = Enum.Font.GothamSemibold,
  382. Text = "Camera control enabled",
  383. TextColor3 = TOAST_FOREGROUND_COLOR,
  384. TextTransparency = 1,
  385. TextSize = 19,
  386. TextXAlignment = Enum.TextXAlignment.Left,
  387. TextYAlignment = Enum.TextYAlignment.Center,
  388. },
  389.  
  390. create("TextLabel"){
  391. Name = "Lower",
  392. AnchorPoint = Vector2.new(0, 0),
  393. BackgroundTransparency = 1,
  394. Position = UDim2.new(0, 0, 0.5, 3),
  395. Size = UDim2.new(1, 0, 0, 15),
  396. Font = Enum.Font.Gotham,
  397. Text = "Right mouse button to toggle",
  398. TextColor3 = TOAST_FOREGROUND_COLOR,
  399. TextTransparency = 1,
  400. TextSize = 15,
  401. TextXAlignment = Enum.TextXAlignment.Left,
  402. TextYAlignment = Enum.TextYAlignment.Center,
  403. },
  404. },
  405. },
  406.  
  407. Parent = PlayerGui,
  408. }
  409.  
  410. toast = uiRoot.Toast
  411. toastIcon = toast.IconBuffer.Icon
  412. toastUpperText = toast.TextBuffer.Upper
  413. toastLowerText = toast.TextBuffer.Lower
  414.  
  415. initialized = true
  416. end
  417.  
  418. local CameraUI = {}
  419.  
  420. do
  421. -- Instantaneously disable the toast or enable for opening later on. Used when switching camera modes.
  422. function CameraUI.setCameraModeToastEnabled(enabled)
  423. if not enabled and not initialized then
  424. return
  425. end
  426.  
  427. if not initialized then
  428. initializeUI()
  429. end
  430.  
  431. toast.Visible = enabled
  432. if not enabled then
  433. CameraUI.setCameraModeToastOpen(false)
  434. end
  435. end
  436.  
  437. local tweenInfo = TweenInfo.new(0.25, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
  438.  
  439. -- Tween the toast in or out. Toast must be enabled with setCameraModeToastEnabled.
  440. function CameraUI.setCameraModeToastOpen(open)
  441. assert(initialized)
  442.  
  443. TweenService:Create(toast, tweenInfo, {
  444. Size = open and TOAST_OPEN_SIZE or TOAST_CLOSED_SIZE,
  445. ImageTransparency = open and TOAST_BACKGROUND_TRANS or 1,
  446. }):Play()
  447.  
  448. TweenService:Create(toastIcon, tweenInfo, {
  449. ImageTransparency = open and TOAST_FOREGROUND_TRANS or 1,
  450. }):Play()
  451.  
  452. TweenService:Create(toastUpperText, tweenInfo, {
  453. TextTransparency = open and TOAST_FOREGROUND_TRANS or 1,
  454. }):Play()
  455.  
  456. TweenService:Create(toastLowerText, tweenInfo, {
  457. TextTransparency = open and TOAST_FOREGROUND_TRANS or 1,
  458. }):Play()
  459. end
  460. end
  461.  
  462. return CameraUI
  463. end
  464.  
  465. function _CameraToggleStateController()
  466. local Players = game:GetService("Players")
  467. local UserInputService = game:GetService("UserInputService")
  468. local GameSettings = UserSettings():GetService("UserGameSettings")
  469.  
  470. local LocalPlayer = Players.LocalPlayer
  471. if not LocalPlayer then
  472. Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  473. LocalPlayer = Players.LocalPlayer
  474. end
  475.  
  476. local Mouse = LocalPlayer:GetMouse()
  477.  
  478. local Input = _CameraInput()
  479. local CameraUI = _CameraUI()
  480.  
  481. local lastTogglePan = false
  482. local lastTogglePanChange = tick()
  483.  
  484. local CROSS_MOUSE_ICON = "rbxasset://textures/Cursors/CrossMouseIcon.png"
  485.  
  486. local lockStateDirty = false
  487. local wasTogglePanOnTheLastTimeYouWentIntoFirstPerson = false
  488. local lastFirstPerson = false
  489.  
  490. CameraUI.setCameraModeToastEnabled(false)
  491.  
  492. return function(isFirstPerson)
  493. local togglePan = Input.getTogglePan()
  494. local toastTimeout = 3
  495.  
  496. if isFirstPerson and togglePan ~= lastTogglePan then
  497. lockStateDirty = true
  498. end
  499.  
  500. if lastTogglePan ~= togglePan or tick() - lastTogglePanChange > toastTimeout then
  501. local doShow = togglePan and tick() - lastTogglePanChange < toastTimeout
  502.  
  503. CameraUI.setCameraModeToastOpen(doShow)
  504.  
  505. if togglePan then
  506. lockStateDirty = false
  507. end
  508. lastTogglePanChange = tick()
  509. lastTogglePan = togglePan
  510. end
  511.  
  512. if isFirstPerson ~= lastFirstPerson then
  513. if isFirstPerson then
  514. wasTogglePanOnTheLastTimeYouWentIntoFirstPerson = Input.getTogglePan()
  515. Input.setTogglePan(true)
  516. elseif not lockStateDirty then
  517. Input.setTogglePan(wasTogglePanOnTheLastTimeYouWentIntoFirstPerson)
  518. end
  519. end
  520.  
  521. if isFirstPerson then
  522. if Input.getTogglePan() then
  523. Mouse.Icon = CROSS_MOUSE_ICON
  524. UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
  525. --GameSettings.RotationType = Enum.RotationType.CameraRelative
  526. else
  527. Mouse.Icon = ""
  528. UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  529. --GameSettings.RotationType = Enum.RotationType.CameraRelative
  530. end
  531.  
  532. elseif Input.getTogglePan() then
  533. Mouse.Icon = CROSS_MOUSE_ICON
  534. UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
  535. GameSettings.RotationType = Enum.RotationType.MovementRelative
  536.  
  537. elseif Input.getHoldPan() then
  538. Mouse.Icon = ""
  539. UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
  540. GameSettings.RotationType = Enum.RotationType.MovementRelative
  541.  
  542. else
  543. Mouse.Icon = ""
  544. UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  545. GameSettings.RotationType = Enum.RotationType.MovementRelative
  546. end
  547.  
  548. lastFirstPerson = isFirstPerson
  549. end
  550. end
  551.  
  552. function _CameraInput()
  553. local UserInputService = game:GetService("UserInputService")
  554.  
  555. local MB_TAP_LENGTH = 0.3 -- length of time for a short mouse button tap to be registered
  556.  
  557. local rmbDown, rmbUp
  558. do
  559. local rmbDownBindable = Instance.new("BindableEvent")
  560. local rmbUpBindable = Instance.new("BindableEvent")
  561.  
  562. rmbDown = rmbDownBindable.Event
  563. rmbUp = rmbUpBindable.Event
  564.  
  565. UserInputService.InputBegan:Connect(function(input, gpe)
  566. if not gpe and input.UserInputType == Enum.UserInputType.MouseButton2 then
  567. rmbDownBindable:Fire()
  568. end
  569. end)
  570.  
  571. UserInputService.InputEnded:Connect(function(input, gpe)
  572. if input.UserInputType == Enum.UserInputType.MouseButton2 then
  573. rmbUpBindable:Fire()
  574. end
  575. end)
  576. end
  577.  
  578. local holdPan = false
  579. local togglePan = false
  580. local lastRmbDown = 0 -- tick() timestamp of the last right mouse button down event
  581.  
  582. local CameraInput = {}
  583.  
  584. function CameraInput.getHoldPan()
  585. return holdPan
  586. end
  587.  
  588. function CameraInput.getTogglePan()
  589. return togglePan
  590. end
  591.  
  592. function CameraInput.getPanning()
  593. return togglePan or holdPan
  594. end
  595.  
  596. function CameraInput.setTogglePan(value)
  597. togglePan = value
  598. end
  599.  
  600. local cameraToggleInputEnabled = false
  601. local rmbDownConnection
  602. local rmbUpConnection
  603.  
  604. function CameraInput.enableCameraToggleInput()
  605. if cameraToggleInputEnabled then
  606. return
  607. end
  608. cameraToggleInputEnabled = true
  609.  
  610. holdPan = false
  611. togglePan = false
  612.  
  613. if rmbDownConnection then
  614. rmbDownConnection:Disconnect()
  615. end
  616.  
  617. if rmbUpConnection then
  618. rmbUpConnection:Disconnect()
  619. end
  620.  
  621. rmbDownConnection = rmbDown:Connect(function()
  622. holdPan = true
  623. lastRmbDown = tick()
  624. end)
  625.  
  626. rmbUpConnection = rmbUp:Connect(function()
  627. holdPan = false
  628. if tick() - lastRmbDown < MB_TAP_LENGTH and (togglePan or UserInputService:GetMouseDelta().Magnitude < 2) then
  629. togglePan = not togglePan
  630. end
  631. end)
  632. end
  633.  
  634. function CameraInput.disableCameraToggleInput()
  635. if not cameraToggleInputEnabled then
  636. return
  637. end
  638. cameraToggleInputEnabled = false
  639.  
  640. if rmbDownConnection then
  641. rmbDownConnection:Disconnect()
  642. rmbDownConnection = nil
  643. end
  644. if rmbUpConnection then
  645. rmbUpConnection:Disconnect()
  646. rmbUpConnection = nil
  647. end
  648. end
  649.  
  650. return CameraInput
  651. end
  652.  
  653. function _BaseCamera()
  654. --[[
  655. BaseCamera - Abstract base class for camera control modules
  656. 2018 Camera Update - AllYourBlox
  657. --]]
  658.  
  659. --[[ Local Constants ]]--
  660. local UNIT_Z = Vector3.new(0,0,1)
  661. local X1_Y0_Z1 = Vector3.new(1,0,1) --Note: not a unit vector, used for projecting onto XZ plane
  662.  
  663. local THUMBSTICK_DEADZONE = 0.2
  664. local DEFAULT_DISTANCE = 12.5 -- Studs
  665. local PORTRAIT_DEFAULT_DISTANCE = 25 -- Studs
  666. local FIRST_PERSON_DISTANCE_THRESHOLD = 1.0 -- Below this value, snap into first person
  667.  
  668. local CAMERA_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
  669.  
  670. -- Note: DotProduct check in CoordinateFrame::lookAt() prevents using values within about
  671. -- 8.11 degrees of the +/- Y axis, that's why these limits are currently 80 degrees
  672. local MIN_Y = math.rad(-80)
  673. local MAX_Y = math.rad(80)
  674.  
  675. local TOUCH_ADJUST_AREA_UP = math.rad(30)
  676. local TOUCH_ADJUST_AREA_DOWN = math.rad(-15)
  677.  
  678. local TOUCH_SENSITIVTY_ADJUST_MAX_Y = 2.1
  679. local TOUCH_SENSITIVTY_ADJUST_MIN_Y = 0.5
  680.  
  681. local VR_ANGLE = math.rad(15)
  682. local VR_LOW_INTENSITY_ROTATION = Vector2.new(math.rad(15), 0)
  683. local VR_HIGH_INTENSITY_ROTATION = Vector2.new(math.rad(45), 0)
  684. local VR_LOW_INTENSITY_REPEAT = 0.1
  685. local VR_HIGH_INTENSITY_REPEAT = 0.4
  686.  
  687. local ZERO_VECTOR2 = Vector2.new(0,0)
  688. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  689.  
  690. local TOUCH_SENSITIVTY = Vector2.new(0.00945 * math.pi, 0.003375 * math.pi)
  691. local MOUSE_SENSITIVITY = Vector2.new( 0.002 * math.pi, 0.0015 * math.pi )
  692.  
  693. local SEAT_OFFSET = Vector3.new(0,5,0)
  694. local VR_SEAT_OFFSET = Vector3.new(0,4,0)
  695. local HEAD_OFFSET = Vector3.new(0,1.5,0)
  696. local R15_HEAD_OFFSET = Vector3.new(0, 1.5, 0)
  697. local R15_HEAD_OFFSET_NO_SCALING = Vector3.new(0, 2, 0)
  698. local HUMANOID_ROOT_PART_SIZE = Vector3.new(2, 2, 1)
  699.  
  700. local GAMEPAD_ZOOM_STEP_1 = 0
  701. local GAMEPAD_ZOOM_STEP_2 = 10
  702. local GAMEPAD_ZOOM_STEP_3 = 20
  703.  
  704. local PAN_SENSITIVITY = 20
  705. local ZOOM_SENSITIVITY_CURVATURE = 0.5
  706.  
  707. local abs = math.abs
  708. local sign = math.sign
  709.  
  710. local FFlagUserCameraToggle do
  711. local success, result = pcall(function()
  712. return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  713. end)
  714. FFlagUserCameraToggle = success and result
  715. end
  716.  
  717. local FFlagUserDontAdjustSensitvityForPortrait do
  718. local success, result = pcall(function()
  719. return UserSettings():IsUserFeatureEnabled("UserDontAdjustSensitvityForPortrait")
  720. end)
  721. FFlagUserDontAdjustSensitvityForPortrait = success and result
  722. end
  723.  
  724. local FFlagUserFixZoomInZoomOutDiscrepancy do
  725. local success, result = pcall(function()
  726. return UserSettings():IsUserFeatureEnabled("UserFixZoomInZoomOutDiscrepancy")
  727. end)
  728. FFlagUserFixZoomInZoomOutDiscrepancy = success and result
  729. end
  730.  
  731. local Util = _CameraUtils()
  732. local ZoomController = _ZoomController()
  733. local CameraToggleStateController = _CameraToggleStateController()
  734. local CameraInput = _CameraInput()
  735. local CameraUI = _CameraUI()
  736.  
  737. --[[ Roblox Services ]]--
  738. local Players = game:GetService("Players")
  739. local UserInputService = game:GetService("UserInputService")
  740. local StarterGui = game:GetService("StarterGui")
  741. local GuiService = game:GetService("GuiService")
  742. local ContextActionService = game:GetService("ContextActionService")
  743. local VRService = game:GetService("VRService")
  744. local UserGameSettings = UserSettings():GetService("UserGameSettings")
  745.  
  746. local player = Players.LocalPlayer
  747.  
  748. --[[ The Module ]]--
  749. local BaseCamera = {}
  750. BaseCamera.__index = BaseCamera
  751.  
  752. function BaseCamera.new()
  753. local self = setmetatable({}, BaseCamera)
  754.  
  755. -- So that derived classes have access to this
  756. self.FIRST_PERSON_DISTANCE_THRESHOLD = FIRST_PERSON_DISTANCE_THRESHOLD
  757.  
  758. self.cameraType = nil
  759. self.cameraMovementMode = nil
  760.  
  761. self.lastCameraTransform = nil
  762. self.rotateInput = ZERO_VECTOR2
  763. self.userPanningCamera = false
  764. self.lastUserPanCamera = tick()
  765.  
  766. self.humanoidRootPart = nil
  767. self.humanoidCache = {}
  768.  
  769. -- Subject and position on last update call
  770. self.lastSubject = nil
  771. self.lastSubjectPosition = Vector3.new(0,5,0)
  772.  
  773. -- These subject distance members refer to the nominal camera-to-subject follow distance that the camera
  774. -- is trying to maintain, not the actual measured value.
  775. -- The default is updated when screen orientation or the min/max distances change,
  776. -- to be sure the default is always in range and appropriate for the orientation.
  777. self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  778. self.currentSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  779.  
  780. self.inFirstPerson = false
  781. self.inMouseLockedMode = false
  782. self.portraitMode = false
  783. self.isSmallTouchScreen = false
  784.  
  785. -- Used by modules which want to reset the camera angle on respawn.
  786. self.resetCameraAngle = true
  787.  
  788. self.enabled = false
  789.  
  790. -- Input Event Connections
  791. self.inputBeganConn = nil
  792. self.inputChangedConn = nil
  793. self.inputEndedConn = nil
  794.  
  795. self.startPos = nil
  796. self.lastPos = nil
  797. self.panBeginLook = nil
  798.  
  799. self.panEnabled = true
  800. self.keyPanEnabled = true
  801. self.distanceChangeEnabled = true
  802.  
  803. self.PlayerGui = nil
  804.  
  805. self.cameraChangedConn = nil
  806. self.viewportSizeChangedConn = nil
  807.  
  808. self.boundContextActions = {}
  809.  
  810. -- VR Support
  811. self.shouldUseVRRotation = false
  812. self.VRRotationIntensityAvailable = false
  813. self.lastVRRotationIntensityCheckTime = 0
  814. self.lastVRRotationTime = 0
  815. self.vrRotateKeyCooldown = {}
  816. self.cameraTranslationConstraints = Vector3.new(1, 1, 1)
  817. self.humanoidJumpOrigin = nil
  818. self.trackingHumanoid = nil
  819. self.cameraFrozen = false
  820. self.subjectStateChangedConn = nil
  821.  
  822. -- Gamepad support
  823. self.activeGamepad = nil
  824. self.gamepadPanningCamera = false
  825. self.lastThumbstickRotate = nil
  826. self.numOfSeconds = 0.7
  827. self.currentSpeed = 0
  828. self.maxSpeed = 6
  829. self.vrMaxSpeed = 4
  830. self.lastThumbstickPos = Vector2.new(0,0)
  831. self.ySensitivity = 0.65
  832. self.lastVelocity = nil
  833. self.gamepadConnectedConn = nil
  834. self.gamepadDisconnectedConn = nil
  835. self.currentZoomSpeed = 1.0
  836. self.L3ButtonDown = false
  837. self.dpadLeftDown = false
  838. self.dpadRightDown = false
  839.  
  840. -- Touch input support
  841. self.isDynamicThumbstickEnabled = false
  842. self.fingerTouches = {}
  843. self.dynamicTouchInput = nil
  844. self.numUnsunkTouches = 0
  845. self.inputStartPositions = {}
  846. self.inputStartTimes = {}
  847. self.startingDiff = nil
  848. self.pinchBeginZoom = nil
  849. self.userPanningTheCamera = false
  850. self.touchActivateConn = nil
  851.  
  852. -- Mouse locked formerly known as shift lock mode
  853. self.mouseLockOffset = ZERO_VECTOR3
  854.  
  855. -- [[ NOTICE ]] --
  856. -- Initialization things used to always execute at game load time, but now these camera modules are instantiated
  857. -- when needed, so the code here may run well after the start of the game
  858.  
  859. if player.Character then
  860. self:OnCharacterAdded(player.Character)
  861. end
  862.  
  863. player.CharacterAdded:Connect(function(char)
  864. self:OnCharacterAdded(char)
  865. end)
  866.  
  867. if self.cameraChangedConn then self.cameraChangedConn:Disconnect() end
  868. self.cameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
  869. self:OnCurrentCameraChanged()
  870. end)
  871. self:OnCurrentCameraChanged()
  872.  
  873. if self.playerCameraModeChangeConn then self.playerCameraModeChangeConn:Disconnect() end
  874. self.playerCameraModeChangeConn = player:GetPropertyChangedSignal("CameraMode"):Connect(function()
  875. self:OnPlayerCameraPropertyChange()
  876. end)
  877.  
  878. if self.minDistanceChangeConn then self.minDistanceChangeConn:Disconnect() end
  879. self.minDistanceChangeConn = player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(function()
  880. self:OnPlayerCameraPropertyChange()
  881. end)
  882.  
  883. if self.maxDistanceChangeConn then self.maxDistanceChangeConn:Disconnect() end
  884. self.maxDistanceChangeConn = player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(function()
  885. self:OnPlayerCameraPropertyChange()
  886. end)
  887.  
  888. if self.playerDevTouchMoveModeChangeConn then self.playerDevTouchMoveModeChangeConn:Disconnect() end
  889. self.playerDevTouchMoveModeChangeConn = player:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
  890. self:OnDevTouchMovementModeChanged()
  891. end)
  892. self:OnDevTouchMovementModeChanged() -- Init
  893.  
  894. if self.gameSettingsTouchMoveMoveChangeConn then self.gameSettingsTouchMoveMoveChangeConn:Disconnect() end
  895. self.gameSettingsTouchMoveMoveChangeConn = UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
  896. self:OnGameSettingsTouchMovementModeChanged()
  897. end)
  898. self:OnGameSettingsTouchMovementModeChanged() -- Init
  899.  
  900. UserGameSettings:SetCameraYInvertVisible()
  901. UserGameSettings:SetGamepadCameraSensitivityVisible()
  902.  
  903. self.hasGameLoaded = game:IsLoaded()
  904. if not self.hasGameLoaded then
  905. self.gameLoadedConn = game.Loaded:Connect(function()
  906. self.hasGameLoaded = true
  907. self.gameLoadedConn:Disconnect()
  908. self.gameLoadedConn = nil
  909. end)
  910. end
  911.  
  912. self:OnPlayerCameraPropertyChange()
  913.  
  914. return self
  915. end
  916.  
  917. function BaseCamera:GetModuleName()
  918. return "BaseCamera"
  919. end
  920.  
  921. function BaseCamera:OnCharacterAdded(char)
  922. self.resetCameraAngle = self.resetCameraAngle or self:GetEnabled()
  923. self.humanoidRootPart = nil
  924. if UserInputService.TouchEnabled then
  925. self.PlayerGui = player:WaitForChild("PlayerGui")
  926. for _, child in ipairs(char:GetChildren()) do
  927. if child:IsA("Tool") then
  928. self.isAToolEquipped = true
  929. end
  930. end
  931. char.ChildAdded:Connect(function(child)
  932. if child:IsA("Tool") then
  933. self.isAToolEquipped = true
  934. end
  935. end)
  936. char.ChildRemoved:Connect(function(child)
  937. if child:IsA("Tool") then
  938. self.isAToolEquipped = false
  939. end
  940. end)
  941. end
  942. end
  943.  
  944. function BaseCamera:GetHumanoidRootPart()
  945. if not self.humanoidRootPart then
  946. if player.Character then
  947. local humanoid = player.Character:FindFirstChildOfClass("Humanoid")
  948. if humanoid then
  949. self.humanoidRootPart = humanoid.RootPart
  950. end
  951. end
  952. end
  953. return self.humanoidRootPart
  954. end
  955.  
  956. function BaseCamera:GetBodyPartToFollow(humanoid, isDead)
  957. -- If the humanoid is dead, prefer the head part if one still exists as a sibling of the humanoid
  958. if humanoid:GetState() == Enum.HumanoidStateType.Dead then
  959. local character = humanoid.Parent
  960. if character and character:IsA("Model") then
  961. return character:FindFirstChild("Head") or humanoid.RootPart
  962. end
  963. end
  964.  
  965. return humanoid.RootPart
  966. end
  967.  
  968. function BaseCamera:GetSubjectPosition()
  969. local result = self.lastSubjectPosition
  970. local camera = game.Workspace.CurrentCamera
  971. local cameraSubject = camera and camera.CameraSubject
  972.  
  973. if cameraSubject then
  974. if cameraSubject:IsA("Humanoid") then
  975. local humanoid = cameraSubject
  976. local humanoidIsDead = humanoid:GetState() == Enum.HumanoidStateType.Dead
  977.  
  978. if VRService.VREnabled and humanoidIsDead and humanoid == self.lastSubject then
  979. result = self.lastSubjectPosition
  980. else
  981. local bodyPartToFollow = humanoid.RootPart
  982.  
  983. -- If the humanoid is dead, prefer their head part as a follow target, if it exists
  984. if humanoidIsDead then
  985. if humanoid.Parent and humanoid.Parent:IsA("Model") then
  986. bodyPartToFollow = humanoid.Parent:FindFirstChild("Head") or bodyPartToFollow
  987. end
  988. end
  989.  
  990. if bodyPartToFollow and bodyPartToFollow:IsA("BasePart") then
  991. local heightOffset
  992. if humanoid.RigType == Enum.HumanoidRigType.R15 then
  993. if humanoid.AutomaticScalingEnabled then
  994. heightOffset = R15_HEAD_OFFSET
  995. if bodyPartToFollow == humanoid.RootPart then
  996. local rootPartSizeOffset = (humanoid.RootPart.Size.Y/2) - (HUMANOID_ROOT_PART_SIZE.Y/2)
  997. heightOffset = heightOffset + Vector3.new(0, rootPartSizeOffset, 0)
  998. end
  999. else
  1000. heightOffset = R15_HEAD_OFFSET_NO_SCALING
  1001. end
  1002. else
  1003. heightOffset = HEAD_OFFSET
  1004. end
  1005.  
  1006. if humanoidIsDead then
  1007. heightOffset = ZERO_VECTOR3
  1008. end
  1009.  
  1010. result = bodyPartToFollow.CFrame.p + bodyPartToFollow.CFrame:vectorToWorldSpace(heightOffset + humanoid.CameraOffset)
  1011. end
  1012. end
  1013.  
  1014. elseif cameraSubject:IsA("VehicleSeat") then
  1015. local offset = SEAT_OFFSET
  1016. if VRService.VREnabled then
  1017. offset = VR_SEAT_OFFSET
  1018. end
  1019. result = cameraSubject.CFrame.p + cameraSubject.CFrame:vectorToWorldSpace(offset)
  1020. elseif cameraSubject:IsA("SkateboardPlatform") then
  1021. result = cameraSubject.CFrame.p + SEAT_OFFSET
  1022. elseif cameraSubject:IsA("BasePart") then
  1023. result = cameraSubject.CFrame.p
  1024. elseif cameraSubject:IsA("Model") then
  1025. if cameraSubject.PrimaryPart then
  1026. result = cameraSubject:GetPrimaryPartCFrame().p
  1027. else
  1028. result = cameraSubject:GetModelCFrame().p
  1029. end
  1030. end
  1031. else
  1032. -- cameraSubject is nil
  1033. -- Note: Previous RootCamera did not have this else case and let self.lastSubject and self.lastSubjectPosition
  1034. -- both get set to nil in the case of cameraSubject being nil. This function now exits here to preserve the
  1035. -- last set valid values for these, as nil values are not handled cases
  1036. return
  1037. end
  1038.  
  1039. self.lastSubject = cameraSubject
  1040. self.lastSubjectPosition = result
  1041.  
  1042. return result
  1043. end
  1044.  
  1045. function BaseCamera:UpdateDefaultSubjectDistance()
  1046. if self.portraitMode then
  1047. self.defaultSubjectDistance = math.clamp(PORTRAIT_DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  1048. else
  1049. self.defaultSubjectDistance = math.clamp(DEFAULT_DISTANCE, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  1050. end
  1051. end
  1052.  
  1053. function BaseCamera:OnViewportSizeChanged()
  1054. local camera = game.Workspace.CurrentCamera
  1055. local size = camera.ViewportSize
  1056. self.portraitMode = size.X < size.Y
  1057. self.isSmallTouchScreen = UserInputService.TouchEnabled and (size.Y < 500 or size.X < 700)
  1058.  
  1059. self:UpdateDefaultSubjectDistance()
  1060. end
  1061.  
  1062. -- Listener for changes to workspace.CurrentCamera
  1063. function BaseCamera:OnCurrentCameraChanged()
  1064. if UserInputService.TouchEnabled then
  1065. if self.viewportSizeChangedConn then
  1066. self.viewportSizeChangedConn:Disconnect()
  1067. self.viewportSizeChangedConn = nil
  1068. end
  1069.  
  1070. local newCamera = game.Workspace.CurrentCamera
  1071.  
  1072. if newCamera then
  1073. self:OnViewportSizeChanged()
  1074. self.viewportSizeChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
  1075. self:OnViewportSizeChanged()
  1076. end)
  1077. end
  1078. end
  1079.  
  1080. -- VR support additions
  1081. if self.cameraSubjectChangedConn then
  1082. self.cameraSubjectChangedConn:Disconnect()
  1083. self.cameraSubjectChangedConn = nil
  1084. end
  1085.  
  1086. local camera = game.Workspace.CurrentCamera
  1087. if camera then
  1088. self.cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
  1089. self:OnNewCameraSubject()
  1090. end)
  1091. self:OnNewCameraSubject()
  1092. end
  1093. end
  1094.  
  1095. function BaseCamera:OnDynamicThumbstickEnabled()
  1096. if UserInputService.TouchEnabled then
  1097. self.isDynamicThumbstickEnabled = true
  1098. end
  1099. end
  1100.  
  1101. function BaseCamera:OnDynamicThumbstickDisabled()
  1102. self.isDynamicThumbstickEnabled = false
  1103. end
  1104.  
  1105. function BaseCamera:OnGameSettingsTouchMovementModeChanged()
  1106. if player.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
  1107. if (UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.DynamicThumbstick
  1108. or UserGameSettings.TouchMovementMode == Enum.TouchMovementMode.Default) then
  1109. self:OnDynamicThumbstickEnabled()
  1110. else
  1111. self:OnDynamicThumbstickDisabled()
  1112. end
  1113. end
  1114. end
  1115.  
  1116. function BaseCamera:OnDevTouchMovementModeChanged()
  1117. if player.DevTouchMovementMode.Name == "DynamicThumbstick" then
  1118. self:OnDynamicThumbstickEnabled()
  1119. else
  1120. self:OnGameSettingsTouchMovementModeChanged()
  1121. end
  1122. end
  1123.  
  1124. function BaseCamera:OnPlayerCameraPropertyChange()
  1125. -- This call forces re-evaluation of player.CameraMode and clamping to min/max distance which may have changed
  1126. self:SetCameraToSubjectDistance(self.currentSubjectDistance)
  1127. end
  1128.  
  1129. function BaseCamera:GetCameraHeight()
  1130. if VRService.VREnabled and not self.inFirstPerson then
  1131. return math.sin(VR_ANGLE) * self.currentSubjectDistance
  1132. end
  1133. return 0
  1134. end
  1135.  
  1136. function BaseCamera:InputTranslationToCameraAngleChange(translationVector, sensitivity)
  1137. if not FFlagUserDontAdjustSensitvityForPortrait then
  1138. local camera = game.Workspace.CurrentCamera
  1139. if camera and camera.ViewportSize.X > 0 and camera.ViewportSize.Y > 0 and (camera.ViewportSize.Y > camera.ViewportSize.X) then
  1140. -- Screen has portrait orientation, swap X and Y sensitivity
  1141. return translationVector * Vector2.new( sensitivity.Y, sensitivity.X)
  1142. end
  1143. end
  1144. return translationVector * sensitivity
  1145. end
  1146.  
  1147. function BaseCamera:Enable(enable)
  1148. if self.enabled ~= enable then
  1149. self.enabled = enable
  1150. if self.enabled then
  1151. self:ConnectInputEvents()
  1152. self:BindContextActions()
  1153.  
  1154. if player.CameraMode == Enum.CameraMode.LockFirstPerson then
  1155. self.currentSubjectDistance = 0.5
  1156. if not self.inFirstPerson then
  1157. self:EnterFirstPerson()
  1158. end
  1159. end
  1160. else
  1161. self:DisconnectInputEvents()
  1162. self:UnbindContextActions()
  1163. -- Clean up additional event listeners and reset a bunch of properties
  1164. self:Cleanup()
  1165. end
  1166. end
  1167. end
  1168.  
  1169. function BaseCamera:GetEnabled()
  1170. return self.enabled
  1171. end
  1172.  
  1173. function BaseCamera:OnInputBegan(input, processed)
  1174. if input.UserInputType == Enum.UserInputType.Touch then
  1175. self:OnTouchBegan(input, processed)
  1176. elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
  1177. self:OnMouse2Down(input, processed)
  1178. elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
  1179. self:OnMouse3Down(input, processed)
  1180. end
  1181. end
  1182.  
  1183. function BaseCamera:OnInputChanged(input, processed)
  1184. if input.UserInputType == Enum.UserInputType.Touch then
  1185. self:OnTouchChanged(input, processed)
  1186. elseif input.UserInputType == Enum.UserInputType.MouseMovement then
  1187. self:OnMouseMoved(input, processed)
  1188. end
  1189. end
  1190.  
  1191. function BaseCamera:OnInputEnded(input, processed)
  1192. if input.UserInputType == Enum.UserInputType.Touch then
  1193. self:OnTouchEnded(input, processed)
  1194. elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
  1195. self:OnMouse2Up(input, processed)
  1196. elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
  1197. self:OnMouse3Up(input, processed)
  1198. end
  1199. end
  1200.  
  1201. function BaseCamera:OnPointerAction(wheel, pan, pinch, processed)
  1202. if processed then
  1203. return
  1204. end
  1205.  
  1206. if pan.Magnitude > 0 then
  1207. local inversionVector = Vector2.new(1, UserGameSettings:GetCameraYInvertValue())
  1208. local rotateDelta = self:InputTranslationToCameraAngleChange(PAN_SENSITIVITY*pan, MOUSE_SENSITIVITY)*inversionVector
  1209. self.rotateInput = self.rotateInput + rotateDelta
  1210. end
  1211.  
  1212. local zoom = self.currentSubjectDistance
  1213. local zoomDelta = -(wheel + pinch)
  1214.  
  1215. if abs(zoomDelta) > 0 then
  1216. local newZoom
  1217. if self.inFirstPerson and zoomDelta > 0 then
  1218. newZoom = FIRST_PERSON_DISTANCE_THRESHOLD
  1219. else
  1220. if FFlagUserFixZoomInZoomOutDiscrepancy then
  1221. if (zoomDelta > 0) then
  1222. newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
  1223. else
  1224. newZoom = (zoom + zoomDelta) / (1 - zoomDelta*ZOOM_SENSITIVITY_CURVATURE)
  1225. end
  1226. else
  1227. newZoom = zoom + zoomDelta*(1 + zoom*ZOOM_SENSITIVITY_CURVATURE)
  1228. end
  1229. end
  1230.  
  1231. self:SetCameraToSubjectDistance(newZoom)
  1232. end
  1233. end
  1234.  
  1235. function BaseCamera:ConnectInputEvents()
  1236. self.pointerActionConn = UserInputService.PointerAction:Connect(function(wheel, pan, pinch, processed)
  1237. self:OnPointerAction(wheel, pan, pinch, processed)
  1238. end)
  1239.  
  1240. self.inputBeganConn = UserInputService.InputBegan:Connect(function(input, processed)
  1241. self:OnInputBegan(input, processed)
  1242. end)
  1243.  
  1244. self.inputChangedConn = UserInputService.InputChanged:Connect(function(input, processed)
  1245. self:OnInputChanged(input, processed)
  1246. end)
  1247.  
  1248. self.inputEndedConn = UserInputService.InputEnded:Connect(function(input, processed)
  1249. self:OnInputEnded(input, processed)
  1250. end)
  1251.  
  1252. self.menuOpenedConn = GuiService.MenuOpened:connect(function()
  1253. self:ResetInputStates()
  1254. end)
  1255.  
  1256. self.gamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
  1257. if self.activeGamepad ~= gamepadEnum then return end
  1258. self.activeGamepad = nil
  1259. self:AssignActivateGamepad()
  1260. end)
  1261.  
  1262. self.gamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum)
  1263. if self.activeGamepad == nil then
  1264. self:AssignActivateGamepad()
  1265. end
  1266. end)
  1267.  
  1268. self:AssignActivateGamepad()
  1269. if not FFlagUserCameraToggle then
  1270. self:UpdateMouseBehavior()
  1271. end
  1272. end
  1273.  
  1274. function BaseCamera:BindContextActions()
  1275. self:BindGamepadInputActions()
  1276. self:BindKeyboardInputActions()
  1277. end
  1278.  
  1279. function BaseCamera:AssignActivateGamepad()
  1280. local connectedGamepads = UserInputService:GetConnectedGamepads()
  1281. if #connectedGamepads > 0 then
  1282. for i = 1, #connectedGamepads do
  1283. if self.activeGamepad == nil then
  1284. self.activeGamepad = connectedGamepads[i]
  1285. elseif connectedGamepads[i].Value < self.activeGamepad.Value then
  1286. self.activeGamepad = connectedGamepads[i]
  1287. end
  1288. end
  1289. end
  1290.  
  1291. if self.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
  1292. self.activeGamepad = Enum.UserInputType.Gamepad1
  1293. end
  1294. end
  1295.  
  1296. function BaseCamera:DisconnectInputEvents()
  1297. if self.inputBeganConn then
  1298. self.inputBeganConn:Disconnect()
  1299. self.inputBeganConn = nil
  1300. end
  1301. if self.inputChangedConn then
  1302. self.inputChangedConn:Disconnect()
  1303. self.inputChangedConn = nil
  1304. end
  1305. if self.inputEndedConn then
  1306. self.inputEndedConn:Disconnect()
  1307. self.inputEndedConn = nil
  1308. end
  1309. end
  1310.  
  1311. function BaseCamera:UnbindContextActions()
  1312. for i = 1, #self.boundContextActions do
  1313. ContextActionService:UnbindAction(self.boundContextActions[i])
  1314. end
  1315. self.boundContextActions = {}
  1316. end
  1317.  
  1318. function BaseCamera:Cleanup()
  1319. if self.pointerActionConn then
  1320. self.pointerActionConn:Disconnect()
  1321. self.pointerActionConn = nil
  1322. end
  1323. if self.menuOpenedConn then
  1324. self.menuOpenedConn:Disconnect()
  1325. self.menuOpenedConn = nil
  1326. end
  1327. if self.mouseLockToggleConn then
  1328. self.mouseLockToggleConn:Disconnect()
  1329. self.mouseLockToggleConn = nil
  1330. end
  1331. if self.gamepadConnectedConn then
  1332. self.gamepadConnectedConn:Disconnect()
  1333. self.gamepadConnectedConn = nil
  1334. end
  1335. if self.gamepadDisconnectedConn then
  1336. self.gamepadDisconnectedConn:Disconnect()
  1337. self.gamepadDisconnectedConn = nil
  1338. end
  1339. if self.subjectStateChangedConn then
  1340. self.subjectStateChangedConn:Disconnect()
  1341. self.subjectStateChangedConn = nil
  1342. end
  1343. if self.viewportSizeChangedConn then
  1344. self.viewportSizeChangedConn:Disconnect()
  1345. self.viewportSizeChangedConn = nil
  1346. end
  1347. if self.touchActivateConn then
  1348. self.touchActivateConn:Disconnect()
  1349. self.touchActivateConn = nil
  1350. end
  1351.  
  1352. self.turningLeft = false
  1353. self.turningRight = false
  1354. self.lastCameraTransform = nil
  1355. self.lastSubjectCFrame = nil
  1356. self.userPanningTheCamera = false
  1357. self.rotateInput = Vector2.new()
  1358. self.gamepadPanningCamera = Vector2.new(0,0)
  1359.  
  1360. -- Reset input states
  1361. self.startPos = nil
  1362. self.lastPos = nil
  1363. self.panBeginLook = nil
  1364. self.isRightMouseDown = false
  1365. self.isMiddleMouseDown = false
  1366.  
  1367. self.fingerTouches = {}
  1368. self.dynamicTouchInput = nil
  1369. self.numUnsunkTouches = 0
  1370.  
  1371. self.startingDiff = nil
  1372. self.pinchBeginZoom = nil
  1373.  
  1374. -- Unlock mouse for example if right mouse button was being held down
  1375. if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
  1376. UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  1377. end
  1378. end
  1379.  
  1380. -- This is called when settings menu is opened
  1381. function BaseCamera:ResetInputStates()
  1382. self.isRightMouseDown = false
  1383. self.isMiddleMouseDown = false
  1384. self:OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters
  1385.  
  1386. if UserInputService.TouchEnabled then
  1387. --[[menu opening was causing serious touch issues
  1388. this should disable all active touch events if
  1389. they're active when menu opens.]]
  1390. for inputObject in pairs(self.fingerTouches) do
  1391. self.fingerTouches[inputObject] = nil
  1392. end
  1393. self.dynamicTouchInput = nil
  1394. self.panBeginLook = nil
  1395. self.startPos = nil
  1396. self.lastPos = nil
  1397. self.userPanningTheCamera = false
  1398. self.startingDiff = nil
  1399. self.pinchBeginZoom = nil
  1400. self.numUnsunkTouches = 0
  1401. end
  1402. end
  1403.  
  1404. function BaseCamera:GetGamepadPan(name, state, input)
  1405. if input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
  1406. -- if self.L3ButtonDown then
  1407. -- -- L3 Thumbstick is depressed, right stick controls dolly in/out
  1408. -- if (input.Position.Y > THUMBSTICK_DEADZONE) then
  1409. -- self.currentZoomSpeed = 0.96
  1410. -- elseif (input.Position.Y < -THUMBSTICK_DEADZONE) then
  1411. -- self.currentZoomSpeed = 1.04
  1412. -- else
  1413. -- self.currentZoomSpeed = 1.00
  1414. -- end
  1415. -- else
  1416. if state == Enum.UserInputState.Cancel then
  1417. self.gamepadPanningCamera = ZERO_VECTOR2
  1418. return
  1419. end
  1420.  
  1421. local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
  1422. if inputVector.magnitude > THUMBSTICK_DEADZONE then
  1423. self.gamepadPanningCamera = Vector2.new(input.Position.X, -input.Position.Y)
  1424. else
  1425. self.gamepadPanningCamera = ZERO_VECTOR2
  1426. end
  1427. --end
  1428. return Enum.ContextActionResult.Sink
  1429. end
  1430. return Enum.ContextActionResult.Pass
  1431. end
  1432.  
  1433. function BaseCamera:DoKeyboardPanTurn(name, state, input)
  1434. if not self.hasGameLoaded and VRService.VREnabled then
  1435. return Enum.ContextActionResult.Pass
  1436. end
  1437.  
  1438. if state == Enum.UserInputState.Cancel then
  1439. self.turningLeft = false
  1440. self.turningRight = false
  1441. return Enum.ContextActionResult.Sink
  1442. end
  1443.  
  1444. if self.panBeginLook == nil and self.keyPanEnabled then
  1445. if input.KeyCode == Enum.KeyCode.Left then
  1446. self.turningLeft = state == Enum.UserInputState.Begin
  1447. elseif input.KeyCode == Enum.KeyCode.Right then
  1448. self.turningRight = state == Enum.UserInputState.Begin
  1449. end
  1450. return Enum.ContextActionResult.Sink
  1451. end
  1452. return Enum.ContextActionResult.Pass
  1453. end
  1454.  
  1455. function BaseCamera:DoPanRotateCamera(rotateAngle)
  1456. local angle = Util.RotateVectorByAngleAndRound(self:GetCameraLookVector() * Vector3.new(1,0,1), rotateAngle, math.pi*0.25)
  1457. if angle ~= 0 then
  1458. self.rotateInput = self.rotateInput + Vector2.new(angle, 0)
  1459. self.lastUserPanCamera = tick()
  1460. self.lastCameraTransform = nil
  1461. end
  1462. end
  1463.  
  1464. function BaseCamera:DoGamepadZoom(name, state, input)
  1465. if input.UserInputType == self.activeGamepad then
  1466. if input.KeyCode == Enum.KeyCode.ButtonR3 then
  1467. if state == Enum.UserInputState.Begin then
  1468. if self.distanceChangeEnabled then
  1469. local dist = self:GetCameraToSubjectDistance()
  1470.  
  1471. if dist > (GAMEPAD_ZOOM_STEP_2 + GAMEPAD_ZOOM_STEP_3)/2 then
  1472. self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_2)
  1473. elseif dist > (GAMEPAD_ZOOM_STEP_1 + GAMEPAD_ZOOM_STEP_2)/2 then
  1474. self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_1)
  1475. else
  1476. self:SetCameraToSubjectDistance(GAMEPAD_ZOOM_STEP_3)
  1477. end
  1478. end
  1479. end
  1480. elseif input.KeyCode == Enum.KeyCode.DPadLeft then
  1481. self.dpadLeftDown = (state == Enum.UserInputState.Begin)
  1482. elseif input.KeyCode == Enum.KeyCode.DPadRight then
  1483. self.dpadRightDown = (state == Enum.UserInputState.Begin)
  1484. end
  1485.  
  1486. if self.dpadLeftDown then
  1487. self.currentZoomSpeed = 1.04
  1488. elseif self.dpadRightDown then
  1489. self.currentZoomSpeed = 0.96
  1490. else
  1491. self.currentZoomSpeed = 1.00
  1492. end
  1493. return Enum.ContextActionResult.Sink
  1494. end
  1495. return Enum.ContextActionResult.Pass
  1496. -- elseif input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonL3 then
  1497. -- if (state == Enum.UserInputState.Begin) then
  1498. -- self.L3ButtonDown = true
  1499. -- elseif (state == Enum.UserInputState.End) then
  1500. -- self.L3ButtonDown = false
  1501. -- self.currentZoomSpeed = 1.00
  1502. -- end
  1503. -- end
  1504. end
  1505.  
  1506. function BaseCamera:DoKeyboardZoom(name, state, input)
  1507. if not self.hasGameLoaded and VRService.VREnabled then
  1508. return Enum.ContextActionResult.Pass
  1509. end
  1510.  
  1511. if state ~= Enum.UserInputState.Begin then
  1512. return Enum.ContextActionResult.Pass
  1513. end
  1514.  
  1515. if self.distanceChangeEnabled and player.CameraMode ~= Enum.CameraMode.LockFirstPerson then
  1516. if input.KeyCode == Enum.KeyCode.I then
  1517. self:SetCameraToSubjectDistance( self.currentSubjectDistance - 5 )
  1518. elseif input.KeyCode == Enum.KeyCode.O then
  1519. self:SetCameraToSubjectDistance( self.currentSubjectDistance + 5 )
  1520. end
  1521. return Enum.ContextActionResult.Sink
  1522. end
  1523. return Enum.ContextActionResult.Pass
  1524. end
  1525.  
  1526. function BaseCamera:BindAction(actionName, actionFunc, createTouchButton, ...)
  1527. table.insert(self.boundContextActions, actionName)
  1528. ContextActionService:BindActionAtPriority(actionName, actionFunc, createTouchButton,
  1529. CAMERA_ACTION_PRIORITY, ...)
  1530. end
  1531.  
  1532. function BaseCamera:BindGamepadInputActions()
  1533. self:BindAction("BaseCameraGamepadPan", function(name, state, input) return self:GetGamepadPan(name, state, input) end,
  1534. false, Enum.KeyCode.Thumbstick2)
  1535. self:BindAction("BaseCameraGamepadZoom", function(name, state, input) return self:DoGamepadZoom(name, state, input) end,
  1536. false, Enum.KeyCode.DPadLeft, Enum.KeyCode.DPadRight, Enum.KeyCode.ButtonR3)
  1537. end
  1538.  
  1539. function BaseCamera:BindKeyboardInputActions()
  1540. self:BindAction("BaseCameraKeyboardPanArrowKeys", function(name, state, input) return self:DoKeyboardPanTurn(name, state, input) end,
  1541. false, Enum.KeyCode.Left, Enum.KeyCode.Right)
  1542. self:BindAction("BaseCameraKeyboardZoom", function(name, state, input) return self:DoKeyboardZoom(name, state, input) end,
  1543. false, Enum.KeyCode.I, Enum.KeyCode.O)
  1544. end
  1545.  
  1546. local function isInDynamicThumbstickArea(input)
  1547. local playerGui = player:FindFirstChildOfClass("PlayerGui")
  1548. local touchGui = playerGui and playerGui:FindFirstChild("TouchGui")
  1549. local touchFrame = touchGui and touchGui:FindFirstChild("TouchControlFrame")
  1550. local thumbstickFrame = touchFrame and touchFrame:FindFirstChild("DynamicThumbstickFrame")
  1551.  
  1552. if not thumbstickFrame then
  1553. return false
  1554. end
  1555.  
  1556. local frameCornerTopLeft = thumbstickFrame.AbsolutePosition
  1557. local frameCornerBottomRight = frameCornerTopLeft + thumbstickFrame.AbsoluteSize
  1558. if input.Position.X >= frameCornerTopLeft.X and input.Position.Y >= frameCornerTopLeft.Y then
  1559. if input.Position.X <= frameCornerBottomRight.X and input.Position.Y <= frameCornerBottomRight.Y then
  1560. return true
  1561. end
  1562. end
  1563.  
  1564. return false
  1565. end
  1566.  
  1567. ---Adjusts the camera Y touch Sensitivity when moving away from the center and in the TOUCH_SENSITIVTY_ADJUST_AREA
  1568. function BaseCamera:AdjustTouchSensitivity(delta, sensitivity)
  1569. local cameraCFrame = game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame
  1570. if not cameraCFrame then
  1571. return sensitivity
  1572. end
  1573. local currPitchAngle = cameraCFrame:ToEulerAnglesYXZ()
  1574.  
  1575. local multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y
  1576. if currPitchAngle > TOUCH_ADJUST_AREA_UP and delta.Y < 0 then
  1577. local fractionAdjust = (currPitchAngle - TOUCH_ADJUST_AREA_UP)/(MAX_Y - TOUCH_ADJUST_AREA_UP)
  1578. fractionAdjust = 1 - (1 - fractionAdjust)^3
  1579. multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y - fractionAdjust * (
  1580. TOUCH_SENSITIVTY_ADJUST_MAX_Y - TOUCH_SENSITIVTY_ADJUST_MIN_Y)
  1581. elseif currPitchAngle < TOUCH_ADJUST_AREA_DOWN and delta.Y > 0 then
  1582. local fractionAdjust = (currPitchAngle - TOUCH_ADJUST_AREA_DOWN)/(MIN_Y - TOUCH_ADJUST_AREA_DOWN)
  1583. fractionAdjust = 1 - (1 - fractionAdjust)^3
  1584. multiplierY = TOUCH_SENSITIVTY_ADJUST_MAX_Y - fractionAdjust * (
  1585. TOUCH_SENSITIVTY_ADJUST_MAX_Y - TOUCH_SENSITIVTY_ADJUST_MIN_Y)
  1586. end
  1587.  
  1588. return Vector2.new(
  1589. sensitivity.X,
  1590. sensitivity.Y * multiplierY
  1591. )
  1592. end
  1593.  
  1594. function BaseCamera:OnTouchBegan(input, processed)
  1595. local canUseDynamicTouch = self.isDynamicThumbstickEnabled and not processed
  1596. if canUseDynamicTouch then
  1597. if self.dynamicTouchInput == nil and isInDynamicThumbstickArea(input) then
  1598. -- First input in the dynamic thumbstick area should always be ignored for camera purposes
  1599. -- Even if the dynamic thumbstick does not process it immediately
  1600. self.dynamicTouchInput = input
  1601. return
  1602. end
  1603. self.fingerTouches[input] = processed
  1604. self.inputStartPositions[input] = input.Position
  1605. self.inputStartTimes[input] = tick()
  1606. self.numUnsunkTouches = self.numUnsunkTouches + 1
  1607. end
  1608. end
  1609.  
  1610. function BaseCamera:OnTouchChanged(input, processed)
  1611. if self.fingerTouches[input] == nil then
  1612. if self.isDynamicThumbstickEnabled then
  1613. return
  1614. end
  1615. self.fingerTouches[input] = processed
  1616. if not processed then
  1617. self.numUnsunkTouches = self.numUnsunkTouches + 1
  1618. end
  1619. end
  1620.  
  1621. if self.numUnsunkTouches == 1 then
  1622. if self.fingerTouches[input] == false then
  1623. self.panBeginLook = self.panBeginLook or self:GetCameraLookVector()
  1624. self.startPos = self.startPos or input.Position
  1625. self.lastPos = self.lastPos or self.startPos
  1626. self.userPanningTheCamera = true
  1627.  
  1628. local delta = input.Position - self.lastPos
  1629. delta = Vector2.new(delta.X, delta.Y * UserGameSettings:GetCameraYInvertValue())
  1630. if self.panEnabled then
  1631. local adjustedTouchSensitivity = TOUCH_SENSITIVTY
  1632. self:AdjustTouchSensitivity(delta, TOUCH_SENSITIVTY)
  1633.  
  1634. local desiredXYVector = self:InputTranslationToCameraAngleChange(delta, adjustedTouchSensitivity)
  1635. self.rotateInput = self.rotateInput + desiredXYVector
  1636. end
  1637. self.lastPos = input.Position
  1638. end
  1639. else
  1640. self.panBeginLook = nil
  1641. self.startPos = nil
  1642. self.lastPos = nil
  1643. self.userPanningTheCamera = false
  1644. end
  1645. if self.numUnsunkTouches == 2 then
  1646. local unsunkTouches = {}
  1647. for touch, wasSunk in pairs(self.fingerTouches) do
  1648. if not wasSunk then
  1649. table.insert(unsunkTouches, touch)
  1650. end
  1651. end
  1652. if #unsunkTouches == 2 then
  1653. local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
  1654. if self.startingDiff and self.pinchBeginZoom then
  1655. local scale = difference / math.max(0.01, self.startingDiff)
  1656. local clampedScale = math.clamp(scale, 0.1, 10)
  1657. if self.distanceChangeEnabled then
  1658. self:SetCameraToSubjectDistance(self.pinchBeginZoom / clampedScale)
  1659. end
  1660. else
  1661. self.startingDiff = difference
  1662. self.pinchBeginZoom = self:GetCameraToSubjectDistance()
  1663. end
  1664. end
  1665. else
  1666. self.startingDiff = nil
  1667. self.pinchBeginZoom = nil
  1668. end
  1669. end
  1670.  
  1671. function BaseCamera:OnTouchEnded(input, processed)
  1672. if input == self.dynamicTouchInput then
  1673. self.dynamicTouchInput = nil
  1674. return
  1675. end
  1676.  
  1677. if self.fingerTouches[input] == false then
  1678. if self.numUnsunkTouches == 1 then
  1679. self.panBeginLook = nil
  1680. self.startPos = nil
  1681. self.lastPos = nil
  1682. self.userPanningTheCamera = false
  1683. elseif self.numUnsunkTouches == 2 then
  1684. self.startingDiff = nil
  1685. self.pinchBeginZoom = nil
  1686. end
  1687. end
  1688.  
  1689. if self.fingerTouches[input] ~= nil and self.fingerTouches[input] == false then
  1690. self.numUnsunkTouches = self.numUnsunkTouches - 1
  1691. end
  1692. self.fingerTouches[input] = nil
  1693. self.inputStartPositions[input] = nil
  1694. self.inputStartTimes[input] = nil
  1695. end
  1696.  
  1697. function BaseCamera:OnMouse2Down(input, processed)
  1698. if processed then return end
  1699.  
  1700. self.isRightMouseDown = true
  1701. self:OnMousePanButtonPressed(input, processed)
  1702. end
  1703.  
  1704. function BaseCamera:OnMouse2Up(input, processed)
  1705. self.isRightMouseDown = false
  1706. self:OnMousePanButtonReleased(input, processed)
  1707. end
  1708.  
  1709. function BaseCamera:OnMouse3Down(input, processed)
  1710. if processed then return end
  1711.  
  1712. self.isMiddleMouseDown = true
  1713. self:OnMousePanButtonPressed(input, processed)
  1714. end
  1715.  
  1716. function BaseCamera:OnMouse3Up(input, processed)
  1717. self.isMiddleMouseDown = false
  1718. self:OnMousePanButtonReleased(input, processed)
  1719. end
  1720.  
  1721. function BaseCamera:OnMouseMoved(input, processed)
  1722. if not self.hasGameLoaded and VRService.VREnabled then
  1723. return
  1724. end
  1725.  
  1726. local inputDelta = input.Delta
  1727. inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * UserGameSettings:GetCameraYInvertValue())
  1728.  
  1729. local isInputPanning = FFlagUserCameraToggle and CameraInput.getPanning()
  1730. local isBeginLook = self.startPos and self.lastPos and self.panBeginLook
  1731. local isPanning = isBeginLook or self.inFirstPerson or self.inMouseLockedMode or isInputPanning
  1732.  
  1733. if self.panEnabled and isPanning then
  1734. local desiredXYVector = self:InputTranslationToCameraAngleChange(inputDelta, MOUSE_SENSITIVITY)
  1735. self.rotateInput = self.rotateInput + desiredXYVector
  1736. end
  1737.  
  1738. if self.startPos and self.lastPos and self.panBeginLook then
  1739. self.lastPos = self.lastPos + input.Delta
  1740. end
  1741. end
  1742.  
  1743. function BaseCamera:OnMousePanButtonPressed(input, processed)
  1744. if processed then return end
  1745. if not FFlagUserCameraToggle then
  1746. self:UpdateMouseBehavior()
  1747. end
  1748. self.panBeginLook = self.panBeginLook or self:GetCameraLookVector()
  1749. self.startPos = self.startPos or input.Position
  1750. self.lastPos = self.lastPos or self.startPos
  1751. self.userPanningTheCamera = true
  1752. end
  1753.  
  1754. function BaseCamera:OnMousePanButtonReleased(input, processed)
  1755. if not FFlagUserCameraToggle then
  1756. self:UpdateMouseBehavior()
  1757. end
  1758. if not (self.isRightMouseDown or self.isMiddleMouseDown) then
  1759. self.panBeginLook = nil
  1760. self.startPos = nil
  1761. self.lastPos = nil
  1762. self.userPanningTheCamera = false
  1763. end
  1764. end
  1765.  
  1766. function BaseCamera:UpdateMouseBehavior()
  1767. if FFlagUserCameraToggle and self.isCameraToggle then
  1768. CameraUI.setCameraModeToastEnabled(true)
  1769. CameraInput.enableCameraToggleInput()
  1770. CameraToggleStateController(self.inFirstPerson)
  1771. else
  1772. if FFlagUserCameraToggle then
  1773. CameraUI.setCameraModeToastEnabled(false)
  1774. CameraInput.disableCameraToggleInput()
  1775. end
  1776. -- first time transition to first person mode or mouse-locked third person
  1777. if self.inFirstPerson or self.inMouseLockedMode then
  1778. --UserGameSettings.RotationType = Enum.RotationType.CameraRelative
  1779. UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
  1780. else
  1781. UserGameSettings.RotationType = Enum.RotationType.MovementRelative
  1782. if self.isRightMouseDown or self.isMiddleMouseDown then
  1783. UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
  1784. else
  1785. UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  1786. end
  1787. end
  1788. end
  1789. end
  1790.  
  1791. function BaseCamera:UpdateForDistancePropertyChange()
  1792. -- Calling this setter with the current value will force checking that it is still
  1793. -- in range after a change to the min/max distance limits
  1794. self:SetCameraToSubjectDistance(self.currentSubjectDistance)
  1795. end
  1796.  
  1797. function BaseCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
  1798. local lastSubjectDistance = self.currentSubjectDistance
  1799.  
  1800. -- By default, camera modules will respect LockFirstPerson and override the currentSubjectDistance with 0
  1801. -- regardless of what Player.CameraMinZoomDistance is set to, so that first person can be made
  1802. -- available by the developer without needing to allow players to mousewheel dolly into first person.
  1803. -- Some modules will override this function to remove or change first-person capability.
  1804. if player.CameraMode == Enum.CameraMode.LockFirstPerson then
  1805. self.currentSubjectDistance = 0.5
  1806. if not self.inFirstPerson then
  1807. self:EnterFirstPerson()
  1808. end
  1809. else
  1810. local newSubjectDistance = math.clamp(desiredSubjectDistance, player.CameraMinZoomDistance, player.CameraMaxZoomDistance)
  1811. if newSubjectDistance < FIRST_PERSON_DISTANCE_THRESHOLD then
  1812. self.currentSubjectDistance = 0.5
  1813. if not self.inFirstPerson then
  1814. self:EnterFirstPerson()
  1815. end
  1816. else
  1817. self.currentSubjectDistance = newSubjectDistance
  1818. if self.inFirstPerson then
  1819. self:LeaveFirstPerson()
  1820. end
  1821. end
  1822. end
  1823.  
  1824. -- Pass target distance and zoom direction to the zoom controller
  1825. ZoomController.SetZoomParameters(self.currentSubjectDistance, math.sign(desiredSubjectDistance - lastSubjectDistance))
  1826.  
  1827. -- Returned only for convenience to the caller to know the outcome
  1828. return self.currentSubjectDistance
  1829. end
  1830.  
  1831. function BaseCamera:SetCameraType( cameraType )
  1832. --Used by derived classes
  1833. self.cameraType = cameraType
  1834. end
  1835.  
  1836. function BaseCamera:GetCameraType()
  1837. return self.cameraType
  1838. end
  1839.  
  1840. -- Movement mode standardized to Enum.ComputerCameraMovementMode values
  1841. function BaseCamera:SetCameraMovementMode( cameraMovementMode )
  1842. self.cameraMovementMode = cameraMovementMode
  1843. end
  1844.  
  1845. function BaseCamera:GetCameraMovementMode()
  1846. return self.cameraMovementMode
  1847. end
  1848.  
  1849. function BaseCamera:SetIsMouseLocked(mouseLocked)
  1850. self.inMouseLockedMode = mouseLocked
  1851. if not FFlagUserCameraToggle then
  1852. self:UpdateMouseBehavior()
  1853. end
  1854. end
  1855.  
  1856. function BaseCamera:GetIsMouseLocked()
  1857. return self.inMouseLockedMode
  1858. end
  1859.  
  1860. function BaseCamera:SetMouseLockOffset(offsetVector)
  1861. self.mouseLockOffset = offsetVector
  1862. end
  1863.  
  1864. function BaseCamera:GetMouseLockOffset()
  1865. return self.mouseLockOffset
  1866. end
  1867.  
  1868. function BaseCamera:InFirstPerson()
  1869. return self.inFirstPerson
  1870. end
  1871.  
  1872. function BaseCamera:EnterFirstPerson()
  1873. -- Overridden in ClassicCamera, the only module which supports FirstPerson
  1874. end
  1875.  
  1876. function BaseCamera:LeaveFirstPerson()
  1877. -- Overridden in ClassicCamera, the only module which supports FirstPerson
  1878. end
  1879.  
  1880. -- Nominal distance, set by dollying in and out with the mouse wheel or equivalent, not measured distance
  1881. function BaseCamera:GetCameraToSubjectDistance()
  1882. return self.currentSubjectDistance
  1883. end
  1884.  
  1885. -- Actual measured distance to the camera Focus point, which may be needed in special circumstances, but should
  1886. -- never be used as the starting point for updating the nominal camera-to-subject distance (self.currentSubjectDistance)
  1887. -- since that is a desired target value set only by mouse wheel (or equivalent) input, PopperCam, and clamped to min max camera distance
  1888. function BaseCamera:GetMeasuredDistanceToFocus()
  1889. local camera = game.Workspace.CurrentCamera
  1890. if camera then
  1891. return (camera.CoordinateFrame.p - camera.Focus.p).magnitude
  1892. end
  1893. return nil
  1894. end
  1895.  
  1896. function BaseCamera:GetCameraLookVector()
  1897. return game.Workspace.CurrentCamera and game.Workspace.CurrentCamera.CFrame.lookVector or UNIT_Z
  1898. end
  1899.  
  1900. -- Replacements for RootCamera:RotateCamera() which did not actually rotate the camera
  1901. -- suppliedLookVector is not normally passed in, it's used only by Watch camera
  1902. function BaseCamera:CalculateNewLookCFrame(suppliedLookVector)
  1903. local currLookVector = suppliedLookVector or self:GetCameraLookVector()
  1904. local currPitchAngle = math.asin(currLookVector.y)
  1905. local yTheta = math.clamp(self.rotateInput.y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
  1906. local constrainedRotateInput = Vector2.new(self.rotateInput.x, yTheta)
  1907. local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
  1908. local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)
  1909. return newLookCFrame
  1910. end
  1911. function BaseCamera:CalculateNewLookVector(suppliedLookVector)
  1912. local newLookCFrame = self:CalculateNewLookCFrame(suppliedLookVector)
  1913. return newLookCFrame.lookVector
  1914. end
  1915.  
  1916. function BaseCamera:CalculateNewLookVectorVR()
  1917. local subjectPosition = self:GetSubjectPosition()
  1918. local vecToSubject = (subjectPosition - game.Workspace.CurrentCamera.CFrame.p)
  1919. local currLookVector = (vecToSubject * X1_Y0_Z1).unit
  1920. local vrRotateInput = Vector2.new(self.rotateInput.x, 0)
  1921. local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
  1922. local yawRotatedVector = (CFrame.Angles(0, -vrRotateInput.x, 0) * startCFrame * CFrame.Angles(-vrRotateInput.y,0,0)).lookVector
  1923. return (yawRotatedVector * X1_Y0_Z1).unit
  1924. end
  1925.  
  1926. function BaseCamera:GetHumanoid()
  1927. local character = player and player.Character
  1928. if character then
  1929. local resultHumanoid = self.humanoidCache[player]
  1930. if resultHumanoid and resultHumanoid.Parent == character then
  1931. return resultHumanoid
  1932. else
  1933. self.humanoidCache[player] = nil -- Bust Old Cache
  1934. local humanoid = character:FindFirstChildOfClass("Humanoid")
  1935. if humanoid then
  1936. self.humanoidCache[player] = humanoid
  1937. end
  1938. return humanoid
  1939. end
  1940. end
  1941. return nil
  1942. end
  1943.  
  1944. function BaseCamera:GetHumanoidPartToFollow(humanoid, humanoidStateType)
  1945. if humanoidStateType == Enum.HumanoidStateType.Dead then
  1946. local character = humanoid.Parent
  1947. if character then
  1948. return character:FindFirstChild("Head") or humanoid.Torso
  1949. else
  1950. return humanoid.Torso
  1951. end
  1952. else
  1953. return humanoid.Torso
  1954. end
  1955. end
  1956.  
  1957. function BaseCamera:UpdateGamepad()
  1958. local gamepadPan = self.gamepadPanningCamera
  1959. if gamepadPan and (self.hasGameLoaded or not VRService.VREnabled) then
  1960. gamepadPan = Util.GamepadLinearToCurve(gamepadPan)
  1961. local currentTime = tick()
  1962. if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
  1963. self.userPanningTheCamera = true
  1964. elseif gamepadPan == ZERO_VECTOR2 then
  1965. self.lastThumbstickRotate = nil
  1966. if self.lastThumbstickPos == ZERO_VECTOR2 then
  1967. self.currentSpeed = 0
  1968. end
  1969. end
  1970.  
  1971. local finalConstant = 0
  1972.  
  1973. if self.lastThumbstickRotate then
  1974. if VRService.VREnabled then
  1975. self.currentSpeed = self.vrMaxSpeed
  1976. else
  1977. local elapsedTime = (currentTime - self.lastThumbstickRotate) * 10
  1978. self.currentSpeed = self.currentSpeed + (self.maxSpeed * ((elapsedTime*elapsedTime)/self.numOfSeconds))
  1979.  
  1980. if self.currentSpeed > self.maxSpeed then self.currentSpeed = self.maxSpeed end
  1981.  
  1982. if self.lastVelocity then
  1983. local velocity = (gamepadPan - self.lastThumbstickPos)/(currentTime - self.lastThumbstickRotate)
  1984. local velocityDeltaMag = (velocity - self.lastVelocity).magnitude
  1985.  
  1986. if velocityDeltaMag > 12 then
  1987. self.currentSpeed = self.currentSpeed * (20/velocityDeltaMag)
  1988. if self.currentSpeed > self.maxSpeed then self.currentSpeed = self.maxSpeed end
  1989. end
  1990. end
  1991. end
  1992.  
  1993. finalConstant = UserGameSettings.GamepadCameraSensitivity * self.currentSpeed
  1994. self.lastVelocity = (gamepadPan - self.lastThumbstickPos)/(currentTime - self.lastThumbstickRotate)
  1995. end
  1996.  
  1997. self.lastThumbstickPos = gamepadPan
  1998. self.lastThumbstickRotate = currentTime
  1999.  
  2000. return Vector2.new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * self.ySensitivity * UserGameSettings:GetCameraYInvertValue())
  2001. end
  2002.  
  2003. return ZERO_VECTOR2
  2004. end
  2005.  
  2006. -- [[ VR Support Section ]] --
  2007.  
  2008. function BaseCamera:ApplyVRTransform()
  2009. if not VRService.VREnabled then
  2010. return
  2011. end
  2012.  
  2013. --we only want this to happen in first person VR
  2014. local rootJoint = self.humanoidRootPart and self.humanoidRootPart:FindFirstChild("RootJoint")
  2015. if not rootJoint then
  2016. return
  2017. end
  2018.  
  2019. local cameraSubject = game.Workspace.CurrentCamera.CameraSubject
  2020. local isInVehicle = cameraSubject and cameraSubject:IsA("VehicleSeat")
  2021.  
  2022. if self.inFirstPerson and not isInVehicle then
  2023. local vrFrame = VRService:GetUserCFrame(Enum.UserCFrame.Head)
  2024. local vrRotation = vrFrame - vrFrame.p
  2025. rootJoint.C0 = CFrame.new(vrRotation:vectorToObjectSpace(vrFrame.p)) * CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
  2026. else
  2027. rootJoint.C0 = CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
  2028. end
  2029. end
  2030.  
  2031. function BaseCamera:IsInFirstPerson()
  2032. return self.inFirstPerson
  2033. end
  2034.  
  2035. function BaseCamera:ShouldUseVRRotation()
  2036. if not VRService.VREnabled then
  2037. return false
  2038. end
  2039.  
  2040. if not self.VRRotationIntensityAvailable and tick() - self.lastVRRotationIntensityCheckTime < 1 then
  2041. return false
  2042. end
  2043.  
  2044. local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
  2045. self.VRRotationIntensityAvailable = success and vrRotationIntensity ~= nil
  2046. self.lastVRRotationIntensityCheckTime = tick()
  2047.  
  2048. self.shouldUseVRRotation = success and vrRotationIntensity ~= nil and vrRotationIntensity ~= "Smooth"
  2049.  
  2050. return self.shouldUseVRRotation
  2051. end
  2052.  
  2053. function BaseCamera:GetVRRotationInput()
  2054. local vrRotateSum = ZERO_VECTOR2
  2055. local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
  2056.  
  2057. if not success then
  2058. return
  2059. end
  2060.  
  2061. local vrGamepadRotation = self.GamepadPanningCamera or ZERO_VECTOR2
  2062. local delayExpired = (tick() - self.lastVRRotationTime) >= self:GetRepeatDelayValue(vrRotationIntensity)
  2063.  
  2064. if math.abs(vrGamepadRotation.x) >= self:GetActivateValue() then
  2065. if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]) then
  2066. local sign = 1
  2067. if vrGamepadRotation.x < 0 then
  2068. sign = -1
  2069. end
  2070. vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity) * sign
  2071. self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = true
  2072. end
  2073. elseif math.abs(vrGamepadRotation.x) < self:GetActivateValue() - 0.1 then
  2074. self.vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = nil
  2075. end
  2076. if self.turningLeft then
  2077. if delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Left] then
  2078. vrRotateSum = vrRotateSum - self:GetRotateAmountValue(vrRotationIntensity)
  2079. self.vrRotateKeyCooldown[Enum.KeyCode.Left] = true
  2080. end
  2081. else
  2082. self.vrRotateKeyCooldown[Enum.KeyCode.Left] = nil
  2083. end
  2084. if self.turningRight then
  2085. if (delayExpired or not self.vrRotateKeyCooldown[Enum.KeyCode.Right]) then
  2086. vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity)
  2087. self.vrRotateKeyCooldown[Enum.KeyCode.Right] = true
  2088. end
  2089. else
  2090. self.vrRotateKeyCooldown[Enum.KeyCode.Right] = nil
  2091. end
  2092.  
  2093. if vrRotateSum ~= ZERO_VECTOR2 then
  2094. self.lastVRRotationTime = tick()
  2095. end
  2096.  
  2097. return vrRotateSum
  2098. end
  2099.  
  2100. function BaseCamera:CancelCameraFreeze(keepConstraints)
  2101. if not keepConstraints then
  2102. self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 1, self.cameraTranslationConstraints.z)
  2103. end
  2104. if self.cameraFrozen then
  2105. self.trackingHumanoid = nil
  2106. self.cameraFrozen = false
  2107. end
  2108. end
  2109.  
  2110. function BaseCamera:StartCameraFreeze(subjectPosition, humanoidToTrack)
  2111. if not self.cameraFrozen then
  2112. self.humanoidJumpOrigin = subjectPosition
  2113. self.trackingHumanoid = humanoidToTrack
  2114. self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, 0, self.cameraTranslationConstraints.z)
  2115. self.cameraFrozen = true
  2116. end
  2117. end
  2118.  
  2119. function BaseCamera:OnNewCameraSubject()
  2120. if self.subjectStateChangedConn then
  2121. self.subjectStateChangedConn:Disconnect()
  2122. self.subjectStateChangedConn = nil
  2123. end
  2124.  
  2125. local humanoid = workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject
  2126. if self.trackingHumanoid ~= humanoid then
  2127. self:CancelCameraFreeze()
  2128. end
  2129. if humanoid and humanoid:IsA("Humanoid") then
  2130. self.subjectStateChangedConn = humanoid.StateChanged:Connect(function(oldState, newState)
  2131. if VRService.VREnabled and newState == Enum.HumanoidStateType.Jumping and not self.inFirstPerson then
  2132. self:StartCameraFreeze(self:GetSubjectPosition(), humanoid)
  2133. elseif newState ~= Enum.HumanoidStateType.Jumping and newState ~= Enum.HumanoidStateType.Freefall then
  2134. self:CancelCameraFreeze(true)
  2135. end
  2136. end)
  2137. end
  2138. end
  2139.  
  2140. function BaseCamera:GetVRFocus(subjectPosition, timeDelta)
  2141. local lastFocus = self.LastCameraFocus or subjectPosition
  2142. if not self.cameraFrozen then
  2143. self.cameraTranslationConstraints = Vector3.new(self.cameraTranslationConstraints.x, math.min(1, self.cameraTranslationConstraints.y + 0.42 * timeDelta), self.cameraTranslationConstraints.z)
  2144. end
  2145.  
  2146. local newFocus
  2147. if self.cameraFrozen and self.humanoidJumpOrigin and self.humanoidJumpOrigin.y > lastFocus.y then
  2148. newFocus = CFrame.new(Vector3.new(subjectPosition.x, math.min(self.humanoidJumpOrigin.y, lastFocus.y + 5 * timeDelta), subjectPosition.z))
  2149. else
  2150. newFocus = CFrame.new(Vector3.new(subjectPosition.x, lastFocus.y, subjectPosition.z):lerp(subjectPosition, self.cameraTranslationConstraints.y))
  2151. end
  2152.  
  2153. if self.cameraFrozen then
  2154. -- No longer in 3rd person
  2155. if self.inFirstPerson then -- not VRService.VREnabled
  2156. self:CancelCameraFreeze()
  2157. end
  2158. -- This case you jumped off a cliff and want to keep your character in view
  2159. -- 0.5 is to fix floating point error when not jumping off cliffs
  2160. if self.humanoidJumpOrigin and subjectPosition.y < (self.humanoidJumpOrigin.y - 0.5) then
  2161. self:CancelCameraFreeze()
  2162. end
  2163. end
  2164.  
  2165. return newFocus
  2166. end
  2167.  
  2168. function BaseCamera:GetRotateAmountValue(vrRotationIntensity)
  2169. vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
  2170. if vrRotationIntensity then
  2171. if vrRotationIntensity == "Low" then
  2172. return VR_LOW_INTENSITY_ROTATION
  2173. elseif vrRotationIntensity == "High" then
  2174. return VR_HIGH_INTENSITY_ROTATION
  2175. end
  2176. end
  2177. return ZERO_VECTOR2
  2178. end
  2179.  
  2180. function BaseCamera:GetRepeatDelayValue(vrRotationIntensity)
  2181. vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
  2182. if vrRotationIntensity then
  2183. if vrRotationIntensity == "Low" then
  2184. return VR_LOW_INTENSITY_REPEAT
  2185. elseif vrRotationIntensity == "High" then
  2186. return VR_HIGH_INTENSITY_REPEAT
  2187. end
  2188. end
  2189. return 0
  2190. end
  2191.  
  2192. function BaseCamera:Update(dt)
  2193. error("BaseCamera:Update() This is a virtual function that should never be getting called.", 2)
  2194. end
  2195.  
  2196. BaseCamera.UpCFrame = CFrame.new()
  2197.  
  2198. function BaseCamera:UpdateUpCFrame(cf)
  2199. self.UpCFrame = cf
  2200. end
  2201. local ZERO = Vector3.new(0, 0, 0)
  2202. function BaseCamera:CalculateNewLookCFrame(suppliedLookVector)
  2203. local currLookVector = suppliedLookVector or self:GetCameraLookVector()
  2204. currLookVector = self.UpCFrame:VectorToObjectSpace(currLookVector)
  2205.  
  2206. local currPitchAngle = math.asin(currLookVector.y)
  2207. local yTheta = math.clamp(self.rotateInput.y, -MAX_Y + currPitchAngle, -MIN_Y + currPitchAngle)
  2208. local constrainedRotateInput = Vector2.new(self.rotateInput.x, yTheta)
  2209. local startCFrame = CFrame.new(ZERO, currLookVector)
  2210. local newLookCFrame = CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)
  2211.  
  2212. return newLookCFrame
  2213. end
  2214.  
  2215. return BaseCamera
  2216. end
  2217.  
  2218. function _BaseOcclusion()
  2219. --[[ The Module ]]--
  2220. local BaseOcclusion = {}
  2221. BaseOcclusion.__index = BaseOcclusion
  2222. setmetatable(BaseOcclusion, {
  2223. __call = function(_, ...)
  2224. return BaseOcclusion.new(...)
  2225. end
  2226. })
  2227.  
  2228. function BaseOcclusion.new()
  2229. local self = setmetatable({}, BaseOcclusion)
  2230. return self
  2231. end
  2232.  
  2233. -- Called when character is added
  2234. function BaseOcclusion:CharacterAdded(char, player)
  2235. end
  2236.  
  2237. -- Called when character is about to be removed
  2238. function BaseOcclusion:CharacterRemoving(char, player)
  2239. end
  2240.  
  2241. function BaseOcclusion:OnCameraSubjectChanged(newSubject)
  2242. end
  2243.  
  2244. --[[ Derived classes are required to override and implement all of the following functions ]]--
  2245. function BaseOcclusion:GetOcclusionMode()
  2246. -- Must be overridden in derived classes to return an Enum.DevCameraOcclusionMode value
  2247. warn("BaseOcclusion GetOcclusionMode must be overridden by derived classes")
  2248. return nil
  2249. end
  2250.  
  2251. function BaseOcclusion:Enable(enabled)
  2252. warn("BaseOcclusion Enable must be overridden by derived classes")
  2253. end
  2254.  
  2255. function BaseOcclusion:Update(dt, desiredCameraCFrame, desiredCameraFocus)
  2256. warn("BaseOcclusion Update must be overridden by derived classes")
  2257. return desiredCameraCFrame, desiredCameraFocus
  2258. end
  2259.  
  2260. return BaseOcclusion
  2261. end
  2262.  
  2263. function _Popper()
  2264.  
  2265. local Players = game:GetService("Players")
  2266.  
  2267. local camera = game.Workspace.CurrentCamera
  2268.  
  2269. local min = math.min
  2270. local tan = math.tan
  2271. local rad = math.rad
  2272. local inf = math.huge
  2273. local ray = Ray.new
  2274.  
  2275. local function getTotalTransparency(part)
  2276. return 1 - (1 - part.Transparency)*(1 - part.LocalTransparencyModifier)
  2277. end
  2278.  
  2279. local function eraseFromEnd(t, toSize)
  2280. for i = #t, toSize + 1, -1 do
  2281. t[i] = nil
  2282. end
  2283. end
  2284.  
  2285. local nearPlaneZ, projX, projY do
  2286. local function updateProjection()
  2287. local fov = rad(camera.FieldOfView)
  2288. local view = camera.ViewportSize
  2289. local ar = view.X/view.Y
  2290.  
  2291. projY = 2*tan(fov/2)
  2292. projX = ar*projY
  2293. end
  2294.  
  2295. camera:GetPropertyChangedSignal("FieldOfView"):Connect(updateProjection)
  2296. camera:GetPropertyChangedSignal("ViewportSize"):Connect(updateProjection)
  2297.  
  2298. updateProjection()
  2299.  
  2300. nearPlaneZ = camera.NearPlaneZ
  2301. camera:GetPropertyChangedSignal("NearPlaneZ"):Connect(function()
  2302. nearPlaneZ = camera.NearPlaneZ
  2303. end)
  2304. end
  2305.  
  2306. local blacklist = {} do
  2307. local charMap = {}
  2308.  
  2309. local function refreshIgnoreList()
  2310. local n = 1
  2311. blacklist = {}
  2312. for _, character in pairs(charMap) do
  2313. blacklist[n] = character
  2314. n = n + 1
  2315. end
  2316. end
  2317.  
  2318. local function playerAdded(player)
  2319. local function characterAdded(character)
  2320. charMap[player] = character
  2321. refreshIgnoreList()
  2322. end
  2323. local function characterRemoving()
  2324. charMap[player] = nil
  2325. refreshIgnoreList()
  2326. end
  2327.  
  2328. player.CharacterAdded:Connect(characterAdded)
  2329. player.CharacterRemoving:Connect(characterRemoving)
  2330. if player.Character then
  2331. characterAdded(player.Character)
  2332. end
  2333. end
  2334.  
  2335. local function playerRemoving(player)
  2336. charMap[player] = nil
  2337. refreshIgnoreList()
  2338. end
  2339.  
  2340. Players.PlayerAdded:Connect(playerAdded)
  2341. Players.PlayerRemoving:Connect(playerRemoving)
  2342.  
  2343. for _, player in ipairs(Players:GetPlayers()) do
  2344. playerAdded(player)
  2345. end
  2346. refreshIgnoreList()
  2347. end
  2348.  
  2349. --------------------------------------------------------------------------------------------
  2350. -- Popper uses the level geometry find an upper bound on subject-to-camera distance.
  2351. --
  2352. -- Hard limits are applied immediately and unconditionally. They are generally caused
  2353. -- when level geometry intersects with the near plane (with exceptions, see below).
  2354. --
  2355. -- Soft limits are only applied under certain conditions.
  2356. -- They are caused when level geometry occludes the subject without actually intersecting
  2357. -- with the near plane at the target distance.
  2358. --
  2359. -- Soft limits can be promoted to hard limits and hard limits can be demoted to soft limits.
  2360. -- We usually don"t want the latter to happen.
  2361. --
  2362. -- A soft limit will be promoted to a hard limit if an obstruction
  2363. -- lies between the current and target camera positions.
  2364. --------------------------------------------------------------------------------------------
  2365.  
  2366. local subjectRoot
  2367. local subjectPart
  2368.  
  2369. camera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
  2370. local subject = camera.CameraSubject
  2371. if subject:IsA("Humanoid") then
  2372. subjectPart = subject.RootPart
  2373. elseif subject:IsA("BasePart") then
  2374. subjectPart = subject
  2375. else
  2376. subjectPart = nil
  2377. end
  2378. end)
  2379.  
  2380. local function canOcclude(part)
  2381. -- Occluders must be:
  2382. -- 1. Opaque
  2383. -- 2. Interactable
  2384. -- 3. Not in the same assembly as the subject
  2385.  
  2386. return
  2387. getTotalTransparency(part) < 0.25 and
  2388. part.CanCollide and
  2389. subjectRoot ~= (part:GetRootPart() or part) and
  2390. not part:IsA("TrussPart")
  2391. end
  2392.  
  2393. -- Offsets for the volume visibility test
  2394. local SCAN_SAMPLE_OFFSETS = {
  2395. Vector2.new( 0.4, 0.0),
  2396. Vector2.new(-0.4, 0.0),
  2397. Vector2.new( 0.0,-0.4),
  2398. Vector2.new( 0.0, 0.4),
  2399. Vector2.new( 0.0, 0.2),
  2400. }
  2401.  
  2402. --------------------------------------------------------------------------------
  2403. -- Piercing raycasts
  2404.  
  2405. local function getCollisionPoint(origin, dir)
  2406. local originalSize = #blacklist
  2407. repeat
  2408. local hitPart, hitPoint = workspace:FindPartOnRayWithIgnoreList(
  2409. ray(origin, dir), blacklist, false, true
  2410. )
  2411.  
  2412. if hitPart then
  2413. if hitPart.CanCollide then
  2414. eraseFromEnd(blacklist, originalSize)
  2415. return hitPoint, true
  2416. end
  2417. blacklist[#blacklist + 1] = hitPart
  2418. end
  2419. until not hitPart
  2420.  
  2421. eraseFromEnd(blacklist, originalSize)
  2422. return origin + dir, false
  2423. end
  2424.  
  2425. --------------------------------------------------------------------------------
  2426.  
  2427. local function queryPoint(origin, unitDir, dist, lastPos)
  2428. debug.profilebegin("queryPoint")
  2429.  
  2430. local originalSize = #blacklist
  2431.  
  2432. dist = dist + nearPlaneZ
  2433. local target = origin + unitDir*dist
  2434.  
  2435. local softLimit = inf
  2436. local hardLimit = inf
  2437. local movingOrigin = origin
  2438.  
  2439. repeat
  2440. local entryPart, entryPos = workspace:FindPartOnRayWithIgnoreList(ray(movingOrigin, target - movingOrigin), blacklist, false, true)
  2441.  
  2442. if entryPart then
  2443. if canOcclude(entryPart) then
  2444. local wl = {entryPart}
  2445. local exitPart = workspace:FindPartOnRayWithWhitelist(ray(target, entryPos - target), wl, true)
  2446.  
  2447. local lim = (entryPos - origin).Magnitude
  2448.  
  2449. if exitPart then
  2450. local promote = false
  2451. if lastPos then
  2452. promote =
  2453. workspace:FindPartOnRayWithWhitelist(ray(lastPos, target - lastPos), wl, true) or
  2454. workspace:FindPartOnRayWithWhitelist(ray(target, lastPos - target), wl, true)
  2455. end
  2456.  
  2457. if promote then
  2458. -- Ostensibly a soft limit, but the camera has passed through it in the last frame, so promote to a hard limit.
  2459. hardLimit = lim
  2460. elseif dist < softLimit then
  2461. -- Trivial soft limit
  2462. softLimit = lim
  2463. end
  2464. else
  2465. -- Trivial hard limit
  2466. hardLimit = lim
  2467. end
  2468. end
  2469.  
  2470. blacklist[#blacklist + 1] = entryPart
  2471. movingOrigin = entryPos - unitDir*1e-3
  2472. end
  2473. until hardLimit < inf or not entryPart
  2474.  
  2475. eraseFromEnd(blacklist, originalSize)
  2476.  
  2477. debug.profileend()
  2478. return softLimit - nearPlaneZ, hardLimit - nearPlaneZ
  2479. end
  2480.  
  2481. local function queryViewport(focus, dist)
  2482. debug.profilebegin("queryViewport")
  2483.  
  2484. local fP = focus.p
  2485. local fX = focus.rightVector
  2486. local fY = focus.upVector
  2487. local fZ = -focus.lookVector
  2488.  
  2489. local viewport = camera.ViewportSize
  2490.  
  2491. local hardBoxLimit = inf
  2492. local softBoxLimit = inf
  2493.  
  2494. -- Center the viewport on the PoI, sweep points on the edge towards the target, and take the minimum limits
  2495. for viewX = 0, 1 do
  2496. local worldX = fX*((viewX - 0.5)*projX)
  2497.  
  2498. for viewY = 0, 1 do
  2499. local worldY = fY*((viewY - 0.5)*projY)
  2500.  
  2501. local origin = fP + nearPlaneZ*(worldX + worldY)
  2502. local lastPos = camera:ViewportPointToRay(
  2503. viewport.x*viewX,
  2504. viewport.y*viewY
  2505. ).Origin
  2506.  
  2507. local softPointLimit, hardPointLimit = queryPoint(origin, fZ, dist, lastPos)
  2508.  
  2509. if hardPointLimit < hardBoxLimit then
  2510. hardBoxLimit = hardPointLimit
  2511. end
  2512. if softPointLimit < softBoxLimit then
  2513. softBoxLimit = softPointLimit
  2514. end
  2515. end
  2516. end
  2517. debug.profileend()
  2518.  
  2519. return softBoxLimit, hardBoxLimit
  2520. end
  2521.  
  2522. local function testPromotion(focus, dist, focusExtrapolation)
  2523. debug.profilebegin("testPromotion")
  2524.  
  2525. local fP = focus.p
  2526. local fX = focus.rightVector
  2527. local fY = focus.upVector
  2528. local fZ = -focus.lookVector
  2529.  
  2530. do
  2531. -- Dead reckoning the camera rotation and focus
  2532. debug.profilebegin("extrapolate")
  2533.  
  2534. local SAMPLE_DT = 0.0625
  2535. local SAMPLE_MAX_T = 1.25
  2536.  
  2537. local maxDist = (getCollisionPoint(fP, focusExtrapolation.posVelocity*SAMPLE_MAX_T) - fP).Magnitude
  2538. -- Metric that decides how many samples to take
  2539. local combinedSpeed = focusExtrapolation.posVelocity.magnitude
  2540.  
  2541. for dt = 0, min(SAMPLE_MAX_T, focusExtrapolation.rotVelocity.magnitude + maxDist/combinedSpeed), SAMPLE_DT do
  2542. local cfDt = focusExtrapolation.extrapolate(dt) -- Extrapolated CFrame at time dt
  2543.  
  2544. if queryPoint(cfDt.p, -cfDt.lookVector, dist) >= dist then
  2545. return false
  2546. end
  2547. end
  2548.  
  2549. debug.profileend()
  2550. end
  2551.  
  2552. do
  2553. -- Test screen-space offsets from the focus for the presence of soft limits
  2554. debug.profilebegin("testOffsets")
  2555.  
  2556. for _, offset in ipairs(SCAN_SAMPLE_OFFSETS) do
  2557. local scaledOffset = offset
  2558. local pos = getCollisionPoint(fP, fX*scaledOffset.x + fY*scaledOffset.y)
  2559. if queryPoint(pos, (fP + fZ*dist - pos).Unit, dist) == inf then
  2560. return false
  2561. end
  2562. end
  2563.  
  2564. debug.profileend()
  2565. end
  2566.  
  2567. debug.profileend()
  2568. return true
  2569. end
  2570.  
  2571. local function Popper(focus, targetDist, focusExtrapolation)
  2572. debug.profilebegin("popper")
  2573.  
  2574. subjectRoot = subjectPart and subjectPart:GetRootPart() or subjectPart
  2575.  
  2576. local dist = targetDist
  2577. local soft, hard = queryViewport(focus, targetDist)
  2578. if hard < dist then
  2579. dist = hard
  2580. end
  2581. if soft < dist and testPromotion(focus, targetDist, focusExtrapolation) then
  2582. dist = soft
  2583. end
  2584.  
  2585. subjectRoot = nil
  2586.  
  2587. debug.profileend()
  2588. return dist
  2589. end
  2590.  
  2591. return Popper
  2592. end
  2593.  
  2594. function _ZoomController()
  2595. local ZOOM_STIFFNESS = 4.5
  2596. local ZOOM_DEFAULT = 12.5
  2597. local ZOOM_ACCELERATION = 0.0375
  2598.  
  2599. local MIN_FOCUS_DIST = 0.5
  2600. local DIST_OPAQUE = 1
  2601.  
  2602. local Popper = _Popper()
  2603.  
  2604. local clamp = math.clamp
  2605. local exp = math.exp
  2606. local min = math.min
  2607. local max = math.max
  2608. local pi = math.pi
  2609.  
  2610. local cameraMinZoomDistance, cameraMaxZoomDistance do
  2611. local Player = game:GetService("Players").LocalPlayer
  2612.  
  2613. local function updateBounds()
  2614. cameraMinZoomDistance = Player.CameraMinZoomDistance
  2615. cameraMaxZoomDistance = Player.CameraMaxZoomDistance
  2616. end
  2617.  
  2618. updateBounds()
  2619.  
  2620. Player:GetPropertyChangedSignal("CameraMinZoomDistance"):Connect(updateBounds)
  2621. Player:GetPropertyChangedSignal("CameraMaxZoomDistance"):Connect(updateBounds)
  2622. end
  2623.  
  2624. local ConstrainedSpring = {} do
  2625. ConstrainedSpring.__index = ConstrainedSpring
  2626.  
  2627. function ConstrainedSpring.new(freq, x, minValue, maxValue)
  2628. x = clamp(x, minValue, maxValue)
  2629. return setmetatable({
  2630. freq = freq, -- Undamped frequency (Hz)
  2631. x = x, -- Current position
  2632. v = 0, -- Current velocity
  2633. minValue = minValue, -- Minimum bound
  2634. maxValue = maxValue, -- Maximum bound
  2635. goal = x, -- Goal position
  2636. }, ConstrainedSpring)
  2637. end
  2638.  
  2639. function ConstrainedSpring:Step(dt)
  2640. local freq = self.freq*2*pi -- Convert from Hz to rad/s
  2641. local x = self.x
  2642. local v = self.v
  2643. local minValue = self.minValue
  2644. local maxValue = self.maxValue
  2645. local goal = self.goal
  2646.  
  2647. -- Solve the spring ODE for position and velocity after time t, assuming critical damping:
  2648. -- 2*f*x'[t] + x''[t] = f^2*(g - x[t])
  2649. -- Knowns are x[0] and x'[0].
  2650. -- Solve for x[t] and x'[t].
  2651.  
  2652. local offset = goal - x
  2653. local step = freq*dt
  2654. local decay = exp(-step)
  2655.  
  2656. local x1 = goal + (v*dt - offset*(step + 1))*decay
  2657. local v1 = ((offset*freq - v)*step + v)*decay
  2658.  
  2659. -- Constrain
  2660. if x1 < minValue then
  2661. x1 = minValue
  2662. v1 = 0
  2663. elseif x1 > maxValue then
  2664. x1 = maxValue
  2665. v1 = 0
  2666. end
  2667.  
  2668. self.x = x1
  2669. self.v = v1
  2670.  
  2671. return x1
  2672. end
  2673. end
  2674.  
  2675. local zoomSpring = ConstrainedSpring.new(ZOOM_STIFFNESS, ZOOM_DEFAULT, MIN_FOCUS_DIST, cameraMaxZoomDistance)
  2676.  
  2677. local function stepTargetZoom(z, dz, zoomMin, zoomMax)
  2678. z = clamp(z + dz*(1 + z*ZOOM_ACCELERATION), zoomMin, zoomMax)
  2679. if z < DIST_OPAQUE then
  2680. z = dz <= 0 and zoomMin or DIST_OPAQUE
  2681. end
  2682. return z
  2683. end
  2684.  
  2685. local zoomDelta = 0
  2686.  
  2687. local Zoom = {} do
  2688. function Zoom.Update(renderDt, focus, extrapolation)
  2689. local poppedZoom = math.huge
  2690.  
  2691. if zoomSpring.goal > DIST_OPAQUE then
  2692. -- Make a pessimistic estimate of zoom distance for this step without accounting for poppercam
  2693. local maxPossibleZoom = max(
  2694. zoomSpring.x,
  2695. stepTargetZoom(zoomSpring.goal, zoomDelta, cameraMinZoomDistance, cameraMaxZoomDistance)
  2696. )
  2697.  
  2698. -- Run the Popper algorithm on the feasible zoom range, [MIN_FOCUS_DIST, maxPossibleZoom]
  2699. poppedZoom = Popper(
  2700. focus*CFrame.new(0, 0, MIN_FOCUS_DIST),
  2701. maxPossibleZoom - MIN_FOCUS_DIST,
  2702. extrapolation
  2703. ) + MIN_FOCUS_DIST
  2704. end
  2705.  
  2706. zoomSpring.minValue = MIN_FOCUS_DIST
  2707. zoomSpring.maxValue = min(cameraMaxZoomDistance, poppedZoom)
  2708.  
  2709. return zoomSpring:Step(renderDt)
  2710. end
  2711.  
  2712. function Zoom.SetZoomParameters(targetZoom, newZoomDelta)
  2713. zoomSpring.goal = targetZoom
  2714. zoomDelta = newZoomDelta
  2715. end
  2716. end
  2717.  
  2718. return Zoom
  2719. end
  2720.  
  2721. function _MouseLockController()
  2722. --[[ Constants ]]--
  2723. local DEFAULT_MOUSE_LOCK_CURSOR = "rbxasset://textures/MouseLockedCursor.png"
  2724.  
  2725. local CONTEXT_ACTION_NAME = "MouseLockSwitchAction"
  2726. local MOUSELOCK_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
  2727.  
  2728. --[[ Services ]]--
  2729. local PlayersService = game:GetService("Players")
  2730. local ContextActionService = game:GetService("ContextActionService")
  2731. local Settings = UserSettings() -- ignore warning
  2732. local GameSettings = Settings.GameSettings
  2733. local Mouse = PlayersService.LocalPlayer:GetMouse()
  2734.  
  2735. --[[ The Module ]]--
  2736. local MouseLockController = {}
  2737. MouseLockController.__index = MouseLockController
  2738.  
  2739. function MouseLockController.new()
  2740. local self = setmetatable({}, MouseLockController)
  2741.  
  2742. self.isMouseLocked = false
  2743. self.savedMouseCursor = nil
  2744. self.boundKeys = {Enum.KeyCode.LeftShift, Enum.KeyCode.RightShift} -- defaults
  2745.  
  2746. self.mouseLockToggledEvent = Instance.new("BindableEvent")
  2747.  
  2748. local boundKeysObj = script:FindFirstChild("BoundKeys")
  2749. if (not boundKeysObj) or (not boundKeysObj:IsA("StringValue")) then
  2750. -- If object with correct name was found, but it's not a StringValue, destroy and replace
  2751. if boundKeysObj then
  2752. boundKeysObj:Destroy()
  2753. end
  2754.  
  2755. boundKeysObj = Instance.new("StringValue")
  2756. boundKeysObj.Name = "BoundKeys"
  2757. boundKeysObj.Value = "LeftShift,RightShift"
  2758. boundKeysObj.Parent = script
  2759. end
  2760.  
  2761. if boundKeysObj then
  2762. boundKeysObj.Changed:Connect(function(value)
  2763. self:OnBoundKeysObjectChanged(value)
  2764. end)
  2765. self:OnBoundKeysObjectChanged(boundKeysObj.Value) -- Initial setup call
  2766. end
  2767.  
  2768. -- Watch for changes to user's ControlMode and ComputerMovementMode settings and update the feature availability accordingly
  2769. GameSettings.Changed:Connect(function(property)
  2770. if property == "ControlMode" or property == "ComputerMovementMode" then
  2771. self:UpdateMouseLockAvailability()
  2772. end
  2773. end)
  2774.  
  2775. -- Watch for changes to DevEnableMouseLock and update the feature availability accordingly
  2776. PlayersService.LocalPlayer:GetPropertyChangedSignal("DevEnableMouseLock"):Connect(function()
  2777. self:UpdateMouseLockAvailability()
  2778. end)
  2779.  
  2780. -- Watch for changes to DevEnableMouseLock and update the feature availability accordingly
  2781. PlayersService.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(function()
  2782. self:UpdateMouseLockAvailability()
  2783. end)
  2784.  
  2785. self:UpdateMouseLockAvailability()
  2786.  
  2787. return self
  2788. end
  2789.  
  2790. function MouseLockController:GetIsMouseLocked()
  2791. return self.isMouseLocked
  2792. end
  2793.  
  2794. function MouseLockController:GetBindableToggleEvent()
  2795. return self.mouseLockToggledEvent.Event
  2796. end
  2797.  
  2798. function MouseLockController:GetMouseLockOffset()
  2799. local offsetValueObj = script:FindFirstChild("CameraOffset")
  2800. if offsetValueObj and offsetValueObj:IsA("Vector3Value") then
  2801. return offsetValueObj.Value
  2802. else
  2803. -- If CameraOffset object was found but not correct type, destroy
  2804. if offsetValueObj then
  2805. offsetValueObj:Destroy()
  2806. end
  2807. offsetValueObj = Instance.new("Vector3Value")
  2808. offsetValueObj.Name = "CameraOffset"
  2809. offsetValueObj.Value = Vector3.new(1.75,0,0) -- Legacy Default Value
  2810. offsetValueObj.Parent = script
  2811. end
  2812.  
  2813. if offsetValueObj and offsetValueObj.Value then
  2814. return offsetValueObj.Value
  2815. end
  2816.  
  2817. return Vector3.new(1.75,0,0)
  2818. end
  2819.  
  2820. function MouseLockController:UpdateMouseLockAvailability()
  2821. local devAllowsMouseLock = PlayersService.LocalPlayer.DevEnableMouseLock
  2822. local devMovementModeIsScriptable = PlayersService.LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.Scriptable
  2823. local userHasMouseLockModeEnabled = GameSettings.ControlMode == Enum.ControlMode.MouseLockSwitch
  2824. local userHasClickToMoveEnabled = GameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove
  2825. local MouseLockAvailable = devAllowsMouseLock and userHasMouseLockModeEnabled and not userHasClickToMoveEnabled and not devMovementModeIsScriptable
  2826.  
  2827. if MouseLockAvailable~=self.enabled then
  2828. self:EnableMouseLock(MouseLockAvailable)
  2829. end
  2830. end
  2831.  
  2832. function MouseLockController:OnBoundKeysObjectChanged(newValue)
  2833. self.boundKeys = {} -- Overriding defaults, note: possibly with nothing at all if boundKeysObj.Value is "" or contains invalid values
  2834. for token in string.gmatch(newValue,"[^%s,]+") do
  2835. for _, keyEnum in pairs(Enum.KeyCode:GetEnumItems()) do
  2836. if token == keyEnum.Name then
  2837. self.boundKeys[#self.boundKeys+1] = keyEnum
  2838. break
  2839. end
  2840. end
  2841. end
  2842. self:UnbindContextActions()
  2843. self:BindContextActions()
  2844. end
  2845.  
  2846. --[[ Local Functions ]]--
  2847. function MouseLockController:OnMouseLockToggled()
  2848. self.isMouseLocked = not self.isMouseLocked
  2849.  
  2850. if self.isMouseLocked then
  2851. local cursorImageValueObj = script:FindFirstChild("CursorImage")
  2852. if cursorImageValueObj and cursorImageValueObj:IsA("StringValue") and cursorImageValueObj.Value then
  2853. self.savedMouseCursor = Mouse.Icon
  2854. Mouse.Icon = cursorImageValueObj.Value
  2855. else
  2856. if cursorImageValueObj then
  2857. cursorImageValueObj:Destroy()
  2858. end
  2859. cursorImageValueObj = Instance.new("StringValue")
  2860. cursorImageValueObj.Name = "CursorImage"
  2861. cursorImageValueObj.Value = DEFAULT_MOUSE_LOCK_CURSOR
  2862. cursorImageValueObj.Parent = script
  2863. self.savedMouseCursor = Mouse.Icon
  2864. Mouse.Icon = DEFAULT_MOUSE_LOCK_CURSOR
  2865. end
  2866. else
  2867. if self.savedMouseCursor then
  2868. Mouse.Icon = self.savedMouseCursor
  2869. self.savedMouseCursor = nil
  2870. end
  2871. end
  2872.  
  2873. self.mouseLockToggledEvent:Fire()
  2874. end
  2875.  
  2876. function MouseLockController:DoMouseLockSwitch(name, state, input)
  2877. if state == Enum.UserInputState.Begin then
  2878. self:OnMouseLockToggled()
  2879. return Enum.ContextActionResult.Sink
  2880. end
  2881. return Enum.ContextActionResult.Pass
  2882. end
  2883.  
  2884. function MouseLockController:BindContextActions()
  2885. ContextActionService:BindActionAtPriority(CONTEXT_ACTION_NAME, function(name, state, input)
  2886. return self:DoMouseLockSwitch(name, state, input)
  2887. end, false, MOUSELOCK_ACTION_PRIORITY, unpack(self.boundKeys))
  2888. end
  2889.  
  2890. function MouseLockController:UnbindContextActions()
  2891. ContextActionService:UnbindAction(CONTEXT_ACTION_NAME)
  2892. end
  2893.  
  2894. function MouseLockController:IsMouseLocked()
  2895. return self.enabled and self.isMouseLocked
  2896. end
  2897.  
  2898. function MouseLockController:EnableMouseLock(enable)
  2899. if enable ~= self.enabled then
  2900.  
  2901. self.enabled = enable
  2902.  
  2903. if self.enabled then
  2904. -- Enabling the mode
  2905. self:BindContextActions()
  2906. else
  2907. -- Disabling
  2908. -- Restore mouse cursor
  2909. if Mouse.Icon~="" then
  2910. Mouse.Icon = ""
  2911. end
  2912.  
  2913. self:UnbindContextActions()
  2914.  
  2915. -- If the mode is disabled while being used, fire the event to toggle it off
  2916. if self.isMouseLocked then
  2917. self.mouseLockToggledEvent:Fire()
  2918. end
  2919.  
  2920. self.isMouseLocked = false
  2921. end
  2922.  
  2923. end
  2924. end
  2925.  
  2926. return MouseLockController
  2927. end
  2928.  
  2929. function _TransparencyController()
  2930.  
  2931. local MAX_TWEEN_RATE = 2.8 -- per second
  2932.  
  2933. local Util = _CameraUtils()
  2934.  
  2935. --[[ The Module ]]--
  2936. local TransparencyController = {}
  2937. TransparencyController.__index = TransparencyController
  2938.  
  2939. function TransparencyController.new()
  2940. local self = setmetatable({}, TransparencyController)
  2941.  
  2942. self.lastUpdate = tick()
  2943. self.transparencyDirty = false
  2944. self.enabled = false
  2945. self.lastTransparency = nil
  2946.  
  2947. self.descendantAddedConn, self.descendantRemovingConn = nil, nil
  2948. self.toolDescendantAddedConns = {}
  2949. self.toolDescendantRemovingConns = {}
  2950. self.cachedParts = {}
  2951.  
  2952. return self
  2953. end
  2954.  
  2955.  
  2956. function TransparencyController:HasToolAncestor(object)
  2957. if object.Parent == nil then return false end
  2958. return object.Parent:IsA('Tool') or self:HasToolAncestor(object.Parent)
  2959. end
  2960.  
  2961. function TransparencyController:IsValidPartToModify(part)
  2962. if part:IsA('BasePart') or part:IsA('Decal') then
  2963. return not self:HasToolAncestor(part)
  2964. end
  2965. return false
  2966. end
  2967.  
  2968. function TransparencyController:CachePartsRecursive(object)
  2969. if object then
  2970. if self:IsValidPartToModify(object) then
  2971. self.cachedParts[object] = true
  2972. self.transparencyDirty = true
  2973. end
  2974. for _, child in pairs(object:GetChildren()) do
  2975. self:CachePartsRecursive(child)
  2976. end
  2977. end
  2978. end
  2979.  
  2980. function TransparencyController:TeardownTransparency()
  2981. for child, _ in pairs(self.cachedParts) do
  2982. child.LocalTransparencyModifier = 0
  2983. end
  2984. self.cachedParts = {}
  2985. self.transparencyDirty = true
  2986. self.lastTransparency = nil
  2987.  
  2988. if self.descendantAddedConn then
  2989. self.descendantAddedConn:disconnect()
  2990. self.descendantAddedConn = nil
  2991. end
  2992. if self.descendantRemovingConn then
  2993. self.descendantRemovingConn:disconnect()
  2994. self.descendantRemovingConn = nil
  2995. end
  2996. for object, conn in pairs(self.toolDescendantAddedConns) do
  2997. conn:Disconnect()
  2998. self.toolDescendantAddedConns[object] = nil
  2999. end
  3000. for object, conn in pairs(self.toolDescendantRemovingConns) do
  3001. conn:Disconnect()
  3002. self.toolDescendantRemovingConns[object] = nil
  3003. end
  3004. end
  3005.  
  3006. function TransparencyController:SetupTransparency(character)
  3007. self:TeardownTransparency()
  3008.  
  3009. if self.descendantAddedConn then self.descendantAddedConn:disconnect() end
  3010. self.descendantAddedConn = character.DescendantAdded:Connect(function(object)
  3011. -- This is a part we want to invisify
  3012. if self:IsValidPartToModify(object) then
  3013. self.cachedParts[object] = true
  3014. self.transparencyDirty = true
  3015. -- There is now a tool under the character
  3016. elseif object:IsA('Tool') then
  3017. if self.toolDescendantAddedConns[object] then self.toolDescendantAddedConns[object]:Disconnect() end
  3018. self.toolDescendantAddedConns[object] = object.DescendantAdded:Connect(function(toolChild)
  3019. self.cachedParts[toolChild] = nil
  3020. if toolChild:IsA('BasePart') or toolChild:IsA('Decal') then
  3021. -- Reset the transparency
  3022. toolChild.LocalTransparencyModifier = 0
  3023. end
  3024. end)
  3025. if self.toolDescendantRemovingConns[object] then self.toolDescendantRemovingConns[object]:disconnect() end
  3026. self.toolDescendantRemovingConns[object] = object.DescendantRemoving:Connect(function(formerToolChild)
  3027. wait() -- wait for new parent
  3028. if character and formerToolChild and formerToolChild:IsDescendantOf(character) then
  3029. if self:IsValidPartToModify(formerToolChild) then
  3030. self.cachedParts[formerToolChild] = true
  3031. self.transparencyDirty = true
  3032. end
  3033. end
  3034. end)
  3035. end
  3036. end)
  3037. if self.descendantRemovingConn then self.descendantRemovingConn:disconnect() end
  3038. self.descendantRemovingConn = character.DescendantRemoving:connect(function(object)
  3039. if self.cachedParts[object] then
  3040. self.cachedParts[object] = nil
  3041. -- Reset the transparency
  3042. object.LocalTransparencyModifier = 0
  3043. end
  3044. end)
  3045. self:CachePartsRecursive(character)
  3046. end
  3047.  
  3048.  
  3049. function TransparencyController:Enable(enable)
  3050. if self.enabled ~= enable then
  3051. self.enabled = enable
  3052. self:Update()
  3053. end
  3054. end
  3055.  
  3056. function TransparencyController:SetSubject(subject)
  3057. local character = nil
  3058. if subject and subject:IsA("Humanoid") then
  3059. character = subject.Parent
  3060. end
  3061. if subject and subject:IsA("VehicleSeat") and subject.Occupant then
  3062. character = subject.Occupant.Parent
  3063. end
  3064. if character then
  3065. self:SetupTransparency(character)
  3066. else
  3067. self:TeardownTransparency()
  3068. end
  3069. end
  3070.  
  3071. function TransparencyController:Update()
  3072. local instant = false
  3073. local now = tick()
  3074. local currentCamera = workspace.CurrentCamera
  3075.  
  3076. if currentCamera then
  3077. local transparency = 0
  3078. if not self.enabled then
  3079. instant = true
  3080. else
  3081. local distance = (currentCamera.Focus.p - currentCamera.CoordinateFrame.p).magnitude
  3082. transparency = (distance<2) and (1.0-(distance-0.5)/1.5) or 0 --(7 - distance) / 5
  3083. if transparency < 0.5 then
  3084. transparency = 0
  3085. end
  3086.  
  3087. if self.lastTransparency then
  3088. local deltaTransparency = transparency - self.lastTransparency
  3089.  
  3090. -- Don't tween transparency if it is instant or your character was fully invisible last frame
  3091. if not instant and transparency < 1 and self.lastTransparency < 0.95 then
  3092. local maxDelta = MAX_TWEEN_RATE * (now - self.lastUpdate)
  3093. deltaTransparency = math.clamp(deltaTransparency, -maxDelta, maxDelta)
  3094. end
  3095. transparency = self.lastTransparency + deltaTransparency
  3096. else
  3097. self.transparencyDirty = true
  3098. end
  3099.  
  3100. transparency = math.clamp(Util.Round(transparency, 2), 0, 1)
  3101. end
  3102.  
  3103. if self.transparencyDirty or self.lastTransparency ~= transparency then
  3104. for child, _ in pairs(self.cachedParts) do
  3105. child.LocalTransparencyModifier = transparency
  3106. end
  3107. self.transparencyDirty = false
  3108. self.lastTransparency = transparency
  3109. end
  3110. end
  3111. self.lastUpdate = now
  3112. end
  3113.  
  3114. return TransparencyController
  3115. end
  3116.  
  3117. function _Poppercam()
  3118. local ZoomController = _ZoomController()
  3119.  
  3120. local TransformExtrapolator = {} do
  3121. TransformExtrapolator.__index = TransformExtrapolator
  3122.  
  3123. local CF_IDENTITY = CFrame.new()
  3124.  
  3125. local function cframeToAxis(cframe)
  3126. local axis, angle = cframe:toAxisAngle()
  3127. return axis*angle
  3128. end
  3129.  
  3130. local function axisToCFrame(axis)
  3131. local angle = axis.magnitude
  3132. if angle > 1e-5 then
  3133. return CFrame.fromAxisAngle(axis, angle)
  3134. end
  3135. return CF_IDENTITY
  3136. end
  3137.  
  3138. local function extractRotation(cf)
  3139. local _, _, _, xx, yx, zx, xy, yy, zy, xz, yz, zz = cf:components()
  3140. return CFrame.new(0, 0, 0, xx, yx, zx, xy, yy, zy, xz, yz, zz)
  3141. end
  3142.  
  3143. function TransformExtrapolator.new()
  3144. return setmetatable({
  3145. lastCFrame = nil,
  3146. }, TransformExtrapolator)
  3147. end
  3148.  
  3149. function TransformExtrapolator:Step(dt, currentCFrame)
  3150. local lastCFrame = self.lastCFrame or currentCFrame
  3151. self.lastCFrame = currentCFrame
  3152.  
  3153. local currentPos = currentCFrame.p
  3154. local currentRot = extractRotation(currentCFrame)
  3155.  
  3156. local lastPos = lastCFrame.p
  3157. local lastRot = extractRotation(lastCFrame)
  3158.  
  3159. -- Estimate velocities from the delta between now and the last frame
  3160. -- This estimation can be a little noisy.
  3161. local dp = (currentPos - lastPos)/dt
  3162. local dr = cframeToAxis(currentRot*lastRot:inverse())/dt
  3163.  
  3164. local function extrapolate(t)
  3165. local p = dp*t + currentPos
  3166. local r = axisToCFrame(dr*t)*currentRot
  3167. return r + p
  3168. end
  3169.  
  3170. return {
  3171. extrapolate = extrapolate,
  3172. posVelocity = dp,
  3173. rotVelocity = dr,
  3174. }
  3175. end
  3176.  
  3177. function TransformExtrapolator:Reset()
  3178. self.lastCFrame = nil
  3179. end
  3180. end
  3181.  
  3182. --[[ The Module ]]--
  3183. local BaseOcclusion = _BaseOcclusion()
  3184. local Poppercam = setmetatable({}, BaseOcclusion)
  3185. Poppercam.__index = Poppercam
  3186.  
  3187. function Poppercam.new()
  3188. local self = setmetatable(BaseOcclusion.new(), Poppercam)
  3189. self.focusExtrapolator = TransformExtrapolator.new()
  3190. return self
  3191. end
  3192.  
  3193. function Poppercam:GetOcclusionMode()
  3194. return Enum.DevCameraOcclusionMode.Zoom
  3195. end
  3196.  
  3197. function Poppercam:Enable(enable)
  3198. self.focusExtrapolator:Reset()
  3199. end
  3200.  
  3201. function Poppercam:Update(renderDt, desiredCameraCFrame, desiredCameraFocus, cameraController)
  3202. local rotatedFocus = CFrame.new(desiredCameraFocus.p, desiredCameraCFrame.p)*CFrame.new(
  3203. 0, 0, 0,
  3204. -1, 0, 0,
  3205. 0, 1, 0,
  3206. 0, 0, -1
  3207. )
  3208. local extrapolation = self.focusExtrapolator:Step(renderDt, rotatedFocus)
  3209. local zoom = ZoomController.Update(renderDt, rotatedFocus, extrapolation)
  3210. return rotatedFocus*CFrame.new(0, 0, zoom), desiredCameraFocus
  3211. end
  3212.  
  3213. -- Called when character is added
  3214. function Poppercam:CharacterAdded(character, player)
  3215. end
  3216.  
  3217. -- Called when character is about to be removed
  3218. function Poppercam:CharacterRemoving(character, player)
  3219. end
  3220.  
  3221. function Poppercam:OnCameraSubjectChanged(newSubject)
  3222. end
  3223.  
  3224. local ZoomController = _ZoomController()
  3225.  
  3226. function Poppercam:Update(renderDt, desiredCameraCFrame, desiredCameraFocus, cameraController)
  3227. local rotatedFocus = desiredCameraFocus * (desiredCameraCFrame - desiredCameraCFrame.p)
  3228. local extrapolation = self.focusExtrapolator:Step(renderDt, rotatedFocus)
  3229. local zoom = ZoomController.Update(renderDt, rotatedFocus, extrapolation)
  3230. return rotatedFocus*CFrame.new(0, 0, zoom), desiredCameraFocus
  3231. end
  3232.  
  3233. return Poppercam
  3234. end
  3235.  
  3236. function _Invisicam()
  3237.  
  3238. --[[ Top Level Roblox Services ]]--
  3239. local PlayersService = game:GetService("Players")
  3240.  
  3241. --[[ Constants ]]--
  3242. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  3243. local USE_STACKING_TRANSPARENCY = true -- Multiple items between the subject and camera get transparency values that add up to TARGET_TRANSPARENCY
  3244. local TARGET_TRANSPARENCY = 0.75 -- Classic Invisicam's Value, also used by new invisicam for parts hit by head and torso rays
  3245. local TARGET_TRANSPARENCY_PERIPHERAL = 0.5 -- Used by new SMART_CIRCLE mode for items not hit by head and torso rays
  3246.  
  3247. local MODE = {
  3248. --CUSTOM = 1, -- Retired, unused
  3249. LIMBS = 2, -- Track limbs
  3250. MOVEMENT = 3, -- Track movement
  3251. CORNERS = 4, -- Char model corners
  3252. CIRCLE1 = 5, -- Circle of casts around character
  3253. CIRCLE2 = 6, -- Circle of casts around character, camera relative
  3254. LIMBMOVE = 7, -- LIMBS mode + MOVEMENT mode
  3255. SMART_CIRCLE = 8, -- More sample points on and around character
  3256. CHAR_OUTLINE = 9, -- Dynamic outline around the character
  3257. }
  3258.  
  3259. local LIMB_TRACKING_SET = {
  3260. -- Body parts common to R15 and R6
  3261. ['Head'] = true,
  3262.  
  3263. -- Body parts unique to R6
  3264. ['Left Arm'] = true,
  3265. ['Right Arm'] = true,
  3266. ['Left Leg'] = true,
  3267. ['Right Leg'] = true,
  3268.  
  3269. -- Body parts unique to R15
  3270. ['LeftLowerArm'] = true,
  3271. ['RightLowerArm'] = true,
  3272. ['LeftUpperLeg'] = true,
  3273. ['RightUpperLeg'] = true
  3274. }
  3275.  
  3276. local CORNER_FACTORS = {
  3277. Vector3.new(1,1,-1),
  3278. Vector3.new(1,-1,-1),
  3279. Vector3.new(-1,-1,-1),
  3280. Vector3.new(-1,1,-1)
  3281. }
  3282.  
  3283. local CIRCLE_CASTS = 10
  3284. local MOVE_CASTS = 3
  3285. local SMART_CIRCLE_CASTS = 24
  3286. local SMART_CIRCLE_INCREMENT = 2.0 * math.pi / SMART_CIRCLE_CASTS
  3287. local CHAR_OUTLINE_CASTS = 24
  3288.  
  3289. -- Used to sanitize user-supplied functions
  3290. local function AssertTypes(param, ...)
  3291. local allowedTypes = {}
  3292. local typeString = ''
  3293. for _, typeName in pairs({...}) do
  3294. allowedTypes[typeName] = true
  3295. typeString = typeString .. (typeString == '' and '' or ' or ') .. typeName
  3296. end
  3297. local theType = type(param)
  3298. assert(allowedTypes[theType], typeString .. " type expected, got: " .. theType)
  3299. end
  3300.  
  3301. -- Helper function for Determinant of 3x3, not in CameraUtils for performance reasons
  3302. local function Det3x3(a,b,c,d,e,f,g,h,i)
  3303. return (a*(e*i-f*h)-b*(d*i-f*g)+c*(d*h-e*g))
  3304. end
  3305.  
  3306. -- Smart Circle mode needs the intersection of 2 rays that are known to be in the same plane
  3307. -- because they are generated from cross products with a common vector. This function is computing
  3308. -- that intersection, but it's actually the general solution for the point halfway between where
  3309. -- two skew lines come nearest to each other, which is more forgiving.
  3310. local function RayIntersection(p0, v0, p1, v1)
  3311. local v2 = v0:Cross(v1)
  3312. local d1 = p1.x - p0.x
  3313. local d2 = p1.y - p0.y
  3314. local d3 = p1.z - p0.z
  3315. local denom = Det3x3(v0.x,-v1.x,v2.x,v0.y,-v1.y,v2.y,v0.z,-v1.z,v2.z)
  3316.  
  3317. if (denom == 0) then
  3318. return ZERO_VECTOR3 -- No solution (rays are parallel)
  3319. end
  3320.  
  3321. local t0 = Det3x3(d1,-v1.x,v2.x,d2,-v1.y,v2.y,d3,-v1.z,v2.z) / denom
  3322. local t1 = Det3x3(v0.x,d1,v2.x,v0.y,d2,v2.y,v0.z,d3,v2.z) / denom
  3323. local s0 = p0 + t0 * v0
  3324. local s1 = p1 + t1 * v1
  3325. local s = s0 + 0.5 * ( s1 - s0 )
  3326.  
  3327. -- 0.25 studs is a threshold for deciding if the rays are
  3328. -- close enough to be considered intersecting, found through testing
  3329. if (s1-s0).Magnitude < 0.25 then
  3330. return s
  3331. else
  3332. return ZERO_VECTOR3
  3333. end
  3334. end
  3335.  
  3336.  
  3337.  
  3338. --[[ The Module ]]--
  3339. local BaseOcclusion = _BaseOcclusion()
  3340. local Invisicam = setmetatable({}, BaseOcclusion)
  3341. Invisicam.__index = Invisicam
  3342.  
  3343. function Invisicam.new()
  3344. local self = setmetatable(BaseOcclusion.new(), Invisicam)
  3345.  
  3346. self.char = nil
  3347. self.humanoidRootPart = nil
  3348. self.torsoPart = nil
  3349. self.headPart = nil
  3350.  
  3351. self.childAddedConn = nil
  3352. self.childRemovedConn = nil
  3353.  
  3354. self.behaviors = {} -- Map of modes to behavior fns
  3355. self.behaviors[MODE.LIMBS] = self.LimbBehavior
  3356. self.behaviors[MODE.MOVEMENT] = self.MoveBehavior
  3357. self.behaviors[MODE.CORNERS] = self.CornerBehavior
  3358. self.behaviors[MODE.CIRCLE1] = self.CircleBehavior
  3359. self.behaviors[MODE.CIRCLE2] = self.CircleBehavior
  3360. self.behaviors[MODE.LIMBMOVE] = self.LimbMoveBehavior
  3361. self.behaviors[MODE.SMART_CIRCLE] = self.SmartCircleBehavior
  3362. self.behaviors[MODE.CHAR_OUTLINE] = self.CharacterOutlineBehavior
  3363.  
  3364. self.mode = MODE.SMART_CIRCLE
  3365. self.behaviorFunction = self.SmartCircleBehavior
  3366.  
  3367. self.savedHits = {} -- Objects currently being faded in/out
  3368. self.trackedLimbs = {} -- Used in limb-tracking casting modes
  3369.  
  3370. self.camera = game.Workspace.CurrentCamera
  3371.  
  3372. self.enabled = false
  3373. return self
  3374. end
  3375.  
  3376. function Invisicam:Enable(enable)
  3377. self.enabled = enable
  3378.  
  3379. if not enable then
  3380. self:Cleanup()
  3381. end
  3382. end
  3383.  
  3384. function Invisicam:GetOcclusionMode()
  3385. return Enum.DevCameraOcclusionMode.Invisicam
  3386. end
  3387.  
  3388. --[[ Module functions ]]--
  3389. function Invisicam:LimbBehavior(castPoints)
  3390. for limb, _ in pairs(self.trackedLimbs) do
  3391. castPoints[#castPoints + 1] = limb.Position
  3392. end
  3393. end
  3394.  
  3395. function Invisicam:MoveBehavior(castPoints)
  3396. for i = 1, MOVE_CASTS do
  3397. local position, velocity = self.humanoidRootPart.Position, self.humanoidRootPart.Velocity
  3398. local horizontalSpeed = Vector3.new(velocity.X, 0, velocity.Z).Magnitude / 2
  3399. local offsetVector = (i - 1) * self.humanoidRootPart.CFrame.lookVector * horizontalSpeed
  3400. castPoints[#castPoints + 1] = position + offsetVector
  3401. end
  3402. end
  3403.  
  3404. function Invisicam:CornerBehavior(castPoints)
  3405. local cframe = self.humanoidRootPart.CFrame
  3406. local centerPoint = cframe.p
  3407. local rotation = cframe - centerPoint
  3408. local halfSize = self.char:GetExtentsSize() / 2 --NOTE: Doesn't update w/ limb animations
  3409. castPoints[#castPoints + 1] = centerPoint
  3410. for i = 1, #CORNER_FACTORS do
  3411. castPoints[#castPoints + 1] = centerPoint + (rotation * (halfSize * CORNER_FACTORS[i]))
  3412. end
  3413. end
  3414.  
  3415. function Invisicam:CircleBehavior(castPoints)
  3416. local cframe
  3417. if self.mode == MODE.CIRCLE1 then
  3418. cframe = self.humanoidRootPart.CFrame
  3419. else
  3420. local camCFrame = self.camera.CoordinateFrame
  3421. cframe = camCFrame - camCFrame.p + self.humanoidRootPart.Position
  3422. end
  3423. castPoints[#castPoints + 1] = cframe.p
  3424. for i = 0, CIRCLE_CASTS - 1 do
  3425. local angle = (2 * math.pi / CIRCLE_CASTS) * i
  3426. local offset = 3 * Vector3.new(math.cos(angle), math.sin(angle), 0)
  3427. castPoints[#castPoints + 1] = cframe * offset
  3428. end
  3429. end
  3430.  
  3431. function Invisicam:LimbMoveBehavior(castPoints)
  3432. self:LimbBehavior(castPoints)
  3433. self:MoveBehavior(castPoints)
  3434. end
  3435.  
  3436. function Invisicam:CharacterOutlineBehavior(castPoints)
  3437. local torsoUp = self.torsoPart.CFrame.upVector.unit
  3438. local torsoRight = self.torsoPart.CFrame.rightVector.unit
  3439.  
  3440. -- Torso cross of points for interior coverage
  3441. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p
  3442. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoUp
  3443. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoUp
  3444. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoRight
  3445. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoRight
  3446. if self.headPart then
  3447. castPoints[#castPoints + 1] = self.headPart.CFrame.p
  3448. end
  3449.  
  3450. local cframe = CFrame.new(ZERO_VECTOR3,Vector3.new(self.camera.CoordinateFrame.lookVector.X,0,self.camera.CoordinateFrame.lookVector.Z))
  3451. local centerPoint = (self.torsoPart and self.torsoPart.Position or self.humanoidRootPart.Position)
  3452.  
  3453. local partsWhitelist = {self.torsoPart}
  3454. if self.headPart then
  3455. partsWhitelist[#partsWhitelist + 1] = self.headPart
  3456. end
  3457.  
  3458. for i = 1, CHAR_OUTLINE_CASTS do
  3459. local angle = (2 * math.pi * i / CHAR_OUTLINE_CASTS)
  3460. local offset = cframe * (3 * Vector3.new(math.cos(angle), math.sin(angle), 0))
  3461.  
  3462. offset = Vector3.new(offset.X, math.max(offset.Y, -2.25), offset.Z)
  3463.  
  3464. local ray = Ray.new(centerPoint + offset, -3 * offset)
  3465. local hit, hitPoint = game.Workspace:FindPartOnRayWithWhitelist(ray, partsWhitelist, false, false)
  3466.  
  3467. if hit then
  3468. -- Use hit point as the cast point, but nudge it slightly inside the character so that bumping up against
  3469. -- walls is less likely to cause a transparency glitch
  3470. castPoints[#castPoints + 1] = hitPoint + 0.2 * (centerPoint - hitPoint).unit
  3471. end
  3472. end
  3473. end
  3474.  
  3475. function Invisicam:SmartCircleBehavior(castPoints)
  3476. local torsoUp = self.torsoPart.CFrame.upVector.unit
  3477. local torsoRight = self.torsoPart.CFrame.rightVector.unit
  3478.  
  3479. -- SMART_CIRCLE mode includes rays to head and 5 to the torso.
  3480. -- Hands, arms, legs and feet are not included since they
  3481. -- are not canCollide and can therefore go inside of parts
  3482. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p
  3483. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoUp
  3484. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoUp
  3485. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p + torsoRight
  3486. castPoints[#castPoints + 1] = self.torsoPart.CFrame.p - torsoRight
  3487. if self.headPart then
  3488. castPoints[#castPoints + 1] = self.headPart.CFrame.p
  3489. end
  3490.  
  3491. local cameraOrientation = self.camera.CFrame - self.camera.CFrame.p
  3492. local torsoPoint = Vector3.new(0,0.5,0) + (self.torsoPart and self.torsoPart.Position or self.humanoidRootPart.Position)
  3493. local radius = 2.5
  3494.  
  3495. -- This loop first calculates points in a circle of radius 2.5 around the torso of the character, in the
  3496. -- plane orthogonal to the camera's lookVector. Each point is then raycast to, to determine if it is within
  3497. -- the free space surrounding the player (not inside anything). Two iterations are done to adjust points that
  3498. -- are inside parts, to try to move them to valid locations that are still on their camera ray, so that the
  3499. -- circle remains circular from the camera's perspective, but does not cast rays into walls or parts that are
  3500. -- behind, below or beside the character and not really obstructing view of the character. This minimizes
  3501. -- the undesirable situation where the character walks up to an exterior wall and it is made invisible even
  3502. -- though it is behind the character.
  3503. for i = 1, SMART_CIRCLE_CASTS do
  3504. local angle = SMART_CIRCLE_INCREMENT * i - 0.5 * math.pi
  3505. local offset = radius * Vector3.new(math.cos(angle), math.sin(angle), 0)
  3506. local circlePoint = torsoPoint + cameraOrientation * offset
  3507.  
  3508. -- Vector from camera to point on the circle being tested
  3509. local vp = circlePoint - self.camera.CFrame.p
  3510.  
  3511. local ray = Ray.new(torsoPoint, circlePoint - torsoPoint)
  3512. local hit, hp, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
  3513. local castPoint = circlePoint
  3514.  
  3515. if hit then
  3516. local hprime = hp + 0.1 * hitNormal.unit -- Slightly offset hit point from the hit surface
  3517. local v0 = hprime - torsoPoint -- Vector from torso to offset hit point
  3518.  
  3519. local perp = (v0:Cross(vp)).unit
  3520.  
  3521. -- Vector from the offset hit point, along the hit surface
  3522. local v1 = (perp:Cross(hitNormal)).unit
  3523.  
  3524. -- Vector from camera to offset hit
  3525. local vprime = (hprime - self.camera.CFrame.p).unit
  3526.  
  3527. -- This dot product checks to see if the vector along the hit surface would hit the correct
  3528. -- side of the invisicam cone, or if it would cross the camera look vector and hit the wrong side
  3529. if ( v0.unit:Dot(-v1) < v0.unit:Dot(vprime)) then
  3530. castPoint = RayIntersection(hprime, v1, circlePoint, vp)
  3531.  
  3532. if castPoint.Magnitude > 0 then
  3533. local ray = Ray.new(hprime, castPoint - hprime)
  3534. local hit, hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
  3535.  
  3536. if hit then
  3537. local hprime2 = hitPoint + 0.1 * hitNormal.unit
  3538. castPoint = hprime2
  3539. end
  3540. else
  3541. castPoint = hprime
  3542. end
  3543. else
  3544. castPoint = hprime
  3545. end
  3546.  
  3547. local ray = Ray.new(torsoPoint, (castPoint - torsoPoint))
  3548. local hit, hitPoint, hitNormal = game.Workspace:FindPartOnRayWithIgnoreList(ray, {self.char}, false, false )
  3549.  
  3550. if hit then
  3551. local castPoint2 = hitPoint - 0.1 * (castPoint - torsoPoint).unit
  3552. castPoint = castPoint2
  3553. end
  3554. end
  3555.  
  3556. castPoints[#castPoints + 1] = castPoint
  3557. end
  3558. end
  3559.  
  3560. function Invisicam:CheckTorsoReference()
  3561. if self.char then
  3562. self.torsoPart = self.char:FindFirstChild("Torso")
  3563. if not self.torsoPart then
  3564. self.torsoPart = self.char:FindFirstChild("UpperTorso")
  3565. if not self.torsoPart then
  3566. self.torsoPart = self.char:FindFirstChild("HumanoidRootPart")
  3567. end
  3568. end
  3569.  
  3570. self.headPart = self.char:FindFirstChild("Head")
  3571. end
  3572. end
  3573.  
  3574. function Invisicam:CharacterAdded(char, player)
  3575. -- We only want the LocalPlayer's character
  3576. if player~=PlayersService.LocalPlayer then return end
  3577.  
  3578. if self.childAddedConn then
  3579. self.childAddedConn:Disconnect()
  3580. self.childAddedConn = nil
  3581. end
  3582. if self.childRemovedConn then
  3583. self.childRemovedConn:Disconnect()
  3584. self.childRemovedConn = nil
  3585. end
  3586.  
  3587. self.char = char
  3588.  
  3589. self.trackedLimbs = {}
  3590. local function childAdded(child)
  3591. if child:IsA("BasePart") then
  3592. if LIMB_TRACKING_SET[child.Name] then
  3593. self.trackedLimbs[child] = true
  3594. end
  3595.  
  3596. if child.Name == "Torso" or child.Name == "UpperTorso" then
  3597. self.torsoPart = child
  3598. end
  3599.  
  3600. if child.Name == "Head" then
  3601. self.headPart = child
  3602. end
  3603. end
  3604. end
  3605.  
  3606. local function childRemoved(child)
  3607. self.trackedLimbs[child] = nil
  3608.  
  3609. -- If removed/replaced part is 'Torso' or 'UpperTorso' double check that we still have a TorsoPart to use
  3610. self:CheckTorsoReference()
  3611. end
  3612.  
  3613. self.childAddedConn = char.ChildAdded:Connect(childAdded)
  3614. self.childRemovedConn = char.ChildRemoved:Connect(childRemoved)
  3615. for _, child in pairs(self.char:GetChildren()) do
  3616. childAdded(child)
  3617. end
  3618. end
  3619.  
  3620. function Invisicam:SetMode(newMode)
  3621. AssertTypes(newMode, 'number')
  3622. for _, modeNum in pairs(MODE) do
  3623. if modeNum == newMode then
  3624. self.mode = newMode
  3625. self.behaviorFunction = self.behaviors[self.mode]
  3626. return
  3627. end
  3628. end
  3629. error("Invalid mode number")
  3630. end
  3631.  
  3632. function Invisicam:GetObscuredParts()
  3633. return self.savedHits
  3634. end
  3635.  
  3636. -- Want to turn off Invisicam? Be sure to call this after.
  3637. function Invisicam:Cleanup()
  3638. for hit, originalFade in pairs(self.savedHits) do
  3639. hit.LocalTransparencyModifier = originalFade
  3640. end
  3641. end
  3642.  
  3643. function Invisicam:Update(dt, desiredCameraCFrame, desiredCameraFocus)
  3644. -- Bail if there is no Character
  3645. if not self.enabled or not self.char then
  3646. return desiredCameraCFrame, desiredCameraFocus
  3647. end
  3648.  
  3649. self.camera = game.Workspace.CurrentCamera
  3650.  
  3651. -- TODO: Move this to a GetHumanoidRootPart helper, probably combine with CheckTorsoReference
  3652. -- Make sure we still have a HumanoidRootPart
  3653. if not self.humanoidRootPart then
  3654. local humanoid = self.char:FindFirstChildOfClass("Humanoid")
  3655. if humanoid and humanoid.RootPart then
  3656. self.humanoidRootPart = humanoid.RootPart
  3657. else
  3658. -- Not set up with Humanoid? Try and see if there's one in the Character at all:
  3659. self.humanoidRootPart = self.char:FindFirstChild("HumanoidRootPart")
  3660. if not self.humanoidRootPart then
  3661. -- Bail out, since we're relying on HumanoidRootPart existing
  3662. return desiredCameraCFrame, desiredCameraFocus
  3663. end
  3664. end
  3665.  
  3666. -- TODO: Replace this with something more sensible
  3667. local ancestryChangedConn
  3668. ancestryChangedConn = self.humanoidRootPart.AncestryChanged:Connect(function(child, parent)
  3669. if child == self.humanoidRootPart and not parent then
  3670. self.humanoidRootPart = nil
  3671. if ancestryChangedConn and ancestryChangedConn.Connected then
  3672. ancestryChangedConn:Disconnect()
  3673. ancestryChangedConn = nil
  3674. end
  3675. end
  3676. end)
  3677. end
  3678.  
  3679. if not self.torsoPart then
  3680. self:CheckTorsoReference()
  3681. if not self.torsoPart then
  3682. -- Bail out, since we're relying on Torso existing, should never happen since we fall back to using HumanoidRootPart as torso
  3683. return desiredCameraCFrame, desiredCameraFocus
  3684. end
  3685. end
  3686.  
  3687. -- Make a list of world points to raycast to
  3688. local castPoints = {}
  3689. self.behaviorFunction(self, castPoints)
  3690.  
  3691. -- Cast to get a list of objects between the camera and the cast points
  3692. local currentHits = {}
  3693. local ignoreList = {self.char}
  3694. local function add(hit)
  3695. currentHits[hit] = true
  3696. if not self.savedHits[hit] then
  3697. self.savedHits[hit] = hit.LocalTransparencyModifier
  3698. end
  3699. end
  3700.  
  3701. local hitParts
  3702. local hitPartCount = 0
  3703.  
  3704. -- Hash table to treat head-ray-hit parts differently than the rest of the hit parts hit by other rays
  3705. -- head/torso ray hit parts will be more transparent than peripheral parts when USE_STACKING_TRANSPARENCY is enabled
  3706. local headTorsoRayHitParts = {}
  3707.  
  3708. local perPartTransparencyHeadTorsoHits = TARGET_TRANSPARENCY
  3709. local perPartTransparencyOtherHits = TARGET_TRANSPARENCY
  3710.  
  3711. if USE_STACKING_TRANSPARENCY then
  3712.  
  3713. -- This first call uses head and torso rays to find out how many parts are stacked up
  3714. -- for the purpose of calculating required per-part transparency
  3715. local headPoint = self.headPart and self.headPart.CFrame.p or castPoints[1]
  3716. local torsoPoint = self.torsoPart and self.torsoPart.CFrame.p or castPoints[2]
  3717. hitParts = self.camera:GetPartsObscuringTarget({headPoint, torsoPoint}, ignoreList)
  3718.  
  3719. -- Count how many things the sample rays passed through, including decals. This should only
  3720. -- count decals facing the camera, but GetPartsObscuringTarget does not return surface normals,
  3721. -- so my compromise for now is to just let any decal increase the part count by 1. Only one
  3722. -- decal per part will be considered.
  3723. for i = 1, #hitParts do
  3724. local hitPart = hitParts[i]
  3725. hitPartCount = hitPartCount + 1 -- count the part itself
  3726. headTorsoRayHitParts[hitPart] = true
  3727. for _, child in pairs(hitPart:GetChildren()) do
  3728. if child:IsA('Decal') or child:IsA('Texture') then
  3729. hitPartCount = hitPartCount + 1 -- count first decal hit, then break
  3730. break
  3731. end
  3732. end
  3733. end
  3734.  
  3735. if (hitPartCount > 0) then
  3736. perPartTransparencyHeadTorsoHits = math.pow( ((0.5 * TARGET_TRANSPARENCY) + (0.5 * TARGET_TRANSPARENCY / hitPartCount)), 1 / hitPartCount )
  3737. perPartTransparencyOtherHits = math.pow( ((0.5 * TARGET_TRANSPARENCY_PERIPHERAL) + (0.5 * TARGET_TRANSPARENCY_PERIPHERAL / hitPartCount)), 1 / hitPartCount )
  3738. end
  3739. end
  3740.  
  3741. -- Now get all the parts hit by all the rays
  3742. hitParts = self.camera:GetPartsObscuringTarget(castPoints, ignoreList)
  3743.  
  3744. local partTargetTransparency = {}
  3745.  
  3746. -- Include decals and textures
  3747. for i = 1, #hitParts do
  3748. local hitPart = hitParts[i]
  3749.  
  3750. partTargetTransparency[hitPart] =headTorsoRayHitParts[hitPart] and perPartTransparencyHeadTorsoHits or perPartTransparencyOtherHits
  3751.  
  3752. -- If the part is not already as transparent or more transparent than what invisicam requires, add it to the list of
  3753. -- parts to be modified by invisicam
  3754. if hitPart.Transparency < partTargetTransparency[hitPart] then
  3755. add(hitPart)
  3756. end
  3757.  
  3758. -- Check all decals and textures on the part
  3759. for _, child in pairs(hitPart:GetChildren()) do
  3760. if child:IsA('Decal') or child:IsA('Texture') then
  3761. if (child.Transparency < partTargetTransparency[hitPart]) then
  3762. partTargetTransparency[child] = partTargetTransparency[hitPart]
  3763. add(child)
  3764. end
  3765. end
  3766. end
  3767. end
  3768.  
  3769. -- Invisibilize objects that are in the way, restore those that aren't anymore
  3770. for hitPart, originalLTM in pairs(self.savedHits) do
  3771. if currentHits[hitPart] then
  3772. -- LocalTransparencyModifier gets whatever value is required to print the part's total transparency to equal perPartTransparency
  3773. hitPart.LocalTransparencyModifier = (hitPart.Transparency < 1) and ((partTargetTransparency[hitPart] - hitPart.Transparency) / (1.0 - hitPart.Transparency)) or 0
  3774. else -- Restore original pre-invisicam value of LTM
  3775. hitPart.LocalTransparencyModifier = originalLTM
  3776. self.savedHits[hitPart] = nil
  3777. end
  3778. end
  3779.  
  3780. -- Invisicam does not change the camera values
  3781. return desiredCameraCFrame, desiredCameraFocus
  3782. end
  3783.  
  3784. return Invisicam
  3785. end
  3786.  
  3787. function _LegacyCamera()
  3788.  
  3789. local ZERO_VECTOR2 = Vector2.new(0,0)
  3790.  
  3791. local Util = _CameraUtils()
  3792.  
  3793. --[[ Services ]]--
  3794. local PlayersService = game:GetService('Players')
  3795.  
  3796. --[[ The Module ]]--
  3797. local BaseCamera = _BaseCamera()
  3798. local LegacyCamera = setmetatable({}, BaseCamera)
  3799. LegacyCamera.__index = LegacyCamera
  3800.  
  3801. function LegacyCamera.new()
  3802. local self = setmetatable(BaseCamera.new(), LegacyCamera)
  3803.  
  3804. self.cameraType = Enum.CameraType.Fixed
  3805. self.lastUpdate = tick()
  3806. self.lastDistanceToSubject = nil
  3807.  
  3808. return self
  3809. end
  3810.  
  3811. function LegacyCamera:GetModuleName()
  3812. return "LegacyCamera"
  3813. end
  3814.  
  3815. --[[ Functions overridden from BaseCamera ]]--
  3816. function LegacyCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
  3817. return BaseCamera.SetCameraToSubjectDistance(self,desiredSubjectDistance)
  3818. end
  3819.  
  3820. function LegacyCamera:Update(dt)
  3821.  
  3822. -- Cannot update until cameraType has been set
  3823. if not self.cameraType then return end
  3824.  
  3825. local now = tick()
  3826. local timeDelta = (now - self.lastUpdate)
  3827. local camera = workspace.CurrentCamera
  3828. local newCameraCFrame = camera.CFrame
  3829. local newCameraFocus = camera.Focus
  3830. local player = PlayersService.LocalPlayer
  3831.  
  3832. if self.lastUpdate == nil or timeDelta > 1 then
  3833. self.lastDistanceToSubject = nil
  3834. end
  3835. local subjectPosition = self:GetSubjectPosition()
  3836.  
  3837. if self.cameraType == Enum.CameraType.Fixed then
  3838. if self.lastUpdate then
  3839. -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  3840. local delta = math.min(0.1, now - self.lastUpdate)
  3841. local gamepadRotation = self:UpdateGamepad()
  3842. self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  3843. end
  3844.  
  3845. if subjectPosition and player and camera then
  3846. local distanceToSubject = self:GetCameraToSubjectDistance()
  3847. local newLookVector = self:CalculateNewLookVector()
  3848. self.rotateInput = ZERO_VECTOR2
  3849.  
  3850. newCameraFocus = camera.Focus -- Fixed camera does not change focus
  3851. newCameraCFrame = CFrame.new(camera.CFrame.p, camera.CFrame.p + (distanceToSubject * newLookVector))
  3852. end
  3853. elseif self.cameraType == Enum.CameraType.Attach then
  3854. if subjectPosition and camera then
  3855. local distanceToSubject = self:GetCameraToSubjectDistance()
  3856. local humanoid = self:GetHumanoid()
  3857. if self.lastUpdate and humanoid and humanoid.RootPart then
  3858.  
  3859. -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  3860. local delta = math.min(0.1, now - self.lastUpdate)
  3861. local gamepadRotation = self:UpdateGamepad()
  3862. self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  3863.  
  3864. local forwardVector = humanoid.RootPart.CFrame.lookVector
  3865.  
  3866. local y = Util.GetAngleBetweenXZVectors(forwardVector, self:GetCameraLookVector())
  3867. if Util.IsFinite(y) then
  3868. -- Preserve vertical rotation from user input
  3869. self.rotateInput = Vector2.new(y, self.rotateInput.Y)
  3870. end
  3871. end
  3872.  
  3873. local newLookVector = self:CalculateNewLookVector()
  3874. self.rotateInput = ZERO_VECTOR2
  3875.  
  3876. newCameraFocus = CFrame.new(subjectPosition)
  3877. newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)
  3878. end
  3879. elseif self.cameraType == Enum.CameraType.Watch then
  3880. if subjectPosition and player and camera then
  3881. local cameraLook = nil
  3882.  
  3883. local humanoid = self:GetHumanoid()
  3884. if humanoid and humanoid.RootPart then
  3885. local diffVector = subjectPosition - camera.CFrame.p
  3886. cameraLook = diffVector.unit
  3887.  
  3888. if self.lastDistanceToSubject and self.lastDistanceToSubject == self:GetCameraToSubjectDistance() then
  3889. -- Don't clobber the zoom if they zoomed the camera
  3890. local newDistanceToSubject = diffVector.magnitude
  3891. self:SetCameraToSubjectDistance(newDistanceToSubject)
  3892. end
  3893. end
  3894.  
  3895. local distanceToSubject = self:GetCameraToSubjectDistance()
  3896. local newLookVector = self:CalculateNewLookVector(cameraLook)
  3897. self.rotateInput = ZERO_VECTOR2
  3898.  
  3899. newCameraFocus = CFrame.new(subjectPosition)
  3900. newCameraCFrame = CFrame.new(subjectPosition - (distanceToSubject * newLookVector), subjectPosition)
  3901.  
  3902. self.lastDistanceToSubject = distanceToSubject
  3903. end
  3904. else
  3905. -- Unsupported type, return current values unchanged
  3906. return camera.CFrame, camera.Focus
  3907. end
  3908.  
  3909. self.lastUpdate = now
  3910. return newCameraCFrame, newCameraFocus
  3911. end
  3912.  
  3913. return LegacyCamera
  3914. end
  3915.  
  3916. function _OrbitalCamera()
  3917.  
  3918. -- Local private variables and constants
  3919. local UNIT_Z = Vector3.new(0,0,1)
  3920. local X1_Y0_Z1 = Vector3.new(1,0,1) --Note: not a unit vector, used for projecting onto XZ plane
  3921. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  3922. local ZERO_VECTOR2 = Vector2.new(0,0)
  3923. local TAU = 2 * math.pi
  3924.  
  3925. --[[ Gamepad Support ]]--
  3926. local THUMBSTICK_DEADZONE = 0.2
  3927.  
  3928. -- Do not edit these values, they are not the developer-set limits, they are limits
  3929. -- to the values the camera system equations can correctly handle
  3930. local MIN_ALLOWED_ELEVATION_DEG = -80
  3931. local MAX_ALLOWED_ELEVATION_DEG = 80
  3932.  
  3933. local externalProperties = {}
  3934. externalProperties["InitialDistance"] = 25
  3935. externalProperties["MinDistance"] = 10
  3936. externalProperties["MaxDistance"] = 100
  3937. externalProperties["InitialElevation"] = 35
  3938. externalProperties["MinElevation"] = 35
  3939. externalProperties["MaxElevation"] = 35
  3940. externalProperties["ReferenceAzimuth"] = -45 -- Angle around the Y axis where the camera starts. -45 offsets the camera in the -X and +Z directions equally
  3941. externalProperties["CWAzimuthTravel"] = 90 -- How many degrees the camera is allowed to rotate from the reference position, CW as seen from above
  3942. externalProperties["CCWAzimuthTravel"] = 90 -- How many degrees the camera is allowed to rotate from the reference position, CCW as seen from above
  3943. externalProperties["UseAzimuthLimits"] = false -- Full rotation around Y axis available by default
  3944.  
  3945. local Util = _CameraUtils()
  3946.  
  3947. --[[ Services ]]--
  3948. local PlayersService = game:GetService('Players')
  3949. local VRService = game:GetService("VRService")
  3950.  
  3951. --[[ The Module ]]--
  3952. local BaseCamera = _BaseCamera()
  3953. local OrbitalCamera = setmetatable({}, BaseCamera)
  3954. OrbitalCamera.__index = OrbitalCamera
  3955.  
  3956.  
  3957. function OrbitalCamera.new()
  3958. local self = setmetatable(BaseCamera.new(), OrbitalCamera)
  3959.  
  3960. self.lastUpdate = tick()
  3961.  
  3962. -- OrbitalCamera-specific members
  3963. self.changedSignalConnections = {}
  3964. self.refAzimuthRad = nil
  3965. self.curAzimuthRad = nil
  3966. self.minAzimuthAbsoluteRad = nil
  3967. self.maxAzimuthAbsoluteRad = nil
  3968. self.useAzimuthLimits = nil
  3969. self.curElevationRad = nil
  3970. self.minElevationRad = nil
  3971. self.maxElevationRad = nil
  3972. self.curDistance = nil
  3973. self.minDistance = nil
  3974. self.maxDistance = nil
  3975.  
  3976. -- Gamepad
  3977. self.r3ButtonDown = false
  3978. self.l3ButtonDown = false
  3979. self.gamepadDollySpeedMultiplier = 1
  3980.  
  3981. self.lastUserPanCamera = tick()
  3982.  
  3983. self.externalProperties = {}
  3984. self.externalProperties["InitialDistance"] = 25
  3985. self.externalProperties["MinDistance"] = 10
  3986. self.externalProperties["MaxDistance"] = 100
  3987. self.externalProperties["InitialElevation"] = 35
  3988. self.externalProperties["MinElevation"] = 35
  3989. self.externalProperties["MaxElevation"] = 35
  3990. self.externalProperties["ReferenceAzimuth"] = -45 -- Angle around the Y axis where the camera starts. -45 offsets the camera in the -X and +Z directions equally
  3991. self.externalProperties["CWAzimuthTravel"] = 90 -- How many degrees the camera is allowed to rotate from the reference position, CW as seen from above
  3992. self.externalProperties["CCWAzimuthTravel"] = 90 -- How many degrees the camera is allowed to rotate from the reference position, CCW as seen from above
  3993. self.externalProperties["UseAzimuthLimits"] = false -- Full rotation around Y axis available by default
  3994. self:LoadNumberValueParameters()
  3995.  
  3996. return self
  3997. end
  3998.  
  3999. function OrbitalCamera:LoadOrCreateNumberValueParameter(name, valueType, updateFunction)
  4000. local valueObj = script:FindFirstChild(name)
  4001.  
  4002. if valueObj and valueObj:isA(valueType) then
  4003. -- Value object exists and is the correct type, use its value
  4004. self.externalProperties[name] = valueObj.Value
  4005. elseif self.externalProperties[name] ~= nil then
  4006. -- Create missing (or replace incorrectly-typed) valueObject with default value
  4007. valueObj = Instance.new(valueType)
  4008. valueObj.Name = name
  4009. valueObj.Parent = script
  4010. valueObj.Value = self.externalProperties[name]
  4011. else
  4012. print("externalProperties table has no entry for ",name)
  4013. return
  4014. end
  4015.  
  4016. if updateFunction then
  4017. if self.changedSignalConnections[name] then
  4018. self.changedSignalConnections[name]:Disconnect()
  4019. end
  4020. self.changedSignalConnections[name] = valueObj.Changed:Connect(function(newValue)
  4021. self.externalProperties[name] = newValue
  4022. updateFunction(self)
  4023. end)
  4024. end
  4025. end
  4026.  
  4027. function OrbitalCamera:SetAndBoundsCheckAzimuthValues()
  4028. self.minAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) - math.abs(math.rad(self.externalProperties["CWAzimuthTravel"]))
  4029. self.maxAzimuthAbsoluteRad = math.rad(self.externalProperties["ReferenceAzimuth"]) + math.abs(math.rad(self.externalProperties["CCWAzimuthTravel"]))
  4030. self.useAzimuthLimits = self.externalProperties["UseAzimuthLimits"]
  4031. if self.useAzimuthLimits then
  4032. self.curAzimuthRad = math.max(self.curAzimuthRad, self.minAzimuthAbsoluteRad)
  4033. self.curAzimuthRad = math.min(self.curAzimuthRad, self.maxAzimuthAbsoluteRad)
  4034. end
  4035. end
  4036.  
  4037. function OrbitalCamera:SetAndBoundsCheckElevationValues()
  4038. -- These degree values are the direct user input values. It is deliberate that they are
  4039. -- ranged checked only against the extremes, and not against each other. Any time one
  4040. -- is changed, both of the internal values in radians are recalculated. This allows for
  4041. -- A developer to change the values in any order and for the end results to be that the
  4042. -- internal values adjust to match intent as best as possible.
  4043. local minElevationDeg = math.max(self.externalProperties["MinElevation"], MIN_ALLOWED_ELEVATION_DEG)
  4044. local maxElevationDeg = math.min(self.externalProperties["MaxElevation"], MAX_ALLOWED_ELEVATION_DEG)
  4045.  
  4046. -- Set internal values in radians
  4047. self.minElevationRad = math.rad(math.min(minElevationDeg, maxElevationDeg))
  4048. self.maxElevationRad = math.rad(math.max(minElevationDeg, maxElevationDeg))
  4049. self.curElevationRad = math.max(self.curElevationRad, self.minElevationRad)
  4050. self.curElevationRad = math.min(self.curElevationRad, self.maxElevationRad)
  4051. end
  4052.  
  4053. function OrbitalCamera:SetAndBoundsCheckDistanceValues()
  4054. self.minDistance = self.externalProperties["MinDistance"]
  4055. self.maxDistance = self.externalProperties["MaxDistance"]
  4056. self.curDistance = math.max(self.curDistance, self.minDistance)
  4057. self.curDistance = math.min(self.curDistance, self.maxDistance)
  4058. end
  4059.  
  4060. -- This loads from, or lazily creates, NumberValue objects for exposed parameters
  4061. function OrbitalCamera:LoadNumberValueParameters()
  4062. -- These initial values do not require change listeners since they are read only once
  4063. self:LoadOrCreateNumberValueParameter("InitialElevation", "NumberValue", nil)
  4064. self:LoadOrCreateNumberValueParameter("InitialDistance", "NumberValue", nil)
  4065.  
  4066. -- Note: ReferenceAzimuth is also used as an initial value, but needs a change listener because it is used in the calculation of the limits
  4067. self:LoadOrCreateNumberValueParameter("ReferenceAzimuth", "NumberValue", self.SetAndBoundsCheckAzimuthValue)
  4068. self:LoadOrCreateNumberValueParameter("CWAzimuthTravel", "NumberValue", self.SetAndBoundsCheckAzimuthValues)
  4069. self:LoadOrCreateNumberValueParameter("CCWAzimuthTravel", "NumberValue", self.SetAndBoundsCheckAzimuthValues)
  4070. self:LoadOrCreateNumberValueParameter("MinElevation", "NumberValue", self.SetAndBoundsCheckElevationValues)
  4071. self:LoadOrCreateNumberValueParameter("MaxElevation", "NumberValue", self.SetAndBoundsCheckElevationValues)
  4072. self:LoadOrCreateNumberValueParameter("MinDistance", "NumberValue", self.SetAndBoundsCheckDistanceValues)
  4073. self:LoadOrCreateNumberValueParameter("MaxDistance", "NumberValue", self.SetAndBoundsCheckDistanceValues)
  4074. self:LoadOrCreateNumberValueParameter("UseAzimuthLimits", "BoolValue", self.SetAndBoundsCheckAzimuthValues)
  4075.  
  4076. -- Internal values set (in radians, from degrees), plus sanitization
  4077. self.curAzimuthRad = math.rad(self.externalProperties["ReferenceAzimuth"])
  4078. self.curElevationRad = math.rad(self.externalProperties["InitialElevation"])
  4079. self.curDistance = self.externalProperties["InitialDistance"]
  4080.  
  4081. self:SetAndBoundsCheckAzimuthValues()
  4082. self:SetAndBoundsCheckElevationValues()
  4083. self:SetAndBoundsCheckDistanceValues()
  4084. end
  4085.  
  4086. function OrbitalCamera:GetModuleName()
  4087. return "OrbitalCamera"
  4088. end
  4089.  
  4090. function OrbitalCamera:SetInitialOrientation(humanoid)
  4091. if not humanoid or not humanoid.RootPart then
  4092. warn("OrbitalCamera could not set initial orientation due to missing humanoid")
  4093. return
  4094. end
  4095. local newDesiredLook = (humanoid.RootPart.CFrame.lookVector - Vector3.new(0,0.23,0)).unit
  4096. local horizontalShift = Util.GetAngleBetweenXZVectors(newDesiredLook, self:GetCameraLookVector())
  4097. local vertShift = math.asin(self:GetCameraLookVector().y) - math.asin(newDesiredLook.y)
  4098. if not Util.IsFinite(horizontalShift) then
  4099. horizontalShift = 0
  4100. end
  4101. if not Util.IsFinite(vertShift) then
  4102. vertShift = 0
  4103. end
  4104. self.rotateInput = Vector2.new(horizontalShift, vertShift)
  4105. end
  4106.  
  4107. --[[ Functions of BaseCamera that are overridden by OrbitalCamera ]]--
  4108. function OrbitalCamera:GetCameraToSubjectDistance()
  4109. return self.curDistance
  4110. end
  4111.  
  4112. function OrbitalCamera:SetCameraToSubjectDistance(desiredSubjectDistance)
  4113. print("OrbitalCamera SetCameraToSubjectDistance ",desiredSubjectDistance)
  4114. local player = PlayersService.LocalPlayer
  4115. if player then
  4116. self.currentSubjectDistance = math.clamp(desiredSubjectDistance, self.minDistance, self.maxDistance)
  4117.  
  4118. -- OrbitalCamera is not allowed to go into the first-person range
  4119. self.currentSubjectDistance = math.max(self.currentSubjectDistance, self.FIRST_PERSON_DISTANCE_THRESHOLD)
  4120. end
  4121. self.inFirstPerson = false
  4122. self:UpdateMouseBehavior()
  4123. return self.currentSubjectDistance
  4124. end
  4125.  
  4126. function OrbitalCamera:CalculateNewLookVector(suppliedLookVector, xyRotateVector)
  4127. local currLookVector = suppliedLookVector or self:GetCameraLookVector()
  4128. local currPitchAngle = math.asin(currLookVector.y)
  4129. local yTheta = math.clamp(xyRotateVector.y, currPitchAngle - math.rad(MAX_ALLOWED_ELEVATION_DEG), currPitchAngle - math.rad(MIN_ALLOWED_ELEVATION_DEG))
  4130. local constrainedRotateInput = Vector2.new(xyRotateVector.x, yTheta)
  4131. local startCFrame = CFrame.new(ZERO_VECTOR3, currLookVector)
  4132. local newLookVector = (CFrame.Angles(0, -constrainedRotateInput.x, 0) * startCFrame * CFrame.Angles(-constrainedRotateInput.y,0,0)).lookVector
  4133. return newLookVector
  4134. end
  4135.  
  4136. function OrbitalCamera:GetGamepadPan(name, state, input)
  4137. if input.UserInputType == self.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
  4138. if self.r3ButtonDown or self.l3ButtonDown then
  4139. -- R3 or L3 Thumbstick is depressed, right stick controls dolly in/out
  4140. if (input.Position.Y > THUMBSTICK_DEADZONE) then
  4141. self.gamepadDollySpeedMultiplier = 0.96
  4142. elseif (input.Position.Y < -THUMBSTICK_DEADZONE) then
  4143. self.gamepadDollySpeedMultiplier = 1.04
  4144. else
  4145. self.gamepadDollySpeedMultiplier = 1.00
  4146. end
  4147. else
  4148. if state == Enum.UserInputState.Cancel then
  4149. self.gamepadPanningCamera = ZERO_VECTOR2
  4150. return
  4151. end
  4152.  
  4153. local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
  4154. if inputVector.magnitude > THUMBSTICK_DEADZONE then
  4155. self.gamepadPanningCamera = Vector2.new(input.Position.X, -input.Position.Y)
  4156. else
  4157. self.gamepadPanningCamera = ZERO_VECTOR2
  4158. end
  4159. end
  4160. return Enum.ContextActionResult.Sink
  4161. end
  4162. return Enum.ContextActionResult.Pass
  4163. end
  4164.  
  4165. function OrbitalCamera:DoGamepadZoom(name, state, input)
  4166. if input.UserInputType == self.activeGamepad and (input.KeyCode == Enum.KeyCode.ButtonR3 or input.KeyCode == Enum.KeyCode.ButtonL3) then
  4167. if (state == Enum.UserInputState.Begin) then
  4168. self.r3ButtonDown = input.KeyCode == Enum.KeyCode.ButtonR3
  4169. self.l3ButtonDown = input.KeyCode == Enum.KeyCode.ButtonL3
  4170. elseif (state == Enum.UserInputState.End) then
  4171. if (input.KeyCode == Enum.KeyCode.ButtonR3) then
  4172. self.r3ButtonDown = false
  4173. elseif (input.KeyCode == Enum.KeyCode.ButtonL3) then
  4174. self.l3ButtonDown = false
  4175. end
  4176. if (not self.r3ButtonDown) and (not self.l3ButtonDown) then
  4177. self.gamepadDollySpeedMultiplier = 1.00
  4178. end
  4179. end
  4180. return Enum.ContextActionResult.Sink
  4181. end
  4182. return Enum.ContextActionResult.Pass
  4183. end
  4184.  
  4185. function OrbitalCamera:BindGamepadInputActions()
  4186. self:BindAction("OrbitalCamGamepadPan", function(name, state, input) return self:GetGamepadPan(name, state, input) end,
  4187. false, Enum.KeyCode.Thumbstick2)
  4188. self:BindAction("OrbitalCamGamepadZoom", function(name, state, input) return self:DoGamepadZoom(name, state, input) end,
  4189. false, Enum.KeyCode.ButtonR3, Enum.KeyCode.ButtonL3)
  4190. end
  4191.  
  4192.  
  4193. -- [[ Update ]]--
  4194. function OrbitalCamera:Update(dt)
  4195. local now = tick()
  4196. local timeDelta = (now - self.lastUpdate)
  4197. local userPanningTheCamera = (self.UserPanningTheCamera == true)
  4198. local camera = workspace.CurrentCamera
  4199. local newCameraCFrame = camera.CFrame
  4200. local newCameraFocus = camera.Focus
  4201. local player = PlayersService.LocalPlayer
  4202. local cameraSubject = camera and camera.CameraSubject
  4203. local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
  4204. local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
  4205.  
  4206. if self.lastUpdate == nil or timeDelta > 1 then
  4207. self.lastCameraTransform = nil
  4208. end
  4209.  
  4210. if self.lastUpdate then
  4211. local gamepadRotation = self:UpdateGamepad()
  4212.  
  4213. if self:ShouldUseVRRotation() then
  4214. self.RotateInput = self.RotateInput + self:GetVRRotationInput()
  4215. else
  4216. -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  4217. local delta = math.min(0.1, timeDelta)
  4218.  
  4219. if gamepadRotation ~= ZERO_VECTOR2 then
  4220. userPanningTheCamera = true
  4221. self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  4222. end
  4223.  
  4224. local angle = 0
  4225. if not (isInVehicle or isOnASkateboard) then
  4226. angle = angle + (self.TurningLeft and -120 or 0)
  4227. angle = angle + (self.TurningRight and 120 or 0)
  4228. end
  4229.  
  4230. if angle ~= 0 then
  4231. self.rotateInput = self.rotateInput + Vector2.new(math.rad(angle * delta), 0)
  4232. userPanningTheCamera = true
  4233. end
  4234. end
  4235. end
  4236.  
  4237. -- Reset tween speed if user is panning
  4238. if userPanningTheCamera then
  4239. self.lastUserPanCamera = tick()
  4240. end
  4241.  
  4242. local subjectPosition = self:GetSubjectPosition()
  4243.  
  4244. if subjectPosition and player and camera then
  4245.  
  4246. -- Process any dollying being done by gamepad
  4247. -- TODO: Move this
  4248. if self.gamepadDollySpeedMultiplier ~= 1 then
  4249. self:SetCameraToSubjectDistance(self.currentSubjectDistance * self.gamepadDollySpeedMultiplier)
  4250. end
  4251.  
  4252. local VREnabled = VRService.VREnabled
  4253. newCameraFocus = VREnabled and self:GetVRFocus(subjectPosition, timeDelta) or CFrame.new(subjectPosition)
  4254.  
  4255. local cameraFocusP = newCameraFocus.p
  4256. if VREnabled and not self:IsInFirstPerson() then
  4257. local cameraHeight = self:GetCameraHeight()
  4258. local vecToSubject = (subjectPosition - camera.CFrame.p)
  4259. local distToSubject = vecToSubject.magnitude
  4260.  
  4261. -- Only move the camera if it exceeded a maximum distance to the subject in VR
  4262. if distToSubject > self.currentSubjectDistance or self.rotateInput.x ~= 0 then
  4263. local desiredDist = math.min(distToSubject, self.currentSubjectDistance)
  4264.  
  4265. -- Note that CalculateNewLookVector is overridden from BaseCamera
  4266. vecToSubject = self:CalculateNewLookVector(vecToSubject.unit * X1_Y0_Z1, Vector2.new(self.rotateInput.x, 0)) * desiredDist
  4267.  
  4268. local newPos = cameraFocusP - vecToSubject
  4269. local desiredLookDir = camera.CFrame.lookVector
  4270. if self.rotateInput.x ~= 0 then
  4271. desiredLookDir = vecToSubject
  4272. end
  4273. local lookAt = Vector3.new(newPos.x + desiredLookDir.x, newPos.y, newPos.z + desiredLookDir.z)
  4274. self.RotateInput = ZERO_VECTOR2
  4275.  
  4276. newCameraCFrame = CFrame.new(newPos, lookAt) + Vector3.new(0, cameraHeight, 0)
  4277. end
  4278. else
  4279. -- self.RotateInput is a Vector2 of mouse movement deltas since last update
  4280. self.curAzimuthRad = self.curAzimuthRad - self.rotateInput.x
  4281.  
  4282. if self.useAzimuthLimits then
  4283. self.curAzimuthRad = math.clamp(self.curAzimuthRad, self.minAzimuthAbsoluteRad, self.maxAzimuthAbsoluteRad)
  4284. else
  4285. self.curAzimuthRad = (self.curAzimuthRad ~= 0) and (math.sign(self.curAzimuthRad) * (math.abs(self.curAzimuthRad) % TAU)) or 0
  4286. end
  4287.  
  4288. self.curElevationRad = math.clamp(self.curElevationRad + self.rotateInput.y, self.minElevationRad, self.maxElevationRad)
  4289.  
  4290. local cameraPosVector = self.currentSubjectDistance * ( CFrame.fromEulerAnglesYXZ( -self.curElevationRad, self.curAzimuthRad, 0 ) * UNIT_Z )
  4291. local camPos = subjectPosition + cameraPosVector
  4292.  
  4293. newCameraCFrame = CFrame.new(camPos, subjectPosition)
  4294.  
  4295. self.rotateInput = ZERO_VECTOR2
  4296. end
  4297.  
  4298. self.lastCameraTransform = newCameraCFrame
  4299. self.lastCameraFocus = newCameraFocus
  4300. if (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
  4301. self.lastSubjectCFrame = cameraSubject.CFrame
  4302. else
  4303. self.lastSubjectCFrame = nil
  4304. end
  4305. end
  4306.  
  4307. self.lastUpdate = now
  4308. return newCameraCFrame, newCameraFocus
  4309. end
  4310.  
  4311. return OrbitalCamera
  4312. end
  4313.  
  4314. function _ClassicCamera()
  4315.  
  4316. -- Local private variables and constants
  4317. local ZERO_VECTOR2 = Vector2.new(0,0)
  4318.  
  4319. local tweenAcceleration = math.rad(220) --Radians/Second^2
  4320. local tweenSpeed = math.rad(0) --Radians/Second
  4321. local tweenMaxSpeed = math.rad(250) --Radians/Second
  4322. local TIME_BEFORE_AUTO_ROTATE = 2.0 --Seconds, used when auto-aligning camera with vehicles
  4323.  
  4324. local INITIAL_CAMERA_ANGLE = CFrame.fromOrientation(math.rad(-15), 0, 0)
  4325.  
  4326. local FFlagUserCameraToggle do
  4327. local success, result = pcall(function()
  4328. return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  4329. end)
  4330. FFlagUserCameraToggle = success and result
  4331. end
  4332.  
  4333. --[[ Services ]]--
  4334. local PlayersService = game:GetService('Players')
  4335. local VRService = game:GetService("VRService")
  4336.  
  4337. local CameraInput = _CameraInput()
  4338. local Util = _CameraUtils()
  4339.  
  4340. --[[ The Module ]]--
  4341. local BaseCamera = _BaseCamera()
  4342. local ClassicCamera = setmetatable({}, BaseCamera)
  4343. ClassicCamera.__index = ClassicCamera
  4344.  
  4345. function ClassicCamera.new()
  4346. local self = setmetatable(BaseCamera.new(), ClassicCamera)
  4347.  
  4348. self.isFollowCamera = false
  4349. self.isCameraToggle = false
  4350. self.lastUpdate = tick()
  4351. self.cameraToggleSpring = Util.Spring.new(5, 0)
  4352.  
  4353. return self
  4354. end
  4355.  
  4356. function ClassicCamera:GetCameraToggleOffset(dt)
  4357. assert(FFlagUserCameraToggle)
  4358.  
  4359. if self.isCameraToggle then
  4360. local zoom = self.currentSubjectDistance
  4361.  
  4362. if CameraInput.getTogglePan() then
  4363. self.cameraToggleSpring.goal = math.clamp(Util.map(zoom, 0.5, self.FIRST_PERSON_DISTANCE_THRESHOLD, 0, 1), 0, 1)
  4364. else
  4365. self.cameraToggleSpring.goal = 0
  4366. end
  4367.  
  4368. local distanceOffset = math.clamp(Util.map(zoom, 0.5, 64, 0, 1), 0, 1) + 1
  4369. return Vector3.new(0, self.cameraToggleSpring:step(dt)*distanceOffset, 0)
  4370. end
  4371.  
  4372. return Vector3.new()
  4373. end
  4374.  
  4375. -- Movement mode standardized to Enum.ComputerCameraMovementMode values
  4376. function ClassicCamera:SetCameraMovementMode(cameraMovementMode)
  4377. BaseCamera.SetCameraMovementMode(self, cameraMovementMode)
  4378.  
  4379. self.isFollowCamera = cameraMovementMode == Enum.ComputerCameraMovementMode.Follow
  4380. self.isCameraToggle = cameraMovementMode == Enum.ComputerCameraMovementMode.CameraToggle
  4381. end
  4382.  
  4383. function ClassicCamera:Update()
  4384. local now = tick()
  4385. local timeDelta = now - self.lastUpdate
  4386.  
  4387. local camera = workspace.CurrentCamera
  4388. local newCameraCFrame = camera.CFrame
  4389. local newCameraFocus = camera.Focus
  4390.  
  4391. local overrideCameraLookVector = nil
  4392. if self.resetCameraAngle then
  4393. local rootPart = self:GetHumanoidRootPart()
  4394. if rootPart then
  4395. overrideCameraLookVector = (rootPart.CFrame * INITIAL_CAMERA_ANGLE).lookVector
  4396. else
  4397. overrideCameraLookVector = INITIAL_CAMERA_ANGLE.lookVector
  4398. end
  4399. self.resetCameraAngle = false
  4400. end
  4401.  
  4402. local player = PlayersService.LocalPlayer
  4403. local humanoid = self:GetHumanoid()
  4404. local cameraSubject = camera.CameraSubject
  4405. local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')
  4406. local isOnASkateboard = cameraSubject and cameraSubject:IsA('SkateboardPlatform')
  4407. local isClimbing = humanoid and humanoid:GetState() == Enum.HumanoidStateType.Climbing
  4408.  
  4409. if self.lastUpdate == nil or timeDelta > 1 then
  4410. self.lastCameraTransform = nil
  4411. end
  4412.  
  4413. if self.lastUpdate then
  4414. local gamepadRotation = self:UpdateGamepad()
  4415.  
  4416. if self:ShouldUseVRRotation() then
  4417. self.rotateInput = self.rotateInput + self:GetVRRotationInput()
  4418. else
  4419. -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from
  4420. local delta = math.min(0.1, timeDelta)
  4421.  
  4422. if gamepadRotation ~= ZERO_VECTOR2 then
  4423. self.rotateInput = self.rotateInput + (gamepadRotation * delta)
  4424. end
  4425.  
  4426. local angle = 0
  4427. if not (isInVehicle or isOnASkateboard) then
  4428. angle = angle + (self.turningLeft and -120 or 0)
  4429. angle = angle + (self.turningRight and 120 or 0)
  4430. end
  4431.  
  4432. if angle ~= 0 then
  4433. self.rotateInput = self.rotateInput + Vector2.new(math.rad(angle * delta), 0)
  4434. end
  4435. end
  4436. end
  4437.  
  4438. local cameraHeight = self:GetCameraHeight()
  4439.  
  4440. -- Reset tween speed if user is panning
  4441. if self.userPanningTheCamera then
  4442. tweenSpeed = 0
  4443. self.lastUserPanCamera = tick()
  4444. end
  4445.  
  4446. local userRecentlyPannedCamera = now - self.lastUserPanCamera < TIME_BEFORE_AUTO_ROTATE
  4447. local subjectPosition = self:GetSubjectPosition()
  4448.  
  4449. if subjectPosition and player and camera then
  4450. local zoom = self:GetCameraToSubjectDistance()
  4451. if zoom < 0.5 then
  4452. zoom = 0.5
  4453. end
  4454.  
  4455. if self:GetIsMouseLocked() and not self:IsInFirstPerson() then
  4456. -- We need to use the right vector of the camera after rotation, not before
  4457. local newLookCFrame = self:CalculateNewLookCFrame(overrideCameraLookVector)
  4458.  
  4459. local offset = self:GetMouseLockOffset()
  4460. local cameraRelativeOffset = offset.X * newLookCFrame.rightVector + offset.Y * newLookCFrame.upVector + offset.Z * newLookCFrame.lookVector
  4461.  
  4462. --offset can be NAN, NAN, NAN if newLookVector has only y component
  4463. if Util.IsFiniteVector3(cameraRelativeOffset) then
  4464. subjectPosition = subjectPosition + cameraRelativeOffset
  4465. end
  4466. else
  4467. if not self.userPanningTheCamera and self.lastCameraTransform then
  4468.  
  4469. local isInFirstPerson = self:IsInFirstPerson()
  4470.  
  4471. if (isInVehicle or isOnASkateboard or (self.isFollowCamera and isClimbing)) and self.lastUpdate and humanoid and humanoid.Torso then
  4472. if isInFirstPerson then
  4473. if self.lastSubjectCFrame and (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
  4474. local y = -Util.GetAngleBetweenXZVectors(self.lastSubjectCFrame.lookVector, cameraSubject.CFrame.lookVector)
  4475. if Util.IsFinite(y) then
  4476. self.rotateInput = self.rotateInput + Vector2.new(y, 0)
  4477. end
  4478. tweenSpeed = 0
  4479. end
  4480. elseif not userRecentlyPannedCamera then
  4481. local forwardVector = humanoid.Torso.CFrame.lookVector
  4482. if isOnASkateboard then
  4483. forwardVector = cameraSubject.CFrame.lookVector
  4484. end
  4485.  
  4486. tweenSpeed = math.clamp(tweenSpeed + tweenAcceleration * timeDelta, 0, tweenMaxSpeed)
  4487.  
  4488. local percent = math.clamp(tweenSpeed * timeDelta, 0, 1)
  4489. if self:IsInFirstPerson() and not (self.isFollowCamera and self.isClimbing) then
  4490. percent = 1
  4491. end
  4492.  
  4493. local y = Util.GetAngleBetweenXZVectors(forwardVector, self:GetCameraLookVector())
  4494. if Util.IsFinite(y) and math.abs(y) > 0.0001 then
  4495. self.rotateInput = self.rotateInput + Vector2.new(y * percent, 0)
  4496. end
  4497. end
  4498.  
  4499. elseif self.isFollowCamera and (not (isInFirstPerson or userRecentlyPannedCamera) and not VRService.VREnabled) then
  4500. -- Logic that was unique to the old FollowCamera module
  4501. local lastVec = -(self.lastCameraTransform.p - subjectPosition)
  4502.  
  4503. local y = Util.GetAngleBetweenXZVectors(lastVec, self:GetCameraLookVector())
  4504.  
  4505. -- This cutoff is to decide if the humanoid's angle of movement,
  4506. -- relative to the camera's look vector, is enough that
  4507. -- we want the camera to be following them. The point is to provide
  4508. -- a sizable dead zone to allow more precise forward movements.
  4509. local thetaCutoff = 0.4
  4510.  
  4511. -- Check for NaNs
  4512. if Util.IsFinite(y) and math.abs(y) > 0.0001 and math.abs(y) > thetaCutoff * timeDelta then
  4513. self.rotateInput = self.rotateInput + Vector2.new(y, 0)
  4514. end
  4515. end
  4516. end
  4517. end
  4518.  
  4519. if not self.isFollowCamera then
  4520. local VREnabled = VRService.VREnabled
  4521.  
  4522. if VREnabled then
  4523. newCameraFocus = self:GetVRFocus(subjectPosition, timeDelta)
  4524. else
  4525. newCameraFocus = CFrame.new(subjectPosition)
  4526. end
  4527.  
  4528. local cameraFocusP = newCameraFocus.p
  4529. if VREnabled and not self:IsInFirstPerson() then
  4530. local vecToSubject = (subjectPosition - camera.CFrame.p)
  4531. local distToSubject = vecToSubject.magnitude
  4532.  
  4533. -- Only move the camera if it exceeded a maximum distance to the subject in VR
  4534. if distToSubject > zoom or self.rotateInput.x ~= 0 then
  4535. local desiredDist = math.min(distToSubject, zoom)
  4536. vecToSubject = self:CalculateNewLookVectorVR() * desiredDist
  4537. local newPos = cameraFocusP - vecToSubject
  4538. local desiredLookDir = camera.CFrame.lookVector
  4539. if self.rotateInput.x ~= 0 then
  4540. desiredLookDir = vecToSubject
  4541. end
  4542. local lookAt = Vector3.new(newPos.x + desiredLookDir.x, newPos.y, newPos.z + desiredLookDir.z)
  4543. self.rotateInput = ZERO_VECTOR2
  4544.  
  4545. newCameraCFrame = CFrame.new(newPos, lookAt) + Vector3.new(0, cameraHeight, 0)
  4546. end
  4547. else
  4548. local newLookVector = self:CalculateNewLookVector(overrideCameraLookVector)
  4549. self.rotateInput = ZERO_VECTOR2
  4550. newCameraCFrame = CFrame.new(cameraFocusP - (zoom * newLookVector), cameraFocusP)
  4551. end
  4552. else -- is FollowCamera
  4553. local newLookVector = self:CalculateNewLookVector(overrideCameraLookVector)
  4554. self.rotateInput = ZERO_VECTOR2
  4555.  
  4556. if VRService.VREnabled then
  4557. newCameraFocus = self:GetVRFocus(subjectPosition, timeDelta)
  4558. else
  4559. newCameraFocus = CFrame.new(subjectPosition)
  4560. end
  4561. newCameraCFrame = CFrame.new(newCameraFocus.p - (zoom * newLookVector), newCameraFocus.p) + Vector3.new(0, cameraHeight, 0)
  4562. end
  4563.  
  4564. if FFlagUserCameraToggle then
  4565. local toggleOffset = self:GetCameraToggleOffset(timeDelta)
  4566. newCameraFocus = newCameraFocus + toggleOffset
  4567. newCameraCFrame = newCameraCFrame + toggleOffset
  4568. end
  4569.  
  4570. self.lastCameraTransform = newCameraCFrame
  4571. self.lastCameraFocus = newCameraFocus
  4572. if (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then
  4573. self.lastSubjectCFrame = cameraSubject.CFrame
  4574. else
  4575. self.lastSubjectCFrame = nil
  4576. end
  4577. end
  4578.  
  4579. self.lastUpdate = now
  4580. return newCameraCFrame, newCameraFocus
  4581. end
  4582.  
  4583. function ClassicCamera:EnterFirstPerson()
  4584. self.inFirstPerson = true
  4585. self:UpdateMouseBehavior()
  4586. end
  4587.  
  4588. function ClassicCamera:LeaveFirstPerson()
  4589. self.inFirstPerson = false
  4590. self:UpdateMouseBehavior()
  4591. end
  4592.  
  4593. return ClassicCamera
  4594. end
  4595.  
  4596. function _CameraUtils()
  4597.  
  4598. local CameraUtils = {}
  4599.  
  4600. local FFlagUserCameraToggle do
  4601. local success, result = pcall(function()
  4602. return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  4603. end)
  4604. FFlagUserCameraToggle = success and result
  4605. end
  4606.  
  4607. local function round(num)
  4608. return math.floor(num + 0.5)
  4609. end
  4610.  
  4611. -- Critically damped spring class for fluid motion effects
  4612. local Spring = {} do
  4613. Spring.__index = Spring
  4614.  
  4615. -- Initialize to a given undamped frequency and default position
  4616. function Spring.new(freq, pos)
  4617. return setmetatable({
  4618. freq = freq,
  4619. goal = pos,
  4620. pos = pos,
  4621. vel = 0,
  4622. }, Spring)
  4623. end
  4624.  
  4625. -- Advance the spring simulation by `dt` seconds
  4626. function Spring:step(dt)
  4627. local f = self.freq*2*math.pi
  4628. local g = self.goal
  4629. local p0 = self.pos
  4630. local v0 = self.vel
  4631.  
  4632. local offset = p0 - g
  4633. local decay = math.exp(-f*dt)
  4634.  
  4635. local p1 = (offset*(1 + f*dt) + v0*dt)*decay + g
  4636. local v1 = (v0*(1 - f*dt) - offset*(f*f*dt))*decay
  4637.  
  4638. self.pos = p1
  4639. self.vel = v1
  4640.  
  4641. return p1
  4642. end
  4643. end
  4644.  
  4645. CameraUtils.Spring = Spring
  4646.  
  4647. -- map a value from one range to another
  4648. function CameraUtils.map(x, inMin, inMax, outMin, outMax)
  4649. return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
  4650. end
  4651.  
  4652. -- From TransparencyController
  4653. function CameraUtils.Round(num, places)
  4654. local decimalPivot = 10^places
  4655. return math.floor(num * decimalPivot + 0.5) / decimalPivot
  4656. end
  4657.  
  4658. function CameraUtils.IsFinite(val)
  4659. return val == val and val ~= math.huge and val ~= -math.huge
  4660. end
  4661.  
  4662. function CameraUtils.IsFiniteVector3(vec3)
  4663. return CameraUtils.IsFinite(vec3.X) and CameraUtils.IsFinite(vec3.Y) and CameraUtils.IsFinite(vec3.Z)
  4664. end
  4665.  
  4666. -- Legacy implementation renamed
  4667. function CameraUtils.GetAngleBetweenXZVectors(v1, v2)
  4668. return math.atan2(v2.X*v1.Z-v2.Z*v1.X, v2.X*v1.X+v2.Z*v1.Z)
  4669. end
  4670.  
  4671. function CameraUtils.RotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
  4672. if camLook.Magnitude > 0 then
  4673. camLook = camLook.unit
  4674. local currAngle = math.atan2(camLook.z, camLook.x)
  4675. local newAngle = round((math.atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
  4676. return newAngle - currAngle
  4677. end
  4678. return 0
  4679. end
  4680.  
  4681. -- K is a tunable parameter that changes the shape of the S-curve
  4682. -- the larger K is the more straight/linear the curve gets
  4683. local k = 0.35
  4684. local lowerK = 0.8
  4685. local function SCurveTranform(t)
  4686. t = math.clamp(t, -1, 1)
  4687. if t >= 0 then
  4688. return (k*t) / (k - t + 1)
  4689. end
  4690. return -((lowerK*-t) / (lowerK + t + 1))
  4691. end
  4692.  
  4693. local DEADZONE = 0.1
  4694. local function toSCurveSpace(t)
  4695. return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
  4696. end
  4697.  
  4698. local function fromSCurveSpace(t)
  4699. return t/2 + 0.5
  4700. end
  4701.  
  4702. function CameraUtils.GamepadLinearToCurve(thumbstickPosition)
  4703. local function onAxis(axisValue)
  4704. local sign = 1
  4705. if axisValue < 0 then
  4706. sign = -1
  4707. end
  4708. local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math.abs(axisValue))))
  4709. point = point * sign
  4710. return math.clamp(point, -1, 1)
  4711. end
  4712. return Vector2.new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
  4713. end
  4714.  
  4715. -- This function converts 4 different, redundant enumeration types to one standard so the values can be compared
  4716. function CameraUtils.ConvertCameraModeEnumToStandard(enumValue)
  4717. if enumValue == Enum.TouchCameraMovementMode.Default then
  4718. return Enum.ComputerCameraMovementMode.Follow
  4719. end
  4720.  
  4721. if enumValue == Enum.ComputerCameraMovementMode.Default then
  4722. return Enum.ComputerCameraMovementMode.Classic
  4723. end
  4724.  
  4725. if enumValue == Enum.TouchCameraMovementMode.Classic or
  4726. enumValue == Enum.DevTouchCameraMovementMode.Classic or
  4727. enumValue == Enum.DevComputerCameraMovementMode.Classic or
  4728. enumValue == Enum.ComputerCameraMovementMode.Classic then
  4729. return Enum.ComputerCameraMovementMode.Classic
  4730. end
  4731.  
  4732. if enumValue == Enum.TouchCameraMovementMode.Follow or
  4733. enumValue == Enum.DevTouchCameraMovementMode.Follow or
  4734. enumValue == Enum.DevComputerCameraMovementMode.Follow or
  4735. enumValue == Enum.ComputerCameraMovementMode.Follow then
  4736. return Enum.ComputerCameraMovementMode.Follow
  4737. end
  4738.  
  4739. if enumValue == Enum.TouchCameraMovementMode.Orbital or
  4740. enumValue == Enum.DevTouchCameraMovementMode.Orbital or
  4741. enumValue == Enum.DevComputerCameraMovementMode.Orbital or
  4742. enumValue == Enum.ComputerCameraMovementMode.Orbital then
  4743. return Enum.ComputerCameraMovementMode.Orbital
  4744. end
  4745.  
  4746. if FFlagUserCameraToggle then
  4747. if enumValue == Enum.ComputerCameraMovementMode.CameraToggle or
  4748. enumValue == Enum.DevComputerCameraMovementMode.CameraToggle then
  4749. return Enum.ComputerCameraMovementMode.CameraToggle
  4750. end
  4751. end
  4752.  
  4753. -- Note: Only the Dev versions of the Enums have UserChoice as an option
  4754. if enumValue == Enum.DevTouchCameraMovementMode.UserChoice or
  4755. enumValue == Enum.DevComputerCameraMovementMode.UserChoice then
  4756. return Enum.DevComputerCameraMovementMode.UserChoice
  4757. end
  4758.  
  4759. -- For any unmapped options return Classic camera
  4760. return Enum.ComputerCameraMovementMode.Classic
  4761. end
  4762.  
  4763. return CameraUtils
  4764. end
  4765.  
  4766. function _CameraModule()
  4767. local CameraModule = {}
  4768. CameraModule.__index = CameraModule
  4769.  
  4770. local FFlagUserCameraToggle do
  4771. local success, result = pcall(function()
  4772. return UserSettings():IsUserFeatureEnabled("UserCameraToggle")
  4773. end)
  4774. FFlagUserCameraToggle = success and result
  4775. end
  4776.  
  4777. local FFlagUserRemoveTheCameraApi do
  4778. local success, result = pcall(function()
  4779. return UserSettings():IsUserFeatureEnabled("UserRemoveTheCameraApi")
  4780. end)
  4781. FFlagUserRemoveTheCameraApi = success and result
  4782. end
  4783.  
  4784. -- NOTICE: Player property names do not all match their StarterPlayer equivalents,
  4785. -- with the differences noted in the comments on the right
  4786. local PLAYER_CAMERA_PROPERTIES =
  4787. {
  4788. "CameraMinZoomDistance",
  4789. "CameraMaxZoomDistance",
  4790. "CameraMode",
  4791. "DevCameraOcclusionMode",
  4792. "DevComputerCameraMode", -- Corresponds to StarterPlayer.DevComputerCameraMovementMode
  4793. "DevTouchCameraMode", -- Corresponds to StarterPlayer.DevTouchCameraMovementMode
  4794.  
  4795. -- Character movement mode
  4796. "DevComputerMovementMode",
  4797. "DevTouchMovementMode",
  4798. "DevEnableMouseLock", -- Corresponds to StarterPlayer.EnableMouseLockOption
  4799. }
  4800.  
  4801. local USER_GAME_SETTINGS_PROPERTIES =
  4802. {
  4803. "ComputerCameraMovementMode",
  4804. "ComputerMovementMode",
  4805. "ControlMode",
  4806. "GamepadCameraSensitivity",
  4807. "MouseSensitivity",
  4808. "RotationType",
  4809. "TouchCameraMovementMode",
  4810. "TouchMovementMode",
  4811. }
  4812.  
  4813. --[[ Roblox Services ]]--
  4814. local Players = game:GetService("Players")
  4815. local RunService = game:GetService("RunService")
  4816. local UserInputService = game:GetService("UserInputService")
  4817. local UserGameSettings = UserSettings():GetService("UserGameSettings")
  4818.  
  4819. -- Camera math utility library
  4820. local CameraUtils = _CameraUtils()
  4821.  
  4822. -- Load Roblox Camera Controller Modules
  4823. local ClassicCamera = _ClassicCamera()
  4824. local OrbitalCamera = _OrbitalCamera()
  4825. local LegacyCamera = _LegacyCamera()
  4826.  
  4827. -- Load Roblox Occlusion Modules
  4828. local Invisicam = _Invisicam()
  4829. local Poppercam = _Poppercam()
  4830.  
  4831. -- Load the near-field character transparency controller and the mouse lock "shift lock" controller
  4832. local TransparencyController = _TransparencyController()
  4833. local MouseLockController = _MouseLockController()
  4834.  
  4835. -- Table of camera controllers that have been instantiated. They are instantiated as they are used.
  4836. local instantiatedCameraControllers = {}
  4837. local instantiatedOcclusionModules = {}
  4838.  
  4839. -- Management of which options appear on the Roblox User Settings screen
  4840. do
  4841. local PlayerScripts = Players.LocalPlayer:WaitForChild("PlayerScripts")
  4842.  
  4843. PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Default)
  4844. PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Follow)
  4845. PlayerScripts:RegisterTouchCameraMovementMode(Enum.TouchCameraMovementMode.Classic)
  4846.  
  4847. PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Default)
  4848. PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Follow)
  4849. PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.Classic)
  4850. if FFlagUserCameraToggle then
  4851. PlayerScripts:RegisterComputerCameraMovementMode(Enum.ComputerCameraMovementMode.CameraToggle)
  4852. end
  4853. end
  4854.  
  4855. CameraModule.FFlagUserCameraToggle = FFlagUserCameraToggle
  4856.  
  4857.  
  4858. function CameraModule.new()
  4859. local self = setmetatable({},CameraModule)
  4860.  
  4861. -- Current active controller instances
  4862. self.activeCameraController = nil
  4863. self.activeOcclusionModule = nil
  4864. self.activeTransparencyController = nil
  4865. self.activeMouseLockController = nil
  4866.  
  4867. self.currentComputerCameraMovementMode = nil
  4868.  
  4869. -- Connections to events
  4870. self.cameraSubjectChangedConn = nil
  4871. self.cameraTypeChangedConn = nil
  4872.  
  4873. -- Adds CharacterAdded and CharacterRemoving event handlers for all current players
  4874. for _,player in pairs(Players:GetPlayers()) do
  4875. self:OnPlayerAdded(player)
  4876. end
  4877.  
  4878. -- Adds CharacterAdded and CharacterRemoving event handlers for all players who join in the future
  4879. Players.PlayerAdded:Connect(function(player)
  4880. self:OnPlayerAdded(player)
  4881. end)
  4882.  
  4883. self.activeTransparencyController = TransparencyController.new()
  4884. self.activeTransparencyController:Enable(true)
  4885.  
  4886. if not UserInputService.TouchEnabled then
  4887. self.activeMouseLockController = MouseLockController.new()
  4888. local toggleEvent = self.activeMouseLockController:GetBindableToggleEvent()
  4889. if toggleEvent then
  4890. toggleEvent:Connect(function()
  4891. self:OnMouseLockToggled()
  4892. end)
  4893. end
  4894. end
  4895.  
  4896. self:ActivateCameraController(self:GetCameraControlChoice())
  4897. self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
  4898. self:OnCurrentCameraChanged() -- Does initializations and makes first camera controller
  4899. RunService:BindToRenderStep("cameraRenderUpdate", Enum.RenderPriority.Camera.Value, function(dt) self:Update(dt) end)
  4900.  
  4901. -- Connect listeners to camera-related properties
  4902. for _, propertyName in pairs(PLAYER_CAMERA_PROPERTIES) do
  4903. Players.LocalPlayer:GetPropertyChangedSignal(propertyName):Connect(function()
  4904. self:OnLocalPlayerCameraPropertyChanged(propertyName)
  4905. end)
  4906. end
  4907.  
  4908. for _, propertyName in pairs(USER_GAME_SETTINGS_PROPERTIES) do
  4909. UserGameSettings:GetPropertyChangedSignal(propertyName):Connect(function()
  4910. self:OnUserGameSettingsPropertyChanged(propertyName)
  4911. end)
  4912. end
  4913. game.Workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(function()
  4914. self:OnCurrentCameraChanged()
  4915. end)
  4916.  
  4917. self.lastInputType = UserInputService:GetLastInputType()
  4918. UserInputService.LastInputTypeChanged:Connect(function(newLastInputType)
  4919. self.lastInputType = newLastInputType
  4920. end)
  4921.  
  4922. return self
  4923. end
  4924.  
  4925. function CameraModule:GetCameraMovementModeFromSettings()
  4926. local cameraMode = Players.LocalPlayer.CameraMode
  4927.  
  4928. -- Lock First Person trumps all other settings and forces ClassicCamera
  4929. if cameraMode == Enum.CameraMode.LockFirstPerson then
  4930. return CameraUtils.ConvertCameraModeEnumToStandard(Enum.ComputerCameraMovementMode.Classic)
  4931. end
  4932.  
  4933. local devMode, userMode
  4934. if UserInputService.TouchEnabled then
  4935. devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevTouchCameraMode)
  4936. userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.TouchCameraMovementMode)
  4937. else
  4938. devMode = CameraUtils.ConvertCameraModeEnumToStandard(Players.LocalPlayer.DevComputerCameraMode)
  4939. userMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
  4940. end
  4941.  
  4942. if devMode == Enum.DevComputerCameraMovementMode.UserChoice then
  4943. -- Developer is allowing user choice, so user setting is respected
  4944. return userMode
  4945. end
  4946.  
  4947. return devMode
  4948. end
  4949.  
  4950. function CameraModule:ActivateOcclusionModule( occlusionMode )
  4951. local newModuleCreator
  4952. if occlusionMode == Enum.DevCameraOcclusionMode.Zoom then
  4953. newModuleCreator = Poppercam
  4954. elseif occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
  4955. newModuleCreator = Invisicam
  4956. else
  4957. warn("CameraScript ActivateOcclusionModule called with unsupported mode")
  4958. return
  4959. end
  4960.  
  4961. -- First check to see if there is actually a change. If the module being requested is already
  4962. -- the currently-active solution then just make sure it's enabled and exit early
  4963. if self.activeOcclusionModule and self.activeOcclusionModule:GetOcclusionMode() == occlusionMode then
  4964. if not self.activeOcclusionModule:GetEnabled() then
  4965. self.activeOcclusionModule:Enable(true)
  4966. end
  4967. return
  4968. end
  4969.  
  4970. -- Save a reference to the current active module (may be nil) so that we can disable it if
  4971. -- we are successful in activating its replacement
  4972. local prevOcclusionModule = self.activeOcclusionModule
  4973.  
  4974. -- If there is no active module, see if the one we need has already been instantiated
  4975. self.activeOcclusionModule = instantiatedOcclusionModules[newModuleCreator]
  4976.  
  4977. -- If the module was not already instantiated and selected above, instantiate it
  4978. if not self.activeOcclusionModule then
  4979. self.activeOcclusionModule = newModuleCreator.new()
  4980. if self.activeOcclusionModule then
  4981. instantiatedOcclusionModules[newModuleCreator] = self.activeOcclusionModule
  4982. end
  4983. end
  4984.  
  4985. -- If we were successful in either selecting or instantiating the module,
  4986. -- enable it if it's not already the currently-active enabled module
  4987. if self.activeOcclusionModule then
  4988. local newModuleOcclusionMode = self.activeOcclusionModule:GetOcclusionMode()
  4989. -- Sanity check that the module we selected or instantiated actually supports the desired occlusionMode
  4990. if newModuleOcclusionMode ~= occlusionMode then
  4991. warn("CameraScript ActivateOcclusionModule mismatch: ",self.activeOcclusionModule:GetOcclusionMode(),"~=",occlusionMode)
  4992. end
  4993.  
  4994. -- Deactivate current module if there is one
  4995. if prevOcclusionModule then
  4996. -- Sanity check that current module is not being replaced by itself (that should have been handled above)
  4997. if prevOcclusionModule ~= self.activeOcclusionModule then
  4998. prevOcclusionModule:Enable(false)
  4999. else
  5000. warn("CameraScript ActivateOcclusionModule failure to detect already running correct module")
  5001. end
  5002. end
  5003.  
  5004. -- Occlusion modules need to be initialized with information about characters and cameraSubject
  5005. -- Invisicam needs the LocalPlayer's character
  5006. -- Poppercam needs all player characters and the camera subject
  5007. if occlusionMode == Enum.DevCameraOcclusionMode.Invisicam then
  5008. -- Optimization to only send Invisicam what we know it needs
  5009. if Players.LocalPlayer.Character then
  5010. self.activeOcclusionModule:CharacterAdded(Players.LocalPlayer.Character, Players.LocalPlayer )
  5011. end
  5012. else
  5013. -- When Poppercam is enabled, we send it all existing player characters for its raycast ignore list
  5014. for _, player in pairs(Players:GetPlayers()) do
  5015. if player and player.Character then
  5016. self.activeOcclusionModule:CharacterAdded(player.Character, player)
  5017. end
  5018. end
  5019. self.activeOcclusionModule:OnCameraSubjectChanged(game.Workspace.CurrentCamera.CameraSubject)
  5020. end
  5021.  
  5022. -- Activate new choice
  5023. self.activeOcclusionModule:Enable(true)
  5024. end
  5025. end
  5026.  
  5027. -- When supplied, legacyCameraType is used and cameraMovementMode is ignored (should be nil anyways)
  5028. -- Next, if userCameraCreator is passed in, that is used as the cameraCreator
  5029. function CameraModule:ActivateCameraController(cameraMovementMode, legacyCameraType)
  5030. local newCameraCreator = nil
  5031.  
  5032. if legacyCameraType~=nil then
  5033. --[[
  5034. This function has been passed a CameraType enum value. Some of these map to the use of
  5035. the LegacyCamera module, the value "Custom" will be translated to a movementMode enum
  5036. value based on Dev and User settings, and "Scriptable" will disable the camera controller.
  5037. --]]
  5038.  
  5039. if legacyCameraType == Enum.CameraType.Scriptable then
  5040. if self.activeCameraController then
  5041. self.activeCameraController:Enable(false)
  5042. self.activeCameraController = nil
  5043. return
  5044. end
  5045. elseif legacyCameraType == Enum.CameraType.Custom then
  5046. cameraMovementMode = self:GetCameraMovementModeFromSettings()
  5047.  
  5048. elseif legacyCameraType == Enum.CameraType.Track then
  5049. -- Note: The TrackCamera module was basically an older, less fully-featured
  5050. -- version of ClassicCamera, no longer actively maintained, but it is re-implemented in
  5051. -- case a game was dependent on its lack of ClassicCamera's extra functionality.
  5052. cameraMovementMode = Enum.ComputerCameraMovementMode.Classic
  5053.  
  5054. elseif legacyCameraType == Enum.CameraType.Follow then
  5055. cameraMovementMode = Enum.ComputerCameraMovementMode.Follow
  5056.  
  5057. elseif legacyCameraType == Enum.CameraType.Orbital then
  5058. cameraMovementMode = Enum.ComputerCameraMovementMode.Orbital
  5059.  
  5060. elseif legacyCameraType == Enum.CameraType.Attach or
  5061. legacyCameraType == Enum.CameraType.Watch or
  5062. legacyCameraType == Enum.CameraType.Fixed then
  5063. newCameraCreator = LegacyCamera
  5064. else
  5065. warn("CameraScript encountered an unhandled Camera.CameraType value: ",legacyCameraType)
  5066. end
  5067. end
  5068.  
  5069. if not newCameraCreator then
  5070. if cameraMovementMode == Enum.ComputerCameraMovementMode.Classic or
  5071. cameraMovementMode == Enum.ComputerCameraMovementMode.Follow or
  5072. cameraMovementMode == Enum.ComputerCameraMovementMode.Default or
  5073. (FFlagUserCameraToggle and cameraMovementMode == Enum.ComputerCameraMovementMode.CameraToggle) then
  5074. newCameraCreator = ClassicCamera
  5075. elseif cameraMovementMode == Enum.ComputerCameraMovementMode.Orbital then
  5076. newCameraCreator = OrbitalCamera
  5077. else
  5078. warn("ActivateCameraController did not select a module.")
  5079. return
  5080. end
  5081. end
  5082.  
  5083. -- Create the camera control module we need if it does not already exist in instantiatedCameraControllers
  5084. local newCameraController
  5085. if not instantiatedCameraControllers[newCameraCreator] then
  5086. newCameraController = newCameraCreator.new()
  5087. instantiatedCameraControllers[newCameraCreator] = newCameraController
  5088. else
  5089. newCameraController = instantiatedCameraControllers[newCameraCreator]
  5090. end
  5091.  
  5092. -- If there is a controller active and it's not the one we need, disable it,
  5093. -- if it is the one we need, make sure it's enabled
  5094. if self.activeCameraController then
  5095. if self.activeCameraController ~= newCameraController then
  5096. self.activeCameraController:Enable(false)
  5097. self.activeCameraController = newCameraController
  5098. self.activeCameraController:Enable(true)
  5099. elseif not self.activeCameraController:GetEnabled() then
  5100. self.activeCameraController:Enable(true)
  5101. end
  5102. elseif newCameraController ~= nil then
  5103. self.activeCameraController = newCameraController
  5104. self.activeCameraController:Enable(true)
  5105. end
  5106.  
  5107. if self.activeCameraController then
  5108. if cameraMovementMode~=nil then
  5109. self.activeCameraController:SetCameraMovementMode(cameraMovementMode)
  5110. elseif legacyCameraType~=nil then
  5111. -- Note that this is only called when legacyCameraType is not a type that
  5112. -- was convertible to a ComputerCameraMovementMode value, i.e. really only applies to LegacyCamera
  5113. self.activeCameraController:SetCameraType(legacyCameraType)
  5114. end
  5115. end
  5116. end
  5117.  
  5118. -- Note: The active transparency controller could be made to listen for this event itself.
  5119. function CameraModule:OnCameraSubjectChanged()
  5120. if self.activeTransparencyController then
  5121. self.activeTransparencyController:SetSubject(game.Workspace.CurrentCamera.CameraSubject)
  5122. end
  5123.  
  5124. if self.activeOcclusionModule then
  5125. self.activeOcclusionModule:OnCameraSubjectChanged(game.Workspace.CurrentCamera.CameraSubject)
  5126. end
  5127. end
  5128.  
  5129. function CameraModule:OnCameraTypeChanged(newCameraType)
  5130. if newCameraType == Enum.CameraType.Scriptable then
  5131. if UserInputService.MouseBehavior == Enum.MouseBehavior.LockCenter then
  5132. UserInputService.MouseBehavior = Enum.MouseBehavior.Default
  5133. end
  5134. end
  5135.  
  5136. -- Forward the change to ActivateCameraController to handle
  5137. self:ActivateCameraController(nil, newCameraType)
  5138. end
  5139.  
  5140. -- Note: Called whenever workspace.CurrentCamera changes, but also on initialization of this script
  5141. function CameraModule:OnCurrentCameraChanged()
  5142. local currentCamera = game.Workspace.CurrentCamera
  5143. if not currentCamera then return end
  5144.  
  5145. if self.cameraSubjectChangedConn then
  5146. self.cameraSubjectChangedConn:Disconnect()
  5147. end
  5148.  
  5149. if self.cameraTypeChangedConn then
  5150. self.cameraTypeChangedConn:Disconnect()
  5151. end
  5152.  
  5153. self.cameraSubjectChangedConn = currentCamera:GetPropertyChangedSignal("CameraSubject"):Connect(function()
  5154. self:OnCameraSubjectChanged(currentCamera.CameraSubject)
  5155. end)
  5156.  
  5157. self.cameraTypeChangedConn = currentCamera:GetPropertyChangedSignal("CameraType"):Connect(function()
  5158. self:OnCameraTypeChanged(currentCamera.CameraType)
  5159. end)
  5160.  
  5161. self:OnCameraSubjectChanged(currentCamera.CameraSubject)
  5162. self:OnCameraTypeChanged(currentCamera.CameraType)
  5163. end
  5164.  
  5165. function CameraModule:OnLocalPlayerCameraPropertyChanged(propertyName)
  5166. if propertyName == "CameraMode" then
  5167. -- CameraMode is only used to turn on/off forcing the player into first person view. The
  5168. -- Note: The case "Classic" is used for all other views and does not correspond only to the ClassicCamera module
  5169. if Players.LocalPlayer.CameraMode == Enum.CameraMode.LockFirstPerson then
  5170. -- Locked in first person, use ClassicCamera which supports this
  5171. if not self.activeCameraController or self.activeCameraController:GetModuleName() ~= "ClassicCamera" then
  5172. self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(Enum.DevComputerCameraMovementMode.Classic))
  5173. end
  5174.  
  5175. if self.activeCameraController then
  5176. self.activeCameraController:UpdateForDistancePropertyChange()
  5177. end
  5178. elseif Players.LocalPlayer.CameraMode == Enum.CameraMode.Classic then
  5179. -- Not locked in first person view
  5180. local cameraMovementMode =self: GetCameraMovementModeFromSettings()
  5181. self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
  5182. else
  5183. warn("Unhandled value for property player.CameraMode: ",Players.LocalPlayer.CameraMode)
  5184. end
  5185.  
  5186. elseif propertyName == "DevComputerCameraMode" or
  5187. propertyName == "DevTouchCameraMode" then
  5188. local cameraMovementMode = self:GetCameraMovementModeFromSettings()
  5189. self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
  5190.  
  5191. elseif propertyName == "DevCameraOcclusionMode" then
  5192. self:ActivateOcclusionModule(Players.LocalPlayer.DevCameraOcclusionMode)
  5193.  
  5194. elseif propertyName == "CameraMinZoomDistance" or propertyName == "CameraMaxZoomDistance" then
  5195. if self.activeCameraController then
  5196. self.activeCameraController:UpdateForDistancePropertyChange()
  5197. end
  5198. elseif propertyName == "DevTouchMovementMode" then
  5199. elseif propertyName == "DevComputerMovementMode" then
  5200. elseif propertyName == "DevEnableMouseLock" then
  5201. -- This is the enabling/disabling of "Shift Lock" mode, not LockFirstPerson (which is a CameraMode)
  5202. -- Note: Enabling and disabling of MouseLock mode is normally only a publish-time choice made via
  5203. -- the corresponding EnableMouseLockOption checkbox of StarterPlayer, and this script does not have
  5204. -- support for changing the availability of MouseLock at runtime (this would require listening to
  5205. -- Player.DevEnableMouseLock changes)
  5206. end
  5207. end
  5208.  
  5209. function CameraModule:OnUserGameSettingsPropertyChanged(propertyName)
  5210. if propertyName == "ComputerCameraMovementMode" then
  5211. local cameraMovementMode = self:GetCameraMovementModeFromSettings()
  5212. self:ActivateCameraController(CameraUtils.ConvertCameraModeEnumToStandard(cameraMovementMode))
  5213. end
  5214. end
  5215.  
  5216. --[[
  5217. Main RenderStep Update. The camera controller and occlusion module both have opportunities
  5218. to set and modify (respectively) the CFrame and Focus before it is set once on CurrentCamera.
  5219. The camera and occlusion modules should only return CFrames, not set the CFrame property of
  5220. CurrentCamera directly.
  5221. --]]
  5222. function CameraModule:Update(dt)
  5223. if self.activeCameraController then
  5224. if FFlagUserCameraToggle then
  5225. self.activeCameraController:UpdateMouseBehavior()
  5226. end
  5227.  
  5228. local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
  5229. self.activeCameraController:ApplyVRTransform()
  5230. if self.activeOcclusionModule then
  5231. newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
  5232. end
  5233.  
  5234. -- Here is where the new CFrame and Focus are set for this render frame
  5235. game.Workspace.CurrentCamera.CFrame = newCameraCFrame
  5236. game.Workspace.CurrentCamera.Focus = newCameraFocus
  5237.  
  5238. -- Update to character local transparency as needed based on camera-to-subject distance
  5239. if self.activeTransparencyController then
  5240. self.activeTransparencyController:Update()
  5241. end
  5242. end
  5243. end
  5244.  
  5245. -- Formerly getCurrentCameraMode, this function resolves developer and user camera control settings to
  5246. -- decide which camera control module should be instantiated. The old method of converting redundant enum types
  5247. function CameraModule:GetCameraControlChoice()
  5248. local player = Players.LocalPlayer
  5249.  
  5250. if player then
  5251. if self.lastInputType == Enum.UserInputType.Touch or UserInputService.TouchEnabled then
  5252. -- Touch
  5253. if player.DevTouchCameraMode == Enum.DevTouchCameraMovementMode.UserChoice then
  5254. return CameraUtils.ConvertCameraModeEnumToStandard( UserGameSettings.TouchCameraMovementMode )
  5255. else
  5256. return CameraUtils.ConvertCameraModeEnumToStandard( player.DevTouchCameraMode )
  5257. end
  5258. else
  5259. -- Computer
  5260. if player.DevComputerCameraMode == Enum.DevComputerCameraMovementMode.UserChoice then
  5261. local computerMovementMode = CameraUtils.ConvertCameraModeEnumToStandard(UserGameSettings.ComputerCameraMovementMode)
  5262. return CameraUtils.ConvertCameraModeEnumToStandard(computerMovementMode)
  5263. else
  5264. return CameraUtils.ConvertCameraModeEnumToStandard(player.DevComputerCameraMode)
  5265. end
  5266. end
  5267. end
  5268. end
  5269.  
  5270. function CameraModule:OnCharacterAdded(char, player)
  5271. if self.activeOcclusionModule then
  5272. self.activeOcclusionModule:CharacterAdded(char, player)
  5273. end
  5274. end
  5275.  
  5276. function CameraModule:OnCharacterRemoving(char, player)
  5277. if self.activeOcclusionModule then
  5278. self.activeOcclusionModule:CharacterRemoving(char, player)
  5279. end
  5280. end
  5281.  
  5282. function CameraModule:OnPlayerAdded(player)
  5283. player.CharacterAdded:Connect(function(char)
  5284. self:OnCharacterAdded(char, player)
  5285. end)
  5286. player.CharacterRemoving:Connect(function(char)
  5287. self:OnCharacterRemoving(char, player)
  5288. end)
  5289. end
  5290.  
  5291. function CameraModule:OnMouseLockToggled()
  5292. if self.activeMouseLockController then
  5293. local mouseLocked = self.activeMouseLockController:GetIsMouseLocked()
  5294. local mouseLockOffset = self.activeMouseLockController:GetMouseLockOffset()
  5295. if self.activeCameraController then
  5296. self.activeCameraController:SetIsMouseLocked(mouseLocked)
  5297. self.activeCameraController:SetMouseLockOffset(mouseLockOffset)
  5298. end
  5299. end
  5300. end
  5301. --begin edit
  5302. local Camera = CameraModule
  5303. local IDENTITYCF = CFrame.new()
  5304. local lastUpCFrame = IDENTITYCF
  5305.  
  5306. Camera.UpVector = Vector3.new(0, 1, 0)
  5307. Camera.TransitionRate = 0.15
  5308. Camera.UpCFrame = IDENTITYCF
  5309.  
  5310. function Camera:GetUpVector(oldUpVector)
  5311. return oldUpVector
  5312. end
  5313. local function getRotationBetween(u, v, axis)
  5314. local dot, uxv = u:Dot(v), u:Cross(v)
  5315. if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
  5316. return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
  5317. end
  5318. function Camera:CalculateUpCFrame()
  5319. local oldUpVector = self.UpVector
  5320. local newUpVector = self:GetUpVector(oldUpVector)
  5321.  
  5322. local backup = game.Workspace.CurrentCamera.CFrame.RightVector
  5323. local transitionCF = getRotationBetween(oldUpVector, newUpVector, backup)
  5324. local vecSlerpCF = IDENTITYCF:Lerp(transitionCF, self.TransitionRate)
  5325.  
  5326. self.UpVector = vecSlerpCF * oldUpVector
  5327. self.UpCFrame = vecSlerpCF * self.UpCFrame
  5328.  
  5329. lastUpCFrame = self.UpCFrame
  5330. end
  5331.  
  5332. function Camera:Update(dt)
  5333. if self.activeCameraController then
  5334. if Camera.FFlagUserCameraToggle then
  5335. self.activeCameraController:UpdateMouseBehavior()
  5336. end
  5337.  
  5338. local newCameraCFrame, newCameraFocus = self.activeCameraController:Update(dt)
  5339. self.activeCameraController:ApplyVRTransform()
  5340.  
  5341. self:CalculateUpCFrame()
  5342. self.activeCameraController:UpdateUpCFrame(self.UpCFrame)
  5343.  
  5344. -- undo shift-lock offset
  5345.  
  5346. local lockOffset = Vector3.new(0, 0, 0)
  5347. if (self.activeMouseLockController and self.activeMouseLockController:GetIsMouseLocked()) then
  5348. lockOffset = self.activeMouseLockController:GetMouseLockOffset()
  5349. end
  5350.  
  5351. local offset = newCameraFocus:ToObjectSpace(newCameraCFrame)
  5352. local camRotation = self.UpCFrame * offset
  5353. newCameraFocus = newCameraFocus - newCameraCFrame:VectorToWorldSpace(lockOffset) + camRotation:VectorToWorldSpace(lockOffset)
  5354. newCameraCFrame = newCameraFocus * camRotation
  5355.  
  5356. --local offset = newCameraFocus:Inverse() * newCameraCFrame
  5357. --newCameraCFrame = newCameraFocus * self.UpCFrame * offset
  5358.  
  5359. if (self.activeCameraController.lastCameraTransform) then
  5360. self.activeCameraController.lastCameraTransform = newCameraCFrame
  5361. self.activeCameraController.lastCameraFocus = newCameraFocus
  5362. end
  5363.  
  5364. if self.activeOcclusionModule then
  5365. newCameraCFrame, newCameraFocus = self.activeOcclusionModule:Update(dt, newCameraCFrame, newCameraFocus)
  5366. end
  5367.  
  5368. game.Workspace.CurrentCamera.CFrame = newCameraCFrame
  5369. game.Workspace.CurrentCamera.Focus = newCameraFocus
  5370.  
  5371. if self.activeTransparencyController then
  5372. self.activeTransparencyController:Update()
  5373. end
  5374. end
  5375. end
  5376.  
  5377. function Camera:IsFirstPerson()
  5378. if self.activeCameraController then
  5379. return self.activeCameraController:InFirstPerson()
  5380. end
  5381. return false
  5382. end
  5383.  
  5384. function Camera:IsMouseLocked()
  5385. if self.activeCameraController then
  5386. return self.activeCameraController:GetIsMouseLocked()
  5387. end
  5388. return false
  5389. end
  5390. function Camera:IsToggleMode()
  5391. if self.activeCameraController then
  5392. return self.activeCameraController.isCameraToggle
  5393. end
  5394. return false
  5395. end
  5396. function Camera:IsCamRelative()
  5397. return self:IsMouseLocked() or self:IsFirstPerson()
  5398. --return self:IsToggleMode(), self:IsMouseLocked(), self:IsFirstPerson()
  5399. end
  5400. --
  5401. local Utils = _CameraUtils()
  5402. function Utils.GetAngleBetweenXZVectors(v1, v2)
  5403. local upCFrame = lastUpCFrame
  5404. v1 = upCFrame:VectorToObjectSpace(v1)
  5405. v2 = upCFrame:VectorToObjectSpace(v2)
  5406. return math.atan2(v2.X*v1.Z-v2.Z*v1.X, v2.X*v1.X+v2.Z*v1.Z)
  5407. end
  5408. --end edit
  5409. local cameraModuleObject = CameraModule.new()
  5410. local cameraApi = {}
  5411. return cameraModuleObject
  5412. end
  5413.  
  5414. function _ClickToMoveDisplay()
  5415. local ClickToMoveDisplay = {}
  5416.  
  5417. local FAILURE_ANIMATION_ID = "rbxassetid://2874840706"
  5418.  
  5419. local TrailDotIcon = "rbxasset://textures/ui/traildot.png"
  5420. local EndWaypointIcon = "rbxasset://textures/ui/waypoint.png"
  5421.  
  5422. local WaypointsAlwaysOnTop = false
  5423.  
  5424. local WAYPOINT_INCLUDE_FACTOR = 2
  5425. local LAST_DOT_DISTANCE = 3
  5426.  
  5427. local WAYPOINT_BILLBOARD_SIZE = UDim2.new(0, 1.68 * 25, 0, 2 * 25)
  5428.  
  5429. local ENDWAYPOINT_SIZE_OFFSET_MIN = Vector2.new(0, 0.5)
  5430. local ENDWAYPOINT_SIZE_OFFSET_MAX = Vector2.new(0, 1)
  5431.  
  5432. local FAIL_WAYPOINT_SIZE_OFFSET_CENTER = Vector2.new(0, 0.5)
  5433. local FAIL_WAYPOINT_SIZE_OFFSET_LEFT = Vector2.new(0.1, 0.5)
  5434. local FAIL_WAYPOINT_SIZE_OFFSET_RIGHT = Vector2.new(-0.1, 0.5)
  5435.  
  5436. local FAILURE_TWEEN_LENGTH = 0.125
  5437. local FAILURE_TWEEN_COUNT = 4
  5438.  
  5439. local TWEEN_WAYPOINT_THRESHOLD = 5
  5440.  
  5441. local TRAIL_DOT_PARENT_NAME = "ClickToMoveDisplay"
  5442.  
  5443. local TrailDotSize = Vector2.new(1.5, 1.5)
  5444.  
  5445. local TRAIL_DOT_MIN_SCALE = 1
  5446. local TRAIL_DOT_MIN_DISTANCE = 10
  5447. local TRAIL_DOT_MAX_SCALE = 2.5
  5448. local TRAIL_DOT_MAX_DISTANCE = 100
  5449.  
  5450. local PlayersService = game:GetService("Players")
  5451. local TweenService = game:GetService("TweenService")
  5452. local RunService = game:GetService("RunService")
  5453. local Workspace = game:GetService("Workspace")
  5454.  
  5455. local LocalPlayer = PlayersService.LocalPlayer
  5456.  
  5457. local function CreateWaypointTemplates()
  5458. local TrailDotTemplate = Instance.new("Part")
  5459. TrailDotTemplate.Size = Vector3.new(1, 1, 1)
  5460. TrailDotTemplate.Anchored = true
  5461. TrailDotTemplate.CanCollide = false
  5462. TrailDotTemplate.Name = "TrailDot"
  5463. TrailDotTemplate.Transparency = 1
  5464. local TrailDotImage = Instance.new("ImageHandleAdornment")
  5465. TrailDotImage.Name = "TrailDotImage"
  5466. TrailDotImage.Size = TrailDotSize
  5467. TrailDotImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
  5468. TrailDotImage.AlwaysOnTop = WaypointsAlwaysOnTop
  5469. TrailDotImage.Image = TrailDotIcon
  5470. TrailDotImage.Adornee = TrailDotTemplate
  5471. TrailDotImage.Parent = TrailDotTemplate
  5472.  
  5473. local EndWaypointTemplate = Instance.new("Part")
  5474. EndWaypointTemplate.Size = Vector3.new(2, 2, 2)
  5475. EndWaypointTemplate.Anchored = true
  5476. EndWaypointTemplate.CanCollide = false
  5477. EndWaypointTemplate.Name = "EndWaypoint"
  5478. EndWaypointTemplate.Transparency = 1
  5479. local EndWaypointImage = Instance.new("ImageHandleAdornment")
  5480. EndWaypointImage.Name = "TrailDotImage"
  5481. EndWaypointImage.Size = TrailDotSize
  5482. EndWaypointImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
  5483. EndWaypointImage.AlwaysOnTop = WaypointsAlwaysOnTop
  5484. EndWaypointImage.Image = TrailDotIcon
  5485. EndWaypointImage.Adornee = EndWaypointTemplate
  5486. EndWaypointImage.Parent = EndWaypointTemplate
  5487. local EndWaypointBillboard = Instance.new("BillboardGui")
  5488. EndWaypointBillboard.Name = "EndWaypointBillboard"
  5489. EndWaypointBillboard.Size = WAYPOINT_BILLBOARD_SIZE
  5490. EndWaypointBillboard.LightInfluence = 0
  5491. EndWaypointBillboard.SizeOffset = ENDWAYPOINT_SIZE_OFFSET_MIN
  5492. EndWaypointBillboard.AlwaysOnTop = true
  5493. EndWaypointBillboard.Adornee = EndWaypointTemplate
  5494. EndWaypointBillboard.Parent = EndWaypointTemplate
  5495. local EndWaypointImageLabel = Instance.new("ImageLabel")
  5496. EndWaypointImageLabel.Image = EndWaypointIcon
  5497. EndWaypointImageLabel.BackgroundTransparency = 1
  5498. EndWaypointImageLabel.Size = UDim2.new(1, 0, 1, 0)
  5499. EndWaypointImageLabel.Parent = EndWaypointBillboard
  5500.  
  5501.  
  5502. local FailureWaypointTemplate = Instance.new("Part")
  5503. FailureWaypointTemplate.Size = Vector3.new(2, 2, 2)
  5504. FailureWaypointTemplate.Anchored = true
  5505. FailureWaypointTemplate.CanCollide = false
  5506. FailureWaypointTemplate.Name = "FailureWaypoint"
  5507. FailureWaypointTemplate.Transparency = 1
  5508. local FailureWaypointImage = Instance.new("ImageHandleAdornment")
  5509. FailureWaypointImage.Name = "TrailDotImage"
  5510. FailureWaypointImage.Size = TrailDotSize
  5511. FailureWaypointImage.SizeRelativeOffset = Vector3.new(0, 0, -0.1)
  5512. FailureWaypointImage.AlwaysOnTop = WaypointsAlwaysOnTop
  5513. FailureWaypointImage.Image = TrailDotIcon
  5514. FailureWaypointImage.Adornee = FailureWaypointTemplate
  5515. FailureWaypointImage.Parent = FailureWaypointTemplate
  5516. local FailureWaypointBillboard = Instance.new("BillboardGui")
  5517. FailureWaypointBillboard.Name = "FailureWaypointBillboard"
  5518. FailureWaypointBillboard.Size = WAYPOINT_BILLBOARD_SIZE
  5519. FailureWaypointBillboard.LightInfluence = 0
  5520. FailureWaypointBillboard.SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_CENTER
  5521. FailureWaypointBillboard.AlwaysOnTop = true
  5522. FailureWaypointBillboard.Adornee = FailureWaypointTemplate
  5523. FailureWaypointBillboard.Parent = FailureWaypointTemplate
  5524. local FailureWaypointFrame = Instance.new("Frame")
  5525. FailureWaypointFrame.BackgroundTransparency = 1
  5526. FailureWaypointFrame.Size = UDim2.new(0, 0, 0, 0)
  5527. FailureWaypointFrame.Position = UDim2.new(0.5, 0, 1, 0)
  5528. FailureWaypointFrame.Parent = FailureWaypointBillboard
  5529. local FailureWaypointImageLabel = Instance.new("ImageLabel")
  5530. FailureWaypointImageLabel.Image = EndWaypointIcon
  5531. FailureWaypointImageLabel.BackgroundTransparency = 1
  5532. FailureWaypointImageLabel.Position = UDim2.new(
  5533. 0, -WAYPOINT_BILLBOARD_SIZE.X.Offset/2, 0, -WAYPOINT_BILLBOARD_SIZE.Y.Offset
  5534. )
  5535. FailureWaypointImageLabel.Size = WAYPOINT_BILLBOARD_SIZE
  5536. FailureWaypointImageLabel.Parent = FailureWaypointFrame
  5537.  
  5538. return TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate
  5539. end
  5540.  
  5541. local TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5542.  
  5543. local function getTrailDotParent()
  5544. local camera = Workspace.CurrentCamera
  5545. local trailParent = camera:FindFirstChild(TRAIL_DOT_PARENT_NAME)
  5546. if not trailParent then
  5547. trailParent = Instance.new("Model")
  5548. trailParent.Name = TRAIL_DOT_PARENT_NAME
  5549. trailParent.Parent = camera
  5550. end
  5551. return trailParent
  5552. end
  5553.  
  5554. local function placePathWaypoint(waypointModel, position)
  5555. local ray = Ray.new(position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
  5556. local hitPart, hitPoint, hitNormal = Workspace:FindPartOnRayWithIgnoreList(
  5557. ray,
  5558. { Workspace.CurrentCamera, LocalPlayer.Character }
  5559. )
  5560. if hitPart then
  5561. waypointModel.CFrame = CFrame.new(hitPoint, hitPoint + hitNormal)
  5562. waypointModel.Parent = getTrailDotParent()
  5563. end
  5564. end
  5565.  
  5566. local TrailDot = {}
  5567. TrailDot.__index = TrailDot
  5568.  
  5569. function TrailDot:Destroy()
  5570. self.DisplayModel:Destroy()
  5571. end
  5572.  
  5573. function TrailDot:NewDisplayModel(position)
  5574. local newDisplayModel = TrailDotTemplate:Clone()
  5575. placePathWaypoint(newDisplayModel, position)
  5576. return newDisplayModel
  5577. end
  5578.  
  5579. function TrailDot.new(position, closestWaypoint)
  5580. local self = setmetatable({}, TrailDot)
  5581.  
  5582. self.DisplayModel = self:NewDisplayModel(position)
  5583. self.ClosestWayPoint = closestWaypoint
  5584.  
  5585. return self
  5586. end
  5587.  
  5588. local EndWaypoint = {}
  5589. EndWaypoint.__index = EndWaypoint
  5590.  
  5591. function EndWaypoint:Destroy()
  5592. self.Destroyed = true
  5593. self.Tween:Cancel()
  5594. self.DisplayModel:Destroy()
  5595. end
  5596.  
  5597. function EndWaypoint:NewDisplayModel(position)
  5598. local newDisplayModel = EndWaypointTemplate:Clone()
  5599. placePathWaypoint(newDisplayModel, position)
  5600. return newDisplayModel
  5601. end
  5602.  
  5603. function EndWaypoint:CreateTween()
  5604. local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, -1, true)
  5605. local tween = TweenService:Create(
  5606. self.DisplayModel.EndWaypointBillboard,
  5607. tweenInfo,
  5608. { SizeOffset = ENDWAYPOINT_SIZE_OFFSET_MAX }
  5609. )
  5610. tween:Play()
  5611. return tween
  5612. end
  5613.  
  5614. function EndWaypoint:TweenInFrom(originalPosition)
  5615. local currentPositon = self.DisplayModel.Position
  5616. local studsOffset = originalPosition - currentPositon
  5617. self.DisplayModel.EndWaypointBillboard.StudsOffset = Vector3.new(0, studsOffset.Y, 0)
  5618. local tweenInfo = TweenInfo.new(1, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
  5619. local tween = TweenService:Create(
  5620. self.DisplayModel.EndWaypointBillboard,
  5621. tweenInfo,
  5622. { StudsOffset = Vector3.new(0, 0, 0) }
  5623. )
  5624. tween:Play()
  5625. return tween
  5626. end
  5627.  
  5628. function EndWaypoint.new(position, closestWaypoint, originalPosition)
  5629. local self = setmetatable({}, EndWaypoint)
  5630.  
  5631. self.DisplayModel = self:NewDisplayModel(position)
  5632. self.Destroyed = false
  5633. if originalPosition and (originalPosition - position).magnitude > TWEEN_WAYPOINT_THRESHOLD then
  5634. self.Tween = self:TweenInFrom(originalPosition)
  5635. coroutine.wrap(function()
  5636. self.Tween.Completed:Wait()
  5637. if not self.Destroyed then
  5638. self.Tween = self:CreateTween()
  5639. end
  5640. end)()
  5641. else
  5642. self.Tween = self:CreateTween()
  5643. end
  5644. self.ClosestWayPoint = closestWaypoint
  5645.  
  5646. return self
  5647. end
  5648.  
  5649. local FailureWaypoint = {}
  5650. FailureWaypoint.__index = FailureWaypoint
  5651.  
  5652. function FailureWaypoint:Hide()
  5653. self.DisplayModel.Parent = nil
  5654. end
  5655.  
  5656. function FailureWaypoint:Destroy()
  5657. self.DisplayModel:Destroy()
  5658. end
  5659.  
  5660. function FailureWaypoint:NewDisplayModel(position)
  5661. local newDisplayModel = FailureWaypointTemplate:Clone()
  5662. placePathWaypoint(newDisplayModel, position)
  5663. local ray = Ray.new(position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
  5664. local hitPart, hitPoint, hitNormal = Workspace:FindPartOnRayWithIgnoreList(
  5665. ray, { Workspace.CurrentCamera, LocalPlayer.Character }
  5666. )
  5667. if hitPart then
  5668. newDisplayModel.CFrame = CFrame.new(hitPoint, hitPoint + hitNormal)
  5669. newDisplayModel.Parent = getTrailDotParent()
  5670. end
  5671. return newDisplayModel
  5672. end
  5673.  
  5674. function FailureWaypoint:RunFailureTween()
  5675. wait(FAILURE_TWEEN_LENGTH) -- Delay one tween length betfore starting tweening
  5676. -- Tween out from center
  5677. local tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH/2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
  5678. local tweenLeft = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
  5679. { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_LEFT })
  5680. tweenLeft:Play()
  5681.  
  5682. local tweenLeftRoation = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
  5683. { Rotation = 10 })
  5684. tweenLeftRoation:Play()
  5685.  
  5686. tweenLeft.Completed:wait()
  5687.  
  5688. -- Tween back and forth
  5689. tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH, Enum.EasingStyle.Sine, Enum.EasingDirection.Out,
  5690. FAILURE_TWEEN_COUNT - 1, true)
  5691. local tweenSideToSide = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
  5692. { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_RIGHT})
  5693. tweenSideToSide:Play()
  5694.  
  5695. -- Tween flash dark and roate left and right
  5696. tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH, Enum.EasingStyle.Sine, Enum.EasingDirection.Out,
  5697. FAILURE_TWEEN_COUNT - 1, true)
  5698. local tweenFlash = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame.ImageLabel, tweenInfo,
  5699. { ImageColor3 = Color3.new(0.75, 0.75, 0.75)})
  5700. tweenFlash:Play()
  5701.  
  5702. local tweenRotate = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
  5703. { Rotation = -10 })
  5704. tweenRotate:Play()
  5705.  
  5706. tweenSideToSide.Completed:wait()
  5707.  
  5708. -- Tween back to center
  5709. tweenInfo = TweenInfo.new(FAILURE_TWEEN_LENGTH/2, Enum.EasingStyle.Sine, Enum.EasingDirection.Out)
  5710. local tweenCenter = TweenService:Create(self.DisplayModel.FailureWaypointBillboard, tweenInfo,
  5711. { SizeOffset = FAIL_WAYPOINT_SIZE_OFFSET_CENTER })
  5712. tweenCenter:Play()
  5713.  
  5714. local tweenRoation = TweenService:Create(self.DisplayModel.FailureWaypointBillboard.Frame, tweenInfo,
  5715. { Rotation = 0 })
  5716. tweenRoation:Play()
  5717.  
  5718. tweenCenter.Completed:wait()
  5719.  
  5720. wait(FAILURE_TWEEN_LENGTH) -- Delay one tween length betfore removing
  5721. end
  5722.  
  5723. function FailureWaypoint.new(position)
  5724. local self = setmetatable({}, FailureWaypoint)
  5725.  
  5726. self.DisplayModel = self:NewDisplayModel(position)
  5727.  
  5728. return self
  5729. end
  5730.  
  5731. local failureAnimation = Instance.new("Animation")
  5732. failureAnimation.AnimationId = FAILURE_ANIMATION_ID
  5733.  
  5734. local lastHumanoid = nil
  5735. local lastFailureAnimationTrack = nil
  5736.  
  5737. local function getFailureAnimationTrack(myHumanoid)
  5738. if myHumanoid == lastHumanoid then
  5739. return lastFailureAnimationTrack
  5740. end
  5741. lastFailureAnimationTrack = myHumanoid:LoadAnimation(failureAnimation)
  5742. lastFailureAnimationTrack.Priority = Enum.AnimationPriority.Action
  5743. lastFailureAnimationTrack.Looped = false
  5744. return lastFailureAnimationTrack
  5745. end
  5746.  
  5747. local function findPlayerHumanoid()
  5748. local character = LocalPlayer.Character
  5749. if character then
  5750. return character:FindFirstChildOfClass("Humanoid")
  5751. end
  5752. end
  5753.  
  5754. local function createTrailDots(wayPoints, originalEndWaypoint)
  5755. local newTrailDots = {}
  5756. local count = 1
  5757. for i = 1, #wayPoints - 1 do
  5758. local closeToEnd = (wayPoints[i].Position - wayPoints[#wayPoints].Position).magnitude < LAST_DOT_DISTANCE
  5759. local includeWaypoint = i % WAYPOINT_INCLUDE_FACTOR == 0 and not closeToEnd
  5760. if includeWaypoint then
  5761. local trailDot = TrailDot.new(wayPoints[i].Position, i)
  5762. newTrailDots[count] = trailDot
  5763. count = count + 1
  5764. end
  5765. end
  5766.  
  5767. local newEndWaypoint = EndWaypoint.new(wayPoints[#wayPoints].Position, #wayPoints, originalEndWaypoint)
  5768. table.insert(newTrailDots, newEndWaypoint)
  5769.  
  5770. local reversedTrailDots = {}
  5771. count = 1
  5772. for i = #newTrailDots, 1, -1 do
  5773. reversedTrailDots[count] = newTrailDots[i]
  5774. count = count + 1
  5775. end
  5776. return reversedTrailDots
  5777. end
  5778.  
  5779. local function getTrailDotScale(distanceToCamera, defaultSize)
  5780. local rangeLength = TRAIL_DOT_MAX_DISTANCE - TRAIL_DOT_MIN_DISTANCE
  5781. local inRangePoint = math.clamp(distanceToCamera - TRAIL_DOT_MIN_DISTANCE, 0, rangeLength)/rangeLength
  5782. local scale = TRAIL_DOT_MIN_SCALE + (TRAIL_DOT_MAX_SCALE - TRAIL_DOT_MIN_SCALE)*inRangePoint
  5783. return defaultSize * scale
  5784. end
  5785.  
  5786. local createPathCount = 0
  5787. -- originalEndWaypoint is optional, causes the waypoint to tween from that position.
  5788. function ClickToMoveDisplay.CreatePathDisplay(wayPoints, originalEndWaypoint)
  5789. createPathCount = createPathCount + 1
  5790. local trailDots = createTrailDots(wayPoints, originalEndWaypoint)
  5791.  
  5792. local function removePathBeforePoint(wayPointNumber)
  5793. -- kill all trailDots before and at wayPointNumber
  5794. for i = #trailDots, 1, -1 do
  5795. local trailDot = trailDots[i]
  5796. if trailDot.ClosestWayPoint <= wayPointNumber then
  5797. trailDot:Destroy()
  5798. trailDots[i] = nil
  5799. else
  5800. break
  5801. end
  5802. end
  5803. end
  5804.  
  5805. local reiszeTrailDotsUpdateName = "ClickToMoveResizeTrail" ..createPathCount
  5806. local function resizeTrailDots()
  5807. if #trailDots == 0 then
  5808. RunService:UnbindFromRenderStep(reiszeTrailDotsUpdateName)
  5809. return
  5810. end
  5811. local cameraPos = Workspace.CurrentCamera.CFrame.p
  5812. for i = 1, #trailDots do
  5813. local trailDotImage = trailDots[i].DisplayModel:FindFirstChild("TrailDotImage")
  5814. if trailDotImage then
  5815. local distanceToCamera = (trailDots[i].DisplayModel.Position - cameraPos).magnitude
  5816. trailDotImage.Size = getTrailDotScale(distanceToCamera, TrailDotSize)
  5817. end
  5818. end
  5819. end
  5820. RunService:BindToRenderStep(reiszeTrailDotsUpdateName, Enum.RenderPriority.Camera.Value - 1, resizeTrailDots)
  5821.  
  5822. local function removePath()
  5823. removePathBeforePoint(#wayPoints)
  5824. end
  5825.  
  5826. return removePath, removePathBeforePoint
  5827. end
  5828.  
  5829. local lastFailureWaypoint = nil
  5830. function ClickToMoveDisplay.DisplayFailureWaypoint(position)
  5831. if lastFailureWaypoint then
  5832. lastFailureWaypoint:Hide()
  5833. end
  5834. local failureWaypoint = FailureWaypoint.new(position)
  5835. lastFailureWaypoint = failureWaypoint
  5836. coroutine.wrap(function()
  5837. failureWaypoint:RunFailureTween()
  5838. failureWaypoint:Destroy()
  5839. failureWaypoint = nil
  5840. end)()
  5841. end
  5842.  
  5843. function ClickToMoveDisplay.CreateEndWaypoint(position)
  5844. return EndWaypoint.new(position)
  5845. end
  5846.  
  5847. function ClickToMoveDisplay.PlayFailureAnimation()
  5848. local myHumanoid = findPlayerHumanoid()
  5849. if myHumanoid then
  5850. local animationTrack = getFailureAnimationTrack(myHumanoid)
  5851. animationTrack:Play()
  5852. end
  5853. end
  5854.  
  5855. function ClickToMoveDisplay.CancelFailureAnimation()
  5856. if lastFailureAnimationTrack ~= nil and lastFailureAnimationTrack.IsPlaying then
  5857. lastFailureAnimationTrack:Stop()
  5858. end
  5859. end
  5860.  
  5861. function ClickToMoveDisplay.SetWaypointTexture(texture)
  5862. TrailDotIcon = texture
  5863. TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5864. end
  5865.  
  5866. function ClickToMoveDisplay.GetWaypointTexture()
  5867. return TrailDotIcon
  5868. end
  5869.  
  5870. function ClickToMoveDisplay.SetWaypointRadius(radius)
  5871. TrailDotSize = Vector2.new(radius, radius)
  5872. TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5873. end
  5874.  
  5875. function ClickToMoveDisplay.GetWaypointRadius()
  5876. return TrailDotSize.X
  5877. end
  5878.  
  5879. function ClickToMoveDisplay.SetEndWaypointTexture(texture)
  5880. EndWaypointIcon = texture
  5881. TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5882. end
  5883.  
  5884. function ClickToMoveDisplay.GetEndWaypointTexture()
  5885. return EndWaypointIcon
  5886. end
  5887.  
  5888. function ClickToMoveDisplay.SetWaypointsAlwaysOnTop(alwaysOnTop)
  5889. WaypointsAlwaysOnTop = alwaysOnTop
  5890. TrailDotTemplate, EndWaypointTemplate, FailureWaypointTemplate = CreateWaypointTemplates()
  5891. end
  5892.  
  5893. function ClickToMoveDisplay.GetWaypointsAlwaysOnTop()
  5894. return WaypointsAlwaysOnTop
  5895. end
  5896.  
  5897. return ClickToMoveDisplay
  5898. end
  5899.  
  5900. function _BaseCharacterController()
  5901.  
  5902. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  5903.  
  5904. --[[ The Module ]]--
  5905. local BaseCharacterController = {}
  5906. BaseCharacterController.__index = BaseCharacterController
  5907.  
  5908. function BaseCharacterController.new()
  5909. local self = setmetatable({}, BaseCharacterController)
  5910. self.enabled = false
  5911. self.moveVector = ZERO_VECTOR3
  5912. self.moveVectorIsCameraRelative = true
  5913. self.isJumping = false
  5914. return self
  5915. end
  5916.  
  5917. function BaseCharacterController:OnRenderStepped(dt)
  5918. -- By default, nothing to do
  5919. end
  5920.  
  5921. function BaseCharacterController:GetMoveVector()
  5922. return self.moveVector
  5923. end
  5924.  
  5925. function BaseCharacterController:IsMoveVectorCameraRelative()
  5926. return self.moveVectorIsCameraRelative
  5927. end
  5928.  
  5929. function BaseCharacterController:GetIsJumping()
  5930. return self.isJumping
  5931. end
  5932.  
  5933. -- Override in derived classes to set self.enabled and return boolean indicating
  5934. -- whether Enable/Disable was successful. Return true if controller is already in the requested state.
  5935. function BaseCharacterController:Enable(enable)
  5936. error("BaseCharacterController:Enable must be overridden in derived classes and should not be called.")
  5937. return false
  5938. end
  5939.  
  5940. return BaseCharacterController
  5941. end
  5942.  
  5943. function _VehicleController()
  5944. local ContextActionService = game:GetService("ContextActionService")
  5945.  
  5946. --[[ Constants ]]--
  5947. -- Set this to true if you want to instead use the triggers for the throttle
  5948. local useTriggersForThrottle = true
  5949. -- Also set this to true if you want the thumbstick to not affect throttle, only triggers when a gamepad is conected
  5950. local onlyTriggersForThrottle = false
  5951. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  5952.  
  5953. local AUTO_PILOT_DEFAULT_MAX_STEERING_ANGLE = 35
  5954.  
  5955.  
  5956. -- Note that VehicleController does not derive from BaseCharacterController, it is a special case
  5957. local VehicleController = {}
  5958. VehicleController.__index = VehicleController
  5959.  
  5960. function VehicleController.new(CONTROL_ACTION_PRIORITY)
  5961. local self = setmetatable({}, VehicleController)
  5962.  
  5963. self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
  5964.  
  5965. self.enabled = false
  5966. self.vehicleSeat = nil
  5967. self.throttle = 0
  5968. self.steer = 0
  5969.  
  5970. self.acceleration = 0
  5971. self.decceleration = 0
  5972. self.turningRight = 0
  5973. self.turningLeft = 0
  5974.  
  5975. self.vehicleMoveVector = ZERO_VECTOR3
  5976.  
  5977. self.autoPilot = {}
  5978. self.autoPilot.MaxSpeed = 0
  5979. self.autoPilot.MaxSteeringAngle = 0
  5980.  
  5981. return self
  5982. end
  5983.  
  5984. function VehicleController:BindContextActions()
  5985. if useTriggersForThrottle then
  5986. ContextActionService:BindActionAtPriority("throttleAccel", (function(actionName, inputState, inputObject)
  5987. self:OnThrottleAccel(actionName, inputState, inputObject)
  5988. return Enum.ContextActionResult.Pass
  5989. end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonR2)
  5990. ContextActionService:BindActionAtPriority("throttleDeccel", (function(actionName, inputState, inputObject)
  5991. self:OnThrottleDeccel(actionName, inputState, inputObject)
  5992. return Enum.ContextActionResult.Pass
  5993. end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonL2)
  5994. end
  5995. ContextActionService:BindActionAtPriority("arrowSteerRight", (function(actionName, inputState, inputObject)
  5996. self:OnSteerRight(actionName, inputState, inputObject)
  5997. return Enum.ContextActionResult.Pass
  5998. end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Right)
  5999. ContextActionService:BindActionAtPriority("arrowSteerLeft", (function(actionName, inputState, inputObject)
  6000. self:OnSteerLeft(actionName, inputState, inputObject)
  6001. return Enum.ContextActionResult.Pass
  6002. end), false, self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Left)
  6003. end
  6004.  
  6005. function VehicleController:Enable(enable, vehicleSeat)
  6006. if enable == self.enabled and vehicleSeat == self.vehicleSeat then
  6007. return
  6008. end
  6009.  
  6010. self.enabled = enable
  6011. self.vehicleMoveVector = ZERO_VECTOR3
  6012.  
  6013. if enable then
  6014. if vehicleSeat then
  6015. self.vehicleSeat = vehicleSeat
  6016.  
  6017. self:SetupAutoPilot()
  6018. self:BindContextActions()
  6019. end
  6020. else
  6021. if useTriggersForThrottle then
  6022. ContextActionService:UnbindAction("throttleAccel")
  6023. ContextActionService:UnbindAction("throttleDeccel")
  6024. end
  6025. ContextActionService:UnbindAction("arrowSteerRight")
  6026. ContextActionService:UnbindAction("arrowSteerLeft")
  6027. self.vehicleSeat = nil
  6028. end
  6029. end
  6030.  
  6031. function VehicleController:OnThrottleAccel(actionName, inputState, inputObject)
  6032. if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  6033. self.acceleration = 0
  6034. else
  6035. self.acceleration = -1
  6036. end
  6037. self.throttle = self.acceleration + self.decceleration
  6038. end
  6039.  
  6040. function VehicleController:OnThrottleDeccel(actionName, inputState, inputObject)
  6041. if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  6042. self.decceleration = 0
  6043. else
  6044. self.decceleration = 1
  6045. end
  6046. self.throttle = self.acceleration + self.decceleration
  6047. end
  6048.  
  6049. function VehicleController:OnSteerRight(actionName, inputState, inputObject)
  6050. if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  6051. self.turningRight = 0
  6052. else
  6053. self.turningRight = 1
  6054. end
  6055. self.steer = self.turningRight + self.turningLeft
  6056. end
  6057.  
  6058. function VehicleController:OnSteerLeft(actionName, inputState, inputObject)
  6059. if inputState == Enum.UserInputState.End or inputState == Enum.UserInputState.Cancel then
  6060. self.turningLeft = 0
  6061. else
  6062. self.turningLeft = -1
  6063. end
  6064. self.steer = self.turningRight + self.turningLeft
  6065. end
  6066.  
  6067. -- Call this from a function bound to Renderstep with Input Priority
  6068. function VehicleController:Update(moveVector, cameraRelative, usingGamepad)
  6069. if self.vehicleSeat then
  6070. if cameraRelative then
  6071. -- This is the default steering mode
  6072. moveVector = moveVector + Vector3.new(self.steer, 0, self.throttle)
  6073. if usingGamepad and onlyTriggersForThrottle and useTriggersForThrottle then
  6074. self.vehicleSeat.ThrottleFloat = -self.throttle
  6075. else
  6076. self.vehicleSeat.ThrottleFloat = -moveVector.Z
  6077. end
  6078. self.vehicleSeat.SteerFloat = moveVector.X
  6079.  
  6080. return moveVector, true
  6081. else
  6082. -- This is the path following mode
  6083. local localMoveVector = self.vehicleSeat.Occupant.RootPart.CFrame:VectorToObjectSpace(moveVector)
  6084.  
  6085. self.vehicleSeat.ThrottleFloat = self:ComputeThrottle(localMoveVector)
  6086. self.vehicleSeat.SteerFloat = self:ComputeSteer(localMoveVector)
  6087.  
  6088. return ZERO_VECTOR3, true
  6089. end
  6090. end
  6091. return moveVector, false
  6092. end
  6093.  
  6094. function VehicleController:ComputeThrottle(localMoveVector)
  6095. if localMoveVector ~= ZERO_VECTOR3 then
  6096. local throttle = -localMoveVector.Z
  6097. return throttle
  6098. else
  6099. return 0.0
  6100. end
  6101. end
  6102.  
  6103. function VehicleController:ComputeSteer(localMoveVector)
  6104. if localMoveVector ~= ZERO_VECTOR3 then
  6105. local steerAngle = -math.atan2(-localMoveVector.x, -localMoveVector.z) * (180 / math.pi)
  6106. return steerAngle / self.autoPilot.MaxSteeringAngle
  6107. else
  6108. return 0.0
  6109. end
  6110. end
  6111.  
  6112. function VehicleController:SetupAutoPilot()
  6113. -- Setup default
  6114. self.autoPilot.MaxSpeed = self.vehicleSeat.MaxSpeed
  6115. self.autoPilot.MaxSteeringAngle = AUTO_PILOT_DEFAULT_MAX_STEERING_ANGLE
  6116.  
  6117. -- VehicleSeat should have a MaxSteeringAngle as well.
  6118. -- Or we could look for a child "AutoPilotConfigModule" to find these values
  6119. -- Or allow developer to set them through the API as like the CLickToMove customization API
  6120. end
  6121.  
  6122. return VehicleController
  6123. end
  6124.  
  6125. function _TouchJump()
  6126.  
  6127. local Players = game:GetService("Players")
  6128. local GuiService = game:GetService("GuiService")
  6129.  
  6130. --[[ Constants ]]--
  6131. local TOUCH_CONTROL_SHEET = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
  6132.  
  6133. --[[ The Module ]]--
  6134. local BaseCharacterController = _BaseCharacterController()
  6135. local TouchJump = setmetatable({}, BaseCharacterController)
  6136. TouchJump.__index = TouchJump
  6137.  
  6138. function TouchJump.new()
  6139. local self = setmetatable(BaseCharacterController.new(), TouchJump)
  6140.  
  6141. self.parentUIFrame = nil
  6142. self.jumpButton = nil
  6143. self.characterAddedConn = nil
  6144. self.humanoidStateEnabledChangedConn = nil
  6145. self.humanoidJumpPowerConn = nil
  6146. self.humanoidParentConn = nil
  6147. self.externallyEnabled = false
  6148. self.jumpPower = 0
  6149. self.jumpStateEnabled = true
  6150. self.isJumping = false
  6151. self.humanoid = nil -- saved reference because property change connections are made using it
  6152.  
  6153. return self
  6154. end
  6155.  
  6156. function TouchJump:EnableButton(enable)
  6157. if enable then
  6158. if not self.jumpButton then
  6159. self:Create()
  6160. end
  6161. local humanoid = Players.LocalPlayer.Character and Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
  6162. if humanoid and self.externallyEnabled then
  6163. if self.externallyEnabled then
  6164. if humanoid.JumpPower > 0 then
  6165. self.jumpButton.Visible = true
  6166. end
  6167. end
  6168. end
  6169. else
  6170. self.jumpButton.Visible = false
  6171. self.isJumping = false
  6172. self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
  6173. end
  6174. end
  6175.  
  6176. function TouchJump:UpdateEnabled()
  6177. if self.jumpPower > 0 and self.jumpStateEnabled then
  6178. self:EnableButton(true)
  6179. else
  6180. self:EnableButton(false)
  6181. end
  6182. end
  6183.  
  6184. function TouchJump:HumanoidChanged(prop)
  6185. local humanoid = Players.LocalPlayer.Character and Players.LocalPlayer.Character:FindFirstChildOfClass("Humanoid")
  6186. if humanoid then
  6187. if prop == "JumpPower" then
  6188. self.jumpPower = humanoid.JumpPower
  6189. self:UpdateEnabled()
  6190. elseif prop == "Parent" then
  6191. if not humanoid.Parent then
  6192. self.humanoidChangeConn:Disconnect()
  6193. end
  6194. end
  6195. end
  6196. end
  6197.  
  6198. function TouchJump:HumanoidStateEnabledChanged(state, isEnabled)
  6199. if state == Enum.HumanoidStateType.Jumping then
  6200. self.jumpStateEnabled = isEnabled
  6201. self:UpdateEnabled()
  6202. end
  6203. end
  6204.  
  6205. function TouchJump:CharacterAdded(char)
  6206. if self.humanoidChangeConn then
  6207. self.humanoidChangeConn:Disconnect()
  6208. self.humanoidChangeConn = nil
  6209. end
  6210.  
  6211. self.humanoid = char:FindFirstChildOfClass("Humanoid")
  6212. while not self.humanoid do
  6213. char.ChildAdded:wait()
  6214. self.humanoid = char:FindFirstChildOfClass("Humanoid")
  6215. end
  6216.  
  6217. self.humanoidJumpPowerConn = self.humanoid:GetPropertyChangedSignal("JumpPower"):Connect(function()
  6218. self.jumpPower = self.humanoid.JumpPower
  6219. self:UpdateEnabled()
  6220. end)
  6221.  
  6222. self.humanoidParentConn = self.humanoid:GetPropertyChangedSignal("Parent"):Connect(function()
  6223. if not self.humanoid.Parent then
  6224. self.humanoidJumpPowerConn:Disconnect()
  6225. self.humanoidJumpPowerConn = nil
  6226. self.humanoidParentConn:Disconnect()
  6227. self.humanoidParentConn = nil
  6228. end
  6229. end)
  6230.  
  6231. self.humanoidStateEnabledChangedConn = self.humanoid.StateEnabledChanged:Connect(function(state, enabled)
  6232. self:HumanoidStateEnabledChanged(state, enabled)
  6233. end)
  6234.  
  6235. self.jumpPower = self.humanoid.JumpPower
  6236. self.jumpStateEnabled = self.humanoid:GetStateEnabled(Enum.HumanoidStateType.Jumping)
  6237. self:UpdateEnabled()
  6238. end
  6239.  
  6240. function TouchJump:SetupCharacterAddedFunction()
  6241. self.characterAddedConn = Players.LocalPlayer.CharacterAdded:Connect(function(char)
  6242. self:CharacterAdded(char)
  6243. end)
  6244. if Players.LocalPlayer.Character then
  6245. self:CharacterAdded(Players.LocalPlayer.Character)
  6246. end
  6247. end
  6248.  
  6249. function TouchJump:Enable(enable, parentFrame)
  6250. if parentFrame then
  6251. self.parentUIFrame = parentFrame
  6252. end
  6253. self.externallyEnabled = enable
  6254. self:EnableButton(enable)
  6255. end
  6256.  
  6257. function TouchJump:Create()
  6258. if not self.parentUIFrame then
  6259. return
  6260. end
  6261.  
  6262. if self.jumpButton then
  6263. self.jumpButton:Destroy()
  6264. self.jumpButton = nil
  6265. end
  6266.  
  6267. local minAxis = math.min(self.parentUIFrame.AbsoluteSize.x, self.parentUIFrame.AbsoluteSize.y)
  6268. local isSmallScreen = minAxis <= 500
  6269. local jumpButtonSize = isSmallScreen and 70 or 120
  6270.  
  6271. self.jumpButton = Instance.new("ImageButton")
  6272. self.jumpButton.Name = "JumpButton"
  6273. self.jumpButton.Visible = false
  6274. self.jumpButton.BackgroundTransparency = 1
  6275. self.jumpButton.Image = TOUCH_CONTROL_SHEET
  6276. self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
  6277. self.jumpButton.ImageRectSize = Vector2.new(144, 144)
  6278. self.jumpButton.Size = UDim2.new(0, jumpButtonSize, 0, jumpButtonSize)
  6279.  
  6280. self.jumpButton.Position = isSmallScreen and UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize - 20) or
  6281. UDim2.new(1, -(jumpButtonSize*1.5-10), 1, -jumpButtonSize * 1.75)
  6282.  
  6283. local touchObject = nil
  6284. self.jumpButton.InputBegan:connect(function(inputObject)
  6285. --A touch that starts elsewhere on the screen will be sent to a frame's InputBegan event
  6286. --if it moves over the frame. So we check that this is actually a new touch (inputObject.UserInputState ~= Enum.UserInputState.Begin)
  6287. if touchObject or inputObject.UserInputType ~= Enum.UserInputType.Touch
  6288. or inputObject.UserInputState ~= Enum.UserInputState.Begin then
  6289. return
  6290. end
  6291.  
  6292. touchObject = inputObject
  6293. self.jumpButton.ImageRectOffset = Vector2.new(146, 146)
  6294. self.isJumping = true
  6295. end)
  6296.  
  6297. local OnInputEnded = function()
  6298. touchObject = nil
  6299. self.isJumping = false
  6300. self.jumpButton.ImageRectOffset = Vector2.new(1, 146)
  6301. end
  6302.  
  6303. self.jumpButton.InputEnded:connect(function(inputObject)
  6304. if inputObject == touchObject then
  6305. OnInputEnded()
  6306. end
  6307. end)
  6308.  
  6309. GuiService.MenuOpened:connect(function()
  6310. if touchObject then
  6311. OnInputEnded()
  6312. end
  6313. end)
  6314.  
  6315. if not self.characterAddedConn then
  6316. self:SetupCharacterAddedFunction()
  6317. end
  6318.  
  6319. self.jumpButton.Parent = self.parentUIFrame
  6320. end
  6321.  
  6322. return TouchJump
  6323. end
  6324.  
  6325. function _ClickToMoveController()
  6326. --[[ Roblox Services ]]--
  6327. local UserInputService = game:GetService("UserInputService")
  6328. local PathfindingService = game:GetService("PathfindingService")
  6329. local Players = game:GetService("Players")
  6330. local DebrisService = game:GetService('Debris')
  6331. local StarterGui = game:GetService("StarterGui")
  6332. local Workspace = game:GetService("Workspace")
  6333. local CollectionService = game:GetService("CollectionService")
  6334. local GuiService = game:GetService("GuiService")
  6335.  
  6336. --[[ Configuration ]]
  6337. local ShowPath = true
  6338. local PlayFailureAnimation = true
  6339. local UseDirectPath = false
  6340. local UseDirectPathForVehicle = true
  6341. local AgentSizeIncreaseFactor = 1.0
  6342. local UnreachableWaypointTimeout = 8
  6343.  
  6344. --[[ Constants ]]--
  6345. local movementKeys = {
  6346. [Enum.KeyCode.W] = true;
  6347. [Enum.KeyCode.A] = true;
  6348. [Enum.KeyCode.S] = true;
  6349. [Enum.KeyCode.D] = true;
  6350. [Enum.KeyCode.Up] = true;
  6351. [Enum.KeyCode.Down] = true;
  6352. }
  6353.  
  6354. local FFlagUserNavigationClickToMoveSkipPassedWaypointsSuccess, FFlagUserNavigationClickToMoveSkipPassedWaypointsResult = pcall(function() return UserSettings():IsUserFeatureEnabled("UserNavigationClickToMoveSkipPassedWaypoints") end)
  6355. local FFlagUserNavigationClickToMoveSkipPassedWaypoints = FFlagUserNavigationClickToMoveSkipPassedWaypointsSuccess and FFlagUserNavigationClickToMoveSkipPassedWaypointsResult
  6356.  
  6357. local Player = Players.LocalPlayer
  6358.  
  6359. local ClickToMoveDisplay = _ClickToMoveDisplay()
  6360.  
  6361. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  6362. local ALMOST_ZERO = 0.000001
  6363.  
  6364.  
  6365. --------------------------UTIL LIBRARY-------------------------------
  6366. local Utility = {}
  6367. do
  6368. local function FindCharacterAncestor(part)
  6369. if part then
  6370. local humanoid = part:FindFirstChildOfClass("Humanoid")
  6371. if humanoid then
  6372. return part, humanoid
  6373. else
  6374. return FindCharacterAncestor(part.Parent)
  6375. end
  6376. end
  6377. end
  6378. Utility.FindCharacterAncestor = FindCharacterAncestor
  6379.  
  6380. local function Raycast(ray, ignoreNonCollidable, ignoreList)
  6381. ignoreList = ignoreList or {}
  6382. local hitPart, hitPos, hitNorm, hitMat = Workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
  6383. if hitPart then
  6384. if ignoreNonCollidable and hitPart.CanCollide == false then
  6385. -- We always include character parts so a user can click on another character
  6386. -- to walk to them.
  6387. local _, humanoid = FindCharacterAncestor(hitPart)
  6388. if humanoid == nil then
  6389. table.insert(ignoreList, hitPart)
  6390. return Raycast(ray, ignoreNonCollidable, ignoreList)
  6391. end
  6392. end
  6393. return hitPart, hitPos, hitNorm, hitMat
  6394. end
  6395. return nil, nil
  6396. end
  6397. Utility.Raycast = Raycast
  6398. end
  6399.  
  6400. local humanoidCache = {}
  6401. local function findPlayerHumanoid(player)
  6402. local character = player and player.Character
  6403. if character then
  6404. local resultHumanoid = humanoidCache[player]
  6405. if resultHumanoid and resultHumanoid.Parent == character then
  6406. return resultHumanoid
  6407. else
  6408. humanoidCache[player] = nil -- Bust Old Cache
  6409. local humanoid = character:FindFirstChildOfClass("Humanoid")
  6410. if humanoid then
  6411. humanoidCache[player] = humanoid
  6412. end
  6413. return humanoid
  6414. end
  6415. end
  6416. end
  6417.  
  6418. --------------------------CHARACTER CONTROL-------------------------------
  6419. local CurrentIgnoreList
  6420. local CurrentIgnoreTag = nil
  6421.  
  6422. local TaggedInstanceAddedConnection = nil
  6423. local TaggedInstanceRemovedConnection = nil
  6424.  
  6425. local function GetCharacter()
  6426. return Player and Player.Character
  6427. end
  6428.  
  6429. local function UpdateIgnoreTag(newIgnoreTag)
  6430. if newIgnoreTag == CurrentIgnoreTag then
  6431. return
  6432. end
  6433. if TaggedInstanceAddedConnection then
  6434. TaggedInstanceAddedConnection:Disconnect()
  6435. TaggedInstanceAddedConnection = nil
  6436. end
  6437. if TaggedInstanceRemovedConnection then
  6438. TaggedInstanceRemovedConnection:Disconnect()
  6439. TaggedInstanceRemovedConnection = nil
  6440. end
  6441. CurrentIgnoreTag = newIgnoreTag
  6442. CurrentIgnoreList = {GetCharacter()}
  6443. if CurrentIgnoreTag ~= nil then
  6444. local ignoreParts = CollectionService:GetTagged(CurrentIgnoreTag)
  6445. for _, ignorePart in ipairs(ignoreParts) do
  6446. table.insert(CurrentIgnoreList, ignorePart)
  6447. end
  6448. TaggedInstanceAddedConnection = CollectionService:GetInstanceAddedSignal(
  6449. CurrentIgnoreTag):Connect(function(ignorePart)
  6450. table.insert(CurrentIgnoreList, ignorePart)
  6451. end)
  6452. TaggedInstanceRemovedConnection = CollectionService:GetInstanceRemovedSignal(
  6453. CurrentIgnoreTag):Connect(function(ignorePart)
  6454. for i = 1, #CurrentIgnoreList do
  6455. if CurrentIgnoreList[i] == ignorePart then
  6456. CurrentIgnoreList[i] = CurrentIgnoreList[#CurrentIgnoreList]
  6457. table.remove(CurrentIgnoreList)
  6458. break
  6459. end
  6460. end
  6461. end)
  6462. end
  6463. end
  6464.  
  6465. local function getIgnoreList()
  6466. if CurrentIgnoreList then
  6467. return CurrentIgnoreList
  6468. end
  6469. CurrentIgnoreList = {}
  6470. table.insert(CurrentIgnoreList, GetCharacter())
  6471. return CurrentIgnoreList
  6472. end
  6473.  
  6474. -----------------------------------PATHER--------------------------------------
  6475.  
  6476. local function Pather(endPoint, surfaceNormal, overrideUseDirectPath)
  6477. local this = {}
  6478.  
  6479. local directPathForHumanoid
  6480. local directPathForVehicle
  6481. if overrideUseDirectPath ~= nil then
  6482. directPathForHumanoid = overrideUseDirectPath
  6483. directPathForVehicle = overrideUseDirectPath
  6484. else
  6485. directPathForHumanoid = UseDirectPath
  6486. directPathForVehicle = UseDirectPathForVehicle
  6487. end
  6488.  
  6489. this.Cancelled = false
  6490. this.Started = false
  6491.  
  6492. this.Finished = Instance.new("BindableEvent")
  6493. this.PathFailed = Instance.new("BindableEvent")
  6494.  
  6495. this.PathComputing = false
  6496. this.PathComputed = false
  6497.  
  6498. this.OriginalTargetPoint = endPoint
  6499. this.TargetPoint = endPoint
  6500. this.TargetSurfaceNormal = surfaceNormal
  6501.  
  6502. this.DiedConn = nil
  6503. this.SeatedConn = nil
  6504. this.BlockedConn = nil
  6505. this.TeleportedConn = nil
  6506.  
  6507. this.CurrentPoint = 0
  6508.  
  6509. this.HumanoidOffsetFromPath = ZERO_VECTOR3
  6510.  
  6511. this.CurrentWaypointPosition = nil
  6512. this.CurrentWaypointPlaneNormal = ZERO_VECTOR3
  6513. this.CurrentWaypointPlaneDistance = 0
  6514. this.CurrentWaypointNeedsJump = false;
  6515.  
  6516. this.CurrentHumanoidPosition = ZERO_VECTOR3
  6517. this.CurrentHumanoidVelocity = 0
  6518.  
  6519. this.NextActionMoveDirection = ZERO_VECTOR3
  6520. this.NextActionJump = false
  6521.  
  6522. this.Timeout = 0
  6523.  
  6524. this.Humanoid = findPlayerHumanoid(Player)
  6525. this.OriginPoint = nil
  6526. this.AgentCanFollowPath = false
  6527. this.DirectPath = false
  6528. this.DirectPathRiseFirst = false
  6529.  
  6530. local rootPart = this.Humanoid and this.Humanoid.RootPart
  6531. if rootPart then
  6532. -- Setup origin
  6533. this.OriginPoint = rootPart.CFrame.p
  6534.  
  6535. -- Setup agent
  6536. local agentRadius = 2
  6537. local agentHeight = 5
  6538. local agentCanJump = true
  6539.  
  6540. local seat = this.Humanoid.SeatPart
  6541. if seat and seat:IsA("VehicleSeat") then
  6542. -- Humanoid is seated on a vehicle
  6543. local vehicle = seat:FindFirstAncestorOfClass("Model")
  6544. if vehicle then
  6545. -- Make sure the PrimaryPart is set to the vehicle seat while we compute the extends.
  6546. local tempPrimaryPart = vehicle.PrimaryPart
  6547. vehicle.PrimaryPart = seat
  6548.  
  6549. -- For now, only direct path
  6550. if directPathForVehicle then
  6551. local extents = vehicle:GetExtentsSize()
  6552. agentRadius = AgentSizeIncreaseFactor * 0.5 * math.sqrt(extents.X * extents.X + extents.Z * extents.Z)
  6553. agentHeight = AgentSizeIncreaseFactor * extents.Y
  6554. agentCanJump = false
  6555. this.AgentCanFollowPath = true
  6556. this.DirectPath = directPathForVehicle
  6557. end
  6558.  
  6559. -- Reset PrimaryPart
  6560. vehicle.PrimaryPart = tempPrimaryPart
  6561. end
  6562. else
  6563. local extents = GetCharacter():GetExtentsSize()
  6564. agentRadius = AgentSizeIncreaseFactor * 0.5 * math.sqrt(extents.X * extents.X + extents.Z * extents.Z)
  6565. agentHeight = AgentSizeIncreaseFactor * extents.Y
  6566. agentCanJump = (this.Humanoid.JumpPower > 0)
  6567. this.AgentCanFollowPath = true
  6568. this.DirectPath = directPathForHumanoid
  6569. this.DirectPathRiseFirst = this.Humanoid.Sit
  6570. end
  6571.  
  6572. -- Build path object
  6573. this.pathResult = PathfindingService:CreatePath({AgentRadius = agentRadius, AgentHeight = agentHeight, AgentCanJump = agentCanJump})
  6574. end
  6575.  
  6576. function this:Cleanup()
  6577. if this.stopTraverseFunc then
  6578. this.stopTraverseFunc()
  6579. this.stopTraverseFunc = nil
  6580. end
  6581.  
  6582. if this.MoveToConn then
  6583. this.MoveToConn:Disconnect()
  6584. this.MoveToConn = nil
  6585. end
  6586.  
  6587. if this.BlockedConn then
  6588. this.BlockedConn:Disconnect()
  6589. this.BlockedConn = nil
  6590. end
  6591.  
  6592. if this.DiedConn then
  6593. this.DiedConn:Disconnect()
  6594. this.DiedConn = nil
  6595. end
  6596.  
  6597. if this.SeatedConn then
  6598. this.SeatedConn:Disconnect()
  6599. this.SeatedConn = nil
  6600. end
  6601.  
  6602. if this.TeleportedConn then
  6603. this.TeleportedConn:Disconnect()
  6604. this.TeleportedConn = nil
  6605. end
  6606.  
  6607. this.Started = false
  6608. end
  6609.  
  6610. function this:Cancel()
  6611. this.Cancelled = true
  6612. this:Cleanup()
  6613. end
  6614.  
  6615. function this:IsActive()
  6616. return this.AgentCanFollowPath and this.Started and not this.Cancelled
  6617. end
  6618.  
  6619. function this:OnPathInterrupted()
  6620. -- Stop moving
  6621. this.Cancelled = true
  6622. this:OnPointReached(false)
  6623. end
  6624.  
  6625. function this:ComputePath()
  6626. if this.OriginPoint then
  6627. if this.PathComputed or this.PathComputing then return end
  6628. this.PathComputing = true
  6629. if this.AgentCanFollowPath then
  6630. if this.DirectPath then
  6631. this.pointList = {
  6632. PathWaypoint.new(this.OriginPoint, Enum.PathWaypointAction.Walk),
  6633. PathWaypoint.new(this.TargetPoint, this.DirectPathRiseFirst and Enum.PathWaypointAction.Jump or Enum.PathWaypointAction.Walk)
  6634. }
  6635. this.PathComputed = true
  6636. else
  6637. this.pathResult:ComputeAsync(this.OriginPoint, this.TargetPoint)
  6638. this.pointList = this.pathResult:GetWaypoints()
  6639. this.BlockedConn = this.pathResult.Blocked:Connect(function(blockedIdx) this:OnPathBlocked(blockedIdx) end)
  6640. this.PathComputed = this.pathResult.Status == Enum.PathStatus.Success
  6641. end
  6642. end
  6643. this.PathComputing = false
  6644. end
  6645. end
  6646.  
  6647. function this:IsValidPath()
  6648. this:ComputePath()
  6649. return this.PathComputed and this.AgentCanFollowPath
  6650. end
  6651.  
  6652. this.Recomputing = false
  6653. function this:OnPathBlocked(blockedWaypointIdx)
  6654. local pathBlocked = blockedWaypointIdx >= this.CurrentPoint
  6655. if not pathBlocked or this.Recomputing then
  6656. return
  6657. end
  6658.  
  6659. this.Recomputing = true
  6660.  
  6661. if this.stopTraverseFunc then
  6662. this.stopTraverseFunc()
  6663. this.stopTraverseFunc = nil
  6664. end
  6665.  
  6666. this.OriginPoint = this.Humanoid.RootPart.CFrame.p
  6667.  
  6668. this.pathResult:ComputeAsync(this.OriginPoint, this.TargetPoint)
  6669. this.pointList = this.pathResult:GetWaypoints()
  6670. if #this.pointList > 0 then
  6671. this.HumanoidOffsetFromPath = this.pointList[1].Position - this.OriginPoint
  6672. end
  6673. this.PathComputed = this.pathResult.Status == Enum.PathStatus.Success
  6674.  
  6675. if ShowPath then
  6676. this.stopTraverseFunc, this.setPointFunc = ClickToMoveDisplay.CreatePathDisplay(this.pointList)
  6677. end
  6678. if this.PathComputed then
  6679. this.CurrentPoint = 1 -- The first waypoint is always the start location. Skip it.
  6680. this:OnPointReached(true) -- Move to first point
  6681. else
  6682. this.PathFailed:Fire()
  6683. this:Cleanup()
  6684. end
  6685.  
  6686. this.Recomputing = false
  6687. end
  6688.  
  6689. function this:OnRenderStepped(dt)
  6690. if this.Started and not this.Cancelled then
  6691. -- Check for Timeout (if a waypoint is not reached within the delay, we fail)
  6692. this.Timeout = this.Timeout + dt
  6693. if this.Timeout > UnreachableWaypointTimeout then
  6694. this:OnPointReached(false)
  6695. return
  6696. end
  6697.  
  6698. -- Get Humanoid position and velocity
  6699. this.CurrentHumanoidPosition = this.Humanoid.RootPart.Position + this.HumanoidOffsetFromPath
  6700. this.CurrentHumanoidVelocity = this.Humanoid.RootPart.Velocity
  6701.  
  6702. -- Check if it has reached some waypoints
  6703. while this.Started and this:IsCurrentWaypointReached() do
  6704. this:OnPointReached(true)
  6705. end
  6706.  
  6707. -- If still started, update actions
  6708. if this.Started then
  6709. -- Move action
  6710. this.NextActionMoveDirection = this.CurrentWaypointPosition - this.CurrentHumanoidPosition
  6711. if this.NextActionMoveDirection.Magnitude > ALMOST_ZERO then
  6712. this.NextActionMoveDirection = this.NextActionMoveDirection.Unit
  6713. else
  6714. this.NextActionMoveDirection = ZERO_VECTOR3
  6715. end
  6716. -- Jump action
  6717. if this.CurrentWaypointNeedsJump then
  6718. this.NextActionJump = true
  6719. this.CurrentWaypointNeedsJump = false -- Request jump only once
  6720. else
  6721. this.NextActionJump = false
  6722. end
  6723. end
  6724. end
  6725. end
  6726.  
  6727. function this:IsCurrentWaypointReached()
  6728. local reached = false
  6729.  
  6730. -- Check we do have a plane, if not, we consider the waypoint reached
  6731. if this.CurrentWaypointPlaneNormal ~= ZERO_VECTOR3 then
  6732. -- Compute distance of Humanoid from destination plane
  6733. local dist = this.CurrentWaypointPlaneNormal:Dot(this.CurrentHumanoidPosition) - this.CurrentWaypointPlaneDistance
  6734. -- Compute the component of the Humanoid velocity that is towards the plane
  6735. local velocity = -this.CurrentWaypointPlaneNormal:Dot(this.CurrentHumanoidVelocity)
  6736. -- Compute the threshold from the destination plane based on Humanoid velocity
  6737. local threshold = math.max(1.0, 0.0625 * velocity)
  6738. -- 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
  6739. reached = dist < threshold
  6740. else
  6741. reached = true
  6742. end
  6743.  
  6744. if reached then
  6745. this.CurrentWaypointPosition = nil
  6746. this.CurrentWaypointPlaneNormal = ZERO_VECTOR3
  6747. this.CurrentWaypointPlaneDistance = 0
  6748. end
  6749.  
  6750. return reached
  6751. end
  6752.  
  6753. function this:OnPointReached(reached)
  6754.  
  6755. if reached and not this.Cancelled then
  6756. -- First, destroyed the current displayed waypoint
  6757. if this.setPointFunc then
  6758. this.setPointFunc(this.CurrentPoint)
  6759. end
  6760.  
  6761. local nextWaypointIdx = this.CurrentPoint + 1
  6762.  
  6763. if nextWaypointIdx > #this.pointList then
  6764. -- End of path reached
  6765. if this.stopTraverseFunc then
  6766. this.stopTraverseFunc()
  6767. end
  6768. this.Finished:Fire()
  6769. this:Cleanup()
  6770. else
  6771. local currentWaypoint = this.pointList[this.CurrentPoint]
  6772. local nextWaypoint = this.pointList[nextWaypointIdx]
  6773.  
  6774. -- If airborne, only allow to keep moving
  6775. -- if nextWaypoint.Action ~= Jump, or path mantains a direction
  6776. -- Otherwise, wait until the humanoid gets to the ground
  6777. local currentState = this.Humanoid:GetState()
  6778. local isInAir = currentState == Enum.HumanoidStateType.FallingDown
  6779. or currentState == Enum.HumanoidStateType.Freefall
  6780. or currentState == Enum.HumanoidStateType.Jumping
  6781.  
  6782. if isInAir then
  6783. local shouldWaitForGround = nextWaypoint.Action == Enum.PathWaypointAction.Jump
  6784. if not shouldWaitForGround and this.CurrentPoint > 1 then
  6785. local prevWaypoint = this.pointList[this.CurrentPoint - 1]
  6786.  
  6787. local prevDir = currentWaypoint.Position - prevWaypoint.Position
  6788. local currDir = nextWaypoint.Position - currentWaypoint.Position
  6789.  
  6790. local prevDirXZ = Vector2.new(prevDir.x, prevDir.z).Unit
  6791. local currDirXZ = Vector2.new(currDir.x, currDir.z).Unit
  6792.  
  6793. local THRESHOLD_COS = 0.996 -- ~cos(5 degrees)
  6794. shouldWaitForGround = prevDirXZ:Dot(currDirXZ) < THRESHOLD_COS
  6795. end
  6796.  
  6797. if shouldWaitForGround then
  6798. this.Humanoid.FreeFalling:Wait()
  6799.  
  6800. -- Give time to the humanoid's state to change
  6801. -- Otherwise, the jump flag in Humanoid
  6802. -- will be reset by the state change
  6803. wait(0.1)
  6804. end
  6805. end
  6806.  
  6807. -- Move to the next point
  6808. if FFlagUserNavigationClickToMoveSkipPassedWaypoints then
  6809. this:MoveToNextWayPoint(currentWaypoint, nextWaypoint, nextWaypointIdx)
  6810. else
  6811. if this.setPointFunc then
  6812. this.setPointFunc(nextWaypointIdx)
  6813. end
  6814. if nextWaypoint.Action == Enum.PathWaypointAction.Jump then
  6815. this.Humanoid.Jump = true
  6816. end
  6817. this.Humanoid:MoveTo(nextWaypoint.Position)
  6818.  
  6819. this.CurrentPoint = nextWaypointIdx
  6820. end
  6821. end
  6822. else
  6823. this.PathFailed:Fire()
  6824. this:Cleanup()
  6825. end
  6826. end
  6827.  
  6828. function this:MoveToNextWayPoint(currentWaypoint, nextWaypoint, nextWaypointIdx)
  6829. -- Build next destination plane
  6830. -- (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))
  6831. -- (plane location is at next waypoint)
  6832. this.CurrentWaypointPlaneNormal = currentWaypoint.Position - nextWaypoint.Position
  6833. this.CurrentWaypointPlaneNormal = Vector3.new(this.CurrentWaypointPlaneNormal.X, 0, this.CurrentWaypointPlaneNormal.Z)
  6834. if this.CurrentWaypointPlaneNormal.Magnitude > ALMOST_ZERO then
  6835. this.CurrentWaypointPlaneNormal = this.CurrentWaypointPlaneNormal.Unit
  6836. this.CurrentWaypointPlaneDistance = this.CurrentWaypointPlaneNormal:Dot(nextWaypoint.Position)
  6837. else
  6838. -- Next waypoint is the same as current waypoint so no plane
  6839. this.CurrentWaypointPlaneNormal = ZERO_VECTOR3
  6840. this.CurrentWaypointPlaneDistance = 0
  6841. end
  6842.  
  6843. -- Should we jump
  6844. this.CurrentWaypointNeedsJump = nextWaypoint.Action == Enum.PathWaypointAction.Jump;
  6845.  
  6846. -- Remember next waypoint position
  6847. this.CurrentWaypointPosition = nextWaypoint.Position
  6848.  
  6849. -- Move to next point
  6850. this.CurrentPoint = nextWaypointIdx
  6851.  
  6852. -- Finally reset Timeout
  6853. this.Timeout = 0
  6854. end
  6855.  
  6856. function this:Start(overrideShowPath)
  6857. if not this.AgentCanFollowPath then
  6858. this.PathFailed:Fire()
  6859. return
  6860. end
  6861.  
  6862. if this.Started then return end
  6863. this.Started = true
  6864.  
  6865. ClickToMoveDisplay.CancelFailureAnimation()
  6866.  
  6867. if ShowPath then
  6868. if overrideShowPath == nil or overrideShowPath then
  6869. this.stopTraverseFunc, this.setPointFunc = ClickToMoveDisplay.CreatePathDisplay(this.pointList, this.OriginalTargetPoint)
  6870. end
  6871. end
  6872.  
  6873. if #this.pointList > 0 then
  6874. -- Determine the humanoid offset from the path's first point
  6875. -- Offset of the first waypoint from the path's origin point
  6876. this.HumanoidOffsetFromPath = Vector3.new(0, this.pointList[1].Position.Y - this.OriginPoint.Y, 0)
  6877.  
  6878. -- As well as its current position and velocity
  6879. this.CurrentHumanoidPosition = this.Humanoid.RootPart.Position + this.HumanoidOffsetFromPath
  6880. this.CurrentHumanoidVelocity = this.Humanoid.RootPart.Velocity
  6881.  
  6882. -- Connect to events
  6883. this.SeatedConn = this.Humanoid.Seated:Connect(function(isSeated, seat) this:OnPathInterrupted() end)
  6884. this.DiedConn = this.Humanoid.Died:Connect(function() this:OnPathInterrupted() end)
  6885. this.TeleportedConn = this.Humanoid.RootPart:GetPropertyChangedSignal("CFrame"):Connect(function() this:OnPathInterrupted() end)
  6886.  
  6887. -- Actually start
  6888. this.CurrentPoint = 1 -- The first waypoint is always the start location. Skip it.
  6889. this:OnPointReached(true) -- Move to first point
  6890. else
  6891. this.PathFailed:Fire()
  6892. if this.stopTraverseFunc then
  6893. this.stopTraverseFunc()
  6894. end
  6895. end
  6896. end
  6897.  
  6898. --We always raycast to the ground in the case that the user clicked a wall.
  6899. local offsetPoint = this.TargetPoint + this.TargetSurfaceNormal*1.5
  6900. local ray = Ray.new(offsetPoint, Vector3.new(0,-1,0)*50)
  6901. local newHitPart, newHitPos = Workspace:FindPartOnRayWithIgnoreList(ray, getIgnoreList())
  6902. if newHitPart then
  6903. this.TargetPoint = newHitPos
  6904. end
  6905. this:ComputePath()
  6906.  
  6907. return this
  6908. end
  6909.  
  6910. -------------------------------------------------------------------------
  6911.  
  6912. local function CheckAlive()
  6913. local humanoid = findPlayerHumanoid(Player)
  6914. return humanoid ~= nil and humanoid.Health > 0
  6915. end
  6916.  
  6917. local function GetEquippedTool(character)
  6918. if character ~= nil then
  6919. for _, child in pairs(character:GetChildren()) do
  6920. if child:IsA('Tool') then
  6921. return child
  6922. end
  6923. end
  6924. end
  6925. end
  6926.  
  6927. local ExistingPather = nil
  6928. local ExistingIndicator = nil
  6929. local PathCompleteListener = nil
  6930. local PathFailedListener = nil
  6931.  
  6932. local function CleanupPath()
  6933. if ExistingPather then
  6934. ExistingPather:Cancel()
  6935. ExistingPather = nil
  6936. end
  6937. if PathCompleteListener then
  6938. PathCompleteListener:Disconnect()
  6939. PathCompleteListener = nil
  6940. end
  6941. if PathFailedListener then
  6942. PathFailedListener:Disconnect()
  6943. PathFailedListener = nil
  6944. end
  6945. if ExistingIndicator then
  6946. ExistingIndicator:Destroy()
  6947. end
  6948. end
  6949.  
  6950. local function HandleMoveTo(thisPather, hitPt, hitChar, character, overrideShowPath)
  6951. if ExistingPather then
  6952. CleanupPath()
  6953. end
  6954. ExistingPather = thisPather
  6955. thisPather:Start(overrideShowPath)
  6956.  
  6957. PathCompleteListener = thisPather.Finished.Event:Connect(function()
  6958. CleanupPath()
  6959. if hitChar then
  6960. local currentWeapon = GetEquippedTool(character)
  6961. if currentWeapon then
  6962. currentWeapon:Activate()
  6963. end
  6964. end
  6965. end)
  6966. PathFailedListener = thisPather.PathFailed.Event:Connect(function()
  6967. CleanupPath()
  6968. if overrideShowPath == nil or overrideShowPath then
  6969. local shouldPlayFailureAnim = PlayFailureAnimation and not (ExistingPather and ExistingPather:IsActive())
  6970. if shouldPlayFailureAnim then
  6971. ClickToMoveDisplay.PlayFailureAnimation()
  6972. end
  6973. ClickToMoveDisplay.DisplayFailureWaypoint(hitPt)
  6974. end
  6975. end)
  6976. end
  6977.  
  6978. local function ShowPathFailedFeedback(hitPt)
  6979. if ExistingPather and ExistingPather:IsActive() then
  6980. ExistingPather:Cancel()
  6981. end
  6982. if PlayFailureAnimation then
  6983. ClickToMoveDisplay.PlayFailureAnimation()
  6984. end
  6985. ClickToMoveDisplay.DisplayFailureWaypoint(hitPt)
  6986. end
  6987.  
  6988. function OnTap(tapPositions, goToPoint, wasTouchTap)
  6989. -- Good to remember if this is the latest tap event
  6990. local camera = Workspace.CurrentCamera
  6991. local character = Player.Character
  6992.  
  6993. if not CheckAlive() then return end
  6994.  
  6995. -- This is a path tap position
  6996. if #tapPositions == 1 or goToPoint then
  6997. if camera then
  6998. local unitRay = camera:ScreenPointToRay(tapPositions[1].x, tapPositions[1].y)
  6999. local ray = Ray.new(unitRay.Origin, unitRay.Direction*1000)
  7000.  
  7001. local myHumanoid = findPlayerHumanoid(Player)
  7002. local hitPart, hitPt, hitNormal = Utility.Raycast(ray, true, getIgnoreList())
  7003.  
  7004. local hitChar, hitHumanoid = Utility.FindCharacterAncestor(hitPart)
  7005. if wasTouchTap and hitHumanoid and StarterGui:GetCore("AvatarContextMenuEnabled") then
  7006. local clickedPlayer = Players:GetPlayerFromCharacter(hitHumanoid.Parent)
  7007. if clickedPlayer then
  7008. CleanupPath()
  7009. return
  7010. end
  7011. end
  7012. if goToPoint then
  7013. hitPt = goToPoint
  7014. hitChar = nil
  7015. end
  7016. if hitPt and character then
  7017. -- Clean up current path
  7018. CleanupPath()
  7019. local thisPather = Pather(hitPt, hitNormal)
  7020. if thisPather:IsValidPath() then
  7021. HandleMoveTo(thisPather, hitPt, hitChar, character)
  7022. else
  7023. -- Clean up
  7024. thisPather:Cleanup()
  7025. -- Feedback here for when we don't have a good path
  7026. ShowPathFailedFeedback(hitPt)
  7027. end
  7028. end
  7029. end
  7030. elseif #tapPositions >= 2 then
  7031. if camera then
  7032. -- Do shoot
  7033. local currentWeapon = GetEquippedTool(character)
  7034. if currentWeapon then
  7035. currentWeapon:Activate()
  7036. end
  7037. end
  7038. end
  7039. end
  7040.  
  7041. local function DisconnectEvent(event)
  7042. if event then
  7043. event:Disconnect()
  7044. end
  7045. end
  7046.  
  7047. --[[ The ClickToMove Controller Class ]]--
  7048. local KeyboardController = _Keyboard()
  7049. local ClickToMove = setmetatable({}, KeyboardController)
  7050. ClickToMove.__index = ClickToMove
  7051.  
  7052. function ClickToMove.new(CONTROL_ACTION_PRIORITY)
  7053. local self = setmetatable(KeyboardController.new(CONTROL_ACTION_PRIORITY), ClickToMove)
  7054.  
  7055. self.fingerTouches = {}
  7056. self.numUnsunkTouches = 0
  7057. -- PC simulation
  7058. self.mouse1Down = tick()
  7059. self.mouse1DownPos = Vector2.new()
  7060. self.mouse2DownTime = tick()
  7061. self.mouse2DownPos = Vector2.new()
  7062. self.mouse2UpTime = tick()
  7063.  
  7064. self.keyboardMoveVector = ZERO_VECTOR3
  7065.  
  7066. self.tapConn = nil
  7067. self.inputBeganConn = nil
  7068. self.inputChangedConn = nil
  7069. self.inputEndedConn = nil
  7070. self.humanoidDiedConn = nil
  7071. self.characterChildAddedConn = nil
  7072. self.onCharacterAddedConn = nil
  7073. self.characterChildRemovedConn = nil
  7074. self.renderSteppedConn = nil
  7075. self.menuOpenedConnection = nil
  7076.  
  7077. self.running = false
  7078.  
  7079. self.wasdEnabled = false
  7080.  
  7081. return self
  7082. end
  7083.  
  7084. function ClickToMove:DisconnectEvents()
  7085. DisconnectEvent(self.tapConn)
  7086. DisconnectEvent(self.inputBeganConn)
  7087. DisconnectEvent(self.inputChangedConn)
  7088. DisconnectEvent(self.inputEndedConn)
  7089. DisconnectEvent(self.humanoidDiedConn)
  7090. DisconnectEvent(self.characterChildAddedConn)
  7091. DisconnectEvent(self.onCharacterAddedConn)
  7092. DisconnectEvent(self.renderSteppedConn)
  7093. DisconnectEvent(self.characterChildRemovedConn)
  7094. DisconnectEvent(self.menuOpenedConnection)
  7095. end
  7096.  
  7097. function ClickToMove:OnTouchBegan(input, processed)
  7098. if self.fingerTouches[input] == nil and not processed then
  7099. self.numUnsunkTouches = self.numUnsunkTouches + 1
  7100. end
  7101. self.fingerTouches[input] = processed
  7102. end
  7103.  
  7104. function ClickToMove:OnTouchChanged(input, processed)
  7105. if self.fingerTouches[input] == nil then
  7106. self.fingerTouches[input] = processed
  7107. if not processed then
  7108. self.numUnsunkTouches = self.numUnsunkTouches + 1
  7109. end
  7110. end
  7111. end
  7112.  
  7113. function ClickToMove:OnTouchEnded(input, processed)
  7114. if self.fingerTouches[input] ~= nil and self.fingerTouches[input] == false then
  7115. self.numUnsunkTouches = self.numUnsunkTouches - 1
  7116. end
  7117. self.fingerTouches[input] = nil
  7118. end
  7119.  
  7120.  
  7121. function ClickToMove:OnCharacterAdded(character)
  7122. self:DisconnectEvents()
  7123.  
  7124. self.inputBeganConn = UserInputService.InputBegan:Connect(function(input, processed)
  7125. if input.UserInputType == Enum.UserInputType.Touch then
  7126. self:OnTouchBegan(input, processed)
  7127. end
  7128.  
  7129. -- Cancel path when you use the keyboard controls if wasd is enabled.
  7130. if self.wasdEnabled and processed == false and input.UserInputType == Enum.UserInputType.Keyboard
  7131. and movementKeys[input.KeyCode] then
  7132. CleanupPath()
  7133. ClickToMoveDisplay.CancelFailureAnimation()
  7134. end
  7135. if input.UserInputType == Enum.UserInputType.MouseButton1 then
  7136. self.mouse1DownTime = tick()
  7137. self.mouse1DownPos = input.Position
  7138. end
  7139. if input.UserInputType == Enum.UserInputType.MouseButton2 then
  7140. self.mouse2DownTime = tick()
  7141. self.mouse2DownPos = input.Position
  7142. end
  7143. end)
  7144.  
  7145. self.inputChangedConn = UserInputService.InputChanged:Connect(function(input, processed)
  7146. if input.UserInputType == Enum.UserInputType.Touch then
  7147. self:OnTouchChanged(input, processed)
  7148. end
  7149. end)
  7150.  
  7151. self.inputEndedConn = UserInputService.InputEnded:Connect(function(input, processed)
  7152. if input.UserInputType == Enum.UserInputType.Touch then
  7153. self:OnTouchEnded(input, processed)
  7154. end
  7155.  
  7156. if input.UserInputType == Enum.UserInputType.MouseButton2 then
  7157. self.mouse2UpTime = tick()
  7158. local currPos = input.Position
  7159. -- We allow click to move during path following or if there is no keyboard movement
  7160. local allowed = ExistingPather or self.keyboardMoveVector.Magnitude <= 0
  7161. if self.mouse2UpTime - self.mouse2DownTime < 0.25 and (currPos - self.mouse2DownPos).magnitude < 5 and allowed then
  7162. local positions = {currPos}
  7163. OnTap(positions)
  7164. end
  7165. end
  7166. end)
  7167.  
  7168. self.tapConn = UserInputService.TouchTap:Connect(function(touchPositions, processed)
  7169. if not processed then
  7170. OnTap(touchPositions, nil, true)
  7171. end
  7172. end)
  7173.  
  7174. self.menuOpenedConnection = GuiService.MenuOpened:Connect(function()
  7175. CleanupPath()
  7176. end)
  7177.  
  7178. local function OnCharacterChildAdded(child)
  7179. if UserInputService.TouchEnabled then
  7180. if child:IsA('Tool') then
  7181. child.ManualActivationOnly = true
  7182. end
  7183. end
  7184. if child:IsA('Humanoid') then
  7185. DisconnectEvent(self.humanoidDiedConn)
  7186. self.humanoidDiedConn = child.Died:Connect(function()
  7187. if ExistingIndicator then
  7188. DebrisService:AddItem(ExistingIndicator.Model, 1)
  7189. end
  7190. end)
  7191. end
  7192. end
  7193.  
  7194. self.characterChildAddedConn = character.ChildAdded:Connect(function(child)
  7195. OnCharacterChildAdded(child)
  7196. end)
  7197. self.characterChildRemovedConn = character.ChildRemoved:Connect(function(child)
  7198. if UserInputService.TouchEnabled then
  7199. if child:IsA('Tool') then
  7200. child.ManualActivationOnly = false
  7201. end
  7202. end
  7203. end)
  7204. for _, child in pairs(character:GetChildren()) do
  7205. OnCharacterChildAdded(child)
  7206. end
  7207. end
  7208.  
  7209. function ClickToMove:Start()
  7210. self:Enable(true)
  7211. end
  7212.  
  7213. function ClickToMove:Stop()
  7214. self:Enable(false)
  7215. end
  7216.  
  7217. function ClickToMove:CleanupPath()
  7218. CleanupPath()
  7219. end
  7220.  
  7221. function ClickToMove:Enable(enable, enableWASD, touchJumpController)
  7222. if enable then
  7223. if not self.running then
  7224. if Player.Character then -- retro-listen
  7225. self:OnCharacterAdded(Player.Character)
  7226. end
  7227. self.onCharacterAddedConn = Player.CharacterAdded:Connect(function(char)
  7228. self:OnCharacterAdded(char)
  7229. end)
  7230. self.running = true
  7231. end
  7232. self.touchJumpController = touchJumpController
  7233. if self.touchJumpController then
  7234. self.touchJumpController:Enable(self.jumpEnabled)
  7235. end
  7236. else
  7237. if self.running then
  7238. self:DisconnectEvents()
  7239. CleanupPath()
  7240. -- Restore tool activation on shutdown
  7241. if UserInputService.TouchEnabled then
  7242. local character = Player.Character
  7243. if character then
  7244. for _, child in pairs(character:GetChildren()) do
  7245. if child:IsA('Tool') then
  7246. child.ManualActivationOnly = false
  7247. end
  7248. end
  7249. end
  7250. end
  7251. self.running = false
  7252. end
  7253. if self.touchJumpController and not self.jumpEnabled then
  7254. self.touchJumpController:Enable(true)
  7255. end
  7256. self.touchJumpController = nil
  7257. end
  7258.  
  7259. -- Extension for initializing Keyboard input as this class now derives from Keyboard
  7260. if UserInputService.KeyboardEnabled and enable ~= self.enabled then
  7261.  
  7262. self.forwardValue = 0
  7263. self.backwardValue = 0
  7264. self.leftValue = 0
  7265. self.rightValue = 0
  7266.  
  7267. self.moveVector = ZERO_VECTOR3
  7268.  
  7269. if enable then
  7270. self:BindContextActions()
  7271. self:ConnectFocusEventListeners()
  7272. else
  7273. self:UnbindContextActions()
  7274. self:DisconnectFocusEventListeners()
  7275. end
  7276. end
  7277.  
  7278. self.wasdEnabled = enable and enableWASD or false
  7279. self.enabled = enable
  7280. end
  7281.  
  7282. function ClickToMove:OnRenderStepped(dt)
  7283. -- Reset jump
  7284. self.isJumping = false
  7285.  
  7286. -- Handle Pather
  7287. if ExistingPather then
  7288. -- Let the Pather update
  7289. ExistingPather:OnRenderStepped(dt)
  7290.  
  7291. -- If we still have a Pather, set the resulting actions
  7292. if ExistingPather then
  7293. -- Setup move (NOT relative to camera)
  7294. self.moveVector = ExistingPather.NextActionMoveDirection
  7295. self.moveVectorIsCameraRelative = false
  7296.  
  7297. -- Setup jump (but do NOT prevent the base Keayboard class from requesting jumps as well)
  7298. if ExistingPather.NextActionJump then
  7299. self.isJumping = true
  7300. end
  7301. else
  7302. self.moveVector = self.keyboardMoveVector
  7303. self.moveVectorIsCameraRelative = true
  7304. end
  7305. else
  7306. self.moveVector = self.keyboardMoveVector
  7307. self.moveVectorIsCameraRelative = true
  7308. end
  7309.  
  7310. -- Handle Keyboard's jump
  7311. if self.jumpRequested then
  7312. self.isJumping = true
  7313. end
  7314. end
  7315.  
  7316. -- Overrides Keyboard:UpdateMovement(inputState) to conditionally consider self.wasdEnabled and let OnRenderStepped handle the movement
  7317. function ClickToMove:UpdateMovement(inputState)
  7318. if inputState == Enum.UserInputState.Cancel then
  7319. self.keyboardMoveVector = ZERO_VECTOR3
  7320. elseif self.wasdEnabled then
  7321. self.keyboardMoveVector = Vector3.new(self.leftValue + self.rightValue, 0, self.forwardValue + self.backwardValue)
  7322. end
  7323. end
  7324.  
  7325. -- Overrides Keyboard:UpdateJump() because jump is handled in OnRenderStepped
  7326. function ClickToMove:UpdateJump()
  7327. -- Nothing to do (handled in OnRenderStepped)
  7328. end
  7329.  
  7330. --Public developer facing functions
  7331. function ClickToMove:SetShowPath(value)
  7332. ShowPath = value
  7333. end
  7334.  
  7335. function ClickToMove:GetShowPath()
  7336. return ShowPath
  7337. end
  7338.  
  7339. function ClickToMove:SetWaypointTexture(texture)
  7340. ClickToMoveDisplay.SetWaypointTexture(texture)
  7341. end
  7342.  
  7343. function ClickToMove:GetWaypointTexture()
  7344. return ClickToMoveDisplay.GetWaypointTexture()
  7345. end
  7346.  
  7347. function ClickToMove:SetWaypointRadius(radius)
  7348. ClickToMoveDisplay.SetWaypointRadius(radius)
  7349. end
  7350.  
  7351. function ClickToMove:GetWaypointRadius()
  7352. return ClickToMoveDisplay.GetWaypointRadius()
  7353. end
  7354.  
  7355. function ClickToMove:SetEndWaypointTexture(texture)
  7356. ClickToMoveDisplay.SetEndWaypointTexture(texture)
  7357. end
  7358.  
  7359. function ClickToMove:GetEndWaypointTexture()
  7360. return ClickToMoveDisplay.GetEndWaypointTexture()
  7361. end
  7362.  
  7363. function ClickToMove:SetWaypointsAlwaysOnTop(alwaysOnTop)
  7364. ClickToMoveDisplay.SetWaypointsAlwaysOnTop(alwaysOnTop)
  7365. end
  7366.  
  7367. function ClickToMove:GetWaypointsAlwaysOnTop()
  7368. return ClickToMoveDisplay.GetWaypointsAlwaysOnTop()
  7369. end
  7370.  
  7371. function ClickToMove:SetFailureAnimationEnabled(enabled)
  7372. PlayFailureAnimation = enabled
  7373. end
  7374.  
  7375. function ClickToMove:GetFailureAnimationEnabled()
  7376. return PlayFailureAnimation
  7377. end
  7378.  
  7379. function ClickToMove:SetIgnoredPartsTag(tag)
  7380. UpdateIgnoreTag(tag)
  7381. end
  7382.  
  7383. function ClickToMove:GetIgnoredPartsTag()
  7384. return CurrentIgnoreTag
  7385. end
  7386.  
  7387. function ClickToMove:SetUseDirectPath(directPath)
  7388. UseDirectPath = directPath
  7389. end
  7390.  
  7391. function ClickToMove:GetUseDirectPath()
  7392. return UseDirectPath
  7393. end
  7394.  
  7395. function ClickToMove:SetAgentSizeIncreaseFactor(increaseFactorPercent)
  7396. AgentSizeIncreaseFactor = 1.0 + (increaseFactorPercent / 100.0)
  7397. end
  7398.  
  7399. function ClickToMove:GetAgentSizeIncreaseFactor()
  7400. return (AgentSizeIncreaseFactor - 1.0) * 100.0
  7401. end
  7402.  
  7403. function ClickToMove:SetUnreachableWaypointTimeout(timeoutInSec)
  7404. UnreachableWaypointTimeout = timeoutInSec
  7405. end
  7406.  
  7407. function ClickToMove:GetUnreachableWaypointTimeout()
  7408. return UnreachableWaypointTimeout
  7409. end
  7410.  
  7411. function ClickToMove:SetUserJumpEnabled(jumpEnabled)
  7412. self.jumpEnabled = jumpEnabled
  7413. if self.touchJumpController then
  7414. self.touchJumpController:Enable(jumpEnabled)
  7415. end
  7416. end
  7417.  
  7418. function ClickToMove:GetUserJumpEnabled()
  7419. return self.jumpEnabled
  7420. end
  7421.  
  7422. function ClickToMove:MoveTo(position, showPath, useDirectPath)
  7423. local character = Player.Character
  7424. if character == nil then
  7425. return false
  7426. end
  7427. local thisPather = Pather(position, Vector3.new(0, 1, 0), useDirectPath)
  7428. if thisPather and thisPather:IsValidPath() then
  7429. HandleMoveTo(thisPather, position, nil, character, showPath)
  7430. return true
  7431. end
  7432. return false
  7433. end
  7434.  
  7435. return ClickToMove
  7436. end
  7437.  
  7438. function _TouchThumbstick()
  7439. local Players = game:GetService("Players")
  7440. local GuiService = game:GetService("GuiService")
  7441. local UserInputService = game:GetService("UserInputService")
  7442. --[[ Constants ]]--
  7443. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  7444. local TOUCH_CONTROL_SHEET = "rbxasset://textures/ui/TouchControlsSheet.png"
  7445. --[[ The Module ]]--
  7446. local BaseCharacterController = _BaseCharacterController()
  7447. local TouchThumbstick = setmetatable({}, BaseCharacterController)
  7448. TouchThumbstick.__index = TouchThumbstick
  7449. function TouchThumbstick.new()
  7450. local self = setmetatable(BaseCharacterController.new(), TouchThumbstick)
  7451.  
  7452. self.isFollowStick = false
  7453.  
  7454. self.thumbstickFrame = nil
  7455. self.moveTouchObject = nil
  7456. self.onTouchMovedConn = nil
  7457. self.onTouchEndedConn = nil
  7458. self.screenPos = nil
  7459. self.stickImage = nil
  7460. self.thumbstickSize = nil -- Float
  7461.  
  7462. return self
  7463. end
  7464. function TouchThumbstick:Enable(enable, uiParentFrame)
  7465. if enable == nil then return false end -- If nil, return false (invalid argument)
  7466. enable = enable and true or false -- Force anything non-nil to boolean before comparison
  7467. if self.enabled == enable then return true end -- If no state change, return true indicating already in requested state
  7468.  
  7469. self.moveVector = ZERO_VECTOR3
  7470. self.isJumping = false
  7471.  
  7472. if enable then
  7473. -- Enable
  7474. if not self.thumbstickFrame then
  7475. self:Create(uiParentFrame)
  7476. end
  7477. self.thumbstickFrame.Visible = true
  7478. else
  7479. -- Disable
  7480. self.thumbstickFrame.Visible = false
  7481. self:OnInputEnded()
  7482. end
  7483. self.enabled = enable
  7484. end
  7485. function TouchThumbstick:OnInputEnded()
  7486. self.thumbstickFrame.Position = self.screenPos
  7487. 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)
  7488.  
  7489. self.moveVector = ZERO_VECTOR3
  7490. self.isJumping = false
  7491. self.thumbstickFrame.Position = self.screenPos
  7492. self.moveTouchObject = nil
  7493. end
  7494. function TouchThumbstick:Create(parentFrame)
  7495.  
  7496. if self.thumbstickFrame then
  7497. self.thumbstickFrame:Destroy()
  7498. self.thumbstickFrame = nil
  7499. if self.onTouchMovedConn then
  7500. self.onTouchMovedConn:Disconnect()
  7501. self.onTouchMovedConn = nil
  7502. end
  7503. if self.onTouchEndedConn then
  7504. self.onTouchEndedConn:Disconnect()
  7505. self.onTouchEndedConn = nil
  7506. end
  7507. end
  7508.  
  7509. local minAxis = math.min(parentFrame.AbsoluteSize.x, parentFrame.AbsoluteSize.y)
  7510. local isSmallScreen = minAxis <= 500
  7511. self.thumbstickSize = isSmallScreen and 70 or 120
  7512. self.screenPos = isSmallScreen and UDim2.new(0, (self.thumbstickSize/2) - 10, 1, -self.thumbstickSize - 20) or
  7513. UDim2.new(0, self.thumbstickSize/2, 1, -self.thumbstickSize * 1.75)
  7514.  
  7515. self.thumbstickFrame = Instance.new("Frame")
  7516. self.thumbstickFrame.Name = "ThumbstickFrame"
  7517. self.thumbstickFrame.Active = true
  7518. self.thumbstickFrame.Visible = false
  7519. self.thumbstickFrame.Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize)
  7520. self.thumbstickFrame.Position = self.screenPos
  7521. self.thumbstickFrame.BackgroundTransparency = 1
  7522.  
  7523. local outerImage = Instance.new("ImageLabel")
  7524. outerImage.Name = "OuterImage"
  7525. outerImage.Image = TOUCH_CONTROL_SHEET
  7526. outerImage.ImageRectOffset = Vector2.new()
  7527. outerImage.ImageRectSize = Vector2.new(220, 220)
  7528. outerImage.BackgroundTransparency = 1
  7529. outerImage.Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize)
  7530. outerImage.Position = UDim2.new(0, 0, 0, 0)
  7531. outerImage.Parent = self.thumbstickFrame
  7532.  
  7533. self.stickImage = Instance.new("ImageLabel")
  7534. self.stickImage.Name = "StickImage"
  7535. self.stickImage.Image = TOUCH_CONTROL_SHEET
  7536. self.stickImage.ImageRectOffset = Vector2.new(220, 0)
  7537. self.stickImage.ImageRectSize = Vector2.new(111, 111)
  7538. self.stickImage.BackgroundTransparency = 1
  7539. self.stickImage.Size = UDim2.new(0, self.thumbstickSize/2, 0, self.thumbstickSize/2)
  7540. self.stickImage.Position = UDim2.new(0, self.thumbstickSize/2 - self.thumbstickSize/4, 0, self.thumbstickSize/2 - self.thumbstickSize/4)
  7541. self.stickImage.ZIndex = 2
  7542. self.stickImage.Parent = self.thumbstickFrame
  7543.  
  7544. local centerPosition = nil
  7545. local deadZone = 0.05
  7546.  
  7547. local function DoMove(direction)
  7548.  
  7549. local currentMoveVector = direction / (self.thumbstickSize/2)
  7550.  
  7551. -- Scaled Radial Dead Zone
  7552. local inputAxisMagnitude = currentMoveVector.magnitude
  7553. if inputAxisMagnitude < deadZone then
  7554. currentMoveVector = Vector3.new()
  7555. else
  7556. currentMoveVector = currentMoveVector.unit * ((inputAxisMagnitude - deadZone) / (1 - deadZone))
  7557. -- NOTE: Making currentMoveVector a unit vector will cause the player to instantly go max speed
  7558. -- must check for zero length vector is using unit
  7559. currentMoveVector = Vector3.new(currentMoveVector.x, 0, currentMoveVector.y)
  7560. end
  7561.  
  7562. self.moveVector = currentMoveVector
  7563. end
  7564.  
  7565. local function MoveStick(pos)
  7566. local relativePosition = Vector2.new(pos.x - centerPosition.x, pos.y - centerPosition.y)
  7567. local length = relativePosition.magnitude
  7568. local maxLength = self.thumbstickFrame.AbsoluteSize.x/2
  7569. if self.isFollowStick and length > maxLength then
  7570. local offset = relativePosition.unit * maxLength
  7571. self.thumbstickFrame.Position = UDim2.new(
  7572. 0, pos.x - self.thumbstickFrame.AbsoluteSize.x/2 - offset.x,
  7573. 0, pos.y - self.thumbstickFrame.AbsoluteSize.y/2 - offset.y)
  7574. else
  7575. length = math.min(length, maxLength)
  7576. relativePosition = relativePosition.unit * length
  7577. end
  7578. self.stickImage.Position = UDim2.new(0, relativePosition.x + self.stickImage.AbsoluteSize.x/2, 0, relativePosition.y + self.stickImage.AbsoluteSize.y/2)
  7579. end
  7580.  
  7581. -- input connections
  7582. self.thumbstickFrame.InputBegan:Connect(function(inputObject)
  7583. --A touch that starts elsewhere on the screen will be sent to a frame's InputBegan event
  7584. --if it moves over the frame. So we check that this is actually a new touch (inputObject.UserInputState ~= Enum.UserInputState.Begin)
  7585. if self.moveTouchObject or inputObject.UserInputType ~= Enum.UserInputType.Touch
  7586. or inputObject.UserInputState ~= Enum.UserInputState.Begin then
  7587. return
  7588. end
  7589.  
  7590. self.moveTouchObject = inputObject
  7591. 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)
  7592. centerPosition = Vector2.new(self.thumbstickFrame.AbsolutePosition.x + self.thumbstickFrame.AbsoluteSize.x/2,
  7593. self.thumbstickFrame.AbsolutePosition.y + self.thumbstickFrame.AbsoluteSize.y/2)
  7594. local direction = Vector2.new(inputObject.Position.x - centerPosition.x, inputObject.Position.y - centerPosition.y)
  7595. end)
  7596.  
  7597. self.onTouchMovedConn = UserInputService.TouchMoved:Connect(function(inputObject, isProcessed)
  7598. if inputObject == self.moveTouchObject then
  7599. centerPosition = Vector2.new(self.thumbstickFrame.AbsolutePosition.x + self.thumbstickFrame.AbsoluteSize.x/2,
  7600. self.thumbstickFrame.AbsolutePosition.y + self.thumbstickFrame.AbsoluteSize.y/2)
  7601. local direction = Vector2.new(inputObject.Position.x - centerPosition.x, inputObject.Position.y - centerPosition.y)
  7602. DoMove(direction)
  7603. MoveStick(inputObject.Position)
  7604. end
  7605. end)
  7606.  
  7607. self.onTouchEndedConn = UserInputService.TouchEnded:Connect(function(inputObject, isProcessed)
  7608. if inputObject == self.moveTouchObject then
  7609. self:OnInputEnded()
  7610. end
  7611. end)
  7612.  
  7613. GuiService.MenuOpened:Connect(function()
  7614. if self.moveTouchObject then
  7615. self:OnInputEnded()
  7616. end
  7617. end)
  7618.  
  7619. self.thumbstickFrame.Parent = parentFrame
  7620. end
  7621. return TouchThumbstick
  7622. end
  7623.  
  7624. function _DynamicThumbstick()
  7625. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  7626. local TOUCH_CONTROLS_SHEET = "rbxasset://textures/ui/Input/TouchControlsSheetV2.png"
  7627.  
  7628. local DYNAMIC_THUMBSTICK_ACTION_NAME = "DynamicThumbstickAction"
  7629. local DYNAMIC_THUMBSTICK_ACTION_PRIORITY = Enum.ContextActionPriority.High.Value
  7630.  
  7631. local MIDDLE_TRANSPARENCIES = {
  7632. 1 - 0.89,
  7633. 1 - 0.70,
  7634. 1 - 0.60,
  7635. 1 - 0.50,
  7636. 1 - 0.40,
  7637. 1 - 0.30,
  7638. 1 - 0.25
  7639. }
  7640. local NUM_MIDDLE_IMAGES = #MIDDLE_TRANSPARENCIES
  7641.  
  7642. local FADE_IN_OUT_BACKGROUND = true
  7643. local FADE_IN_OUT_MAX_ALPHA = 0.35
  7644.  
  7645. local FADE_IN_OUT_HALF_DURATION_DEFAULT = 0.3
  7646. local FADE_IN_OUT_BALANCE_DEFAULT = 0.5
  7647. local ThumbstickFadeTweenInfo = TweenInfo.new(0.15, Enum.EasingStyle.Quad, Enum.EasingDirection.InOut)
  7648.  
  7649. local Players = game:GetService("Players")
  7650. local GuiService = game:GetService("GuiService")
  7651. local UserInputService = game:GetService("UserInputService")
  7652. local ContextActionService = game:GetService("ContextActionService")
  7653. local RunService = game:GetService("RunService")
  7654. local TweenService = game:GetService("TweenService")
  7655.  
  7656. local LocalPlayer = Players.LocalPlayer
  7657. if not LocalPlayer then
  7658. Players:GetPropertyChangedSignal("LocalPlayer"):Wait()
  7659. LocalPlayer = Players.LocalPlayer
  7660. end
  7661.  
  7662. --[[ The Module ]]--
  7663. local BaseCharacterController = _BaseCharacterController()
  7664. local DynamicThumbstick = setmetatable({}, BaseCharacterController)
  7665. DynamicThumbstick.__index = DynamicThumbstick
  7666.  
  7667. function DynamicThumbstick.new()
  7668. local self = setmetatable(BaseCharacterController.new(), DynamicThumbstick)
  7669.  
  7670. self.moveTouchObject = nil
  7671. self.moveTouchLockedIn = false
  7672. self.moveTouchFirstChanged = false
  7673. self.moveTouchStartPosition = nil
  7674.  
  7675. self.startImage = nil
  7676. self.endImage = nil
  7677. self.middleImages = {}
  7678.  
  7679. self.startImageFadeTween = nil
  7680. self.endImageFadeTween = nil
  7681. self.middleImageFadeTweens = {}
  7682.  
  7683. self.isFirstTouch = true
  7684.  
  7685. self.thumbstickFrame = nil
  7686.  
  7687. self.onRenderSteppedConn = nil
  7688.  
  7689. self.fadeInAndOutBalance = FADE_IN_OUT_BALANCE_DEFAULT
  7690. self.fadeInAndOutHalfDuration = FADE_IN_OUT_HALF_DURATION_DEFAULT
  7691. self.hasFadedBackgroundInPortrait = false
  7692. self.hasFadedBackgroundInLandscape = false
  7693.  
  7694. self.tweenInAlphaStart = nil
  7695. self.tweenOutAlphaStart = nil
  7696.  
  7697. return self
  7698. end
  7699.  
  7700. -- Note: Overrides base class GetIsJumping with get-and-clear behavior to do a single jump
  7701. -- rather than sustained jumping. This is only to preserve the current behavior through the refactor.
  7702. function DynamicThumbstick:GetIsJumping()
  7703. local wasJumping = self.isJumping
  7704. self.isJumping = false
  7705. return wasJumping
  7706. end
  7707.  
  7708. function DynamicThumbstick:Enable(enable, uiParentFrame)
  7709. if enable == nil then return false end -- If nil, return false (invalid argument)
  7710. enable = enable and true or false -- Force anything non-nil to boolean before comparison
  7711. if self.enabled == enable then return true end -- If no state change, return true indicating already in requested state
  7712.  
  7713. if enable then
  7714. -- Enable
  7715. if not self.thumbstickFrame then
  7716. self:Create(uiParentFrame)
  7717. end
  7718.  
  7719. self:BindContextActions()
  7720. else
  7721. ContextActionService:UnbindAction(DYNAMIC_THUMBSTICK_ACTION_NAME)
  7722. -- Disable
  7723. self:OnInputEnded() -- Cleanup
  7724. end
  7725.  
  7726. self.enabled = enable
  7727. self.thumbstickFrame.Visible = enable
  7728. end
  7729.  
  7730. -- Was called OnMoveTouchEnded in previous version
  7731. function DynamicThumbstick:OnInputEnded()
  7732. self.moveTouchObject = nil
  7733. self.moveVector = ZERO_VECTOR3
  7734. self:FadeThumbstick(false)
  7735. end
  7736.  
  7737. function DynamicThumbstick:FadeThumbstick(visible)
  7738. if not visible and self.moveTouchObject then
  7739. return
  7740. end
  7741. if self.isFirstTouch then return end
  7742.  
  7743. if self.startImageFadeTween then
  7744. self.startImageFadeTween:Cancel()
  7745. end
  7746. if self.endImageFadeTween then
  7747. self.endImageFadeTween:Cancel()
  7748. end
  7749. for i = 1, #self.middleImages do
  7750. if self.middleImageFadeTweens[i] then
  7751. self.middleImageFadeTweens[i]:Cancel()
  7752. end
  7753. end
  7754.  
  7755. if visible then
  7756. self.startImageFadeTween = TweenService:Create(self.startImage, ThumbstickFadeTweenInfo, { ImageTransparency = 0 })
  7757. self.startImageFadeTween:Play()
  7758.  
  7759. self.endImageFadeTween = TweenService:Create(self.endImage, ThumbstickFadeTweenInfo, { ImageTransparency = 0.2 })
  7760. self.endImageFadeTween:Play()
  7761.  
  7762. for i = 1, #self.middleImages do
  7763. self.middleImageFadeTweens[i] = TweenService:Create(self.middleImages[i], ThumbstickFadeTweenInfo, { ImageTransparency = MIDDLE_TRANSPARENCIES[i] })
  7764. self.middleImageFadeTweens[i]:Play()
  7765. end
  7766. else
  7767. self.startImageFadeTween = TweenService:Create(self.startImage, ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
  7768. self.startImageFadeTween:Play()
  7769.  
  7770. self.endImageFadeTween = TweenService:Create(self.endImage, ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
  7771. self.endImageFadeTween:Play()
  7772.  
  7773. for i = 1, #self.middleImages do
  7774. self.middleImageFadeTweens[i] = TweenService:Create(self.middleImages[i], ThumbstickFadeTweenInfo, { ImageTransparency = 1 })
  7775. self.middleImageFadeTweens[i]:Play()
  7776. end
  7777. end
  7778. end
  7779.  
  7780. function DynamicThumbstick:FadeThumbstickFrame(fadeDuration, fadeRatio)
  7781. self.fadeInAndOutHalfDuration = fadeDuration * 0.5
  7782. self.fadeInAndOutBalance = fadeRatio
  7783. self.tweenInAlphaStart = tick()
  7784. end
  7785.  
  7786. function DynamicThumbstick:InputInFrame(inputObject)
  7787. local frameCornerTopLeft = self.thumbstickFrame.AbsolutePosition
  7788. local frameCornerBottomRight = frameCornerTopLeft + self.thumbstickFrame.AbsoluteSize
  7789. local inputPosition = inputObject.Position
  7790. if inputPosition.X >= frameCornerTopLeft.X and inputPosition.Y >= frameCornerTopLeft.Y then
  7791. if inputPosition.X <= frameCornerBottomRight.X and inputPosition.Y <= frameCornerBottomRight.Y then
  7792. return true
  7793. end
  7794. end
  7795. return false
  7796. end
  7797.  
  7798. function DynamicThumbstick:DoFadeInBackground()
  7799. local playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
  7800. local hasFadedBackgroundInOrientation = false
  7801.  
  7802. -- only fade in/out the background once per orientation
  7803. if playerGui then
  7804. if playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeLeft or
  7805. playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeRight then
  7806. hasFadedBackgroundInOrientation = self.hasFadedBackgroundInLandscape
  7807. self.hasFadedBackgroundInLandscape = true
  7808. elseif playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.Portrait then
  7809. hasFadedBackgroundInOrientation = self.hasFadedBackgroundInPortrait
  7810. self.hasFadedBackgroundInPortrait = true
  7811. end
  7812. end
  7813.  
  7814. if not hasFadedBackgroundInOrientation then
  7815. self.fadeInAndOutHalfDuration = FADE_IN_OUT_HALF_DURATION_DEFAULT
  7816. self.fadeInAndOutBalance = FADE_IN_OUT_BALANCE_DEFAULT
  7817. self.tweenInAlphaStart = tick()
  7818. end
  7819. end
  7820.  
  7821. function DynamicThumbstick:DoMove(direction)
  7822. local currentMoveVector = direction
  7823.  
  7824. -- Scaled Radial Dead Zone
  7825. local inputAxisMagnitude = currentMoveVector.magnitude
  7826. if inputAxisMagnitude < self.radiusOfDeadZone then
  7827. currentMoveVector = ZERO_VECTOR3
  7828. else
  7829. currentMoveVector = currentMoveVector.unit*(
  7830. 1 - math.max(0, (self.radiusOfMaxSpeed - currentMoveVector.magnitude)/self.radiusOfMaxSpeed)
  7831. )
  7832. currentMoveVector = Vector3.new(currentMoveVector.x, 0, currentMoveVector.y)
  7833. end
  7834.  
  7835. self.moveVector = currentMoveVector
  7836. end
  7837.  
  7838.  
  7839. function DynamicThumbstick:LayoutMiddleImages(startPos, endPos)
  7840. local startDist = (self.thumbstickSize / 2) + self.middleSize
  7841. local vector = endPos - startPos
  7842. local distAvailable = vector.magnitude - (self.thumbstickRingSize / 2) - self.middleSize
  7843. local direction = vector.unit
  7844.  
  7845. local distNeeded = self.middleSpacing * NUM_MIDDLE_IMAGES
  7846. local spacing = self.middleSpacing
  7847.  
  7848. if distNeeded < distAvailable then
  7849. spacing = distAvailable / NUM_MIDDLE_IMAGES
  7850. end
  7851.  
  7852. for i = 1, NUM_MIDDLE_IMAGES do
  7853. local image = self.middleImages[i]
  7854. local distWithout = startDist + (spacing * (i - 2))
  7855. local currentDist = startDist + (spacing * (i - 1))
  7856.  
  7857. if distWithout < distAvailable then
  7858. local pos = endPos - direction * currentDist
  7859. local exposedFraction = math.clamp(1 - ((currentDist - distAvailable) / spacing), 0, 1)
  7860.  
  7861. image.Visible = true
  7862. image.Position = UDim2.new(0, pos.X, 0, pos.Y)
  7863. image.Size = UDim2.new(0, self.middleSize * exposedFraction, 0, self.middleSize * exposedFraction)
  7864. else
  7865. image.Visible = false
  7866. end
  7867. end
  7868. end
  7869.  
  7870. function DynamicThumbstick:MoveStick(pos)
  7871. local vector2StartPosition = Vector2.new(self.moveTouchStartPosition.X, self.moveTouchStartPosition.Y)
  7872. local startPos = vector2StartPosition - self.thumbstickFrame.AbsolutePosition
  7873. local endPos = Vector2.new(pos.X, pos.Y) - self.thumbstickFrame.AbsolutePosition
  7874. self.endImage.Position = UDim2.new(0, endPos.X, 0, endPos.Y)
  7875. self:LayoutMiddleImages(startPos, endPos)
  7876. end
  7877.  
  7878. function DynamicThumbstick:BindContextActions()
  7879. local function inputBegan(inputObject)
  7880. if self.moveTouchObject then
  7881. return Enum.ContextActionResult.Pass
  7882. end
  7883.  
  7884. if not self:InputInFrame(inputObject) then
  7885. return Enum.ContextActionResult.Pass
  7886. end
  7887.  
  7888. if self.isFirstTouch then
  7889. self.isFirstTouch = false
  7890. local tweenInfo = TweenInfo.new(0.5, Enum.EasingStyle.Quad, Enum.EasingDirection.Out,0,false,0)
  7891. TweenService:Create(self.startImage, tweenInfo, {Size = UDim2.new(0, 0, 0, 0)}):Play()
  7892. TweenService:Create(
  7893. self.endImage,
  7894. tweenInfo,
  7895. {Size = UDim2.new(0, self.thumbstickSize, 0, self.thumbstickSize), ImageColor3 = Color3.new(0,0,0)}
  7896. ):Play()
  7897. end
  7898.  
  7899. self.moveTouchLockedIn = false
  7900. self.moveTouchObject = inputObject
  7901. self.moveTouchStartPosition = inputObject.Position
  7902. self.moveTouchFirstChanged = true
  7903.  
  7904. if FADE_IN_OUT_BACKGROUND then
  7905. self:DoFadeInBackground()
  7906. end
  7907.  
  7908. return Enum.ContextActionResult.Pass
  7909. end
  7910.  
  7911. local function inputChanged(inputObject)
  7912. if inputObject == self.moveTouchObject then
  7913. if self.moveTouchFirstChanged then
  7914. self.moveTouchFirstChanged = false
  7915.  
  7916. local startPosVec2 = Vector2.new(
  7917. inputObject.Position.X - self.thumbstickFrame.AbsolutePosition.X,
  7918. inputObject.Position.Y - self.thumbstickFrame.AbsolutePosition.Y
  7919. )
  7920. self.startImage.Visible = true
  7921. self.startImage.Position = UDim2.new(0, startPosVec2.X, 0, startPosVec2.Y)
  7922. self.endImage.Visible = true
  7923. self.endImage.Position = self.startImage.Position
  7924.  
  7925. self:FadeThumbstick(true)
  7926. self:MoveStick(inputObject.Position)
  7927. end
  7928.  
  7929. self.moveTouchLockedIn = true
  7930.  
  7931. local direction = Vector2.new(
  7932. inputObject.Position.x - self.moveTouchStartPosition.x,
  7933. inputObject.Position.y - self.moveTouchStartPosition.y
  7934. )
  7935. if math.abs(direction.x) > 0 or math.abs(direction.y) > 0 then
  7936. self:DoMove(direction)
  7937. self:MoveStick(inputObject.Position)
  7938. end
  7939. return Enum.ContextActionResult.Sink
  7940. end
  7941. return Enum.ContextActionResult.Pass
  7942. end
  7943.  
  7944. local function inputEnded(inputObject)
  7945. if inputObject == self.moveTouchObject then
  7946. self:OnInputEnded()
  7947. if self.moveTouchLockedIn then
  7948. return Enum.ContextActionResult.Sink
  7949. end
  7950. end
  7951. return Enum.ContextActionResult.Pass
  7952. end
  7953.  
  7954. local function handleInput(actionName, inputState, inputObject)
  7955. if inputState == Enum.UserInputState.Begin then
  7956. return inputBegan(inputObject)
  7957. elseif inputState == Enum.UserInputState.Change then
  7958. return inputChanged(inputObject)
  7959. elseif inputState == Enum.UserInputState.End then
  7960. return inputEnded(inputObject)
  7961. elseif inputState == Enum.UserInputState.Cancel then
  7962. self:OnInputEnded()
  7963. end
  7964. end
  7965.  
  7966. ContextActionService:BindActionAtPriority(
  7967. DYNAMIC_THUMBSTICK_ACTION_NAME,
  7968. handleInput,
  7969. false,
  7970. DYNAMIC_THUMBSTICK_ACTION_PRIORITY,
  7971. Enum.UserInputType.Touch)
  7972. end
  7973.  
  7974. function DynamicThumbstick:Create(parentFrame)
  7975. if self.thumbstickFrame then
  7976. self.thumbstickFrame:Destroy()
  7977. self.thumbstickFrame = nil
  7978. if self.onRenderSteppedConn then
  7979. self.onRenderSteppedConn:Disconnect()
  7980. self.onRenderSteppedConn = nil
  7981. end
  7982. end
  7983.  
  7984. self.thumbstickSize = 45
  7985. self.thumbstickRingSize = 20
  7986. self.middleSize = 10
  7987. self.middleSpacing = self.middleSize + 4
  7988. self.radiusOfDeadZone = 2
  7989. self.radiusOfMaxSpeed = 20
  7990.  
  7991. local screenSize = parentFrame.AbsoluteSize
  7992. local isBigScreen = math.min(screenSize.x, screenSize.y) > 500
  7993. if isBigScreen then
  7994. self.thumbstickSize = self.thumbstickSize * 2
  7995. self.thumbstickRingSize = self.thumbstickRingSize * 2
  7996. self.middleSize = self.middleSize * 2
  7997. self.middleSpacing = self.middleSpacing * 2
  7998. self.radiusOfDeadZone = self.radiusOfDeadZone * 2
  7999. self.radiusOfMaxSpeed = self.radiusOfMaxSpeed * 2
  8000. end
  8001.  
  8002. local function layoutThumbstickFrame(portraitMode)
  8003. if portraitMode then
  8004. self.thumbstickFrame.Size = UDim2.new(1, 0, 0.4, 0)
  8005. self.thumbstickFrame.Position = UDim2.new(0, 0, 0.6, 0)
  8006. else
  8007. self.thumbstickFrame.Size = UDim2.new(0.4, 0, 2/3, 0)
  8008. self.thumbstickFrame.Position = UDim2.new(0, 0, 1/3, 0)
  8009. end
  8010. end
  8011.  
  8012. self.thumbstickFrame = Instance.new("Frame")
  8013. self.thumbstickFrame.BorderSizePixel = 0
  8014. self.thumbstickFrame.Name = "DynamicThumbstickFrame"
  8015. self.thumbstickFrame.Visible = false
  8016. self.thumbstickFrame.BackgroundTransparency = 1.0
  8017. self.thumbstickFrame.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
  8018. self.thumbstickFrame.Active = false
  8019. layoutThumbstickFrame(false)
  8020.  
  8021. self.startImage = Instance.new("ImageLabel")
  8022. self.startImage.Name = "ThumbstickStart"
  8023. self.startImage.Visible = true
  8024. self.startImage.BackgroundTransparency = 1
  8025. self.startImage.Image = TOUCH_CONTROLS_SHEET
  8026. self.startImage.ImageRectOffset = Vector2.new(1,1)
  8027. self.startImage.ImageRectSize = Vector2.new(144, 144)
  8028. self.startImage.ImageColor3 = Color3.new(0, 0, 0)
  8029. self.startImage.AnchorPoint = Vector2.new(0.5, 0.5)
  8030. self.startImage.Position = UDim2.new(0, self.thumbstickRingSize * 3.3, 1, -self.thumbstickRingSize * 2.8)
  8031. self.startImage.Size = UDim2.new(0, self.thumbstickRingSize * 3.7, 0, self.thumbstickRingSize * 3.7)
  8032. self.startImage.ZIndex = 10
  8033. self.startImage.Parent = self.thumbstickFrame
  8034.  
  8035. self.endImage = Instance.new("ImageLabel")
  8036. self.endImage.Name = "ThumbstickEnd"
  8037. self.endImage.Visible = true
  8038. self.endImage.BackgroundTransparency = 1
  8039. self.endImage.Image = TOUCH_CONTROLS_SHEET
  8040. self.endImage.ImageRectOffset = Vector2.new(1,1)
  8041. self.endImage.ImageRectSize = Vector2.new(144, 144)
  8042. self.endImage.AnchorPoint = Vector2.new(0.5, 0.5)
  8043. self.endImage.Position = self.startImage.Position
  8044. self.endImage.Size = UDim2.new(0, self.thumbstickSize * 0.8, 0, self.thumbstickSize * 0.8)
  8045. self.endImage.ZIndex = 10
  8046. self.endImage.Parent = self.thumbstickFrame
  8047.  
  8048. for i = 1, NUM_MIDDLE_IMAGES do
  8049. self.middleImages[i] = Instance.new("ImageLabel")
  8050. self.middleImages[i].Name = "ThumbstickMiddle"
  8051. self.middleImages[i].Visible = false
  8052. self.middleImages[i].BackgroundTransparency = 1
  8053. self.middleImages[i].Image = TOUCH_CONTROLS_SHEET
  8054. self.middleImages[i].ImageRectOffset = Vector2.new(1,1)
  8055. self.middleImages[i].ImageRectSize = Vector2.new(144, 144)
  8056. self.middleImages[i].ImageTransparency = MIDDLE_TRANSPARENCIES[i]
  8057. self.middleImages[i].AnchorPoint = Vector2.new(0.5, 0.5)
  8058. self.middleImages[i].ZIndex = 9
  8059. self.middleImages[i].Parent = self.thumbstickFrame
  8060. end
  8061.  
  8062. local CameraChangedConn = nil
  8063. local function onCurrentCameraChanged()
  8064. if CameraChangedConn then
  8065. CameraChangedConn:Disconnect()
  8066. CameraChangedConn = nil
  8067. end
  8068. local newCamera = workspace.CurrentCamera
  8069. if newCamera then
  8070. local function onViewportSizeChanged()
  8071. local size = newCamera.ViewportSize
  8072. local portraitMode = size.X < size.Y
  8073. layoutThumbstickFrame(portraitMode)
  8074. end
  8075. CameraChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(onViewportSizeChanged)
  8076. onViewportSizeChanged()
  8077. end
  8078. end
  8079. workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onCurrentCameraChanged)
  8080. if workspace.CurrentCamera then
  8081. onCurrentCameraChanged()
  8082. end
  8083.  
  8084. self.moveTouchStartPosition = nil
  8085.  
  8086. self.startImageFadeTween = nil
  8087. self.endImageFadeTween = nil
  8088. self.middleImageFadeTweens = {}
  8089.  
  8090. self.onRenderSteppedConn = RunService.RenderStepped:Connect(function()
  8091. if self.tweenInAlphaStart ~= nil then
  8092. local delta = tick() - self.tweenInAlphaStart
  8093. local fadeInTime = (self.fadeInAndOutHalfDuration * 2 * self.fadeInAndOutBalance)
  8094. self.thumbstickFrame.BackgroundTransparency = 1 - FADE_IN_OUT_MAX_ALPHA*math.min(delta/fadeInTime, 1)
  8095. if delta > fadeInTime then
  8096. self.tweenOutAlphaStart = tick()
  8097. self.tweenInAlphaStart = nil
  8098. end
  8099. elseif self.tweenOutAlphaStart ~= nil then
  8100. local delta = tick() - self.tweenOutAlphaStart
  8101. local fadeOutTime = (self.fadeInAndOutHalfDuration * 2) - (self.fadeInAndOutHalfDuration * 2 * self.fadeInAndOutBalance)
  8102. self.thumbstickFrame.BackgroundTransparency = 1 - FADE_IN_OUT_MAX_ALPHA + FADE_IN_OUT_MAX_ALPHA*math.min(delta/fadeOutTime, 1)
  8103. if delta > fadeOutTime then
  8104. self.tweenOutAlphaStart = nil
  8105. end
  8106. end
  8107. end)
  8108.  
  8109. self.onTouchEndedConn = UserInputService.TouchEnded:connect(function(inputObject)
  8110. if inputObject == self.moveTouchObject then
  8111. self:OnInputEnded()
  8112. end
  8113. end)
  8114.  
  8115. GuiService.MenuOpened:connect(function()
  8116. if self.moveTouchObject then
  8117. self:OnInputEnded()
  8118. end
  8119. end)
  8120.  
  8121. local playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
  8122. while not playerGui do
  8123. LocalPlayer.ChildAdded:wait()
  8124. playerGui = LocalPlayer:FindFirstChildOfClass("PlayerGui")
  8125. end
  8126.  
  8127. local playerGuiChangedConn = nil
  8128. local originalScreenOrientationWasLandscape = playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeLeft or
  8129. playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.LandscapeRight
  8130.  
  8131. local function longShowBackground()
  8132. self.fadeInAndOutHalfDuration = 2.5
  8133. self.fadeInAndOutBalance = 0.05
  8134. self.tweenInAlphaStart = tick()
  8135. end
  8136.  
  8137. playerGuiChangedConn = playerGui:GetPropertyChangedSignal("CurrentScreenOrientation"):Connect(function()
  8138. if (originalScreenOrientationWasLandscape and playerGui.CurrentScreenOrientation == Enum.ScreenOrientation.Portrait) or
  8139. (not originalScreenOrientationWasLandscape and playerGui.CurrentScreenOrientation ~= Enum.ScreenOrientation.Portrait) then
  8140.  
  8141. playerGuiChangedConn:disconnect()
  8142. longShowBackground()
  8143.  
  8144. if originalScreenOrientationWasLandscape then
  8145. self.hasFadedBackgroundInPortrait = true
  8146. else
  8147. self.hasFadedBackgroundInLandscape = true
  8148. end
  8149. end
  8150. end)
  8151.  
  8152. self.thumbstickFrame.Parent = parentFrame
  8153.  
  8154. if game:IsLoaded() then
  8155. longShowBackground()
  8156. else
  8157. coroutine.wrap(function()
  8158. game.Loaded:Wait()
  8159. longShowBackground()
  8160. end)()
  8161. end
  8162. end
  8163.  
  8164. return DynamicThumbstick
  8165. end
  8166.  
  8167. function _Gamepad()
  8168. local UserInputService = game:GetService("UserInputService")
  8169. local ContextActionService = game:GetService("ContextActionService")
  8170.  
  8171. --[[ Constants ]]--
  8172. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  8173. local NONE = Enum.UserInputType.None
  8174. local thumbstickDeadzone = 0.2
  8175.  
  8176. --[[ The Module ]]--
  8177. local BaseCharacterController = _BaseCharacterController()
  8178. local Gamepad = setmetatable({}, BaseCharacterController)
  8179. Gamepad.__index = Gamepad
  8180.  
  8181. function Gamepad.new(CONTROL_ACTION_PRIORITY)
  8182. local self = setmetatable(BaseCharacterController.new(), Gamepad)
  8183.  
  8184. self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
  8185.  
  8186. self.forwardValue = 0
  8187. self.backwardValue = 0
  8188. self.leftValue = 0
  8189. self.rightValue = 0
  8190.  
  8191. self.activeGamepad = NONE -- Enum.UserInputType.Gamepad1, 2, 3...
  8192. self.gamepadConnectedConn = nil
  8193. self.gamepadDisconnectedConn = nil
  8194. return self
  8195. end
  8196.  
  8197. function Gamepad:Enable(enable)
  8198. if not UserInputService.GamepadEnabled then
  8199. return false
  8200. end
  8201.  
  8202. if enable == self.enabled then
  8203. -- Module is already in the state being requested. True is returned here since the module will be in the state
  8204. -- expected by the code that follows the Enable() call. This makes more sense than returning false to indicate
  8205. -- no action was necessary. False indicates failure to be in requested/expected state.
  8206. return true
  8207. end
  8208.  
  8209. self.forwardValue = 0
  8210. self.backwardValue = 0
  8211. self.leftValue = 0
  8212. self.rightValue = 0
  8213. self.moveVector = ZERO_VECTOR3
  8214. self.isJumping = false
  8215.  
  8216. if enable then
  8217. self.activeGamepad = self:GetHighestPriorityGamepad()
  8218. if self.activeGamepad ~= NONE then
  8219. self:BindContextActions()
  8220. self:ConnectGamepadConnectionListeners()
  8221. else
  8222. -- No connected gamepads, failure to enable
  8223. return false
  8224. end
  8225. else
  8226. self:UnbindContextActions()
  8227. self:DisconnectGamepadConnectionListeners()
  8228. self.activeGamepad = NONE
  8229. end
  8230.  
  8231. self.enabled = enable
  8232. return true
  8233. end
  8234.  
  8235. -- This function selects the lowest number gamepad from the currently-connected gamepad
  8236. -- and sets it as the active gamepad
  8237. function Gamepad:GetHighestPriorityGamepad()
  8238. local connectedGamepads = UserInputService:GetConnectedGamepads()
  8239. local bestGamepad = NONE -- Note that this value is higher than all valid gamepad values
  8240. for _, gamepad in pairs(connectedGamepads) do
  8241. if gamepad.Value < bestGamepad.Value then
  8242. bestGamepad = gamepad
  8243. end
  8244. end
  8245. return bestGamepad
  8246. end
  8247.  
  8248. function Gamepad:BindContextActions()
  8249.  
  8250. if self.activeGamepad == NONE then
  8251. -- There must be an active gamepad to set up bindings
  8252. return false
  8253. end
  8254.  
  8255. local handleJumpAction = function(actionName, inputState, inputObject)
  8256. self.isJumping = (inputState == Enum.UserInputState.Begin)
  8257. return Enum.ContextActionResult.Sink
  8258. end
  8259.  
  8260. local handleThumbstickInput = function(actionName, inputState, inputObject)
  8261.  
  8262. if inputState == Enum.UserInputState.Cancel then
  8263. self.moveVector = ZERO_VECTOR3
  8264. return Enum.ContextActionResult.Sink
  8265. end
  8266.  
  8267. if self.activeGamepad ~= inputObject.UserInputType then
  8268. return Enum.ContextActionResult.Pass
  8269. end
  8270. if inputObject.KeyCode ~= Enum.KeyCode.Thumbstick1 then return end
  8271.  
  8272. if inputObject.Position.magnitude > thumbstickDeadzone then
  8273. self.moveVector = Vector3.new(inputObject.Position.X, 0, -inputObject.Position.Y)
  8274. else
  8275. self.moveVector = ZERO_VECTOR3
  8276. end
  8277. return Enum.ContextActionResult.Sink
  8278. end
  8279.  
  8280. ContextActionService:BindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8281. ContextActionService:BindActionAtPriority("jumpAction", handleJumpAction, false,
  8282. self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.ButtonA)
  8283. ContextActionService:BindActionAtPriority("moveThumbstick", handleThumbstickInput, false,
  8284. self.CONTROL_ACTION_PRIORITY, Enum.KeyCode.Thumbstick1)
  8285.  
  8286. return true
  8287. end
  8288.  
  8289. function Gamepad:UnbindContextActions()
  8290. if self.activeGamepad ~= NONE then
  8291. ContextActionService:UnbindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8292. end
  8293. ContextActionService:UnbindAction("moveThumbstick")
  8294. ContextActionService:UnbindAction("jumpAction")
  8295. end
  8296.  
  8297. function Gamepad:OnNewGamepadConnected()
  8298. -- A new gamepad has been connected.
  8299. local bestGamepad = self:GetHighestPriorityGamepad()
  8300.  
  8301. if bestGamepad == self.activeGamepad then
  8302. -- A new gamepad was connected, but our active gamepad is not changing
  8303. return
  8304. end
  8305.  
  8306. if bestGamepad == NONE then
  8307. -- There should be an active gamepad when GamepadConnected fires, so this should not
  8308. -- normally be hit. If there is no active gamepad, unbind actions but leave
  8309. -- the module enabled and continue to listen for a new gamepad connection.
  8310. warn("Gamepad:OnNewGamepadConnected found no connected gamepads")
  8311. self:UnbindContextActions()
  8312. return
  8313. end
  8314.  
  8315. if self.activeGamepad ~= NONE then
  8316. -- Switching from one active gamepad to another
  8317. self:UnbindContextActions()
  8318. end
  8319.  
  8320. self.activeGamepad = bestGamepad
  8321. self:BindContextActions()
  8322. end
  8323.  
  8324. function Gamepad:OnCurrentGamepadDisconnected()
  8325. if self.activeGamepad ~= NONE then
  8326. ContextActionService:UnbindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8327. end
  8328.  
  8329. local bestGamepad = self:GetHighestPriorityGamepad()
  8330.  
  8331. if self.activeGamepad ~= NONE and bestGamepad == self.activeGamepad then
  8332. warn("Gamepad:OnCurrentGamepadDisconnected found the supposedly disconnected gamepad in connectedGamepads.")
  8333. self:UnbindContextActions()
  8334. self.activeGamepad = NONE
  8335. return
  8336. end
  8337.  
  8338. if bestGamepad == NONE then
  8339. -- No active gamepad, unbinding actions but leaving gamepad connection listener active
  8340. self:UnbindContextActions()
  8341. self.activeGamepad = NONE
  8342. else
  8343. -- Set new gamepad as active and bind to tool activation
  8344. self.activeGamepad = bestGamepad
  8345. ContextActionService:BindActivate(self.activeGamepad, Enum.KeyCode.ButtonR2)
  8346. end
  8347. end
  8348.  
  8349. function Gamepad:ConnectGamepadConnectionListeners()
  8350. self.gamepadConnectedConn = UserInputService.GamepadConnected:Connect(function(gamepadEnum)
  8351. self:OnNewGamepadConnected()
  8352. end)
  8353.  
  8354. self.gamepadDisconnectedConn = UserInputService.GamepadDisconnected:Connect(function(gamepadEnum)
  8355. if self.activeGamepad == gamepadEnum then
  8356. self:OnCurrentGamepadDisconnected()
  8357. end
  8358. end)
  8359.  
  8360. end
  8361.  
  8362. function Gamepad:DisconnectGamepadConnectionListeners()
  8363. if self.gamepadConnectedConn then
  8364. self.gamepadConnectedConn:Disconnect()
  8365. self.gamepadConnectedConn = nil
  8366. end
  8367.  
  8368. if self.gamepadDisconnectedConn then
  8369. self.gamepadDisconnectedConn:Disconnect()
  8370. self.gamepadDisconnectedConn = nil
  8371. end
  8372. end
  8373.  
  8374. return Gamepad
  8375. end
  8376.  
  8377. function _Keyboard()
  8378.  
  8379. --[[ Roblox Services ]]--
  8380. local UserInputService = game:GetService("UserInputService")
  8381. local ContextActionService = game:GetService("ContextActionService")
  8382.  
  8383. --[[ Constants ]]--
  8384. local ZERO_VECTOR3 = Vector3.new(0,0,0)
  8385.  
  8386. --[[ The Module ]]--
  8387. local BaseCharacterController = _BaseCharacterController()
  8388. local Keyboard = setmetatable({}, BaseCharacterController)
  8389. Keyboard.__index = Keyboard
  8390.  
  8391. function Keyboard.new(CONTROL_ACTION_PRIORITY)
  8392. local self = setmetatable(BaseCharacterController.new(), Keyboard)
  8393.  
  8394. self.CONTROL_ACTION_PRIORITY = CONTROL_ACTION_PRIORITY
  8395.  
  8396. self.textFocusReleasedConn = nil
  8397. self.textFocusGainedConn = nil
  8398. self.windowFocusReleasedConn = nil
  8399.  
  8400. self.forwardValue = 0
  8401. self.backwardValue = 0
  8402. self.leftValue = 0
  8403. self.rightValue = 0
  8404.  
  8405. self.jumpEnabled = true
  8406.  
  8407. return self
  8408. end
  8409.  
  8410. function Keyboard:Enable(enable)
  8411. if not UserInputService.KeyboardEnabled then
  8412. return false
  8413. end
  8414.  
  8415. if enable == self.enabled then
  8416. -- Module is already in the state being requested. True is returned here since the module will be in the state
  8417. -- expected by the code that follows the Enable() call. This makes more sense than returning false to indicate
  8418. -- no action was necessary. False indicates failure to be in requested/expected state.
  8419. return true
  8420. end
  8421.  
  8422. self.forwardValue = 0
  8423. self.backwardValue = 0
  8424. self.leftValue = 0
  8425. self.rightValue = 0
  8426. self.moveVector = ZERO_VECTOR3
  8427. self.jumpRequested = false
  8428. self:UpdateJump()
  8429.  
  8430. if enable then
  8431. self:BindContextActions()
  8432. self:ConnectFocusEventListeners()
  8433. else
  8434. self:UnbindContextActions()
  8435. self:DisconnectFocusEventListeners()
  8436. end
  8437.  
  8438. self.enabled = enable
  8439. return true
  8440. end
  8441.  
  8442. function Keyboard:UpdateMovement(inputState)
  8443. if inputState == Enum.UserInputState.Cancel then
  8444. self.moveVector = ZERO_VECTOR3
  8445. else
  8446. self.moveVector = Vector3.new(self.leftValue + self.rightValue, 0, self.forwardValue + self.backwardValue)
  8447. end
  8448. end
  8449.  
  8450. function Keyboard:UpdateJump()
  8451. self.isJumping = self.jumpRequested
  8452. end
  8453.  
  8454. function Keyboard:BindContextActions()
  8455.  
  8456. -- Note: In the previous version of this code, the movement values were not zeroed-out on UserInputState. Cancel, now they are,
  8457. -- which fixes them from getting stuck on.
  8458. -- We return ContextActionResult.Pass here for legacy reasons.
  8459. -- Many games rely on gameProcessedEvent being false on UserInputService.InputBegan for these control actions.
  8460. local handleMoveForward = function(actionName, inputState, inputObject)
  8461. self.forwardValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
  8462. self:UpdateMovement(inputState)
  8463. return Enum.ContextActionResult.Pass
  8464. end
  8465.  
  8466. local handleMoveBackward = function(actionName, inputState, inputObject)
  8467. self.backwardValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
  8468. self:UpdateMovement(inputState)
  8469. return Enum.ContextActionResult.Pass
  8470. end
  8471.  
  8472. local handleMoveLeft = function(actionName, inputState, inputObject)
  8473. self.leftValue = (inputState == Enum.UserInputState.Begin) and -1 or 0
  8474. self:UpdateMovement(inputState)
  8475. return Enum.ContextActionResult.Pass
  8476. end
  8477.  
  8478. local handleMoveRight = function(actionName, inputState, inputObject)
  8479. self.rightValue = (inputState == Enum.UserInputState.Begin) and 1 or 0
  8480. self:UpdateMovement(inputState)
  8481. return Enum.ContextActionResult.Pass
  8482. end
  8483.  
  8484. local handleJumpAction = function(actionName, inputState, inputObject)
  8485. self.jumpRequested = self.jumpEnabled and (inputState == Enum.UserInputState.Begin)
  8486. self:UpdateJump()
  8487. return Enum.ContextActionResult.Pass
  8488. end
  8489.  
  8490. -- TODO: Revert to KeyCode bindings so that in the future the abstraction layer from actual keys to
  8491. -- movement direction is done in Lua
  8492. ContextActionService:BindActionAtPriority("moveForwardAction", handleMoveForward, false,
  8493. self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterForward)
  8494. ContextActionService:BindActionAtPriority("moveBackwardAction", handleMoveBackward, false,
  8495. self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterBackward)
  8496. ContextActionService:BindActionAtPriority("moveLeftAction", handleMoveLeft, false,
  8497. self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterLeft)
  8498. ContextActionService:BindActionAtPriority("moveRightAction", handleMoveRight, false,
  8499. self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterRight)
  8500. ContextActionService:BindActionAtPriority("jumpAction", handleJumpAction, false,
  8501. self.CONTROL_ACTION_PRIORITY, Enum.PlayerActions.CharacterJump)
  8502. end
  8503.  
  8504. function Keyboard:UnbindContextActions()
  8505. ContextActionService:UnbindAction("moveForwardAction")
  8506. ContextActionService:UnbindAction("moveBackwardAction")
  8507. ContextActionService:UnbindAction("moveLeftAction")
  8508. ContextActionService:UnbindAction("moveRightAction")
  8509. ContextActionService:UnbindAction("jumpAction")
  8510. end
  8511.  
  8512. function Keyboard:ConnectFocusEventListeners()
  8513. local function onFocusReleased()
  8514. self.moveVector = ZERO_VECTOR3
  8515. self.forwardValue = 0
  8516. self.backwardValue = 0
  8517. self.leftValue = 0
  8518. self.rightValue = 0
  8519. self.jumpRequested = false
  8520. self:UpdateJump()
  8521. end
  8522.  
  8523. local function onTextFocusGained(textboxFocused)
  8524. self.jumpRequested = false
  8525. self:UpdateJump()
  8526. end
  8527.  
  8528. self.textFocusReleasedConn = UserInputService.TextBoxFocusReleased:Connect(onFocusReleased)
  8529. self.textFocusGainedConn = UserInputService.TextBoxFocused:Connect(onTextFocusGained)
  8530. self.windowFocusReleasedConn = UserInputService.WindowFocused:Connect(onFocusReleased)
  8531. end
  8532.  
  8533. function Keyboard:DisconnectFocusEventListeners()
  8534. if self.textFocusReleasedCon then
  8535. self.textFocusReleasedCon:Disconnect()
  8536. self.textFocusReleasedCon = nil
  8537. end
  8538. if self.textFocusGainedConn then
  8539. self.textFocusGainedConn:Disconnect()
  8540. self.textFocusGainedConn = nil
  8541. end
  8542. if self.windowFocusReleasedConn then
  8543. self.windowFocusReleasedConn:Disconnect()
  8544. self.windowFocusReleasedConn = nil
  8545. end
  8546. end
  8547.  
  8548. return Keyboard
  8549. end
  8550.  
  8551. function _ControlModule()
  8552. local ControlModule = {}
  8553. ControlModule.__index = ControlModule
  8554.  
  8555. --[[ Roblox Services ]]--
  8556. local Players = game:GetService("Players")
  8557. local RunService = game:GetService("RunService")
  8558. local UserInputService = game:GetService("UserInputService")
  8559. local Workspace = game:GetService("Workspace")
  8560. local UserGameSettings = UserSettings():GetService("UserGameSettings")
  8561.  
  8562. -- Roblox User Input Control Modules - each returns a new() constructor function used to create controllers as needed
  8563. local Keyboard = _Keyboard()
  8564. local Gamepad = _Gamepad()
  8565. local DynamicThumbstick = _DynamicThumbstick()
  8566.  
  8567. local FFlagUserMakeThumbstickDynamic do
  8568. local success, value = pcall(function()
  8569. return UserSettings():IsUserFeatureEnabled("UserMakeThumbstickDynamic")
  8570. end)
  8571. FFlagUserMakeThumbstickDynamic = success and value
  8572. end
  8573.  
  8574. local TouchThumbstick = FFlagUserMakeThumbstickDynamic and DynamicThumbstick or _TouchThumbstick()
  8575.  
  8576. -- These controllers handle only walk/run movement, jumping is handled by the
  8577. -- TouchJump controller if any of these are active
  8578. local ClickToMove = _ClickToMoveController()
  8579. local TouchJump = _TouchJump()
  8580.  
  8581. local VehicleController = _VehicleController()
  8582.  
  8583. local CONTROL_ACTION_PRIORITY = Enum.ContextActionPriority.Default.Value
  8584.  
  8585. -- Mapping from movement mode and lastInputType enum values to control modules to avoid huge if elseif switching
  8586. local movementEnumToModuleMap = {
  8587. [Enum.TouchMovementMode.DPad] = DynamicThumbstick,
  8588. [Enum.DevTouchMovementMode.DPad] = DynamicThumbstick,
  8589. [Enum.TouchMovementMode.Thumbpad] = DynamicThumbstick,
  8590. [Enum.DevTouchMovementMode.Thumbpad] = DynamicThumbstick,
  8591. [Enum.TouchMovementMode.Thumbstick] = TouchThumbstick,
  8592. [Enum.DevTouchMovementMode.Thumbstick] = TouchThumbstick,
  8593. [Enum.TouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
  8594. [Enum.DevTouchMovementMode.DynamicThumbstick] = DynamicThumbstick,
  8595. [Enum.TouchMovementMode.ClickToMove] = ClickToMove,
  8596. [Enum.DevTouchMovementMode.ClickToMove] = ClickToMove,
  8597.  
  8598. -- Current default
  8599. [Enum.TouchMovementMode.Default] = DynamicThumbstick,
  8600.  
  8601. [Enum.ComputerMovementMode.Default] = Keyboard,
  8602. [Enum.ComputerMovementMode.KeyboardMouse] = Keyboard,
  8603. [Enum.DevComputerMovementMode.KeyboardMouse] = Keyboard,
  8604. [Enum.DevComputerMovementMode.Scriptable] = nil,
  8605. [Enum.ComputerMovementMode.ClickToMove] = ClickToMove,
  8606. [Enum.DevComputerMovementMode.ClickToMove] = ClickToMove,
  8607. }
  8608.  
  8609. -- Keyboard controller is really keyboard and mouse controller
  8610. local computerInputTypeToModuleMap = {
  8611. [Enum.UserInputType.Keyboard] = Keyboard,
  8612. [Enum.UserInputType.MouseButton1] = Keyboard,
  8613. [Enum.UserInputType.MouseButton2] = Keyboard,
  8614. [Enum.UserInputType.MouseButton3] = Keyboard,
  8615. [Enum.UserInputType.MouseWheel] = Keyboard,
  8616. [Enum.UserInputType.MouseMovement] = Keyboard,
  8617. [Enum.UserInputType.Gamepad1] = Gamepad,
  8618. [Enum.UserInputType.Gamepad2] = Gamepad,
  8619. [Enum.UserInputType.Gamepad3] = Gamepad,
  8620. [Enum.UserInputType.Gamepad4] = Gamepad,
  8621. }
  8622.  
  8623. local lastInputType
  8624.  
  8625. function ControlModule.new()
  8626. local self = setmetatable({},ControlModule)
  8627.  
  8628. -- The Modules above are used to construct controller instances as-needed, and this
  8629. -- table is a map from Module to the instance created from it
  8630. self.controllers = {}
  8631.  
  8632. self.activeControlModule = nil -- Used to prevent unnecessarily expensive checks on each input event
  8633. self.activeController = nil
  8634. self.touchJumpController = nil
  8635. self.moveFunction = Players.LocalPlayer.Move
  8636. self.humanoid = nil
  8637. self.lastInputType = Enum.UserInputType.None
  8638.  
  8639. -- For Roblox self.vehicleController
  8640. self.humanoidSeatedConn = nil
  8641. self.vehicleController = nil
  8642.  
  8643. self.touchControlFrame = nil
  8644.  
  8645. self.vehicleController = VehicleController.new(CONTROL_ACTION_PRIORITY)
  8646.  
  8647. Players.LocalPlayer.CharacterAdded:Connect(function(char) self:OnCharacterAdded(char) end)
  8648. Players.LocalPlayer.CharacterRemoving:Connect(function(char) self:OnCharacterRemoving(char) end)
  8649. if Players.LocalPlayer.Character then
  8650. self:OnCharacterAdded(Players.LocalPlayer.Character)
  8651. end
  8652.  
  8653. RunService:BindToRenderStep("ControlScriptRenderstep", Enum.RenderPriority.Input.Value, function(dt)
  8654. self:OnRenderStepped(dt)
  8655. end)
  8656.  
  8657. UserInputService.LastInputTypeChanged:Connect(function(newLastInputType)
  8658. self:OnLastInputTypeChanged(newLastInputType)
  8659. end)
  8660.  
  8661.  
  8662. UserGameSettings:GetPropertyChangedSignal("TouchMovementMode"):Connect(function()
  8663. self:OnTouchMovementModeChange()
  8664. end)
  8665. Players.LocalPlayer:GetPropertyChangedSignal("DevTouchMovementMode"):Connect(function()
  8666. self:OnTouchMovementModeChange()
  8667. end)
  8668.  
  8669. UserGameSettings:GetPropertyChangedSignal("ComputerMovementMode"):Connect(function()
  8670. self:OnComputerMovementModeChange()
  8671. end)
  8672. Players.LocalPlayer:GetPropertyChangedSignal("DevComputerMovementMode"):Connect(function()
  8673. self:OnComputerMovementModeChange()
  8674. end)
  8675.  
  8676. --[[ Touch Device UI ]]--
  8677. self.playerGui = nil
  8678. self.touchGui = nil
  8679. self.playerGuiAddedConn = nil
  8680.  
  8681. if UserInputService.TouchEnabled then
  8682. self.playerGui = Players.LocalPlayer:FindFirstChildOfClass("PlayerGui")
  8683. if self.playerGui then
  8684. self:CreateTouchGuiContainer()
  8685. self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
  8686. else
  8687. self.playerGuiAddedConn = Players.LocalPlayer.ChildAdded:Connect(function(child)
  8688. if child:IsA("PlayerGui") then
  8689. self.playerGui = child
  8690. self:CreateTouchGuiContainer()
  8691. self.playerGuiAddedConn:Disconnect()
  8692. self.playerGuiAddedConn = nil
  8693. self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
  8694. end
  8695. end)
  8696. end
  8697. else
  8698. self:OnLastInputTypeChanged(UserInputService:GetLastInputType())
  8699. end
  8700.  
  8701. return self
  8702. end
  8703.  
  8704. -- Convenience function so that calling code does not have to first get the activeController
  8705. -- and then call GetMoveVector on it. When there is no active controller, this function returns
  8706. -- nil so that this case can be distinguished from no current movement (which returns zero vector).
  8707. function ControlModule:GetMoveVector()
  8708. if self.activeController then
  8709. return self.activeController:GetMoveVector()
  8710. end
  8711. return Vector3.new(0,0,0)
  8712. end
  8713.  
  8714. function ControlModule:GetActiveController()
  8715. return self.activeController
  8716. end
  8717.  
  8718. function ControlModule:EnableActiveControlModule()
  8719. if self.activeControlModule == ClickToMove then
  8720. -- For ClickToMove, when it is the player's choice, we also enable the full keyboard controls.
  8721. -- When the developer is forcing click to move, the most keyboard controls (WASD) are not available, only jump.
  8722. self.activeController:Enable(
  8723. true,
  8724. Players.LocalPlayer.DevComputerMovementMode == Enum.DevComputerMovementMode.UserChoice,
  8725. self.touchJumpController
  8726. )
  8727. elseif self.touchControlFrame then
  8728. self.activeController:Enable(true, self.touchControlFrame)
  8729. else
  8730. self.activeController:Enable(true)
  8731. end
  8732. end
  8733.  
  8734. function ControlModule:Enable(enable)
  8735. if not self.activeController then
  8736. return
  8737. end
  8738.  
  8739. if enable == nil then
  8740. enable = true
  8741. end
  8742. if enable then
  8743. self:EnableActiveControlModule()
  8744. else
  8745. self:Disable()
  8746. end
  8747. end
  8748.  
  8749. -- For those who prefer distinct functions
  8750. function ControlModule:Disable()
  8751. if self.activeController then
  8752. self.activeController:Enable(false)
  8753.  
  8754. if self.moveFunction then
  8755. self.moveFunction(Players.LocalPlayer, Vector3.new(0,0,0), true)
  8756. end
  8757. end
  8758. end
  8759.  
  8760.  
  8761. -- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
  8762. function ControlModule:SelectComputerMovementModule()
  8763. if not (UserInputService.KeyboardEnabled or UserInputService.GamepadEnabled) then
  8764. return nil, false
  8765. end
  8766.  
  8767. local computerModule
  8768. local DevMovementMode = Players.LocalPlayer.DevComputerMovementMode
  8769.  
  8770. if DevMovementMode == Enum.DevComputerMovementMode.UserChoice then
  8771. computerModule = computerInputTypeToModuleMap[lastInputType]
  8772. if UserGameSettings.ComputerMovementMode == Enum.ComputerMovementMode.ClickToMove and computerModule == Keyboard then
  8773. -- User has ClickToMove set in Settings, prefer ClickToMove controller for keyboard and mouse lastInputTypes
  8774. computerModule = ClickToMove
  8775. end
  8776. else
  8777. -- Developer has selected a mode that must be used.
  8778. computerModule = movementEnumToModuleMap[DevMovementMode]
  8779.  
  8780. -- computerModule is expected to be nil here only when developer has selected Scriptable
  8781. if (not computerModule) and DevMovementMode ~= Enum.DevComputerMovementMode.Scriptable then
  8782. warn("No character control module is associated with DevComputerMovementMode ", DevMovementMode)
  8783. end
  8784. end
  8785.  
  8786. if computerModule then
  8787. return computerModule, true
  8788. elseif DevMovementMode == Enum.DevComputerMovementMode.Scriptable then
  8789. -- Special case where nil is returned and we actually want to set self.activeController to nil for Scriptable
  8790. return nil, true
  8791. else
  8792. -- This case is for when computerModule is nil because of an error and no suitable control module could
  8793. -- be found.
  8794. return nil, false
  8795. end
  8796. end
  8797.  
  8798. -- Choose current Touch control module based on settings (user, dev)
  8799. -- Returns module (possibly nil) and success code to differentiate returning nil due to error vs Scriptable
  8800. function ControlModule:SelectTouchModule()
  8801. if not UserInputService.TouchEnabled then
  8802. return nil, false
  8803. end
  8804. local touchModule
  8805. local DevMovementMode = Players.LocalPlayer.DevTouchMovementMode
  8806. if DevMovementMode == Enum.DevTouchMovementMode.UserChoice then
  8807. touchModule = movementEnumToModuleMap[UserGameSettings.TouchMovementMode]
  8808. elseif DevMovementMode == Enum.DevTouchMovementMode.Scriptable then
  8809. return nil, true
  8810. else
  8811. touchModule = movementEnumToModuleMap[DevMovementMode]
  8812. end
  8813. return touchModule, true
  8814. end
  8815.  
  8816. local function calculateRawMoveVector(humanoid, cameraRelativeMoveVector)
  8817. local camera = Workspace.CurrentCamera
  8818. if not camera then
  8819. return cameraRelativeMoveVector
  8820. end
  8821.  
  8822. if humanoid:GetState() == Enum.HumanoidStateType.Swimming then
  8823. return camera.CFrame:VectorToWorldSpace(cameraRelativeMoveVector)
  8824. end
  8825.  
  8826. local c, s
  8827. local _, _, _, R00, R01, R02, _, _, R12, _, _, R22 = camera.CFrame:GetComponents()
  8828. if R12 < 1 and R12 > -1 then
  8829. -- X and Z components from back vector.
  8830. c = R22
  8831. s = R02
  8832. else
  8833. -- In this case the camera is looking straight up or straight down.
  8834. -- Use X components from right and up vectors.
  8835. c = R00
  8836. s = -R01*math.sign(R12)
  8837. end
  8838. local norm = math.sqrt(c*c + s*s)
  8839. return Vector3.new(
  8840. (c*cameraRelativeMoveVector.x + s*cameraRelativeMoveVector.z)/norm,
  8841. 0,
  8842. (c*cameraRelativeMoveVector.z - s*cameraRelativeMoveVector.x)/norm
  8843. )
  8844. end
  8845.  
  8846. function ControlModule:OnRenderStepped(dt)
  8847. if self.activeController and self.activeController.enabled and self.humanoid then
  8848. -- Give the controller a chance to adjust its state
  8849. self.activeController:OnRenderStepped(dt)
  8850.  
  8851. -- Now retrieve info from the controller
  8852. local moveVector = self.activeController:GetMoveVector()
  8853. local cameraRelative = self.activeController:IsMoveVectorCameraRelative()
  8854.  
  8855. local clickToMoveController = self:GetClickToMoveController()
  8856. if self.activeController ~= clickToMoveController then
  8857. if moveVector.magnitude > 0 then
  8858. -- Clean up any developer started MoveTo path
  8859. clickToMoveController:CleanupPath()
  8860. else
  8861. -- Get move vector for developer started MoveTo
  8862. clickToMoveController:OnRenderStepped(dt)
  8863. moveVector = clickToMoveController:GetMoveVector()
  8864. cameraRelative = clickToMoveController:IsMoveVectorCameraRelative()
  8865. end
  8866. end
  8867.  
  8868. -- Are we driving a vehicle ?
  8869. local vehicleConsumedInput = false
  8870. if self.vehicleController then
  8871. moveVector, vehicleConsumedInput = self.vehicleController:Update(moveVector, cameraRelative, self.activeControlModule==Gamepad)
  8872. end
  8873.  
  8874. -- If not, move the player
  8875. -- Verification of vehicleConsumedInput is commented out to preserve legacy behavior,
  8876. -- in case some game relies on Humanoid.MoveDirection still being set while in a VehicleSeat
  8877. --if not vehicleConsumedInput then
  8878. if cameraRelative then
  8879. moveVector = calculateRawMoveVector(self.humanoid, moveVector)
  8880. end
  8881. self.moveFunction(Players.LocalPlayer, moveVector, false)
  8882. --end
  8883.  
  8884. -- And make them jump if needed
  8885. self.humanoid.Jump = self.activeController:GetIsJumping() or (self.touchJumpController and self.touchJumpController:GetIsJumping())
  8886. end
  8887. end
  8888.  
  8889. function ControlModule:OnHumanoidSeated(active, currentSeatPart)
  8890. if active then
  8891. if currentSeatPart and currentSeatPart:IsA("VehicleSeat") then
  8892. if not self.vehicleController then
  8893. self.vehicleController = self.vehicleController.new(CONTROL_ACTION_PRIORITY)
  8894. end
  8895. self.vehicleController:Enable(true, currentSeatPart)
  8896. end
  8897. else
  8898. if self.vehicleController then
  8899. self.vehicleController:Enable(false, currentSeatPart)
  8900. end
  8901. end
  8902. end
  8903.  
  8904. function ControlModule:OnCharacterAdded(char)
  8905. self.humanoid = char:FindFirstChildOfClass("Humanoid")
  8906. while not self.humanoid do
  8907. char.ChildAdded:wait()
  8908. self.humanoid = char:FindFirstChildOfClass("Humanoid")
  8909. end
  8910.  
  8911. if self.touchGui then
  8912. self.touchGui.Enabled = true
  8913. end
  8914.  
  8915. if self.humanoidSeatedConn then
  8916. self.humanoidSeatedConn:Disconnect()
  8917. self.humanoidSeatedConn = nil
  8918. end
  8919. self.humanoidSeatedConn = self.humanoid.Seated:Connect(function(active, currentSeatPart)
  8920. self:OnHumanoidSeated(active, currentSeatPart)
  8921. end)
  8922. end
  8923.  
  8924. function ControlModule:OnCharacterRemoving(char)
  8925. self.humanoid = nil
  8926.  
  8927. if self.touchGui then
  8928. self.touchGui.Enabled = false
  8929. end
  8930. end
  8931.  
  8932. -- Helper function to lazily instantiate a controller if it does not yet exist,
  8933. -- disable the active controller if it is different from the on being switched to,
  8934. -- and then enable the requested controller. The argument to this function must be
  8935. -- a reference to one of the control modules, i.e. Keyboard, Gamepad, etc.
  8936. function ControlModule:SwitchToController(controlModule)
  8937. if not controlModule then
  8938. if self.activeController then
  8939. self.activeController:Enable(false)
  8940. end
  8941. self.activeController = nil
  8942. self.activeControlModule = nil
  8943. else
  8944. if not self.controllers[controlModule] then
  8945. self.controllers[controlModule] = controlModule.new(CONTROL_ACTION_PRIORITY)
  8946. end
  8947.  
  8948. if self.activeController ~= self.controllers[controlModule] then
  8949. if self.activeController then
  8950. self.activeController:Enable(false)
  8951. end
  8952. self.activeController = self.controllers[controlModule]
  8953. self.activeControlModule = controlModule -- Only used to check if controller switch is necessary
  8954.  
  8955. if self.touchControlFrame and (self.activeControlModule == ClickToMove
  8956. or self.activeControlModule == TouchThumbstick
  8957. or self.activeControlModule == DynamicThumbstick) then
  8958. if not self.controllers[TouchJump] then
  8959. self.controllers[TouchJump] = TouchJump.new()
  8960. end
  8961. self.touchJumpController = self.controllers[TouchJump]
  8962. self.touchJumpController:Enable(true, self.touchControlFrame)
  8963. else
  8964. if self.touchJumpController then
  8965. self.touchJumpController:Enable(false)
  8966. end
  8967. end
  8968.  
  8969. self:EnableActiveControlModule()
  8970. end
  8971. end
  8972. end
  8973.  
  8974. function ControlModule:OnLastInputTypeChanged(newLastInputType)
  8975. if lastInputType == newLastInputType then
  8976. warn("LastInputType Change listener called with current type.")
  8977. end
  8978. lastInputType = newLastInputType
  8979.  
  8980. if lastInputType == Enum.UserInputType.Touch then
  8981. -- TODO: Check if touch module already active
  8982. local touchModule, success = self:SelectTouchModule()
  8983. if success then
  8984. while not self.touchControlFrame do
  8985. wait()
  8986. end
  8987. self:SwitchToController(touchModule)
  8988. end
  8989. elseif computerInputTypeToModuleMap[lastInputType] ~= nil then
  8990. local computerModule = self:SelectComputerMovementModule()
  8991. if computerModule then
  8992. self:SwitchToController(computerModule)
  8993. end
  8994. end
  8995. end
  8996.  
  8997. -- Called when any relevant values of GameSettings or LocalPlayer change, forcing re-evalulation of
  8998. -- current control scheme
  8999. function ControlModule:OnComputerMovementModeChange()
  9000. local controlModule, success = self:SelectComputerMovementModule()
  9001. if success then
  9002. self:SwitchToController(controlModule)
  9003. end
  9004. end
  9005.  
  9006. function ControlModule:OnTouchMovementModeChange()
  9007. local touchModule, success = self:SelectTouchModule()
  9008. if success then
  9009. while not self.touchControlFrame do
  9010. wait()
  9011. end
  9012. self:SwitchToController(touchModule)
  9013. end
  9014. end
  9015.  
  9016. function ControlModule:CreateTouchGuiContainer()
  9017. if self.touchGui then self.touchGui:Destroy() end
  9018.  
  9019. -- Container for all touch device guis
  9020. self.touchGui = Instance.new("ScreenGui")
  9021. self.touchGui.Name = "TouchGui"
  9022. self.touchGui.ResetOnSpawn = false
  9023. self.touchGui.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
  9024. self.touchGui.Enabled = self.humanoid ~= nil
  9025.  
  9026. self.touchControlFrame = Instance.new("Frame")
  9027. self.touchControlFrame.Name = "TouchControlFrame"
  9028. self.touchControlFrame.Size = UDim2.new(1, 0, 1, 0)
  9029. self.touchControlFrame.BackgroundTransparency = 1
  9030. self.touchControlFrame.Parent = self.touchGui
  9031.  
  9032. self.touchGui.Parent = self.playerGui
  9033. end
  9034.  
  9035. function ControlModule:GetClickToMoveController()
  9036. if not self.controllers[ClickToMove] then
  9037. self.controllers[ClickToMove] = ClickToMove.new(CONTROL_ACTION_PRIORITY)
  9038. end
  9039. return self.controllers[ClickToMove]
  9040. end
  9041.  
  9042. function ControlModule:IsJumping()
  9043. if self.activeController then
  9044. return self.activeController:GetIsJumping() or (self.touchJumpController and self.touchJumpController:GetIsJumping())
  9045. end
  9046. return false
  9047. end
  9048.  
  9049. return ControlModule.new()
  9050. end
  9051.  
  9052. function _PlayerModule()
  9053. local PlayerModule = {}
  9054. PlayerModule.__index = PlayerModule
  9055. function PlayerModule.new()
  9056. local self = setmetatable({},PlayerModule)
  9057. self.cameras = _CameraModule()
  9058. self.controls = _ControlModule()
  9059. return self
  9060. end
  9061. function PlayerModule:GetCameras()
  9062. return self.cameras
  9063. end
  9064. function PlayerModule:GetControls()
  9065. return self.controls
  9066. end
  9067. function PlayerModule:GetClickToMoveController()
  9068. return self.controls:GetClickToMoveController()
  9069. end
  9070. return PlayerModule.new()
  9071. end
  9072.  
  9073. function _sounds()
  9074.  
  9075. local SetState = Instance.new("BindableEvent",script)
  9076.  
  9077. local Players = game:GetService("Players")
  9078. local RunService = game:GetService("RunService")
  9079.  
  9080. local SOUND_DATA = {
  9081. Climbing = {
  9082. SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
  9083. Looped = true,
  9084. },
  9085. Died = {
  9086. SoundId = "rbxasset://sounds/uuhhh.mp3",
  9087. },
  9088. FreeFalling = {
  9089. SoundId = "rbxasset://sounds/action_falling.mp3",
  9090. Looped = true,
  9091. },
  9092. GettingUp = {
  9093. SoundId = "rbxasset://sounds/action_get_up.mp3",
  9094. },
  9095. Jumping = {
  9096. SoundId = "rbxasset://sounds/action_jump.mp3",
  9097. },
  9098. Landing = {
  9099. SoundId = "rbxasset://sounds/action_jump_land.mp3",
  9100. },
  9101. Running = {
  9102. SoundId = "rbxasset://sounds/action_footsteps_plastic.mp3",
  9103. Looped = true,
  9104. Pitch = 1.85,
  9105. },
  9106. Splash = {
  9107. SoundId = "rbxasset://sounds/impact_water.mp3",
  9108. },
  9109. Swimming = {
  9110. SoundId = "rbxasset://sounds/action_swim.mp3",
  9111. Looped = true,
  9112. Pitch = 1.6,
  9113. },
  9114. }
  9115.  
  9116. -- wait for the first of the passed signals to fire
  9117. local function waitForFirst(...)
  9118. local shunt = Instance.new("BindableEvent")
  9119. local slots = {...}
  9120.  
  9121. local function fire(...)
  9122. for i = 1, #slots do
  9123. slots[i]:Disconnect()
  9124. end
  9125.  
  9126. return shunt:Fire(...)
  9127. end
  9128.  
  9129. for i = 1, #slots do
  9130. slots[i] = slots[i]:Connect(fire)
  9131. end
  9132.  
  9133. return shunt.Event:Wait()
  9134. end
  9135.  
  9136. -- map a value from one range to another
  9137. local function map(x, inMin, inMax, outMin, outMax)
  9138. return (x - inMin)*(outMax - outMin)/(inMax - inMin) + outMin
  9139. end
  9140.  
  9141. local function playSound(sound)
  9142. sound.TimePosition = 0
  9143. sound.Playing = true
  9144. end
  9145.  
  9146. local function stopSound(sound)
  9147. sound.Playing = false
  9148. sound.TimePosition = 0
  9149. end
  9150.  
  9151. local function shallowCopy(t)
  9152. local out = {}
  9153. for k, v in pairs(t) do
  9154. out[k] = v
  9155. end
  9156. return out
  9157. end
  9158.  
  9159. local function initializeSoundSystem(player, humanoid, rootPart)
  9160. local sounds = {}
  9161.  
  9162. -- initialize sounds
  9163. for name, props in pairs(SOUND_DATA) do
  9164. local sound = Instance.new("Sound")
  9165. sound.Name = name
  9166.  
  9167. -- set default values
  9168. sound.Archivable = false
  9169. sound.EmitterSize = 5
  9170. sound.MaxDistance = 150
  9171. sound.Volume = 0.65
  9172.  
  9173. for propName, propValue in pairs(props) do
  9174. sound[propName] = propValue
  9175. end
  9176.  
  9177. sound.Parent = rootPart
  9178. sounds[name] = sound
  9179. end
  9180.  
  9181. local playingLoopedSounds = {}
  9182.  
  9183. local function stopPlayingLoopedSounds(except)
  9184. for sound in pairs(shallowCopy(playingLoopedSounds)) do
  9185. if sound ~= except then
  9186. sound.Playing = false
  9187. playingLoopedSounds[sound] = nil
  9188. end
  9189. end
  9190. end
  9191.  
  9192. -- state transition callbacks
  9193. local stateTransitions = {
  9194. [Enum.HumanoidStateType.FallingDown] = function()
  9195. stopPlayingLoopedSounds()
  9196. end,
  9197.  
  9198. [Enum.HumanoidStateType.GettingUp] = function()
  9199. stopPlayingLoopedSounds()
  9200. playSound(sounds.GettingUp)
  9201. end,
  9202.  
  9203. [Enum.HumanoidStateType.Jumping] = function()
  9204. stopPlayingLoopedSounds()
  9205. playSound(sounds.Jumping)
  9206. end,
  9207.  
  9208. [Enum.HumanoidStateType.Swimming] = function()
  9209. local verticalSpeed = math.abs(rootPart.Velocity.Y)
  9210. if verticalSpeed > 0.1 then
  9211. sounds.Splash.Volume = math.clamp(map(verticalSpeed, 100, 350, 0.28, 1), 0, 1)
  9212. playSound(sounds.Splash)
  9213. end
  9214. stopPlayingLoopedSounds(sounds.Swimming)
  9215. sounds.Swimming.Playing = true
  9216. playingLoopedSounds[sounds.Swimming] = true
  9217. end,
  9218.  
  9219. [Enum.HumanoidStateType.Freefall] = function()
  9220. sounds.FreeFalling.Volume = 0
  9221. stopPlayingLoopedSounds(sounds.FreeFalling)
  9222. playingLoopedSounds[sounds.FreeFalling] = true
  9223. end,
  9224.  
  9225. [Enum.HumanoidStateType.Landed] = function()
  9226. stopPlayingLoopedSounds()
  9227. local verticalSpeed = math.abs(rootPart.Velocity.Y)
  9228. if verticalSpeed > 75 then
  9229. sounds.Landing.Volume = math.clamp(map(verticalSpeed, 50, 100, 0, 1), 0, 1)
  9230. playSound(sounds.Landing)
  9231. end
  9232. end,
  9233.  
  9234. [Enum.HumanoidStateType.Running] = function()
  9235. stopPlayingLoopedSounds(sounds.Running)
  9236. sounds.Running.Playing = true
  9237. playingLoopedSounds[sounds.Running] = true
  9238. end,
  9239.  
  9240. [Enum.HumanoidStateType.Climbing] = function()
  9241. local sound = sounds.Climbing
  9242. if math.abs(rootPart.Velocity.Y) > 0.1 then
  9243. sound.Playing = true
  9244. stopPlayingLoopedSounds(sound)
  9245. else
  9246. stopPlayingLoopedSounds()
  9247. end
  9248. playingLoopedSounds[sound] = true
  9249. end,
  9250.  
  9251. [Enum.HumanoidStateType.Seated] = function()
  9252. stopPlayingLoopedSounds()
  9253. end,
  9254.  
  9255. [Enum.HumanoidStateType.Dead] = function()
  9256. stopPlayingLoopedSounds()
  9257. playSound(sounds.Died)
  9258. end,
  9259. }
  9260.  
  9261. -- updaters for looped sounds
  9262. local loopedSoundUpdaters = {
  9263. [sounds.Climbing] = function(dt, sound, vel)
  9264. sound.Playing = vel.Magnitude > 0.1
  9265. end,
  9266.  
  9267. [sounds.FreeFalling] = function(dt, sound, vel)
  9268. if vel.Magnitude > 75 then
  9269. sound.Volume = math.clamp(sound.Volume + 0.9*dt, 0, 1)
  9270. else
  9271. sound.Volume = 0
  9272. end
  9273. end,
  9274.  
  9275. [sounds.Running] = function(dt, sound, vel)
  9276. sound.Playing = vel.Magnitude > 0.5 and humanoid.MoveDirection.Magnitude > 0.5
  9277. end,
  9278. }
  9279.  
  9280. -- state substitutions to avoid duplicating entries in the state table
  9281. local stateRemap = {
  9282. [Enum.HumanoidStateType.RunningNoPhysics] = Enum.HumanoidStateType.Running,
  9283. }
  9284.  
  9285. local activeState = stateRemap[humanoid:GetState()] or humanoid:GetState()
  9286. local activeConnections = {}
  9287.  
  9288. local stateChangedConn = humanoid.StateChanged:Connect(function(_, state)
  9289. state = stateRemap[state] or state
  9290.  
  9291. if state ~= activeState then
  9292. local transitionFunc = stateTransitions[state]
  9293.  
  9294. if transitionFunc then
  9295. transitionFunc()
  9296. end
  9297.  
  9298. activeState = state
  9299. end
  9300. end)
  9301.  
  9302. local customStateChangedConn = SetState.Event:Connect(function(state)
  9303. state = stateRemap[state] or state
  9304.  
  9305. if state ~= activeState then
  9306. local transitionFunc = stateTransitions[state]
  9307.  
  9308. if transitionFunc then
  9309. transitionFunc()
  9310. end
  9311.  
  9312. activeState = state
  9313. end
  9314. end)
  9315.  
  9316. local steppedConn = RunService.Stepped:Connect(function(_, worldDt)
  9317. -- update looped sounds on stepped
  9318. for sound in pairs(playingLoopedSounds) do
  9319. local updater = loopedSoundUpdaters[sound]
  9320.  
  9321. if updater then
  9322. updater(worldDt, sound, rootPart.Velocity)
  9323. end
  9324. end
  9325. end)
  9326.  
  9327. local humanoidAncestryChangedConn
  9328. local rootPartAncestryChangedConn
  9329. local characterAddedConn
  9330.  
  9331. local function terminate()
  9332. stateChangedConn:Disconnect()
  9333. customStateChangedConn:Disconnect()
  9334. steppedConn:Disconnect()
  9335. humanoidAncestryChangedConn:Disconnect()
  9336. rootPartAncestryChangedConn:Disconnect()
  9337. characterAddedConn:Disconnect()
  9338. end
  9339.  
  9340. humanoidAncestryChangedConn = humanoid.AncestryChanged:Connect(function(_, parent)
  9341. if not parent then
  9342. terminate()
  9343. end
  9344. end)
  9345.  
  9346. rootPartAncestryChangedConn = rootPart.AncestryChanged:Connect(function(_, parent)
  9347. if not parent then
  9348. terminate()
  9349. end
  9350. end)
  9351.  
  9352. characterAddedConn = player.CharacterAdded:Connect(terminate)
  9353. end
  9354.  
  9355. local function playerAdded(player)
  9356. local function characterAdded(character)
  9357. -- Avoiding memory leaks in the face of Character/Humanoid/RootPart lifetime has a few complications:
  9358. -- * character deparenting is a Remove instead of a Destroy, so signals are not cleaned up automatically.
  9359. -- ** must use a waitForFirst on everything and listen for hierarchy changes.
  9360. -- * the character might not be in the dm by the time CharacterAdded fires
  9361. -- ** constantly check consistency with player.Character and abort if CharacterAdded is fired again
  9362. -- * Humanoid may not exist immediately, and by the time it's inserted the character might be deparented.
  9363. -- * RootPart probably won't exist immediately.
  9364. -- ** by the time RootPart is inserted and Humanoid.RootPart is set, the character or the humanoid might be deparented.
  9365.  
  9366. if not character.Parent then
  9367. waitForFirst(character.AncestryChanged, player.CharacterAdded)
  9368. end
  9369.  
  9370. if player.Character ~= character or not character.Parent then
  9371. return
  9372. end
  9373.  
  9374. local humanoid = character:FindFirstChildOfClass("Humanoid")
  9375. while character:IsDescendantOf(game) and not humanoid do
  9376. waitForFirst(character.ChildAdded, character.AncestryChanged, player.CharacterAdded)
  9377. humanoid = character:FindFirstChildOfClass("Humanoid")
  9378. end
  9379.  
  9380. if player.Character ~= character or not character:IsDescendantOf(game) then
  9381. return
  9382. end
  9383.  
  9384. -- must rely on HumanoidRootPart naming because Humanoid.RootPart does not fire changed signals
  9385. local rootPart = character:FindFirstChild("HumanoidRootPart")
  9386. while character:IsDescendantOf(game) and not rootPart do
  9387. waitForFirst(character.ChildAdded, character.AncestryChanged, humanoid.AncestryChanged, player.CharacterAdded)
  9388. rootPart = character:FindFirstChild("HumanoidRootPart")
  9389. end
  9390.  
  9391. if rootPart and humanoid:IsDescendantOf(game) and character:IsDescendantOf(game) and player.Character == character then
  9392. initializeSoundSystem(player, humanoid, rootPart)
  9393. end
  9394. end
  9395.  
  9396. if player.Character then
  9397. characterAdded(player.Character)
  9398. end
  9399. player.CharacterAdded:Connect(characterAdded)
  9400. end
  9401.  
  9402. Players.PlayerAdded:Connect(playerAdded)
  9403. for _, player in ipairs(Players:GetPlayers()) do
  9404. playerAdded(player)
  9405. end
  9406. return SetState
  9407. end
  9408.  
  9409. function _StateTracker()
  9410. local EPSILON = 0.1
  9411.  
  9412. local SPEED = {
  9413. ["onRunning"] = true,
  9414. ["onClimbing"] = true
  9415. }
  9416.  
  9417. local INAIR = {
  9418. ["onFreeFall"] = true,
  9419. ["onJumping"] = true
  9420. }
  9421.  
  9422. local STATEMAP = {
  9423. ["onRunning"] = Enum.HumanoidStateType.Running,
  9424. ["onJumping"] = Enum.HumanoidStateType.Jumping,
  9425. ["onFreeFall"] = Enum.HumanoidStateType.Freefall
  9426. }
  9427.  
  9428. local StateTracker = {}
  9429. StateTracker.__index = StateTracker
  9430.  
  9431. function StateTracker.new(humanoid, soundState)
  9432. local self = setmetatable({}, StateTracker)
  9433.  
  9434. self.Humanoid = humanoid
  9435. self.HRP = humanoid.RootPart
  9436.  
  9437. self.Speed = 0
  9438. self.State = "onRunning"
  9439. self.Jumped = false
  9440. self.JumpTick = tick()
  9441.  
  9442. self.SoundState = soundState
  9443.  
  9444. self._ChangedEvent = Instance.new("BindableEvent")
  9445. self.Changed = self._ChangedEvent.Event
  9446.  
  9447. return self
  9448. end
  9449.  
  9450. function StateTracker:Destroy()
  9451. self._ChangedEvent:Destroy()
  9452. end
  9453.  
  9454. function StateTracker:RequestedJump()
  9455. self.Jumped = true
  9456. self.JumpTick = tick()
  9457. end
  9458.  
  9459. function StateTracker:OnStep(gravityUp, grounded, isMoving)
  9460. local cVelocity = self.HRP.Velocity
  9461. local gVelocity = cVelocity:Dot(gravityUp)
  9462.  
  9463. local oldState, oldSpeed = self.State, self.Speed
  9464.  
  9465. local newState
  9466. local newSpeed = cVelocity.Magnitude
  9467.  
  9468. if (not grounded) then
  9469. if (gVelocity > 0) then
  9470. if (self.Jumped) then
  9471. newState = "onJumping"
  9472. else
  9473. newState = "onFreeFall"
  9474. end
  9475. else
  9476. if (self.Jumped) then
  9477. self.Jumped = false
  9478. end
  9479. newState = "onFreeFall"
  9480. end
  9481. else
  9482. if (self.Jumped and tick() - self.JumpTick > 0.1) then
  9483. self.Jumped = false
  9484. end
  9485. newSpeed = (cVelocity - gVelocity*gravityUp).Magnitude
  9486. newState = "onRunning"
  9487. end
  9488.  
  9489. newSpeed = isMoving and newSpeed or 0
  9490.  
  9491. if (oldState ~= newState or (SPEED[newState] and math.abs(oldSpeed - newSpeed) > EPSILON)) then
  9492. self.State = newState
  9493. self.Speed = newSpeed
  9494. self.SoundState:Fire(STATEMAP[newState])
  9495. self._ChangedEvent:Fire(self.State, self.Speed)
  9496. end
  9497. end
  9498.  
  9499. return StateTracker
  9500. end
  9501. function _InitObjects()
  9502. local model = workspace:FindFirstChild("objects") or game:GetObjects("rbxassetid://5045408489")[1]
  9503. local SPHERE = model:WaitForChild("Sphere")
  9504. local FLOOR = model:WaitForChild("Floor")
  9505. local VFORCE = model:WaitForChild("VectorForce")
  9506. local BGYRO = model:WaitForChild("BodyGyro")
  9507. local function initObjects(self)
  9508. local hrp = self.HRP
  9509. local humanoid = self.Humanoid
  9510. local sphere = SPHERE:Clone()
  9511. sphere.Parent = self.Character
  9512. local floor = FLOOR:Clone()
  9513. floor.Parent = self.Character
  9514. local isR15 = (humanoid.RigType == Enum.HumanoidRigType.R15)
  9515. local height = isR15 and (humanoid.HipHeight + 0.05) or 2
  9516. local weld = Instance.new("Weld")
  9517. weld.C0 = CFrame.new(0, -height, 0.1)
  9518. weld.Part0 = hrp
  9519. weld.Part1 = sphere
  9520. weld.Parent = sphere
  9521. local weld2 = Instance.new("Weld")
  9522. weld2.C0 = CFrame.new(0, -(height + 1.5), 0)
  9523. weld2.Part0 = hrp
  9524. weld2.Part1 = floor
  9525. weld2.Parent = floor
  9526. local gyro = BGYRO:Clone()
  9527. gyro.CFrame = hrp.CFrame
  9528. gyro.Parent = hrp
  9529. local vForce = VFORCE:Clone()
  9530. vForce.Attachment0 = isR15 and hrp:WaitForChild("RootRigAttachment") or hrp:WaitForChild("RootAttachment")
  9531. vForce.Parent = hrp
  9532. return sphere, gyro, vForce, floor
  9533. end
  9534. return initObjects
  9535. end
  9536. local plr = game.Players.LocalPlayer
  9537. local ms = plr:GetMouse()
  9538. local char
  9539. plr.CharacterAdded:Connect(function(c)
  9540. char = c
  9541. end)
  9542. function _R6()
  9543. function r6()
  9544. local Figure = char
  9545. local Torso = Figure:WaitForChild("Torso")
  9546. local RightShoulder = Torso:WaitForChild("Right Shoulder")
  9547. local LeftShoulder = Torso:WaitForChild("Left Shoulder")
  9548. local RightHip = Torso:WaitForChild("Right Hip")
  9549. local LeftHip = Torso:WaitForChild("Left Hip")
  9550. local Neck = Torso:WaitForChild("Neck")
  9551. local Humanoid = Figure:WaitForChild("Humanoid")
  9552. local pose = "Standing"
  9553. local currentAnim = ""
  9554. local currentAnimInstance = nil
  9555. local currentAnimTrack = nil
  9556. local currentAnimKeyframeHandler = nil
  9557. local currentAnimSpeed = 1.0
  9558. local animTable = {}
  9559. local animNames = {
  9560. idle = {
  9561. { id = "http://www.roblox.com/asset/?id=180435571", weight = 9 },
  9562. { id = "http://www.roblox.com/asset/?id=180435792", weight = 1 }
  9563. },
  9564. walk = {
  9565. { id = "http://www.roblox.com/asset/?id=180426354", weight = 10 }
  9566. },
  9567. run = {
  9568. { id = "run.xml", weight = 10 }
  9569. },
  9570. jump = {
  9571. { id = "http://www.roblox.com/asset/?id=125750702", weight = 10 }
  9572. },
  9573. fall = {
  9574. { id = "http://www.roblox.com/asset/?id=180436148", weight = 10 }
  9575. },
  9576. climb = {
  9577. { id = "http://www.roblox.com/asset/?id=180436334", weight = 10 }
  9578. },
  9579. sit = {
  9580. { id = "http://www.roblox.com/asset/?id=178130996", weight = 10 }
  9581. },
  9582. toolnone = {
  9583. { id = "http://www.roblox.com/asset/?id=182393478", weight = 10 }
  9584. },
  9585. toolslash = {
  9586. { id = "http://www.roblox.com/asset/?id=129967390", weight = 10 }
  9587. -- { id = "slash.xml", weight = 10 }
  9588. },
  9589. toollunge = {
  9590. { id = "http://www.roblox.com/asset/?id=129967478", weight = 10 }
  9591. },
  9592. wave = {
  9593. { id = "http://www.roblox.com/asset/?id=128777973", weight = 10 }
  9594. },
  9595. point = {
  9596. { id = "http://www.roblox.com/asset/?id=128853357", weight = 10 }
  9597. },
  9598. dance1 = {
  9599. { id = "http://www.roblox.com/asset/?id=182435998", weight = 10 },
  9600. { id = "http://www.roblox.com/asset/?id=182491037", weight = 10 },
  9601. { id = "http://www.roblox.com/asset/?id=182491065", weight = 10 }
  9602. },
  9603. dance2 = {
  9604. { id = "http://www.roblox.com/asset/?id=182436842", weight = 10 },
  9605. { id = "http://www.roblox.com/asset/?id=182491248", weight = 10 },
  9606. { id = "http://www.roblox.com/asset/?id=182491277", weight = 10 }
  9607. },
  9608. dance3 = {
  9609. { id = "http://www.roblox.com/asset/?id=182436935", weight = 10 },
  9610. { id = "http://www.roblox.com/asset/?id=182491368", weight = 10 },
  9611. { id = "http://www.roblox.com/asset/?id=182491423", weight = 10 }
  9612. },
  9613. laugh = {
  9614. { id = "http://www.roblox.com/asset/?id=129423131", weight = 10 }
  9615. },
  9616. cheer = {
  9617. { id = "http://www.roblox.com/asset/?id=129423030", weight = 10 }
  9618. },
  9619. }
  9620. local dances = {"dance1", "dance2", "dance3"}
  9621. -- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
  9622. local emoteNames = { wave = false, point = false, dance1 = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
  9623. function configureAnimationSet(name, fileList)
  9624. if (animTable[name] ~= nil) then
  9625. for _, connection in pairs(animTable[name].connections) do
  9626. connection:disconnect()
  9627. end
  9628. end
  9629. animTable[name] = {}
  9630. animTable[name].count = 0
  9631. animTable[name].totalWeight = 0
  9632. animTable[name].connections = {}
  9633. -- check for config values
  9634. local config = script:FindFirstChild(name)
  9635. if (config ~= nil) then
  9636. -- print("Loading anims " .. name)
  9637. table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
  9638. table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
  9639. local idx = 1
  9640. for _, childPart in pairs(config:GetChildren()) do
  9641. if (childPart:IsA("Animation")) then
  9642. table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
  9643. animTable[name][idx] = {}
  9644. animTable[name][idx].anim = childPart
  9645. local weightObject = childPart:FindFirstChild("Weight")
  9646. if (weightObject == nil) then
  9647. animTable[name][idx].weight = 1
  9648. else
  9649. animTable[name][idx].weight = weightObject.Value
  9650. end
  9651. animTable[name].count = animTable[name].count + 1
  9652. animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
  9653. -- print(name .. " [" .. idx .. "] " .. animTable[name][idx].anim.AnimationId .. " (" .. animTable[name][idx].weight .. ")")
  9654. idx = idx + 1
  9655. end
  9656. end
  9657. end
  9658. -- fallback to defaults
  9659. if (animTable[name].count <= 0) then
  9660. for idx, anim in pairs(fileList) do
  9661. animTable[name][idx] = {}
  9662. animTable[name][idx].anim = Instance.new("Animation")
  9663. animTable[name][idx].anim.Name = name
  9664. animTable[name][idx].anim.AnimationId = anim.id
  9665. animTable[name][idx].weight = anim.weight
  9666. animTable[name].count = animTable[name].count + 1
  9667. animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
  9668. -- print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
  9669. end
  9670. end
  9671. end
  9672. -- Setup animation objects
  9673. function scriptChildModified(child)
  9674. local fileList = animNames[child.Name]
  9675. if (fileList ~= nil) then
  9676. configureAnimationSet(child.Name, fileList)
  9677. end
  9678. end
  9679.  
  9680. script.ChildAdded:connect(scriptChildModified)
  9681. script.ChildRemoved:connect(scriptChildModified)
  9682.  
  9683.  
  9684. for name, fileList in pairs(animNames) do
  9685. configureAnimationSet(name, fileList)
  9686. end
  9687.  
  9688. -- ANIMATION
  9689.  
  9690. -- declarations
  9691. local toolAnim = "None"
  9692. local toolAnimTime = 0
  9693.  
  9694. local jumpAnimTime = 0
  9695. local jumpAnimDuration = 0.3
  9696.  
  9697. local toolTransitionTime = 0.1
  9698. local fallTransitionTime = 0.3
  9699. local jumpMaxLimbVelocity = 0.75
  9700.  
  9701. -- functions
  9702.  
  9703. function stopAllAnimations()
  9704. local oldAnim = currentAnim
  9705.  
  9706. -- return to idle if finishing an emote
  9707. if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
  9708. oldAnim = "idle"
  9709. end
  9710.  
  9711. currentAnim = ""
  9712. currentAnimInstance = nil
  9713. if (currentAnimKeyframeHandler ~= nil) then
  9714. currentAnimKeyframeHandler:disconnect()
  9715. end
  9716.  
  9717. if (currentAnimTrack ~= nil) then
  9718. currentAnimTrack:Stop()
  9719. currentAnimTrack:Destroy()
  9720. currentAnimTrack = nil
  9721. end
  9722. return oldAnim
  9723. end
  9724.  
  9725. function setAnimationSpeed(speed)
  9726. if speed ~= currentAnimSpeed then
  9727. currentAnimSpeed = speed
  9728. currentAnimTrack:AdjustSpeed(currentAnimSpeed)
  9729. end
  9730. end
  9731.  
  9732. function keyFrameReachedFunc(frameName)
  9733. if (frameName == "End") then
  9734.  
  9735. local repeatAnim = currentAnim
  9736. -- return to idle if finishing an emote
  9737. if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
  9738. repeatAnim = "idle"
  9739. end
  9740.  
  9741. local animSpeed = currentAnimSpeed
  9742. playAnimation(repeatAnim, 0.0, Humanoid)
  9743. setAnimationSpeed(animSpeed)
  9744. end
  9745. end
  9746.  
  9747. -- Preload animations
  9748. function playAnimation(animName, transitionTime, humanoid)
  9749.  
  9750. local roll = math.random(1, animTable[animName].totalWeight)
  9751. local origRoll = roll
  9752. local idx = 1
  9753. while (roll > animTable[animName][idx].weight) do
  9754. roll = roll - animTable[animName][idx].weight
  9755. idx = idx + 1
  9756. end
  9757. -- print(animName .. " " .. idx .. " [" .. origRoll .. "]")
  9758. local anim = animTable[animName][idx].anim
  9759.  
  9760. -- switch animation
  9761. if (anim ~= currentAnimInstance) then
  9762.  
  9763. if (currentAnimTrack ~= nil) then
  9764. currentAnimTrack:Stop(transitionTime)
  9765. currentAnimTrack:Destroy()
  9766. end
  9767.  
  9768. currentAnimSpeed = 1.0
  9769.  
  9770. -- load it to the humanoid; get AnimationTrack
  9771. currentAnimTrack = humanoid:LoadAnimation(anim)
  9772. currentAnimTrack.Priority = Enum.AnimationPriority.Core
  9773.  
  9774. -- play the animation
  9775. currentAnimTrack:Play(transitionTime)
  9776. currentAnim = animName
  9777. currentAnimInstance = anim
  9778.  
  9779. -- set up keyframe name triggers
  9780. if (currentAnimKeyframeHandler ~= nil) then
  9781. currentAnimKeyframeHandler:disconnect()
  9782. end
  9783. currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
  9784.  
  9785. end
  9786.  
  9787. end
  9788.  
  9789. -------------------------------------------------------------------------------------------
  9790. -------------------------------------------------------------------------------------------
  9791.  
  9792. local toolAnimName = ""
  9793. local toolAnimTrack = nil
  9794. local toolAnimInstance = nil
  9795. local currentToolAnimKeyframeHandler = nil
  9796.  
  9797. function toolKeyFrameReachedFunc(frameName)
  9798. if (frameName == "End") then
  9799. -- print("Keyframe : ".. frameName)
  9800. playToolAnimation(toolAnimName, 0.0, Humanoid)
  9801. end
  9802. end
  9803.  
  9804.  
  9805. function playToolAnimation(animName, transitionTime, humanoid, priority)
  9806.  
  9807. local roll = math.random(1, animTable[animName].totalWeight)
  9808. local origRoll = roll
  9809. local idx = 1
  9810. while (roll > animTable[animName][idx].weight) do
  9811. roll = roll - animTable[animName][idx].weight
  9812. idx = idx + 1
  9813. end
  9814. -- print(animName .. " * " .. idx .. " [" .. origRoll .. "]")
  9815. local anim = animTable[animName][idx].anim
  9816.  
  9817. if (toolAnimInstance ~= anim) then
  9818.  
  9819. if (toolAnimTrack ~= nil) then
  9820. toolAnimTrack:Stop()
  9821. toolAnimTrack:Destroy()
  9822. transitionTime = 0
  9823. end
  9824.  
  9825. -- load it to the humanoid; get AnimationTrack
  9826. toolAnimTrack = humanoid:LoadAnimation(anim)
  9827. if priority then
  9828. toolAnimTrack.Priority = priority
  9829. end
  9830.  
  9831. -- play the animation
  9832. toolAnimTrack:Play(transitionTime)
  9833. toolAnimName = animName
  9834. toolAnimInstance = anim
  9835.  
  9836. currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
  9837. end
  9838. end
  9839.  
  9840. function stopToolAnimations()
  9841. local oldAnim = toolAnimName
  9842.  
  9843. if (currentToolAnimKeyframeHandler ~= nil) then
  9844. currentToolAnimKeyframeHandler:disconnect()
  9845. end
  9846.  
  9847. toolAnimName = ""
  9848. toolAnimInstance = nil
  9849. if (toolAnimTrack ~= nil) then
  9850. toolAnimTrack:Stop()
  9851. toolAnimTrack:Destroy()
  9852. toolAnimTrack = nil
  9853. end
  9854.  
  9855.  
  9856. return oldAnim
  9857. end
  9858.  
  9859. -------------------------------------------------------------------------------------------
  9860. -------------------------------------------------------------------------------------------
  9861.  
  9862.  
  9863. function onRunning(speed)
  9864. if speed > 0.01 then
  9865. playAnimation("walk", 0.1, Humanoid)
  9866. if currentAnimInstance and currentAnimInstance.AnimationId == "http://www.roblox.com/asset/?id=180426354" then
  9867. setAnimationSpeed(speed / 14.5)
  9868. end
  9869. pose = "Running"
  9870. else
  9871. if emoteNames[currentAnim] == nil then
  9872. playAnimation("idle", 0.1, Humanoid)
  9873. pose = "Standing"
  9874. end
  9875. end
  9876. end
  9877.  
  9878. function onDied()
  9879. pose = "Dead"
  9880. end
  9881.  
  9882. function onJumping()
  9883. playAnimation("jump", 0.1, Humanoid)
  9884. jumpAnimTime = jumpAnimDuration
  9885. pose = "Jumping"
  9886. end
  9887.  
  9888. function onClimbing(speed)
  9889. playAnimation("climb", 0.1, Humanoid)
  9890. setAnimationSpeed(speed / 12.0)
  9891. pose = "Climbing"
  9892. end
  9893.  
  9894. function onGettingUp()
  9895. pose = "GettingUp"
  9896. end
  9897.  
  9898. function onFreeFall()
  9899. if (jumpAnimTime <= 0) then
  9900. playAnimation("fall", fallTransitionTime, Humanoid)
  9901. end
  9902. pose = "FreeFall"
  9903. end
  9904.  
  9905. function onFallingDown()
  9906. pose = "FallingDown"
  9907. end
  9908.  
  9909. function onSeated()
  9910. pose = "Seated"
  9911. end
  9912.  
  9913. function onPlatformStanding()
  9914. pose = "PlatformStanding"
  9915. end
  9916.  
  9917. function onSwimming(speed)
  9918. if speed > 0 then
  9919. pose = "Running"
  9920. else
  9921. pose = "Standing"
  9922. end
  9923. end
  9924.  
  9925. function getTool()
  9926. for _, kid in ipairs(Figure:GetChildren()) do
  9927. if kid.className == "Tool" then return kid end
  9928. end
  9929. return nil
  9930. end
  9931.  
  9932. function getToolAnim(tool)
  9933. for _, c in ipairs(tool:GetChildren()) do
  9934. if c.Name == "toolanim" and c.className == "StringValue" then
  9935. return c
  9936. end
  9937. end
  9938. return nil
  9939. end
  9940.  
  9941. function animateTool()
  9942.  
  9943. if (toolAnim == "None") then
  9944. playToolAnimation("toolnone", toolTransitionTime, Humanoid, Enum.AnimationPriority.Idle)
  9945. return
  9946. end
  9947.  
  9948. if (toolAnim == "Slash") then
  9949. playToolAnimation("toolslash", 0, Humanoid, Enum.AnimationPriority.Action)
  9950. return
  9951. end
  9952.  
  9953. if (toolAnim == "Lunge") then
  9954. playToolAnimation("toollunge", 0, Humanoid, Enum.AnimationPriority.Action)
  9955. return
  9956. end
  9957. end
  9958.  
  9959. function moveSit()
  9960. RightShoulder.MaxVelocity = 0.15
  9961. LeftShoulder.MaxVelocity = 0.15
  9962. RightShoulder:SetDesiredAngle(3.14 /2)
  9963. LeftShoulder:SetDesiredAngle(-3.14 /2)
  9964. RightHip:SetDesiredAngle(3.14 /2)
  9965. LeftHip:SetDesiredAngle(-3.14 /2)
  9966. end
  9967.  
  9968. local lastTick = 0
  9969.  
  9970. function move(time)
  9971. local amplitude = 1
  9972. local frequency = 1
  9973. local deltaTime = time - lastTick
  9974. lastTick = time
  9975.  
  9976. local climbFudge = 0
  9977. local setAngles = false
  9978.  
  9979. if (jumpAnimTime > 0) then
  9980. jumpAnimTime = jumpAnimTime - deltaTime
  9981. end
  9982.  
  9983. if (pose == "FreeFall" and jumpAnimTime <= 0) then
  9984. playAnimation("fall", fallTransitionTime, Humanoid)
  9985. elseif (pose == "Seated") then
  9986. playAnimation("sit", 0.5, Humanoid)
  9987. return
  9988. elseif (pose == "Running") then
  9989. playAnimation("walk", 0.1, Humanoid)
  9990. elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
  9991. -- print("Wha " .. pose)
  9992. stopAllAnimations()
  9993. amplitude = 0.1
  9994. frequency = 1
  9995. setAngles = true
  9996. end
  9997.  
  9998. if (setAngles) then
  9999. local desiredAngle = amplitude * math.sin(time * frequency)
  10000.  
  10001. RightShoulder:SetDesiredAngle(desiredAngle + climbFudge)
  10002. LeftShoulder:SetDesiredAngle(desiredAngle - climbFudge)
  10003. RightHip:SetDesiredAngle(-desiredAngle)
  10004. LeftHip:SetDesiredAngle(-desiredAngle)
  10005. end
  10006.  
  10007. -- Tool Animation handling
  10008. local tool = getTool()
  10009. if tool and tool:FindFirstChild("Handle") then
  10010.  
  10011. local animStringValueObject = getToolAnim(tool)
  10012.  
  10013. if animStringValueObject then
  10014. toolAnim = animStringValueObject.Value
  10015. -- message recieved, delete StringValue
  10016. animStringValueObject.Parent = nil
  10017. toolAnimTime = time + .3
  10018. end
  10019.  
  10020. if time > toolAnimTime then
  10021. toolAnimTime = 0
  10022. toolAnim = "None"
  10023. end
  10024.  
  10025. animateTool()
  10026. else
  10027. stopToolAnimations()
  10028. toolAnim = "None"
  10029. toolAnimInstance = nil
  10030. toolAnimTime = 0
  10031. end
  10032. end
  10033.  
  10034.  
  10035. local events = {}
  10036. local eventHum = Humanoid
  10037.  
  10038. local function onUnhook()
  10039. for i = 1, #events do
  10040. events[i]:Disconnect()
  10041. end
  10042. events = {}
  10043. end
  10044.  
  10045. local function onHook()
  10046. onUnhook()
  10047.  
  10048. pose = eventHum.Sit and "Seated" or "Standing"
  10049.  
  10050. events = {
  10051. eventHum.Died:connect(onDied),
  10052. eventHum.Running:connect(onRunning),
  10053. eventHum.Jumping:connect(onJumping),
  10054. eventHum.Climbing:connect(onClimbing),
  10055. eventHum.GettingUp:connect(onGettingUp),
  10056. eventHum.FreeFalling:connect(onFreeFall),
  10057. eventHum.FallingDown:connect(onFallingDown),
  10058. eventHum.Seated:connect(onSeated),
  10059. eventHum.PlatformStanding:connect(onPlatformStanding),
  10060. eventHum.Swimming:connect(onSwimming)
  10061. }
  10062. end
  10063.  
  10064.  
  10065. onHook()
  10066.  
  10067. -- setup emote chat hook
  10068. game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
  10069. local emote = ""
  10070. if msg == "/e dance" then
  10071. emote = dances[math.random(1, #dances)]
  10072. elseif (string.sub(msg, 1, 3) == "/e ") then
  10073. emote = string.sub(msg, 4)
  10074. elseif (string.sub(msg, 1, 7) == "/emote ") then
  10075. emote = string.sub(msg, 8)
  10076. end
  10077.  
  10078. if (pose == "Standing" and emoteNames[emote] ~= nil) then
  10079. playAnimation(emote, 0.1, Humanoid)
  10080. end
  10081.  
  10082. end)
  10083.  
  10084.  
  10085. -- main program
  10086.  
  10087. -- initialize to idle
  10088. playAnimation("idle", 0.1, Humanoid)
  10089. pose = "Standing"
  10090.  
  10091. spawn(function()
  10092. while Figure.Parent ~= nil do
  10093. local _, time = wait(0.1)
  10094. move(time)
  10095. end
  10096. end)
  10097.  
  10098. return {
  10099. onRunning = onRunning,
  10100. onDied = onDied,
  10101. onJumping = onJumping,
  10102. onClimbing = onClimbing,
  10103. onGettingUp = onGettingUp,
  10104. onFreeFall = onFreeFall,
  10105. onFallingDown = onFallingDown,
  10106. onSeated = onSeated,
  10107. onPlatformStanding = onPlatformStanding,
  10108. onHook = onHook,
  10109. onUnhook = onUnhook
  10110. }
  10111.  
  10112. end
  10113. return r6()
  10114. end
  10115.  
  10116. function _R15()
  10117. local function r15()
  10118.  
  10119. local Character = char
  10120. local Humanoid = Character:WaitForChild("Humanoid")
  10121. local pose = "Standing"
  10122.  
  10123. local userNoUpdateOnLoopSuccess, userNoUpdateOnLoopValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserNoUpdateOnLoop") end)
  10124. local userNoUpdateOnLoop = userNoUpdateOnLoopSuccess and userNoUpdateOnLoopValue
  10125. local userAnimationSpeedDampeningSuccess, userAnimationSpeedDampeningValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserAnimationSpeedDampening") end)
  10126. local userAnimationSpeedDampening = userAnimationSpeedDampeningSuccess and userAnimationSpeedDampeningValue
  10127.  
  10128. local animateScriptEmoteHookFlagExists, animateScriptEmoteHookFlagEnabled = pcall(function()
  10129. return UserSettings():IsUserFeatureEnabled("UserAnimateScriptEmoteHook")
  10130. end)
  10131. local FFlagAnimateScriptEmoteHook = animateScriptEmoteHookFlagExists and animateScriptEmoteHookFlagEnabled
  10132.  
  10133. local AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
  10134. local HumanoidHipHeight = 2
  10135.  
  10136. local EMOTE_TRANSITION_TIME = 0.1
  10137.  
  10138. local currentAnim = ""
  10139. local currentAnimInstance = nil
  10140. local currentAnimTrack = nil
  10141. local currentAnimKeyframeHandler = nil
  10142. local currentAnimSpeed = 1.0
  10143.  
  10144. local runAnimTrack = nil
  10145. local runAnimKeyframeHandler = nil
  10146.  
  10147. local animTable = {}
  10148. local animNames = {
  10149. idle = {
  10150. { id = "http://www.roblox.com/asset/?id=507766666", weight = 1 },
  10151. { id = "http://www.roblox.com/asset/?id=507766951", weight = 1 },
  10152. { id = "http://www.roblox.com/asset/?id=507766388", weight = 9 }
  10153. },
  10154. walk = {
  10155. { id = "http://www.roblox.com/asset/?id=507777826", weight = 10 }
  10156. },
  10157. run = {
  10158. { id = "http://www.roblox.com/asset/?id=507767714", weight = 10 }
  10159. },
  10160. swim = {
  10161. { id = "http://www.roblox.com/asset/?id=507784897", weight = 10 }
  10162. },
  10163. swimidle = {
  10164. { id = "http://www.roblox.com/asset/?id=507785072", weight = 10 }
  10165. },
  10166. jump = {
  10167. { id = "http://www.roblox.com/asset/?id=507765000", weight = 10 }
  10168. },
  10169. fall = {
  10170. { id = "http://www.roblox.com/asset/?id=507767968", weight = 10 }
  10171. },
  10172. climb = {
  10173. { id = "http://www.roblox.com/asset/?id=507765644", weight = 10 }
  10174. },
  10175. sit = {
  10176. { id = "http://www.roblox.com/asset/?id=2506281703", weight = 10 }
  10177. },
  10178. toolnone = {
  10179. { id = "http://www.roblox.com/asset/?id=507768375", weight = 10 }
  10180. },
  10181. toolslash = {
  10182. { id = "http://www.roblox.com/asset/?id=522635514", weight = 10 }
  10183. },
  10184. toollunge = {
  10185. { id = "http://www.roblox.com/asset/?id=522638767", weight = 10 }
  10186. },
  10187. wave = {
  10188. { id = "http://www.roblox.com/asset/?id=507770239", weight = 10 }
  10189. },
  10190. point = {
  10191. { id = "http://www.roblox.com/asset/?id=507770453", weight = 10 }
  10192. },
  10193. dance = {
  10194. { id = "http://www.roblox.com/asset/?id=507771019", weight = 10 },
  10195. { id = "http://www.roblox.com/asset/?id=507771955", weight = 10 },
  10196. { id = "http://www.roblox.com/asset/?id=507772104", weight = 10 }
  10197. },
  10198. dance2 = {
  10199. { id = "http://www.roblox.com/asset/?id=507776043", weight = 10 },
  10200. { id = "http://www.roblox.com/asset/?id=507776720", weight = 10 },
  10201. { id = "http://www.roblox.com/asset/?id=507776879", weight = 10 }
  10202. },
  10203. dance3 = {
  10204. { id = "http://www.roblox.com/asset/?id=507777268", weight = 10 },
  10205. { id = "http://www.roblox.com/asset/?id=507777451", weight = 10 },
  10206. { id = "http://www.roblox.com/asset/?id=507777623", weight = 10 }
  10207. },
  10208. laugh = {
  10209. { id = "http://www.roblox.com/asset/?id=507770818", weight = 10 }
  10210. },
  10211. cheer = {
  10212. { id = "http://www.roblox.com/asset/?id=507770677", weight = 10 }
  10213. },
  10214. }
  10215.  
  10216. -- Existance in this list signifies that it is an emote, the value indicates if it is a looping emote
  10217. local emoteNames = { wave = false, point = false, dance = true, dance2 = true, dance3 = true, laugh = false, cheer = false}
  10218.  
  10219. local PreloadAnimsUserFlag = false
  10220. local PreloadedAnims = {}
  10221. local successPreloadAnim, msgPreloadAnim = pcall(function()
  10222. PreloadAnimsUserFlag = UserSettings():IsUserFeatureEnabled("UserPreloadAnimations")
  10223. end)
  10224. if not successPreloadAnim then
  10225. PreloadAnimsUserFlag = false
  10226. end
  10227.  
  10228. math.randomseed(tick())
  10229.  
  10230. function findExistingAnimationInSet(set, anim)
  10231. if set == nil or anim == nil then
  10232. return 0
  10233. end
  10234.  
  10235. for idx = 1, set.count, 1 do
  10236. if set[idx].anim.AnimationId == anim.AnimationId then
  10237. return idx
  10238. end
  10239. end
  10240.  
  10241. return 0
  10242. end
  10243.  
  10244. function configureAnimationSet(name, fileList)
  10245. if (animTable[name] ~= nil) then
  10246. for _, connection in pairs(animTable[name].connections) do
  10247. connection:disconnect()
  10248. end
  10249. end
  10250. animTable[name] = {}
  10251. animTable[name].count = 0
  10252. animTable[name].totalWeight = 0
  10253. animTable[name].connections = {}
  10254.  
  10255. local allowCustomAnimations = true
  10256.  
  10257. local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
  10258. if not success then
  10259. allowCustomAnimations = true
  10260. end
  10261.  
  10262. -- check for config values
  10263. local config = script:FindFirstChild(name)
  10264. if (allowCustomAnimations and config ~= nil) then
  10265. table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
  10266. table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
  10267.  
  10268. local idx = 0
  10269. for _, childPart in pairs(config:GetChildren()) do
  10270. if (childPart:IsA("Animation")) then
  10271. local newWeight = 1
  10272. local weightObject = childPart:FindFirstChild("Weight")
  10273. if (weightObject ~= nil) then
  10274. newWeight = weightObject.Value
  10275. end
  10276. animTable[name].count = animTable[name].count + 1
  10277. idx = animTable[name].count
  10278. animTable[name][idx] = {}
  10279. animTable[name][idx].anim = childPart
  10280. animTable[name][idx].weight = newWeight
  10281. animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
  10282. table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
  10283. table.insert(animTable[name].connections, childPart.ChildAdded:connect(function(property) configureAnimationSet(name, fileList) end))
  10284. table.insert(animTable[name].connections, childPart.ChildRemoved:connect(function(property) configureAnimationSet(name, fileList) end))
  10285. end
  10286. end
  10287. end
  10288.  
  10289. -- fallback to defaults
  10290. if (animTable[name].count <= 0) then
  10291. for idx, anim in pairs(fileList) do
  10292. animTable[name][idx] = {}
  10293. animTable[name][idx].anim = Instance.new("Animation")
  10294. animTable[name][idx].anim.Name = name
  10295. animTable[name][idx].anim.AnimationId = anim.id
  10296. animTable[name][idx].weight = anim.weight
  10297. animTable[name].count = animTable[name].count + 1
  10298. animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
  10299. end
  10300. end
  10301.  
  10302. -- preload anims
  10303. if PreloadAnimsUserFlag then
  10304. for i, animType in pairs(animTable) do
  10305. for idx = 1, animType.count, 1 do
  10306. if PreloadedAnims[animType[idx].anim.AnimationId] == nil then
  10307. Humanoid:LoadAnimation(animType[idx].anim)
  10308. PreloadedAnims[animType[idx].anim.AnimationId] = true
  10309. end
  10310. end
  10311. end
  10312. end
  10313. end
  10314.  
  10315. ------------------------------------------------------------------------------------------------------------
  10316.  
  10317. function configureAnimationSetOld(name, fileList)
  10318. if (animTable[name] ~= nil) then
  10319. for _, connection in pairs(animTable[name].connections) do
  10320. connection:disconnect()
  10321. end
  10322. end
  10323. animTable[name] = {}
  10324. animTable[name].count = 0
  10325. animTable[name].totalWeight = 0
  10326. animTable[name].connections = {}
  10327.  
  10328. local allowCustomAnimations = true
  10329.  
  10330. local success, msg = pcall(function() allowCustomAnimations = game:GetService("StarterPlayer").AllowCustomAnimations end)
  10331. if not success then
  10332. allowCustomAnimations = true
  10333. end
  10334.  
  10335. -- check for config values
  10336. local config = script:FindFirstChild(name)
  10337. if (allowCustomAnimations and config ~= nil) then
  10338. table.insert(animTable[name].connections, config.ChildAdded:connect(function(child) configureAnimationSet(name, fileList) end))
  10339. table.insert(animTable[name].connections, config.ChildRemoved:connect(function(child) configureAnimationSet(name, fileList) end))
  10340. local idx = 1
  10341. for _, childPart in pairs(config:GetChildren()) do
  10342. if (childPart:IsA("Animation")) then
  10343. table.insert(animTable[name].connections, childPart.Changed:connect(function(property) configureAnimationSet(name, fileList) end))
  10344. animTable[name][idx] = {}
  10345. animTable[name][idx].anim = childPart
  10346. local weightObject = childPart:FindFirstChild("Weight")
  10347. if (weightObject == nil) then
  10348. animTable[name][idx].weight = 1
  10349. else
  10350. animTable[name][idx].weight = weightObject.Value
  10351. end
  10352. animTable[name].count = animTable[name].count + 1
  10353. animTable[name].totalWeight = animTable[name].totalWeight + animTable[name][idx].weight
  10354. idx = idx + 1
  10355. end
  10356. end
  10357. end
  10358.  
  10359. -- fallback to defaults
  10360. if (animTable[name].count <= 0) then
  10361. for idx, anim in pairs(fileList) do
  10362. animTable[name][idx] = {}
  10363. animTable[name][idx].anim = Instance.new("Animation")
  10364. animTable[name][idx].anim.Name = name
  10365. animTable[name][idx].anim.AnimationId = anim.id
  10366. animTable[name][idx].weight = anim.weight
  10367. animTable[name].count = animTable[name].count + 1
  10368. animTable[name].totalWeight = animTable[name].totalWeight + anim.weight
  10369. -- print(name .. " [" .. idx .. "] " .. anim.id .. " (" .. anim.weight .. ")")
  10370. end
  10371. end
  10372.  
  10373. -- preload anims
  10374. if PreloadAnimsUserFlag then
  10375. for i, animType in pairs(animTable) do
  10376. for idx = 1, animType.count, 1 do
  10377. Humanoid:LoadAnimation(animType[idx].anim)
  10378. end
  10379. end
  10380. end
  10381. end
  10382.  
  10383. -- Setup animation objects
  10384. function scriptChildModified(child)
  10385. local fileList = animNames[child.Name]
  10386. if (fileList ~= nil) then
  10387. configureAnimationSet(child.Name, fileList)
  10388. end
  10389. end
  10390.  
  10391. script.ChildAdded:connect(scriptChildModified)
  10392. script.ChildRemoved:connect(scriptChildModified)
  10393.  
  10394.  
  10395. for name, fileList in pairs(animNames) do
  10396. configureAnimationSet(name, fileList)
  10397. end
  10398.  
  10399. -- ANIMATION
  10400.  
  10401. -- declarations
  10402. local toolAnim = "None"
  10403. local toolAnimTime = 0
  10404.  
  10405. local jumpAnimTime = 0
  10406. local jumpAnimDuration = 0.31
  10407.  
  10408. local toolTransitionTime = 0.1
  10409. local fallTransitionTime = 0.2
  10410.  
  10411. local currentlyPlayingEmote = false
  10412.  
  10413. -- functions
  10414.  
  10415. function stopAllAnimations()
  10416. local oldAnim = currentAnim
  10417.  
  10418. -- return to idle if finishing an emote
  10419. if (emoteNames[oldAnim] ~= nil and emoteNames[oldAnim] == false) then
  10420. oldAnim = "idle"
  10421. end
  10422.  
  10423. if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
  10424. oldAnim = "idle"
  10425. currentlyPlayingEmote = false
  10426. end
  10427.  
  10428. currentAnim = ""
  10429. currentAnimInstance = nil
  10430. if (currentAnimKeyframeHandler ~= nil) then
  10431. currentAnimKeyframeHandler:disconnect()
  10432. end
  10433.  
  10434. if (currentAnimTrack ~= nil) then
  10435. currentAnimTrack:Stop()
  10436. currentAnimTrack:Destroy()
  10437. currentAnimTrack = nil
  10438. end
  10439.  
  10440. -- clean up walk if there is one
  10441. if (runAnimKeyframeHandler ~= nil) then
  10442. runAnimKeyframeHandler:disconnect()
  10443. end
  10444.  
  10445. if (runAnimTrack ~= nil) then
  10446. runAnimTrack:Stop()
  10447. runAnimTrack:Destroy()
  10448. runAnimTrack = nil
  10449. end
  10450.  
  10451. return oldAnim
  10452. end
  10453.  
  10454. function getHeightScale()
  10455. if Humanoid then
  10456. if not Humanoid.AutomaticScalingEnabled then
  10457. return 1
  10458. end
  10459.  
  10460. local scale = Humanoid.HipHeight / HumanoidHipHeight
  10461. if userAnimationSpeedDampening then
  10462. if AnimationSpeedDampeningObject == nil then
  10463. AnimationSpeedDampeningObject = script:FindFirstChild("ScaleDampeningPercent")
  10464. end
  10465. if AnimationSpeedDampeningObject ~= nil then
  10466. scale = 1 + (Humanoid.HipHeight - HumanoidHipHeight) * AnimationSpeedDampeningObject.Value / HumanoidHipHeight
  10467. end
  10468. end
  10469. return scale
  10470. end
  10471. return 1
  10472. end
  10473.  
  10474. local smallButNotZero = 0.0001
  10475. function setRunSpeed(speed)
  10476. local speedScaled = speed * 1.25
  10477. local heightScale = getHeightScale()
  10478. local runSpeed = speedScaled / heightScale
  10479.  
  10480. if runSpeed ~= currentAnimSpeed then
  10481. if runSpeed < 0.33 then
  10482. currentAnimTrack:AdjustWeight(1.0)
  10483. runAnimTrack:AdjustWeight(smallButNotZero)
  10484. elseif runSpeed < 0.66 then
  10485. local weight = ((runSpeed - 0.33) / 0.33)
  10486. currentAnimTrack:AdjustWeight(1.0 - weight + smallButNotZero)
  10487. runAnimTrack:AdjustWeight(weight + smallButNotZero)
  10488. else
  10489. currentAnimTrack:AdjustWeight(smallButNotZero)
  10490. runAnimTrack:AdjustWeight(1.0)
  10491. end
  10492. currentAnimSpeed = runSpeed
  10493. runAnimTrack:AdjustSpeed(runSpeed)
  10494. currentAnimTrack:AdjustSpeed(runSpeed)
  10495. end
  10496. end
  10497.  
  10498. function setAnimationSpeed(speed)
  10499. if currentAnim == "walk" then
  10500. setRunSpeed(speed)
  10501. else
  10502. if speed ~= currentAnimSpeed then
  10503. currentAnimSpeed = speed
  10504. currentAnimTrack:AdjustSpeed(currentAnimSpeed)
  10505. end
  10506. end
  10507. end
  10508.  
  10509. function keyFrameReachedFunc(frameName)
  10510. if (frameName == "End") then
  10511. if currentAnim == "walk" then
  10512. if userNoUpdateOnLoop == true then
  10513. if runAnimTrack.Looped ~= true then
  10514. runAnimTrack.TimePosition = 0.0
  10515. end
  10516. if currentAnimTrack.Looped ~= true then
  10517. currentAnimTrack.TimePosition = 0.0
  10518. end
  10519. else
  10520. runAnimTrack.TimePosition = 0.0
  10521. currentAnimTrack.TimePosition = 0.0
  10522. end
  10523. else
  10524. local repeatAnim = currentAnim
  10525. -- return to idle if finishing an emote
  10526. if (emoteNames[repeatAnim] ~= nil and emoteNames[repeatAnim] == false) then
  10527. repeatAnim = "idle"
  10528. end
  10529.  
  10530. if FFlagAnimateScriptEmoteHook and currentlyPlayingEmote then
  10531. if currentAnimTrack.Looped then
  10532. -- Allow the emote to loop
  10533. return
  10534. end
  10535.  
  10536. repeatAnim = "idle"
  10537. currentlyPlayingEmote = false
  10538. end
  10539.  
  10540. local animSpeed = currentAnimSpeed
  10541. playAnimation(repeatAnim, 0.15, Humanoid)
  10542. setAnimationSpeed(animSpeed)
  10543. end
  10544. end
  10545. end
  10546.  
  10547. function rollAnimation(animName)
  10548. local roll = math.random(1, animTable[animName].totalWeight)
  10549. local origRoll = roll
  10550. local idx = 1
  10551. while (roll > animTable[animName][idx].weight) do
  10552. roll = roll - animTable[animName][idx].weight
  10553. idx = idx + 1
  10554. end
  10555. return idx
  10556. end
  10557.  
  10558. local function switchToAnim(anim, animName, transitionTime, humanoid)
  10559. -- switch animation
  10560. if (anim ~= currentAnimInstance) then
  10561.  
  10562. if (currentAnimTrack ~= nil) then
  10563. currentAnimTrack:Stop(transitionTime)
  10564. currentAnimTrack:Destroy()
  10565. end
  10566.  
  10567. if (runAnimTrack ~= nil) then
  10568. runAnimTrack:Stop(transitionTime)
  10569. runAnimTrack:Destroy()
  10570. if userNoUpdateOnLoop == true then
  10571. runAnimTrack = nil
  10572. end
  10573. end
  10574.  
  10575. currentAnimSpeed = 1.0
  10576.  
  10577. -- load it to the humanoid; get AnimationTrack
  10578. currentAnimTrack = humanoid:LoadAnimation(anim)
  10579. currentAnimTrack.Priority = Enum.AnimationPriority.Core
  10580.  
  10581. -- play the animation
  10582. currentAnimTrack:Play(transitionTime)
  10583. currentAnim = animName
  10584. currentAnimInstance = anim
  10585.  
  10586. -- set up keyframe name triggers
  10587. if (currentAnimKeyframeHandler ~= nil) then
  10588. currentAnimKeyframeHandler:disconnect()
  10589. end
  10590. currentAnimKeyframeHandler = currentAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
  10591.  
  10592. -- check to see if we need to blend a walk/run animation
  10593. if animName == "walk" then
  10594. local runAnimName = "run"
  10595. local runIdx = rollAnimation(runAnimName)
  10596.  
  10597. runAnimTrack = humanoid:LoadAnimation(animTable[runAnimName][runIdx].anim)
  10598. runAnimTrack.Priority = Enum.AnimationPriority.Core
  10599. runAnimTrack:Play(transitionTime)
  10600.  
  10601. if (runAnimKeyframeHandler ~= nil) then
  10602. runAnimKeyframeHandler:disconnect()
  10603. end
  10604. runAnimKeyframeHandler = runAnimTrack.KeyframeReached:connect(keyFrameReachedFunc)
  10605. end
  10606. end
  10607. end
  10608.  
  10609. function playAnimation(animName, transitionTime, humanoid)
  10610. local idx = rollAnimation(animName)
  10611. local anim = animTable[animName][idx].anim
  10612.  
  10613. switchToAnim(anim, animName, transitionTime, humanoid)
  10614. currentlyPlayingEmote = false
  10615. end
  10616.  
  10617. function playEmote(emoteAnim, transitionTime, humanoid)
  10618. switchToAnim(emoteAnim, emoteAnim.Name, transitionTime, humanoid)
  10619. currentlyPlayingEmote = true
  10620. end
  10621.  
  10622. -------------------------------------------------------------------------------------------
  10623. -------------------------------------------------------------------------------------------
  10624.  
  10625. local toolAnimName = ""
  10626. local toolAnimTrack = nil
  10627. local toolAnimInstance = nil
  10628. local currentToolAnimKeyframeHandler = nil
  10629.  
  10630. function toolKeyFrameReachedFunc(frameName)
  10631. if (frameName == "End") then
  10632. playToolAnimation(toolAnimName, 0.0, Humanoid)
  10633. end
  10634. end
  10635.  
  10636.  
  10637. function playToolAnimation(animName, transitionTime, humanoid, priority)
  10638. local idx = rollAnimation(animName)
  10639. local anim = animTable[animName][idx].anim
  10640.  
  10641. if (toolAnimInstance ~= anim) then
  10642.  
  10643. if (toolAnimTrack ~= nil) then
  10644. toolAnimTrack:Stop()
  10645. toolAnimTrack:Destroy()
  10646. transitionTime = 0
  10647. end
  10648.  
  10649. -- load it to the humanoid; get AnimationTrack
  10650. toolAnimTrack = humanoid:LoadAnimation(anim)
  10651. if priority then
  10652. toolAnimTrack.Priority = priority
  10653. end
  10654.  
  10655. -- play the animation
  10656. toolAnimTrack:Play(transitionTime)
  10657. toolAnimName = animName
  10658. toolAnimInstance = anim
  10659.  
  10660. currentToolAnimKeyframeHandler = toolAnimTrack.KeyframeReached:connect(toolKeyFrameReachedFunc)
  10661. end
  10662. end
  10663.  
  10664. function stopToolAnimations()
  10665. local oldAnim = toolAnimName
  10666.  
  10667. if (currentToolAnimKeyframeHandler ~= nil) then
  10668. currentToolAnimKeyframeHandler:disconnect()
  10669. end
  10670.  
  10671. toolAnimName = ""
  10672. toolAnimInstance = nil
  10673. if (toolAnimTrack ~= nil) then
  10674. toolAnimTrack:Stop()
  10675. toolAnimTrack:Destroy()
  10676. toolAnimTrack = nil
  10677. end
  10678.  
  10679. return oldAnim
  10680. end
  10681.  
  10682. -------------------------------------------------------------------------------------------
  10683. -------------------------------------------------------------------------------------------
  10684. -- STATE CHANGE HANDLERS
  10685.  
  10686. function onRunning(speed)
  10687. if speed > 0.75 then
  10688. local scale = 16.0
  10689. playAnimation("walk", 0.2, Humanoid)
  10690. setAnimationSpeed(speed / scale)
  10691. pose = "Running"
  10692. else
  10693. if emoteNames[currentAnim] == nil and not currentlyPlayingEmote then
  10694. playAnimation("idle", 0.2, Humanoid)
  10695. pose = "Standing"
  10696. end
  10697. end
  10698. end
  10699.  
  10700. function onDied()
  10701. pose = "Dead"
  10702. end
  10703.  
  10704. function onJumping()
  10705. playAnimation("jump", 0.1, Humanoid)
  10706. jumpAnimTime = jumpAnimDuration
  10707. pose = "Jumping"
  10708. end
  10709.  
  10710. function onClimbing(speed)
  10711. local scale = 5.0
  10712. playAnimation("climb", 0.1, Humanoid)
  10713. setAnimationSpeed(speed / scale)
  10714. pose = "Climbing"
  10715. end
  10716.  
  10717. function onGettingUp()
  10718. pose = "GettingUp"
  10719. end
  10720.  
  10721. function onFreeFall()
  10722. if (jumpAnimTime <= 0) then
  10723. playAnimation("fall", fallTransitionTime, Humanoid)
  10724. end
  10725. pose = "FreeFall"
  10726. end
  10727.  
  10728. function onFallingDown()
  10729. pose = "FallingDown"
  10730. end
  10731.  
  10732. function onSeated()
  10733. pose = "Seated"
  10734. end
  10735.  
  10736. function onPlatformStanding()
  10737. pose = "PlatformStanding"
  10738. end
  10739.  
  10740. -------------------------------------------------------------------------------------------
  10741. -------------------------------------------------------------------------------------------
  10742.  
  10743. function onSwimming(speed)
  10744. if speed > 1.00 then
  10745. local scale = 10.0
  10746. playAnimation("swim", 0.4, Humanoid)
  10747. setAnimationSpeed(speed / scale)
  10748. pose = "Swimming"
  10749. else
  10750. playAnimation("swimidle", 0.4, Humanoid)
  10751. pose = "Standing"
  10752. end
  10753. end
  10754.  
  10755. function animateTool()
  10756. if (toolAnim == "None") then
  10757. playToolAnimation("toolnone", toolTransitionTime, Humanoid, Enum.AnimationPriority.Idle)
  10758. return
  10759. end
  10760.  
  10761. if (toolAnim == "Slash") then
  10762. playToolAnimation("toolslash", 0, Humanoid, Enum.AnimationPriority.Action)
  10763. return
  10764. end
  10765.  
  10766. if (toolAnim == "Lunge") then
  10767. playToolAnimation("toollunge", 0, Humanoid, Enum.AnimationPriority.Action)
  10768. return
  10769. end
  10770. end
  10771.  
  10772. function getToolAnim(tool)
  10773. for _, c in ipairs(tool:GetChildren()) do
  10774. if c.Name == "toolanim" and c.className == "StringValue" then
  10775. return c
  10776. end
  10777. end
  10778. return nil
  10779. end
  10780.  
  10781. local lastTick = 0
  10782.  
  10783. function stepAnimate(currentTime)
  10784. local amplitude = 1
  10785. local frequency = 1
  10786. local deltaTime = currentTime - lastTick
  10787. lastTick = currentTime
  10788.  
  10789. local climbFudge = 0
  10790. local setAngles = false
  10791.  
  10792. if (jumpAnimTime > 0) then
  10793. jumpAnimTime = jumpAnimTime - deltaTime
  10794. end
  10795.  
  10796. if (pose == "FreeFall" and jumpAnimTime <= 0) then
  10797. playAnimation("fall", fallTransitionTime, Humanoid)
  10798. elseif (pose == "Seated") then
  10799. playAnimation("sit", 0.5, Humanoid)
  10800. return
  10801. elseif (pose == "Running") then
  10802. playAnimation("walk", 0.2, Humanoid)
  10803. elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
  10804. stopAllAnimations()
  10805. amplitude = 0.1
  10806. frequency = 1
  10807. setAngles = true
  10808. end
  10809.  
  10810. -- Tool Animation handling
  10811. local tool = Character:FindFirstChildOfClass("Tool")
  10812. if tool and tool:FindFirstChild("Handle") then
  10813. local animStringValueObject = getToolAnim(tool)
  10814.  
  10815. if animStringValueObject then
  10816. toolAnim = animStringValueObject.Value
  10817. -- message recieved, delete StringValue
  10818. animStringValueObject.Parent = nil
  10819. toolAnimTime = currentTime + .3
  10820. end
  10821.  
  10822. if currentTime > toolAnimTime then
  10823. toolAnimTime = 0
  10824. toolAnim = "None"
  10825. end
  10826.  
  10827. animateTool()
  10828. else
  10829. stopToolAnimations()
  10830. toolAnim = "None"
  10831. toolAnimInstance = nil
  10832. toolAnimTime = 0
  10833. end
  10834. end
  10835.  
  10836. -- connect events
  10837.  
  10838. local events = {}
  10839. local eventHum = Humanoid
  10840.  
  10841. local function onUnhook()
  10842. for i = 1, #events do
  10843. events[i]:Disconnect()
  10844. end
  10845. events = {}
  10846. end
  10847.  
  10848. local function onHook()
  10849. onUnhook()
  10850.  
  10851. pose = eventHum.Sit and "Seated" or "Standing"
  10852.  
  10853. events = {
  10854. eventHum.Died:connect(onDied),
  10855. eventHum.Running:connect(onRunning),
  10856. eventHum.Jumping:connect(onJumping),
  10857. eventHum.Climbing:connect(onClimbing),
  10858. eventHum.GettingUp:connect(onGettingUp),
  10859. eventHum.FreeFalling:connect(onFreeFall),
  10860. eventHum.FallingDown:connect(onFallingDown),
  10861. eventHum.Seated:connect(onSeated),
  10862. eventHum.PlatformStanding:connect(onPlatformStanding),
  10863. eventHum.Swimming:connect(onSwimming)
  10864. }
  10865. end
  10866.  
  10867.  
  10868. onHook()
  10869.  
  10870. -- setup emote chat hook
  10871. game:GetService("Players").LocalPlayer.Chatted:connect(function(msg)
  10872. local emote = ""
  10873. if (string.sub(msg, 1, 3) == "/e ") then
  10874. emote = string.sub(msg, 4)
  10875. elseif (string.sub(msg, 1, 7) == "/emote ") then
  10876. emote = string.sub(msg, 8)
  10877. end
  10878.  
  10879. if (pose == "Standing" and emoteNames[emote] ~= nil) then
  10880. playAnimation(emote, EMOTE_TRANSITION_TIME, Humanoid)
  10881. end
  10882. end)
  10883.  
  10884. --[[ emote bindable hook
  10885. if FFlagAnimateScriptEmoteHook then
  10886. script:WaitForChild("PlayEmote").OnInvoke = function(emote)
  10887. -- Only play emotes when idling
  10888. if pose ~= "Standing" then
  10889. return
  10890. end
  10891. if emoteNames[emote] ~= nil then
  10892. -- Default emotes
  10893. playAnimation(emote, EMOTE_TRANSITION_TIME, Humanoid)
  10894. return true
  10895. elseif typeof(emote) == "Instance" and emote:IsA("Animation") then
  10896. -- Non-default emotes
  10897. playEmote(emote, EMOTE_TRANSITION_TIME, Humanoid)
  10898. return true
  10899. end
  10900. -- Return false to indicate that the emote could not be played
  10901. return false
  10902. end
  10903. end
  10904. ]]
  10905. -- initialize to idle
  10906. playAnimation("idle", 0.1, Humanoid)
  10907. pose = "Standing"
  10908. -- loop to handle timed state transitions and tool animations
  10909. spawn(function()
  10910. while Character.Parent ~= nil do
  10911. local _, currentGameTime = wait(0.1)
  10912. stepAnimate(currentGameTime)
  10913. end
  10914. end)
  10915. return {
  10916. onRunning = onRunning,
  10917. onDied = onDied,
  10918. onJumping = onJumping,
  10919. onClimbing = onClimbing,
  10920. onGettingUp = onGettingUp,
  10921. onFreeFall = onFreeFall,
  10922. onFallingDown = onFallingDown,
  10923. onSeated = onSeated,
  10924. onPlatformStanding = onPlatformStanding,
  10925. onHook = onHook,
  10926. onUnhook = onUnhook
  10927. }
  10928. end
  10929. return r15()
  10930. end
  10931. while true do
  10932. wait(.1)
  10933. if plr.Character ~= nil then
  10934. char = plr.Character
  10935. break
  10936. end
  10937. end
  10938. function _Controller()
  10939. local humanoid = char:WaitForChild("Humanoid")
  10940. local animFuncs = {}
  10941. if (humanoid.RigType == Enum.HumanoidRigType.R6) then
  10942. animFuncs = _R6()
  10943. else
  10944. animFuncs = _R15()
  10945. end
  10946. print("Animation succes")
  10947. return animFuncs
  10948. end
  10949. function _AnimationHandler()
  10950. local AnimationHandler = {}
  10951. AnimationHandler.__index = AnimationHandler
  10952.  
  10953. function AnimationHandler.new(humanoid, animate)
  10954. local self = setmetatable({}, AnimationHandler)
  10955.  
  10956. self._AnimFuncs = _Controller()
  10957. self.Humanoid = humanoid
  10958.  
  10959. return self
  10960. end
  10961.  
  10962. function AnimationHandler:EnableDefault(bool)
  10963. if (bool) then
  10964. self._AnimFuncs.onHook()
  10965. else
  10966. self._AnimFuncs.onUnhook()
  10967. end
  10968. end
  10969.  
  10970. function AnimationHandler:Run(name, ...)
  10971. self._AnimFuncs[name](...)
  10972. end
  10973.  
  10974. return AnimationHandler
  10975. end
  10976.  
  10977. function _GravityController()
  10978.  
  10979. local ZERO = Vector3.new(0, 0, 0)
  10980. local UNIT_X = Vector3.new(1, 0, 0)
  10981. local UNIT_Y = Vector3.new(0, 1, 0)
  10982. local UNIT_Z = Vector3.new(0, 0, 1)
  10983. local VEC_XY = Vector3.new(1, 0, 1)
  10984.  
  10985. local IDENTITYCF = CFrame.new()
  10986.  
  10987. local JUMPMODIFIER = 1.2
  10988. local TRANSITION = 0.15
  10989. local WALKF = 200 / 3
  10990.  
  10991. local UIS = game:GetService("UserInputService")
  10992. local RUNSERVICE = game:GetService("RunService")
  10993.  
  10994. local InitObjects = _InitObjects()
  10995. local AnimationHandler = _AnimationHandler()
  10996. local StateTracker = _StateTracker()
  10997.  
  10998. -- Class
  10999.  
  11000. local GravityController = {}
  11001. GravityController.__index = GravityController
  11002.  
  11003. -- Private Functions
  11004.  
  11005. local function getRotationBetween(u, v, axis)
  11006. local dot, uxv = u:Dot(v), u:Cross(v)
  11007. if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
  11008. return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
  11009. end
  11010.  
  11011. local function lookAt(pos, forward, up)
  11012. local r = forward:Cross(up)
  11013. local u = r:Cross(forward)
  11014. return CFrame.fromMatrix(pos, r.Unit, u.Unit)
  11015. end
  11016.  
  11017. local function getMass(array)
  11018. local mass = 0
  11019. for _, part in next, array do
  11020. if (part:IsA("BasePart")) then
  11021. mass = mass + part:GetMass()
  11022. end
  11023. end
  11024. return mass
  11025. end
  11026.  
  11027. -- Public Constructor
  11028. local ExecutedPlayerModule = _PlayerModule()
  11029. local ExecutedSounds = _sounds()
  11030. function GravityController.new(player)
  11031. local self = setmetatable({}, GravityController)
  11032.  
  11033. --[[ Camera
  11034. local loaded = player.PlayerScripts:WaitForChild("PlayerScriptsLoader"):WaitForChild("Loaded")
  11035. if (not loaded.Value) then
  11036. --loaded.Changed:Wait()
  11037. end
  11038. ]]
  11039. local playerModule = ExecutedPlayerModule
  11040. self.Controls = playerModule:GetControls()
  11041. self.Camera = playerModule:GetCameras()
  11042.  
  11043. -- Player and character
  11044. self.Player = player
  11045. self.Character = player.Character
  11046. self.Humanoid = player.Character:WaitForChild("Humanoid")
  11047. self.HRP = player.Character:WaitForChild("HumanoidRootPart")
  11048.  
  11049. -- Animation
  11050. self.AnimationHandler = AnimationHandler.new(self.Humanoid, self.Character:WaitForChild("Animate"))
  11051. self.AnimationHandler:EnableDefault(false)
  11052. local ssss = game:GetService("Players").LocalPlayer.PlayerScripts:FindFirstChild("SetState") or Instance.new("BindableEvent",game:GetService("Players").LocalPlayer.PlayerScripts)
  11053. local soundState = ExecutedSounds
  11054. ssss.Name = "SetState"
  11055.  
  11056. self.StateTracker = StateTracker.new(self.Humanoid, soundState)
  11057. self.StateTracker.Changed:Connect(function(name, speed)
  11058. self.AnimationHandler:Run(name, speed)
  11059. end)
  11060.  
  11061. -- Collider and forces
  11062. local collider, gyro, vForce, floor = InitObjects(self)
  11063.  
  11064. floor.Touched:Connect(function() end)
  11065. collider.Touched:Connect(function() end)
  11066.  
  11067. self.Collider = collider
  11068. self.VForce = vForce
  11069. self.Gyro = gyro
  11070. self.Floor = floor
  11071.  
  11072. -- Attachment to parts
  11073. self.LastPart = workspace.Terrain
  11074. self.LastPartCFrame = IDENTITYCF
  11075.  
  11076. -- Gravity properties
  11077. self.GravityUp = UNIT_Y
  11078. self.Ignores = {self.Character}
  11079.  
  11080. function self.Camera.GetUpVector(this, oldUpVector)
  11081. return self.GravityUp
  11082. end
  11083.  
  11084. -- Events etc
  11085. self.Humanoid.PlatformStand = true
  11086.  
  11087. self.CharacterMass = getMass(self.Character:GetDescendants())
  11088. self.Character.AncestryChanged:Connect(function() self.CharacterMass = getMass(self.Character:GetDescendants()) end)
  11089.  
  11090. self.JumpCon = RUNSERVICE.RenderStepped:Connect(function(dt)
  11091. if (self.Controls:IsJumping()) then
  11092. self:OnJumpRequest()
  11093. end
  11094. end)
  11095.  
  11096. self.DeathCon = self.Humanoid.Died:Connect(function() self:Destroy() end)
  11097. self.SeatCon = self.Humanoid.Seated:Connect(function(active) if (active) then self:Destroy() end end)
  11098. self.HeartCon = RUNSERVICE.Heartbeat:Connect(function(dt) self:OnHeartbeatStep(dt) end)
  11099. RUNSERVICE:BindToRenderStep("GravityStep", Enum.RenderPriority.Input.Value + 1, function(dt) self:OnGravityStep(dt) end)
  11100.  
  11101.  
  11102. return self
  11103. end
  11104.  
  11105. -- Public Methods
  11106.  
  11107. function GravityController:Destroy()
  11108. self.JumpCon:Disconnect()
  11109. self.DeathCon:Disconnect()
  11110. self.SeatCon:Disconnect()
  11111. self.HeartCon:Disconnect()
  11112.  
  11113. RUNSERVICE:UnbindFromRenderStep("GravityStep")
  11114.  
  11115. self.Collider:Destroy()
  11116. self.VForce:Destroy()
  11117. self.Gyro:Destroy()
  11118. self.StateTracker:Destroy()
  11119.  
  11120. self.Humanoid.PlatformStand = false
  11121. self.AnimationHandler:EnableDefault(true)
  11122.  
  11123. self.GravityUp = UNIT_Y
  11124. end
  11125.  
  11126. function GravityController:GetGravityUp(oldGravity)
  11127. return oldGravity
  11128. end
  11129.  
  11130. function GravityController:IsGrounded(isJumpCheck)
  11131. if (not isJumpCheck) then
  11132. local parts = self.Floor:GetTouchingParts()
  11133. for _, part in next, parts do
  11134. if (not part:IsDescendantOf(self.Character)) then
  11135. return true
  11136. end
  11137. end
  11138. else
  11139. if (self.StateTracker.Jumped) then
  11140. return false
  11141. end
  11142.  
  11143. -- 1. check we are touching something with the collider
  11144. local valid = {}
  11145. local parts = self.Collider:GetTouchingParts()
  11146. for _, part in next, parts do
  11147. if (not part:IsDescendantOf(self.Character)) then
  11148. table.insert(valid, part)
  11149. end
  11150. end
  11151.  
  11152. if (#valid > 0) then
  11153. -- 2. do a decently long downwards raycast
  11154. local max = math.cos(self.Humanoid.MaxSlopeAngle)
  11155. local ray = Ray.new(self.Collider.Position, -10 * self.GravityUp)
  11156. local hit, pos, normal = workspace:FindPartOnRayWithWhitelist(ray, valid, true)
  11157.  
  11158. -- 3. use slope to decide on jump
  11159. if (hit and max <= self.GravityUp:Dot(normal)) then
  11160. return true
  11161. end
  11162. end
  11163. end
  11164. return false
  11165. end
  11166.  
  11167. function GravityController:OnJumpRequest()
  11168. if (not self.StateTracker.Jumped and self:IsGrounded(true)) then
  11169. local hrpVel = self.HRP.Velocity
  11170. self.HRP.Velocity = hrpVel + self.GravityUp*self.Humanoid.JumpPower*JUMPMODIFIER
  11171. self.StateTracker:RequestedJump()
  11172. end
  11173. end
  11174.  
  11175. function GravityController:GetMoveVector()
  11176. return self.Controls:GetMoveVector()
  11177. end
  11178.  
  11179. function GravityController:OnHeartbeatStep(dt)
  11180. local ray = Ray.new(self.Collider.Position, -1.1*self.GravityUp)
  11181. local hit, pos, normal = workspace:FindPartOnRayWithIgnoreList(ray, self.Ignores)
  11182. local lastPart = self.LastPart
  11183.  
  11184. if (hit and lastPart and lastPart == hit) then
  11185. local offset = self.LastPartCFrame:ToObjectSpace(self.HRP.CFrame)
  11186. self.HRP.CFrame = hit.CFrame:ToWorldSpace(offset)
  11187. end
  11188.  
  11189. self.LastPart = hit
  11190. self.LastPartCFrame = hit and hit.CFrame
  11191. end
  11192.  
  11193. function GravityController:OnGravityStep(dt)
  11194. -- update gravity up vector
  11195. local oldGravity = self.GravityUp
  11196. local newGravity = self:GetGravityUp(oldGravity)
  11197.  
  11198. local rotation = getRotationBetween(oldGravity, newGravity, workspace.CurrentCamera.CFrame.RightVector)
  11199. rotation = IDENTITYCF:Lerp(rotation, TRANSITION)
  11200.  
  11201. self.GravityUp = rotation * oldGravity
  11202.  
  11203. -- get world move vector
  11204. local camCF = workspace.CurrentCamera.CFrame
  11205. local fDot = camCF.LookVector:Dot(newGravity)
  11206. local cForward = math.abs(fDot) > 0.5 and -math.sign(fDot)*camCF.UpVector or camCF.LookVector
  11207.  
  11208. local left = cForward:Cross(-newGravity).Unit
  11209. local forward = -left:Cross(newGravity).Unit
  11210.  
  11211. local move = self:GetMoveVector()
  11212. local worldMove = forward*move.z - left*move.x
  11213. worldMove = worldMove:Dot(worldMove) > 1 and worldMove.Unit or worldMove
  11214.  
  11215. local isInputMoving = worldMove:Dot(worldMove) > 0
  11216.  
  11217. -- get the desired character cframe
  11218. local hrpCFLook = self.HRP.CFrame.LookVector
  11219. local charF = hrpCFLook:Dot(forward)*forward + hrpCFLook:Dot(left)*left
  11220. local charR = charF:Cross(newGravity).Unit
  11221. local newCharCF = CFrame.fromMatrix(ZERO, charR, newGravity, -charF)
  11222.  
  11223. local newCharRotation = IDENTITYCF
  11224. if (isInputMoving) then
  11225. newCharRotation = IDENTITYCF:Lerp(getRotationBetween(charF, worldMove, newGravity), 0.7)
  11226. end
  11227.  
  11228. -- calculate forces
  11229. local g = workspace.Gravity
  11230. local gForce = g * self.CharacterMass * (UNIT_Y - newGravity)
  11231.  
  11232. local cVelocity = self.HRP.Velocity
  11233. local tVelocity = self.Humanoid.WalkSpeed * worldMove
  11234. local gVelocity = cVelocity:Dot(newGravity)*newGravity
  11235. local hVelocity = cVelocity - gVelocity
  11236.  
  11237. if (hVelocity:Dot(hVelocity) < 1) then
  11238. hVelocity = ZERO
  11239. end
  11240.  
  11241. local dVelocity = tVelocity - hVelocity
  11242. local walkForceM = math.min(10000, WALKF * self.CharacterMass * dVelocity.Magnitude / (dt*60))
  11243. local walkForce = walkForceM > 0 and dVelocity.Unit*walkForceM or ZERO
  11244.  
  11245. -- mouse lock
  11246. local charRotation = newCharRotation * newCharCF
  11247.  
  11248. if (self.Camera:IsCamRelative()) then
  11249. local lv = workspace.CurrentCamera.CFrame.LookVector
  11250. local hlv = lv - charRotation.UpVector:Dot(lv)*charRotation.UpVector
  11251. charRotation = lookAt(ZERO, hlv, charRotation.UpVector)
  11252. end
  11253.  
  11254. -- get state
  11255. self.StateTracker:OnStep(self.GravityUp, self:IsGrounded(), isInputMoving)
  11256.  
  11257. -- update values
  11258. self.VForce.Force = walkForce + gForce
  11259. self.Gyro.CFrame = charRotation
  11260. end
  11261. return GravityController
  11262. end
  11263. function _Draw3D()
  11264. local module = {}
  11265.  
  11266. -- Style Guide
  11267.  
  11268. module.StyleGuide = {
  11269. Point = {
  11270. Thickness = 0.5;
  11271. Color = Color3.new(0, 1, 0);
  11272. },
  11273.  
  11274. Line = {
  11275. Thickness = 0.1;
  11276. Color = Color3.new(1, 1, 0);
  11277. },
  11278.  
  11279. Ray = {
  11280. Thickness = 0.1;
  11281. Color = Color3.new(1, 0, 1);
  11282. },
  11283.  
  11284. Triangle = {
  11285. Thickness = 0.05;
  11286. };
  11287.  
  11288. CFrame = {
  11289. Thickness = 0.1;
  11290. RightColor3 = Color3.new(1, 0, 0);
  11291. UpColor3 = Color3.new(0, 1, 0);
  11292. BackColor3 = Color3.new(0, 0, 1);
  11293. PartProperties = {
  11294. Material = Enum.Material.SmoothPlastic;
  11295. };
  11296. }
  11297. }
  11298.  
  11299. -- CONSTANTS
  11300.  
  11301. local WEDGE = Instance.new("WedgePart")
  11302. WEDGE.Material = Enum.Material.SmoothPlastic
  11303. WEDGE.Anchored = true
  11304. WEDGE.CanCollide = false
  11305.  
  11306. local PART = Instance.new("Part")
  11307. PART.Size = Vector3.new(0.1, 0.1, 0.1)
  11308. PART.Anchored = true
  11309. PART.CanCollide = false
  11310. PART.TopSurface = Enum.SurfaceType.Smooth
  11311. PART.BottomSurface = Enum.SurfaceType.Smooth
  11312. PART.Material = Enum.Material.SmoothPlastic
  11313.  
  11314. -- Functions
  11315.  
  11316. local function draw(properties, style)
  11317. local part = PART:Clone()
  11318. for k, v in next, properties do
  11319. part[k] = v
  11320. end
  11321. if (style) then
  11322. for k, v in next, style do
  11323. if (k ~= "Thickness") then
  11324. part[k] = v
  11325. end
  11326. end
  11327. end
  11328. return part
  11329. end
  11330.  
  11331. function module.Draw(parent, properties)
  11332. properties.Parent = parent
  11333. return draw(properties, nil)
  11334. end
  11335.  
  11336. function module.Point(parent, cf_v3)
  11337. local thickness = module.StyleGuide.Point.Thickness
  11338. return draw({
  11339. Size = Vector3.new(thickness, thickness, thickness);
  11340. CFrame = (typeof(cf_v3) == "CFrame" and cf_v3 or CFrame.new(cf_v3));
  11341. Parent = parent;
  11342. }, module.StyleGuide.Point)
  11343. end
  11344.  
  11345. function module.Line(parent, a, b)
  11346. local thickness = module.StyleGuide.Line.Thickness
  11347. return draw({
  11348. CFrame = CFrame.new((a + b)/2, b);
  11349. Size = Vector3.new(thickness, thickness, (b - a).Magnitude);
  11350. Parent = parent;
  11351. }, module.StyleGuide.Line)
  11352. end
  11353.  
  11354. function module.Ray(parent, origin, direction)
  11355. local thickness = module.StyleGuide.Ray.Thickness
  11356. return draw({
  11357. CFrame = CFrame.new(origin + direction/2, origin + direction);
  11358. Size = Vector3.new(thickness, thickness, direction.Magnitude);
  11359. Parent = parent;
  11360. }, module.StyleGuide.Ray)
  11361. end
  11362.  
  11363. function module.Triangle(parent, a, b, c)
  11364. local ab, ac, bc = b - a, c - a, c - b
  11365. local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc)
  11366.  
  11367. if (abd > acd and abd > bcd) then
  11368. c, a = a, c
  11369. elseif (acd > bcd and acd > abd) then
  11370. a, b = b, a
  11371. end
  11372.  
  11373. ab, ac, bc = b - a, c - a, c - b
  11374.  
  11375. local right = ac:Cross(ab).Unit
  11376. local up = bc:Cross(right).Unit
  11377. local back = bc.Unit
  11378.  
  11379. local height = math.abs(ab:Dot(up))
  11380. local width1 = math.abs(ab:Dot(back))
  11381. local width2 = math.abs(ac:Dot(back))
  11382.  
  11383. local thickness = module.StyleGuide.Triangle.Thickness
  11384.  
  11385. local w1 = WEDGE:Clone()
  11386. w1.Size = Vector3.new(thickness, height, width1)
  11387. w1.CFrame = CFrame.fromMatrix((a + b)/2, right, up, back)
  11388. w1.Parent = parent
  11389.  
  11390. local w2 = WEDGE:Clone()
  11391. w2.Size = Vector3.new(thickness, height, width2)
  11392. w2.CFrame = CFrame.fromMatrix((a + c)/2, -right, up, -back)
  11393. w2.Parent = parent
  11394.  
  11395. for k, v in next, module.StyleGuide.Triangle do
  11396. if (k ~= "Thickness") then
  11397. w1[k] = v
  11398. w2[k] = v
  11399. end
  11400. end
  11401.  
  11402. return w1, w2
  11403. end
  11404.  
  11405. function module.CFrame(parent, cf)
  11406. local origin = cf.Position
  11407. local r = cf.RightVector
  11408. local u = cf.UpVector
  11409. local b = -cf.LookVector
  11410.  
  11411. local thickness = module.StyleGuide.CFrame.Thickness
  11412.  
  11413. local right = draw({
  11414. CFrame = CFrame.new(origin + r/2, origin + r);
  11415. Size = Vector3.new(thickness, thickness, r.Magnitude);
  11416. Color = module.StyleGuide.CFrame.RightColor3;
  11417. Parent = parent;
  11418. }, module.StyleGuide.CFrame.PartProperties)
  11419.  
  11420. local up = draw({
  11421. CFrame = CFrame.new(origin + u/2, origin + u);
  11422. Size = Vector3.new(thickness, thickness, r.Magnitude);
  11423. Color = module.StyleGuide.CFrame.UpColor3;
  11424. Parent = parent;
  11425. }, module.StyleGuide.CFrame.PartProperties)
  11426.  
  11427. local back = draw({
  11428. CFrame = CFrame.new(origin + b/2, origin + b);
  11429. Size = Vector3.new(thickness, thickness, u.Magnitude);
  11430. Color = module.StyleGuide.CFrame.BackColor3;
  11431. Parent = parent;
  11432. }, module.StyleGuide.CFrame.PartProperties)
  11433.  
  11434. return right, up, back
  11435. end
  11436.  
  11437. -- Return
  11438.  
  11439. return module
  11440. end
  11441. function _Draw2D()
  11442. local module = {}
  11443.  
  11444. -- Style Guide
  11445.  
  11446. module.StyleGuide = {
  11447. Point = {
  11448. BorderSizePixel = 0;
  11449. Size = UDim2.new(0, 4, 0, 4);
  11450. BorderColor3 = Color3.new(0, 0, 0);
  11451. BackgroundColor3 = Color3.new(0, 1, 0);
  11452. },
  11453.  
  11454. Line = {
  11455. Thickness = 1;
  11456. BorderSizePixel = 0;
  11457. BorderColor3 = Color3.new(0, 0, 0);
  11458. BackgroundColor3 = Color3.new(0, 1, 0);
  11459. },
  11460.  
  11461. Ray = {
  11462. Thickness = 1;
  11463. BorderSizePixel = 0;
  11464. BorderColor3 = Color3.new(0, 0, 0);
  11465. BackgroundColor3 = Color3.new(0, 1, 0);
  11466. },
  11467.  
  11468. Triangle = {
  11469. ImageTransparency = 0;
  11470. ImageColor3 = Color3.new(0, 1, 0);
  11471. }
  11472. }
  11473.  
  11474. -- CONSTANTS
  11475.  
  11476. local HALF = Vector2.new(0.5, 0.5)
  11477.  
  11478. local RIGHT = "rbxassetid://2798177521"
  11479. local LEFT = "rbxassetid://2798177955"
  11480.  
  11481. local IMG = Instance.new("ImageLabel")
  11482. IMG.BackgroundTransparency = 1
  11483. IMG.AnchorPoint = HALF
  11484. IMG.BorderSizePixel = 0
  11485.  
  11486. local FRAME = Instance.new("Frame")
  11487. FRAME.BorderSizePixel = 0
  11488. FRAME.Size = UDim2.new(0, 0, 0, 0)
  11489. FRAME.BackgroundColor3 = Color3.new(1, 1, 1)
  11490.  
  11491. -- Functions
  11492.  
  11493. function draw(properties, style)
  11494. local frame = FRAME:Clone()
  11495. for k, v in next, properties do
  11496. frame[k] = v
  11497. end
  11498. if (style) then
  11499. for k, v in next, style do
  11500. if (k ~= "Thickness") then
  11501. frame[k] = v
  11502. end
  11503. end
  11504. end
  11505. return frame
  11506. end
  11507.  
  11508. function module.Draw(parent, properties)
  11509. properties.Parent = parent
  11510. return draw(properties, nil)
  11511. end
  11512.  
  11513. function module.Point(parent, v2)
  11514. return draw({
  11515. AnchorPoint = HALF;
  11516. Position = UDim2.new(0, v2.x, 0, v2.y);
  11517. Parent = parent;
  11518. }, module.StyleGuide.Point)
  11519. end
  11520.  
  11521. function module.Line(parent, a, b)
  11522. local v = (b - a)
  11523. local m = (a + b)/2
  11524.  
  11525. return draw({
  11526. AnchorPoint = HALF;
  11527. Position = UDim2.new(0, m.x, 0, m.y);
  11528. Size = UDim2.new(0, module.StyleGuide.Line.Thickness, 0, v.magnitude);
  11529. Rotation = math.deg(math.atan2(v.y, v.x)) - 90;
  11530. BackgroundColor3 = Color3.new(1, 1, 0);
  11531. Parent = parent;
  11532. }, module.StyleGuide.Line)
  11533. end
  11534.  
  11535. function module.Ray(parent, origin, direction)
  11536. local a, b = origin, origin + direction
  11537. local v = (b - a)
  11538. local m = (a + b)/2
  11539.  
  11540. return draw({
  11541. AnchorPoint = HALF;
  11542. Position = UDim2.new(0, m.x, 0, m.y);
  11543. Size = UDim2.new(0, module.StyleGuide.Ray.Thickness, 0, v.magnitude);
  11544. Rotation = math.deg(math.atan2(v.y, v.x)) - 90;
  11545. Parent = parent;
  11546. }, module.StyleGuide.Ray)
  11547. end
  11548.  
  11549. function module.Triangle(parent, a, b, c)
  11550. local ab, ac, bc = b - a, c - a, c - b
  11551. local abd, acd, bcd = ab:Dot(ab), ac:Dot(ac), bc:Dot(bc)
  11552.  
  11553. if (abd > acd and abd > bcd) then
  11554. c, a = a, c
  11555. elseif (acd > bcd and acd > abd) then
  11556. a, b = b, a
  11557. end
  11558.  
  11559. ab, ac, bc = b - a, c - a, c - b
  11560.  
  11561. local unit = bc.unit
  11562. local height = unit:Cross(ab)
  11563. local flip = (height >= 0)
  11564. local theta = math.deg(math.atan2(unit.y, unit.x)) + (flip and 0 or 180)
  11565.  
  11566. local m1 = (a + b)/2
  11567. local m2 = (a + c)/2
  11568.  
  11569. local w1 = IMG:Clone()
  11570. w1.Image = flip and RIGHT or LEFT
  11571. w1.AnchorPoint = HALF
  11572. w1.Size = UDim2.new(0, math.abs(unit:Dot(ab)), 0, height)
  11573. w1.Position = UDim2.new(0, m1.x, 0, m1.y)
  11574. w1.Rotation = theta
  11575. w1.Parent = parent
  11576.  
  11577. local w2 = IMG:Clone()
  11578. w2.Image = flip and LEFT or RIGHT
  11579. w2.AnchorPoint = HALF
  11580. w2.Size = UDim2.new(0, math.abs(unit:Dot(ac)), 0, height)
  11581. w2.Position = UDim2.new(0, m2.x, 0, m2.y)
  11582. w2.Rotation = theta
  11583. w2.Parent = parent
  11584.  
  11585. for k, v in next, module.StyleGuide.Triangle do
  11586. w1[k] = v
  11587. w2[k] = v
  11588. end
  11589.  
  11590. return w1, w2
  11591. end
  11592.  
  11593. -- Return
  11594.  
  11595. return module
  11596. end
  11597. function _DrawClass()
  11598. local Draw2DModule = _Draw2D()
  11599. local Draw3DModule = _Draw3D()
  11600.  
  11601. --
  11602.  
  11603. local DrawClass = {}
  11604. local DrawClassStorage = setmetatable({}, {__mode = "k"})
  11605. DrawClass.__index = DrawClass
  11606.  
  11607. function DrawClass.new(parent)
  11608. local self = setmetatable({}, DrawClass)
  11609.  
  11610. self.Parent = parent
  11611. DrawClassStorage[self] = {}
  11612.  
  11613. self.Draw3D = {}
  11614. for key, func in next, Draw3DModule do
  11615. self.Draw3D[key] = function(...)
  11616. local returns = {func(self.Parent, ...)}
  11617. for i = 1, #returns do
  11618. table.insert(DrawClassStorage[self], returns[i])
  11619. end
  11620. return unpack(returns)
  11621. end
  11622. end
  11623.  
  11624. self.Draw2D = {}
  11625. for key, func in next, Draw2DModule do
  11626. self.Draw2D[key] = function(...)
  11627. local returns = {func(self.Parent, ...)}
  11628. for i = 1, #returns do
  11629. table.insert(DrawClassStorage[self], returns[i])
  11630. end
  11631. return unpack(returns)
  11632. end
  11633. end
  11634.  
  11635. return self
  11636. end
  11637.  
  11638. --
  11639.  
  11640. function DrawClass:Clear()
  11641. local t = DrawClassStorage[self]
  11642. while (#t > 0) do
  11643. local part = table.remove(t)
  11644. if (part) then
  11645. part:Destroy()
  11646. end
  11647. end
  11648. DrawClassStorage[self] = {}
  11649. end
  11650.  
  11651. --
  11652.  
  11653. return DrawClass
  11654. end
  11655.  
  11656.  
  11657. --END TEST
  11658.  
  11659. local PLAYERS = game:GetService("Players")
  11660.  
  11661. local GravityController = _GravityController()
  11662. local Controller = GravityController.new(PLAYERS.LocalPlayer)
  11663.  
  11664. local DrawClass = _DrawClass()
  11665.  
  11666. local PI2 = math.pi*2
  11667. local ZERO = Vector3.new(0, 0, 0)
  11668.  
  11669. local LOWER_RADIUS_OFFSET = 3
  11670. local NUM_DOWN_RAYS = 24
  11671. local ODD_DOWN_RAY_START_RADIUS = 3
  11672. local EVEN_DOWN_RAY_START_RADIUS = 2
  11673. local ODD_DOWN_RAY_END_RADIUS = 1.66666
  11674. local EVEN_DOWN_RAY_END_RADIUS = 1
  11675.  
  11676. local NUM_FEELER_RAYS = 9
  11677. local FEELER_LENGTH = 2
  11678. local FEELER_START_OFFSET = 2
  11679. local FEELER_RADIUS = 3.5
  11680. local FEELER_APEX_OFFSET = 1
  11681. local FEELER_WEIGHTING = 8
  11682.  
  11683. function GetGravityUp(self, oldGravityUp)
  11684. local ignoreList = {}
  11685. for i, player in next, PLAYERS:GetPlayers() do
  11686. ignoreList[i] = player.Character
  11687. end
  11688.  
  11689. -- get the normal
  11690.  
  11691. local hrpCF = self.HRP.CFrame
  11692. local isR15 = (self.Humanoid.RigType == Enum.HumanoidRigType.R15)
  11693.  
  11694. local origin = isR15 and hrpCF.p or hrpCF.p + 0.35*oldGravityUp
  11695. local radialVector = math.abs(hrpCF.LookVector:Dot(oldGravityUp)) < 0.999 and hrpCF.LookVector:Cross(oldGravityUp) or hrpCF.RightVector:Cross(oldGravityUp)
  11696.  
  11697. local centerRayLength = 25
  11698. local centerRay = Ray.new(origin, -centerRayLength * oldGravityUp)
  11699. local centerHit, centerHitPoint, centerHitNormal = workspace:FindPartOnRayWithIgnoreList(centerRay, ignoreList)
  11700.  
  11701. --[[disable
  11702. DrawClass:Clear()
  11703. DrawClass.Draw3D.Ray(centerRay.Origin, centerRay.Direction)
  11704. ]]
  11705. local downHitCount = 0
  11706. local totalHitCount = 0
  11707. local centerRayHitCount = 0
  11708. local evenRayHitCount = 0
  11709. local oddRayHitCount = 0
  11710.  
  11711. local mainDownNormal = ZERO
  11712. if (centerHit) then
  11713. mainDownNormal = centerHitNormal
  11714. centerRayHitCount = 0
  11715. end
  11716.  
  11717. local downRaySum = ZERO
  11718. for i = 1, NUM_DOWN_RAYS do
  11719. local dtheta = PI2 * ((i-1)/NUM_DOWN_RAYS)
  11720.  
  11721. local angleWeight = 0.25 + 0.75 * math.abs(math.cos(dtheta))
  11722. local isEvenRay = (i%2 == 0)
  11723. local startRadius = isEvenRay and EVEN_DOWN_RAY_START_RADIUS or ODD_DOWN_RAY_START_RADIUS
  11724. local endRadius = isEvenRay and EVEN_DOWN_RAY_END_RADIUS or ODD_DOWN_RAY_END_RADIUS
  11725. local downRayLength = centerRayLength
  11726.  
  11727. local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
  11728. local dir = (LOWER_RADIUS_OFFSET * -oldGravityUp + (endRadius - startRadius) * offset)
  11729. local ray = Ray.new(origin + startRadius * offset, downRayLength * dir.unit)
  11730. local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
  11731. --[[disable
  11732. DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)
  11733. ]]
  11734. if (hit) then
  11735. downRaySum = downRaySum + angleWeight * hitNormal
  11736. downHitCount = downHitCount + 1
  11737. if isEvenRay then
  11738. evenRayHitCount = evenRayHitCount + 1
  11739. else
  11740. oddRayHitCount = oddRayHitCount + 1
  11741. end
  11742. end
  11743. end
  11744.  
  11745. local feelerHitCount = 0
  11746. local feelerNormalSum = ZERO
  11747.  
  11748. for i = 1, NUM_FEELER_RAYS do
  11749. local dtheta = 2 * math.pi * ((i-1)/NUM_FEELER_RAYS)
  11750. local angleWeight = 0.25 + 0.75 * math.abs(math.cos(dtheta))
  11751. local offset = CFrame.fromAxisAngle(oldGravityUp, dtheta) * radialVector
  11752. local dir = (FEELER_RADIUS * offset + LOWER_RADIUS_OFFSET * -oldGravityUp).unit
  11753. local feelerOrigin = origin - FEELER_APEX_OFFSET * -oldGravityUp + FEELER_START_OFFSET * dir
  11754. local ray = Ray.new(feelerOrigin, FEELER_LENGTH * dir)
  11755. local hit, hitPoint, hitNormal = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
  11756. --[[disable
  11757. DrawClass.Draw3D.Ray(ray.Origin, ray.Direction)
  11758. ]]
  11759. if (hit) then
  11760. feelerNormalSum = feelerNormalSum + FEELER_WEIGHTING * angleWeight * hitNormal --* hitDistSqInv
  11761. feelerHitCount = feelerHitCount + 1
  11762. end
  11763. end
  11764.  
  11765. if (centerRayHitCount + downHitCount + feelerHitCount > 0) then
  11766. local normalSum = mainDownNormal + downRaySum + feelerNormalSum
  11767. if (normalSum ~= ZERO) then
  11768. return normalSum.unit
  11769. end
  11770. end
  11771.  
  11772. return oldGravityUp
  11773. end
  11774.  
  11775. Controller.GetGravityUp = GetGravityUp
  11776.  
  11777. -- E is toggle
  11778. game:GetService("ContextActionService"):BindAction("Toggle", function(action, state, input)
  11779. if not (state == Enum.UserInputState.Begin) then
  11780. return
  11781. end
  11782.  
  11783. if (Controller) then
  11784. Controller:Destroy()
  11785. Controller = nil
  11786. else
  11787. Controller = GravityController.new(PLAYERS.LocalPlayer)
  11788. Controller.GetGravityUp = GetGravityUp
  11789. end
  11790. end, false, Enum.KeyCode.Z)
  11791. print("end")
  11792.  
  11793. elseif cht:match(".replicated mesh") then
  11794. local screenGui = game:GetObjects("rbxassetid://3546404827")[1]
  11795. local mainFrame = screenGui.Frame
  11796. local pages = mainFrame.Pages
  11797. local pageLayout = pages.UIPageLayout
  11798. local pageScripts = pages.Scripts
  11799. local interactiveButton = pageScripts.Character.Elements.TextButton
  11800. local utility = pageScripts.Utility
  11801. local utilityElements = utility.Elements
  11802. local pageOptions = pages.Options
  11803. local titleFrame = mainFrame.Title
  11804. local toolbar = mainFrame.Toolbar
  11805.  
  11806. screenGui.LocalScript:Destroy()
  11807. screenGui.Parent = game:GetService("CoreGui")
  11808. interactiveButton.Parent = nil
  11809. mainFrame.Visible = true
  11810.  
  11811. gripCenter = CFrame.new(0, 0, 1) * CFrame.Angles(math.rad(90), 0, 0)
  11812. offset = CFrame.Angles(0, 0, 0)
  11813.  
  11814. _player = {
  11815. p = game:GetService("Players").LocalPlayer;
  11816. b = function()
  11817. return _player.p:FindFirstChild("Backpack")
  11818. end;
  11819. c = function()
  11820. return _player.p.Character
  11821. end;
  11822. }
  11823. _functions = {
  11824. respawn = function()
  11825. local c = _player.c()
  11826. if workspace:FindFirstChild(_player.p.Name) then
  11827. spawn(function()
  11828. wait(.5)
  11829. if _player.c() ~= workspace:FindFirstChild(_player.p.Name) then
  11830. workspace:FindFirstChild(_player.p.Name):BreakJoints()
  11831. end
  11832. end)
  11833. end
  11834. if c then
  11835. c:BreakJoints()
  11836. c:ClearAllChildren()
  11837. end
  11838. local newChar = Instance.new("Model", workspace)
  11839. local hum = Instance.new("Humanoid", newChar)
  11840. local torso = Instance.new("Part", newChar)
  11841. newChar.Name = "respawn_"
  11842. torso.Name = "Torso"
  11843. torso.Transparency = 1
  11844. _player.p.Character = newChar
  11845. torso.Name = ""
  11846. torso.CanCollide = false
  11847. newChar:MoveTo(Vector3.new(999999, 999999, 999999))
  11848. end;
  11849. tools = function(int)
  11850. local toolamount = int or 200
  11851.  
  11852. local LocalPlayer = game:GetService("Players").LocalPlayer
  11853. local runservice = game:GetService("RunService")
  11854. LocalPlayer.Character:FindFirstChild("Humanoid"):UnequipTools()
  11855. local currentamount = #LocalPlayer.Backpack:GetChildren()
  11856. LocalPlayer.Character.Archivable = true
  11857. local tempchar = LocalPlayer.Character:Clone()
  11858. local lastchar = nil
  11859. tempchar.Parent = workspace
  11860. local savepos = LocalPlayer.Character:FindFirstChild("HumanoidRootPart").CFrame
  11861. local renderstepped = runservice.RenderStepped:Connect(function()
  11862. workspace.CurrentCamera.CameraSubject = tempchar:FindFirstChild("Humanoid")
  11863. for _, tool in pairs(LocalPlayer.Backpack:GetChildren()) do
  11864. if tool:IsA("Tool") then
  11865. tool.Parent = LocalPlayer
  11866. end
  11867. end
  11868. for _, char in pairs(workspace:GetChildren()) do
  11869. if char.Name == LocalPlayer.Name and char:FindFirstChildWhichIsA("Humanoid") then
  11870. char.Humanoid.Health = 0
  11871. end
  11872. end
  11873. LocalPlayer.Character:ClearAllChildren()
  11874. local char = Instance.new("Model", workspace)
  11875. Instance.new("Humanoid", char)
  11876. LocalPlayer.Character = char
  11877. if lastchar ~= nil then
  11878. lastchar:Destroy()
  11879. end
  11880. repeat runservice.RenderStepped:Wait() until LocalPlayer.Character ~= nil
  11881. lastchar = char
  11882. end)
  11883. repeat runservice.RenderStepped:Wait() until #LocalPlayer:GetChildren() - 3 - currentamount >= toolamount
  11884. renderstepped:Disconnect()
  11885. repeat runservice.RenderStepped:Wait() until LocalPlayer.Character:FindFirstChild("HumanoidRootPart") ~= nil
  11886. lastchar:Destroy()
  11887.  
  11888. utilityElements.Tools.Text = "Done respawning?"
  11889. repeat runservice.RenderStepped:Wait() until utilityElements.Tools.Text == "Load Tools"
  11890.  
  11891. for _, tool in pairs(LocalPlayer:GetChildren()) do
  11892. if tool:IsA("Tool") then
  11893. tool.Parent = LocalPlayer.Backpack
  11894. end
  11895. end
  11896. LocalPlayer.Character:FindFirstChild("HumanoidRootPart").CFrame = savepos
  11897. tempchar:Destroy()
  11898. end;
  11899. load = function()
  11900. local b = _player.b()
  11901. local c = _player.c()
  11902. local grip = function(tool, cf)
  11903. local origin = CFrame.new(cf.p)
  11904. local x, y, z = cf:toEulerAnglesXYZ()
  11905. local new = CFrame.Angles(x, y, z)
  11906. local grip = (origin * new):inverse()
  11907. tool.Grip = grip
  11908. end
  11909. local model = c:WaitForChild("gay") -- stupid name choice from what a few months ago?? i have nothing against it i swear
  11910. local parts = {}
  11911. wait(1)
  11912.  
  11913. for i, Model in pairs(c:GetChildren()) do
  11914. if Model:IsA("Model") and Model.Name == "gay" then
  11915. for i, v in pairs(Model:GetDescendants()) do
  11916. if v:IsA("BasePart") then
  11917. v.LocalTransparencyModifier = 1
  11918. if v.Name ~= "Ignore" then
  11919. table.insert(parts, v)
  11920. end
  11921. end
  11922. if not v:IsA("BasePart") and not v:IsA("JointInstance") and not v:IsA("BodyMover") and not v:isA("Humanoid") and not v:IsA("Model") then
  11923. v:Remove()
  11924. end
  11925. end
  11926. Model.ChildAdded:Connect(function(c)
  11927. c.Parent = nil
  11928. end)
  11929. end
  11930. end
  11931.  
  11932. for _, part in pairs(parts) do
  11933. local m = Instance.new("BlockMesh", part)
  11934. m.Scale = Vector3.new(0, 0, 0)
  11935. end
  11936.  
  11937. local tools = b:GetChildren()
  11938. for i, tool in pairs(tools) do
  11939. if tool.Parent then
  11940. wrap(function()
  11941. tool.Parent = c
  11942. for i, v in pairs(tool:GetDescendants()) do
  11943. if v:IsA("Sound") or v:IsA("TouchTransmitter") then
  11944. v:Destroy()
  11945. elseif v:IsA("BasePart") then
  11946. v.Massless = true
  11947. v.CanCollide = false
  11948. elseif v:IsA("LocalScript") or v:IsA("Script") then
  11949. v:Destroy()
  11950. end
  11951. end
  11952.  
  11953. if tool:FindFirstChild("Handle") then
  11954. local handle = tool.Handle
  11955. local fakeHandle = handle:Clone()
  11956. fakeHandle.Name = "FakeHandle"
  11957. fakeHandle.Parent = tool
  11958. local w = Instance.new("Weld", handle)
  11959. w.Part0 = fakeHandle
  11960. w.Part1 = handle
  11961. handle.Size = Vector3.new(0, 0, 0)
  11962. end
  11963.  
  11964. tool.Parent = b
  11965. end)
  11966. end
  11967. if (not parts[i]) then
  11968. tools[i] = nil
  11969. tool:Destroy()
  11970. end
  11971. end
  11972.  
  11973. wait(.2)
  11974. local amt = #tools
  11975.  
  11976. wrap(function()
  11977. local connection1
  11978.  
  11979. connection1 = game:GetService("RunService").RenderStepped:Connect(function(step)
  11980. for i = 1, amt do
  11981. local tool = tools[i]
  11982. if parts[i] then
  11983. tool.Parent = b
  11984. tool.Parent = c
  11985. end
  11986. end
  11987.  
  11988. if _player.c() ~= c or c:FindFirstChildWhichIsA("Humanoid").Health == 0 then
  11989. connection1:Disconnect()
  11990. end
  11991.  
  11992. utilityElements.Replicate.Text = ("Replicate (%dfps)"):format(1/step)
  11993. end)
  11994. end)
  11995.  
  11996. local connection2
  11997. connection2 = game:GetService("RunService").Heartbeat:Connect(function()
  11998. for i = 1, amt do
  11999. local tool = tools[i]
  12000. if parts[i] then
  12001. if parts[i].Transparency ~= 1 then
  12002. local p = _cframe.toWorldPoint(parts[i].CFrame) * offset
  12003. grip(tool, p)
  12004. else
  12005. local p = _cframe.toWorldPoint(CFrame.new(0, -250, 0))
  12006. grip(tool, p)
  12007. end
  12008. end
  12009. end
  12010. if _player.c() ~= c or c:FindFirstChildWhichIsA("Humanoid").Health == 0 then
  12011. connection2:Disconnect()
  12012. end
  12013. end)
  12014. end;
  12015. }
  12016. _cframe = {
  12017. diff = function(a, b)
  12018. return (a:Inverse() * b)
  12019. end;
  12020. toWorldPoint = function(a)
  12021. local arm = _player.c():FindFirstChild('Right Arm') or _player.c():FindFirstChild('RightHand')
  12022. local off = arm.CFrame * gripCenter:Inverse()
  12023. if arm.Name == "RightHand" then
  12024. off = arm.CFrame * CFrame.Angles(math.rad(-90), 0, 0)
  12025. end
  12026. return _cframe.diff(off, a)
  12027. end;
  12028. tween = function(object,style,direction,t,goal)
  12029. local tweenservice = game:GetService("TweenService")
  12030. local tweenInfo = TweenInfo.new(t,Enum.EasingStyle[style],Enum.EasingDirection[direction])
  12031. local tween = tweenservice:Create(object,tweenInfo,goal)
  12032. tween:Play()
  12033. return tween
  12034. end;
  12035. }
  12036. scriptList = {
  12037. Character = {
  12038. ['Titan'] = {"rbxassetid://3600286284", "rbxassetid://3070358214"};
  12039. ['Winged Titan'] = {"rbxassetid://3593786550", "rbxassetid://3070358214"};
  12040. ['Part Surround'] = {"rbxassetid://3546680077", "rbxassetid://3546673786"};
  12041. },
  12042. Mechanical = {
  12043. ['Spiderbot'] = {"rbxassetid://3040869607", "rbxassetid://3070355648"};
  12044. ['Drone'] = {"rbxassetid://3074236927", "rbxassetid://3074245958"};
  12045. ['Noise'] = {"rbxassetid://3193712297", "rbxassetid://3070358314"};
  12046. },
  12047. Aesthetic = {
  12048. ['Wings'] = {"rbxassetid://3040946714", "rbxassetid://3070358381"};
  12049. ['Tail'] = {"rbxassetid://3041387168", "rbxassetid://3070358314"};
  12050. ['Star Glitcher'] = {"rbxassetid://3074734519", "rbxassetid://3074751071"};
  12051. ['Tentacles'] = {"rbxassetid://3074817074", "rbxassetid://3074820325"};
  12052. },
  12053. Weapon = {
  12054. ['Master Hand'] = {"rbxassetid://3040943596", "rbxassetid://3070358267"};
  12055. ['Ravenger'] = {"rbxassetid://3040945482", "rbxassetid://3070358154"};
  12056. },
  12057. }
  12058. wrap = function(f)
  12059. return coroutine.wrap(f)()
  12060. end
  12061.  
  12062. if game:GetService("RunService"):IsStudio() then
  12063. guiLib = require(screenGui.GuiLib)
  12064. else
  12065. guiLib = loadstring(screenGui.GuiLib.Source)()
  12066. end
  12067.  
  12068. for title, tbl in pairs(scriptList) do
  12069. local frame = pageScripts[title]
  12070. for name, info in pairs(tbl) do
  12071. local btn = interactiveButton:Clone()
  12072. btn.Name = name
  12073. btn.Text = name
  12074. btn.Parent = frame.Elements
  12075. btn.Icon.Image = info[2]
  12076.  
  12077. guiLib.circleInteractive(btn)
  12078. guiLib.colorInteractive(btn)
  12079.  
  12080. btn.MouseButton1Click:Connect(function()
  12081. local scr = game:GetObjects(info[1])[1]
  12082. local source = scr.Source
  12083.  
  12084. if game:GetService("RunService"):IsStudio() == false then
  12085. for i, v in pairs(scr:GetChildren()) do
  12086. v.Parent = script
  12087. end
  12088. wrap(function()
  12089. local func = loadstring(source)
  12090. spawn(func)
  12091. end)
  12092. else
  12093. scr.Disabled = true
  12094. scr.Parent = _player.c()
  12095. wait(.1)
  12096. scr.Disabled = false
  12097. end
  12098. end)
  12099. end
  12100. end
  12101.  
  12102. repeat wait() until pageLayout.CurrentPage
  12103.  
  12104. guiLib.draggable(mainFrame, titleFrame)
  12105. for i, v in pairs(toolbar:GetChildren()) do
  12106. if v:IsA("TextButton") then
  12107. guiLib.circleInteractive(v, 2, 1)
  12108. v.MouseButton1Click:Connect(function()
  12109. pageLayout:JumpTo(pages[v.Name])
  12110. end)
  12111. pageLayout.Changed:Connect(function(p)
  12112. if pageLayout.CurrentPage.Name == v.Name then
  12113. guiLib.tween(v, "Sine", "Out", .5, {BackgroundColor3 = Color3.fromRGB(255, 118, 26)})
  12114. else
  12115. guiLib.tween(v, "Sine", "Out", .5, {BackgroundColor3 = Color3.fromRGB(236, 109, 17)})
  12116. end
  12117. end)
  12118. end
  12119. end
  12120.  
  12121. for i, v in pairs(utilityElements:GetChildren()) do
  12122. if v:IsA("TextButton") then
  12123. guiLib.circleInteractive(v)
  12124. guiLib.colorInteractive(v)
  12125. end
  12126. end
  12127.  
  12128. utilityElements.Reset.MouseButton1Click:Connect(function()
  12129. local c = _player.c()
  12130. c.Humanoid.Health = 0
  12131. wait(.55)
  12132. for i, v in pairs(c:GetChildren()) do
  12133. if v:IsA("BackpackItem") then
  12134. v.Parent = _player.b()
  12135. v.Parent = _player.p
  12136. end
  12137. end
  12138. repeat wait() until _player.c() ~= c
  12139. _player.c():WaitForChild("Right Arm")
  12140. wait(.25)
  12141. for i, v in pairs(_player.p:GetChildren()) do
  12142. if v:IsA("Tool") then
  12143. v.Parent = _player.b()
  12144. end
  12145. end
  12146. end)
  12147.  
  12148. utilityElements.Block.MouseButton1Click:Connect(function()
  12149. local c = _player.c()
  12150. for _, v in pairs(c:GetChildren()) do
  12151. if v:IsA("BackpackItem") then
  12152. for _, m in pairs(v:GetDescendants()) do
  12153. if m:IsA("DataModelMesh") then
  12154. m:Destroy()
  12155. end
  12156. end
  12157. end
  12158. end
  12159. end)
  12160.  
  12161. local noclip = false
  12162. utilityElements.Noclip.MouseButton1Click:Connect(function()
  12163. noclip = not noclip
  12164. end)
  12165.  
  12166. utilityElements.Tools.MouseButton1Click:Connect(function()
  12167. if utilityElements.Tools.Text == "Load Tools" then
  12168. _functions.tools(120)
  12169. else
  12170. utilityElements.Tools.Text = "Load Tools"
  12171. end
  12172. end)
  12173.  
  12174. utilityElements.Replicate.MouseButton1Click:Connect(function()
  12175. _functions.load()
  12176. end)
  12177.  
  12178. local flipped = 0
  12179. utilityElements.Flip.MouseButton1Click:Connect(function()
  12180. flipped = (flipped + 1) % 4
  12181. if flipped == 0 then
  12182. offset = CFrame.Angles(0, 0, 0)
  12183. elseif flipped == 1 then
  12184. offset = CFrame.Angles(math.rad(90), 0, 0)
  12185. elseif flipped == 2 then
  12186. offset = CFrame.Angles(0, math.rad(90), 0)
  12187. elseif flipped == 3 then
  12188. offset = CFrame.Angles(0, 0, math.rad(90))
  12189. end
  12190. end)
  12191.  
  12192. game:GetService("RunService").Stepped:Connect(function()
  12193. local c = _player.c()
  12194. if noclip then
  12195. local descendants = c:GetDescendants()
  12196. for i = 1, #descendants do
  12197. local v = descendants[i]
  12198. if v:IsA("BasePart") and v.Name ~= "HumanoidRootPart" then
  12199. v.CanCollide = false
  12200. end
  12201. end
  12202. end
  12203. end)
  12204.  
  12205. workspace:WaitForChild("S")
  12206.  
  12207. elseif cht:match(".problem") then
  12208. local ProblemsGUI = Instance.new("ScreenGui")
  12209. local Problem = Instance.new("ImageLabel")
  12210. local Close = Instance.new("TextButton")
  12211.  
  12212. ProblemsGUI.Name = "ProblemsGUI"
  12213. ProblemsGUI.Parent = game.CoreGui
  12214. ProblemsGUI.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
  12215.  
  12216. Problem.Name = "Problem"
  12217. Problem.Parent = ProblemsGUI
  12218. Problem.BackgroundColor3 = Color3.new(1, 1, 1)
  12219. Problem.Position = UDim2.new(0.313868612, 0, 0.0322580636, 0)
  12220. Problem.Size = UDim2.new(0, 495, 0, 376)
  12221. Problem.Image = "rbxassetid://6308255604"
  12222.  
  12223. Close.Name = "Close"
  12224. Close.Parent = Problem
  12225. Close.BackgroundColor3 = Color3.new(1, 1, 1)
  12226. Close.Position = UDim2.new(0.886868715, 0, 0.0797872394, 0)
  12227. Close.Size = UDim2.new(0, 56, 0, 41)
  12228. Close.Font = Enum.Font.SourceSans
  12229. Close.Text = "Close"
  12230. Close.TextColor3 = Color3.new(0, 0, 0)
  12231. Close.TextScaled = true
  12232. Close.TextSize = 14
  12233. Close.TextWrapped = true
  12234. Close.MouseButton1Click:connect(function()
  12235. Problem.Visible = false
  12236. end)
  12237.  
  12238. elseif cht:match(".commands") then
  12239. local Commands = Instance.new("ScreenGui")
  12240. local commands = Instance.new("ImageLabel")
  12241. local Close = Instance.new("TextButton")
  12242.  
  12243. Commands.Name = "Commands"
  12244. Commands.Parent = game.CoreGui
  12245. Commands.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
  12246.  
  12247. commands.Name = "commands"
  12248. commands.Parent = Commands
  12249. commands.BackgroundColor3 = Color3.new(1, 1, 1)
  12250. commands.Position = UDim2.new(0.182481751, 0, 0.130929798, 0)
  12251. commands.Size = UDim2.new(0, 491, 0, 388)
  12252. commands.Image = "rbxassetid://6308468091"
  12253.  
  12254. Close.Name = "Close"
  12255. Close.Parent = commands
  12256. Close.BackgroundColor3 = Color3.new(1, 1, 1)
  12257. Close.Position = UDim2.new(0.883910358, 0, 0.164948449, 0)
  12258. Close.Size = UDim2.new(0, 57, 0, 59)
  12259. Close.Font = Enum.Font.SourceSans
  12260. Close.Text = "Close"
  12261. Close.TextColor3 = Color3.new(0, 0, 0)
  12262. Close.TextSize = 14
  12263. Close.MouseButton1Click:connect(function()
  12264. commands.Visible = false
  12265. end)
  12266.  
  12267. elseif cht:match(".discord") then
  12268. local DiscordGUI = Instance.new("ScreenGui")
  12269. local DIscord = Instance.new("TextLabel")
  12270. local Clos = Instance.new("TextButton")
  12271.  
  12272. DiscordGUI.Name = "DiscordGUI"
  12273. DiscordGUI.Parent = game.CoreGui
  12274. DiscordGUI.ZIndexBehavior = Enum.ZIndexBehavior.Sibling
  12275.  
  12276. DIscord.Name = "DIscord"
  12277. DIscord.Parent = DiscordGUI
  12278. DIscord.BackgroundColor3 = Color3.new(1, 1, 1)
  12279. DIscord.Position = UDim2.new(0.478102177, 0, 0.527514219, 0)
  12280. DIscord.Size = UDim2.new(0, 276, 0, 140)
  12281. DIscord.Font = Enum.Font.SourceSans
  12282. DIscord.Text = "Discord: ruth#3661"
  12283. DIscord.TextColor3 = Color3.new(0, 0, 0)
  12284. DIscord.TextScaled = true
  12285. DIscord.TextSize = 14
  12286. DIscord.TextWrapped = true
  12287.  
  12288. Clos.Name = "Clos"
  12289. Clos.Parent = DIscord
  12290. Clos.BackgroundColor3 = Color3.new(1, 1, 1)
  12291. Clos.Position = UDim2.new(0.833333373, 0, 0, 0)
  12292. Clos.Size = UDim2.new(0, 46, 0, 39)
  12293. Clos.Font = Enum.Font.SourceSans
  12294. Clos.Text = "X"
  12295. Clos.TextColor3 = Color3.new(0.0117647, 1, 0.454902)
  12296. Clos.TextScaled = true
  12297. Clos.TextSize = 14
  12298. Clos.TextWrapped = true
  12299. Clos.MouseButton1Click:connect(function()
  12300. DIscord.Visible = false
  12301. end)
  12302. end
  12303. end)
Add Comment
Please, Sign In to add comment