local function t(t)if UserInputService.TouchEnabled then for t,i in ipairs(LocalPlayer.Character:GetChildren())do if i:IsA("\084\111\111\108")then IsAToolEquipped=true end end t.ChildAdded:Connect(function(t)if t:IsA("\084\111\111\108")then IsAToolEquipped=true end end)t.ChildRemoved:Connect(function(t)if t:IsA("\084\111\111\108")then IsAToolEquipped=false end end)if PlayerGui then local t=PlayerGui:FindFirstChild("\084\111\117\099\104\071\117\105")if t and t:WaitForChild("\071\101\115\116\117\114\101\065\114\101\097",.5)then GestureArea=t.GestureArea GestureAreaManagedByControlScript=true else GestureAreaManagedByControlScript=false local t=Instance.new("\083\099\114\101\101\110\071\117\105")t.Name="\071\101\115\116\117\114\101\065\114\101\097"t.Parent=PlayerGui GestureArea=Instance.new("\070\114\097\109\101")GestureArea.BackgroundTransparency=1.0 GestureArea.Visible=true GestureArea.BackgroundColor3=Color3.fromRGB(0,0,0)layoutGestureArea(PortraitMode)GestureArea.Parent=t end end end end if LocalPlayer then if LocalPlayer.Character~=nil then t(LocalPlayer.Character)end LocalPlayer.CharacterAdded:connect(function(i)t(i)end)end local function i(t,i)if t.X<i.AbsolutePosition.X+i.AbsoluteSize.X and(t.X>i.AbsolutePosition.X and(t.Y<i.AbsolutePosition.Y+i.AbsoluteSize.Y and t.Y>i.AbsolutePosition.Y))then return true end return false end local function o(t)return t:GetRenderCFrame()end local function x()local t={}local i=R15_HEAD_OFFSET function t.GetActivateValue(t)return.7 end function t.IsPortraitMode(t)return PortraitMode end function t.GetRotateAmountValue(i,t)t=t or StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")if t then if t=="\076\111\119"then return VR_LOW_INTENSITY_ROTATION elseif t=="\072\105\103\104"then return VR_HIGH_INTENSITY_ROTATION end end return ZERO_VECTOR2 end function t.GetRepeatDelayValue(i,t)t=t or StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")if t then if t=="\076\111\119"then return VR_LOW_INTENSITY_REPEAT elseif t=="\072\105\103\104"then return VR_HIGH_INTENSITY_REPEAT end end return 0 end t.ShiftLock=false t.Enabled=false local x=false local P=false local Y=false t.RotateInput=ZERO_VECTOR2 t.DefaultZoom=LANDSCAPE_DEFAULT_ZOOM t.activeGamepad=nil local U={}t.lastSubject=nil t.lastSubjectPosition=Vector3.new(0,5,0)local l=0 local Z={}local r=false local n=nil local function G()if n and n:IsDescendantOf(game)then return n else local t=PlayerGui:FindFirstChild("\084\111\117\099\104\071\117\105")if not t then return nil end local i=t:FindFirstChild("\084\111\117\099\104\067\111\110\116\114\111\108\070\114\097\109\101")if not i then return nil end n=i:FindFirstChild("\068\121\110\097\109\105\099\084\104\117\109\098\115\116\105\099\107\070\114\097\109\101")return n end end local A=nil local I=nil local function R()if UserInputService.TouchEnabled then if A then A:Disconnect()A=nil end local t=workspace.CurrentCamera if t then local i=t.ViewportSize PortraitMode=i.X<i.Y layoutGestureArea(PortraitMode)DefaultZoom=PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM A=(t:GetPropertyChangedSignal("\086\105\101\119\112\111\114\116\083\105\122\101")):Connect(function()i=t.ViewportSize PortraitMode=i.X<i.Y layoutGestureArea(PortraitMode)DefaultZoom=PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM end)end end end I=(workspace:GetPropertyChangedSignal("\067\117\114\114\101\110\116\067\097\109\101\114\097")):Connect(R)if workspace.CurrentCamera then R()end function t.GetShiftLock(t)return ShiftLockController:IsShiftLocked()end function t.GetHumanoid(t)local i=PlayersService.LocalPlayer return findPlayerHumanoid(i)end function t.GetHumanoidRootPart(i)local o=t:GetHumanoid()return o and o.Torso end function t.GetRenderCFrame(i,t)o(t)end local y=Enum.HumanoidStateType.Dead local function F(t,i)if i==y then local i=t.Parent if i then return i:FindFirstChild("\072\101\097\100")or t.Torso else return t.Torso end else return t.Torso end end local C=Enum.HumanoidStateType.Dead function t.GetSubjectPosition(x)local P=nil local Y=workspace.CurrentCamera local U=Y and Y.CameraSubject if U then if U:IsA("\072\117\109\097\110\111\105\100")then local x=U:GetState()if VRService.VREnabled and(x==y and U==t.lastSubject)then P=t.lastSubjectPosition else local t=F(U,x)if t and t:IsA("\066\097\115\101\080\097\114\116")then local Y=o(t)local l=ZERO_VECTOR3 if x~=y then l=U.RigType==Enum.HumanoidRigType.R15 and i or HEAD_OFFSET end if PortraitMode then l=l+Vector3.new(0,PORTRAIT_MODE_CAMERA_OFFSET,0)end P=Y.p+Y:vectorToWorldSpace(l+U.CameraOffset)end end elseif U:IsA("\086\101\104\105\099\108\101\083\101\097\116")then local t=o(U)local i=SEAT_OFFSET if VRService.VREnabled then i=VR_SEAT_OFFSET end P=t.p+t:vectorToWorldSpace(i)elseif U:IsA("\083\107\097\116\101\098\111\097\114\100\080\108\097\116\102\111\114\109")then local t=o(U)P=t.p+SEAT_OFFSET elseif U:IsA("\066\097\115\101\080\097\114\116")then local t=o(U)P=t.p elseif U:IsA("\077\111\100\101\108")then P=(U:GetModelCFrame()).p end end t.lastSubject=U t.lastSubjectPosition=P return P end function t.ResetCameraLook(t) end function t.GetCameraLook(t)return workspace.CurrentCamera and workspace.CurrentCamera.CoordinateFrame.lookVector or Vector3.new(0,0,1)end function t.GetCameraZoom(i)if t.currentZoom==nil then local i=PlayersService.LocalPlayer t.currentZoom=i and clamp(i.CameraMinZoomDistance,i.CameraMaxZoomDistance,t.DefaultZoom)or t.DefaultZoom end return t.currentZoom end function t.GetCameraActualZoom(t)local i=workspace.CurrentCamera if i then return(i.CoordinateFrame.p-i.Focus.p).magnitude end end function t.GetCameraHeight(i)if VRService.VREnabled and not t:IsInFirstPerson()then local i=t:GetCameraZoom()return math.sin(VR_ANGLE)*i end return 0 end function t.ViewSizeX(t)local i=1024 local o=workspace.CurrentCamera if o then i=o.ViewportSize.X end return i end function t.ViewSizeY(t)local i=768 local o=workspace.CurrentCamera if o then i=o.ViewportSize.Y end return i end local Q=math.asin local j=math.atan2 local T=math.floor local b=math.max local V=math.pi local a=Vector2.new local L=Vector3.new local J=CFrame.Angles local v=CFrame.new function t.ScreenTranslationToAngle(o,i)local x=t:ViewSizeX()local P=t:ViewSizeY()local Y=i.x/x local U=i.y/P return a(Y,U)end function t.MouseTranslationToAngle(i,t)local o=t.x/1920 local x=t.y/1200 return a(o,x)end function t.RotateVector(o,t,i)local x=v(ZERO_VECTOR3,t)local P=((J(0,-i.x,0)*x)*J(-i.y,0,0)).lookVector return P,a(i.x,i.y)end function t.RotateCamera(o,t,i)if VRService.VREnabled then local x,P=o:RotateVector(t,Vector2.new(i.x,0))return(L(x.x,0,x.z)).unit,P else local x=Q(t.y)local P=clamp(-MAX_Y+x,-MIN_Y+x,i.y)return o:RotateVector(t,a(i.x,P))end end function t.IsInFirstPerson(t)return x end function t.UpdateMouseBehavior(t)local i=workspace.CurrentCamera if i.CameraType==Enum.CameraType.Scriptable then return end if x or t:GetShiftLock()then pcall(function()GameSettings.RotationType=Enum.RotationType.CameraRelative end)if UserInputService.MouseBehavior~=Enum.MouseBehavior.LockCenter then UserInputService.MouseBehavior=Enum.MouseBehavior.LockCenter end else pcall(function()GameSettings.RotationType=Enum.RotationType.MovementRelative end)if P or Y then UserInputService.MouseBehavior=Enum.MouseBehavior.LockCurrentPosition else UserInputService.MouseBehavior=Enum.MouseBehavior.Default end end end function t.ZoomCamera(o,i)local P=PlayersService.LocalPlayer if P then if P.CameraMode==Enum.CameraMode.LockFirstPerson then t.currentZoom=0 else t.currentZoom=clamp(P.CameraMinZoomDistance,P.CameraMaxZoomDistance,i)end end x=o:GetCameraZoom()<2 ShiftLockController:SetIsInFirstPerson(x)o:UpdateMouseBehavior()return o:GetCameraZoom()end function t.rk4Integrator(x,t,i,o)local P=i<0 and-1 or 1 local function Y(t,i)local o=P*b(1,t/3.3+.5)return o end local U=t local l=i local Z=Y(U,l)local r=U+l*(o/2)local n=l+Z*(o/2)local G=Y(r,n)local A=U+n*(o/2)local I=l+G*(o/2)local R=Y(A,I)local y=U+I*o local F=l+R*o local C=Y(y,F)local Q=t+(((l+2*n)+2*I)+F)*(o/6)local j=i+(((Z+2*G)+2*R)+C)*(o/6)return Q,j end function t.ZoomCameraBy(o,i)local x=t:GetCameraActualZoom()if x then x=o:rk4Integrator(x,i,1)o:ZoomCamera(x)end return o:GetCameraZoom()end function t.ZoomCameraFixedBy(i,t)return i:ZoomCamera(i:GetCameraZoom()+t)end function t.Update(t) end function t.ApplyVRTransform(i)if not VRService.VREnabled then return end local o=PlayersService.LocalPlayer if not(o and(o.Character and(o.Character:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")and o.Character.HumanoidRootPart:FindFirstChild("\082\111\111\116\074\111\105\110\116"))))then return end local x=workspace.CurrentCamera local P=x.CameraSubject local Y=P and P:IsA("\086\101\104\105\099\108\101\083\101\097\116")if t:IsInFirstPerson()and not Y then local t=VRService:GetUserCFrame(Enum.UserCFrame.Head)local i=t-t.p local x=o.Character.HumanoidRootPart.RootJoint x.C0=CFrame.new(i:vectorToObjectSpace(t.p))*CFrame.new(0,0,0,-1,0,0,0,0,1,0,1,0)else local t=o.Character.HumanoidRootPart.RootJoint t.C0=CFrame.new(0,0,0,-1,0,0,0,0,1,0,1,0)end end local w=true local O=0 function t.ShouldUseVRRotation(t)if not VRService.VREnabled then return false end if not w and tick()-O<1 then return false end local i,o=pcall(function()return StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")end)w=i and o~=nil O=tick()return i and(o~=nil and o~="\083\109\111\111\116\104")end function t.GetVRRotationInput(t)local i=ZERO_VECTOR2 local o=StarterGui:GetCore("\086\082\082\111\116\097\116\105\111\110\073\110\116\101\110\115\105\116\121")local x=t.GamepadPanningCamera or ZERO_VECTOR2 local P=tick()-l>=t:GetRepeatDelayValue(o)if math.abs(x.x)>=t:GetActivateValue()then if P or not Z[Enum.KeyCode.Thumbstick2]then local P=1 if x.x<0 then P=-1 end i=i+t:GetRotateAmountValue(o)*P Z[Enum.KeyCode.Thumbstick2]=true end elseif math.abs(x.x)<t:GetActivateValue()-.1 then Z[Enum.KeyCode.Thumbstick2]=nil end if t.TurningLeft then if P or not Z[Enum.KeyCode.Left]then i=i-t:GetRotateAmountValue(o)Z[Enum.KeyCode.Left]=true end else Z[Enum.KeyCode.Left]=nil end if t.TurningRight then if P or not Z[Enum.KeyCode.Right]then i=i+t:GetRotateAmountValue(o)Z[Enum.KeyCode.Right]=true end else Z[Enum.KeyCode.Right]=nil end if i~=ZERO_VECTOR2 then l=tick()end return i end local z=Vector3.new(1,1,1)local e=nil local D=nil local E=false local f=nil local K=nil local S=nil local q=nil local X=nil local function h(t)if not t then z=Vector3.new(z.x,1,z.z)end if E then D=nil E=false end end local function u(t,i)if not E then e=t D=i z=Vector3.new(z.x,0,z.z)E=true end end local function W(t)i=R15_HEAD_OFFSET*t end local function c(t)if t.Name=="\066\111\100\121\072\101\105\103\104\116\083\099\097\108\101"and t:IsA("\078\117\109\098\101\114\086\097\108\117\101")then if heightScaleChangedConn then heightScaleChangedConn:disconnect()end heightScaleChangedConn=t.Changed:connect(W)W(t.Value)end end local function N(t)if t.Name=="\066\111\100\121\072\101\105\103\104\116\083\099\097\108\101"then W(1)if heightScaleChangedConn then heightScaleChangedConn:disconnect()heightScaleChangedConn=nil end end end local function g()if f then f:disconnect()f=nil end if q then q:disconnect()q=nil end if X then X:disconnect()X=nil end if heightScaleChangedConn then heightScaleChangedConn:disconnect()heightScaleChangedConn=nil end local i=workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject if D~=i then h()end if i and i:IsA("\072\117\109\097\110\111\105\100")then q=i.ChildAdded:connect(c)X=i.ChildRemoved:connect(N)for t,i in pairs(i:GetChildren())do c(i)end f=i.StateChanged:connect(function(o,x)if VRService.VREnabled and(x==Enum.HumanoidStateType.Jumping and not t:IsInFirstPerson())then u(t:GetSubjectPosition(),i)elseif x~=Enum.HumanoidStateType.Jumping and x~=Enum.HumanoidStateType.Freefall then h(true)end end)end end local function d()if K then K:disconnect()K=nil end local t=workspace.CurrentCamera if t then K=(t:GetPropertyChangedSignal("\067\097\109\101\114\097\083\117\098\106\101\099\116")):connect(g)g()end end function t.GetVRFocus(o,t,i)local x=nil local P=workspace.CurrentCamera local Y=o.LastCameraFocus or t if not E then z=Vector3.new(z.x,math.min(1,z.y+.42*i),z.z)end if E and(e and e.y>Y.y)then x=CFrame.new(Vector3.new(t.x,math.min(e.y,Y.y+5*i),t.z))else x=CFrame.new((Vector3.new(t.x,Y.y,t.z)):lerp(t,z.y))end if E then if o:IsInFirstPerson()then h()end if e and t.y<e.y-.5 then h()end end return x end local M=nil local B=nil local H=nil local k=nil local p={}local m=0 local s={}local t6={}local i6=nil local o6=nil t.ZoomEnabled=true t.PanEnabled=true t.KeyPanEnabled=true local function x6(t,i)local o=G()local x=false if r then local i=CameraScript.Parent:FindFirstChild("\067\111\110\116\114\111\108\083\099\114\105\112\116")if i then local o=i:FindFirstChild("\077\097\115\116\101\114\067\111\110\116\114\111\108")if o then local i=o:FindFirstChild("\068\121\110\097\109\105\099\084\104\117\109\098\115\116\105\099\107")if i then i=require(i)local o=i:GetInputObject()x=o==t end end end end if not x then p[t]=i if not i then s[t]=t.Position t6[t]=tick()m=m+1 end end end local function P6(i,o)if p[i]==nil then if r then return end p[i]=o if not o then m=m+1 end end if m==1 then if p[i]==false then H=H or t:GetCameraLook()M=M or i.Position B=B or M t.UserPanningTheCamera=true local o=i.Position-B o=Vector2.new(o.X,o.Y*GameSettings:GetCameraYInvertValue())if t.PanEnabled then local i=t:ScreenTranslationToAngle(o)*TOUCH_SENSITIVTY t.RotateInput=t.RotateInput+i end B=i.Position end else H=nil M=nil B=nil t.UserPanningTheCamera=false end if m==2 then local i={}for t,o in pairs(p)do if not o then table.insert(i,t)end end if#i==2 then local o=(i[1].Position-i[2].Position).magnitude if i6 and o6 then local i=o/b(.01,i6)local x=clamp(.1,10,i)if t.ZoomEnabled then t:ZoomCamera(o6/x)end else i6=o o6=t:GetCameraActualZoom()end end else i6=nil o6=nil end end local function Y6(i)if i then local o=(i.CFrame.lookVector-Vector3.new(0,math.sin(math.rad(DEFAULT_CAMERA_ANGLE),0))).unit local x=findAngleBetweenXZVectors(o,t:GetCameraLook())local P=math.asin((t:GetCameraLook()).y)-math.asin(o.y)if not IsFinite(x)then x=0 end if not IsFinite(P)then P=0 end return Vector2.new(x,P)end return nil end local function U6(t)if s[t]then local i=(s[t]-t.Position).magnitude if i<MAX_TAP_POS_DELTA then local i=t6[t]-tick()if i<MAX_TAP_TIME_DELTA then return true end end end return false end local function l6(i,o)if p[i]==false then if m==1 then H=nil M=nil B=nil t.UserPanningTheCamera=false elseif m==2 then i6=nil o6=nil end end if p[i]~=nil and p[i]==false then m=m-1 end p[i]=nil s[i]=nil t6[i]=nil end local function Z6(i,o)if o then return end t:UpdateMouseBehavior()H=H or t:GetCameraLook()M=M or i.Position B=B or M t.UserPanningTheCamera=true end local function r6(i,o)t:UpdateMouseBehavior()if not(P or Y)then H=nil M=nil B=nil t.UserPanningTheCamera=false end end local function n6(t,i)if i then return end P=true Z6(t,i)end local function G6(t,i)P=false r6(t,i)end local function A6(t,i)if i then return end Y=true Z6(t,i)end local function I6(t,i)Y=false r6(t,i)end local function R6(i,o)if not hasGameLoaded and VRService.VREnabled then return end local x=i.Delta x=Vector2.new(x.X,x.Y*GameSettings:GetCameraYInvertValue())if M and(B and H)then local o=B+i.Delta local P=o-M if t.PanEnabled then local i=t:MouseTranslationToAngle(x)*MOUSE_SENSITIVITY t.RotateInput=t.RotateInput+i end B=o elseif t:IsInFirstPerson()or t:GetShiftLock()then if t.PanEnabled then local i=t:MouseTranslationToAngle(x)*MOUSE_SENSITIVITY t.RotateInput=t.RotateInput+i end end end local function y6(i,o)if not hasGameLoaded and VRService.VREnabled then return end if not o then if t.ZoomEnabled then t:ZoomCameraBy(clamp(-1,1,-i.Position.Z)*1.4)end end end local function F6(t)return T(t+.5)end local C6=V/4 local function Q6(t,i,o)if t~=ZERO_VECTOR3 then t=t.unit local x=j(t.z,t.x)local P=F6((j(t.z,t.x)+i)/o)*o return P-x end return 0 end local function j6(i,o)if not hasGameLoaded and VRService.VREnabled then return end if o then return end if t.ZoomEnabled then if i.KeyCode==Enum.KeyCode.I then t:ZoomCameraBy(-5)elseif i.KeyCode==Enum.KeyCode.O then t:ZoomCameraBy(5)end end if H==nil and t.KeyPanEnabled then if i.KeyCode==Enum.KeyCode.Left then t.TurningLeft=true elseif i.KeyCode==Enum.KeyCode.Right then t.TurningRight=true elseif i.KeyCode==Enum.KeyCode.Comma then local i=Q6(t:GetCameraLook()*Vector3.new(1,0,1),-C6*.75,C6)if i~=0 then t.RotateInput=t.RotateInput+Vector2.new(i,0)t.LastUserPanCamera=tick()t.LastCameraTransform=nil end elseif i.KeyCode==Enum.KeyCode.Period then local i=Q6(t:GetCameraLook()*Vector3.new(1,0,1),C6*.75,C6)if i~=0 then t.RotateInput=t.RotateInput+Vector2.new(i,0)t.LastUserPanCamera=tick()t.LastCameraTransform=nil end elseif i.KeyCode==Enum.KeyCode.PageUp then t.RotateInput=t.RotateInput+Vector2.new(0,math.rad(15))t.LastCameraTransform=nil elseif i.KeyCode==Enum.KeyCode.PageDown then t.RotateInput=t.RotateInput+Vector2.new(0,math.rad(-15))t.LastCameraTransform=nil end end end local function T6(i,o)if i.KeyCode==Enum.KeyCode.Left then t.TurningLeft=false elseif i.KeyCode==Enum.KeyCode.Right then t.TurningRight=false end end local function b6()t:ResetInputStates()end local V6=nil local a6=.7 local L6=0 local J6=6 local v6=4 local w6=Vector2.new(0,0)local O6=.65 local z6=nil local e6=.35 local D6=.8 local function E6(t)t=clamp(-1,1,t)if t>=0 then return(e6*t)/((e6-t)+1)end return-((D6*-t)/((D6+t)+1))end local f6=.1 local function K6(t)return(1+f6)*(2*math.abs(t)-1)-f6 end local function S6(t)return t/2+.5 end local function q6(t)local function i(t)local i=1 if t<0 then i=-1 end local o=S6(E6(K6(math.abs(t))))o=o*i return clamp(-1,1,o)end return a(i(t.x),i(t.y))end function t.UpdateGamepad(i)local o=t.GamepadPanningCamera if o and(hasGameLoaded or not VRService.VREnabled)then o=q6(o)local i=tick()if o.X~=0 or o.Y~=0 then t.userPanningTheCamera=true elseif o==ZERO_VECTOR2 then V6=nil if w6==ZERO_VECTOR2 then L6=0 end end local x=0 if V6 then if VRService.VREnabled then L6=v6 else local t=(i-V6)*10 L6=L6+J6*((t*t)/a6)if L6>J6 then L6=J6 end if z6 then local t=(o-w6)/(i-V6)local x=(t-z6).magnitude if x>12 then L6=L6*(20/x)if L6>J6 then L6=J6 end end end end local t,P=pcall(function()return GameSettings.GamepadCameraSensitivity end)x=t and P*L6 or L6 z6=(o-w6)/(i-V6)end w6=o V6=i return a(o.X*x,((o.Y*x)*O6)*GameSettings:GetCameraYInvertValue())end return ZERO_VECTOR2 end local X6,h6,u6,W6,c6,N6,g6,d6,M6=nil,nil,nil,nil,nil,nil,nil,nil,nil function t.DisconnectInputEvents(i)if X6 then X6:disconnect()X6=nil end if h6 then h6:disconnect()h6=nil end if u6 then u6:disconnect()u6=nil end if W6 then W6:disconnect()W6=nil end if c6 then c6:disconnect()c6=nil end if N6 then N6:disconnect()N6=nil end if g6 then g6:disconnect()g6=nil end if d6 then d6:disconnect()d6=nil end if f then f:disconnect()f=nil end if S then S:disconnect()S=nil end if M6 then M6:disconnect()M6=nil end t.TurningLeft=false t.TurningRight=false t.LastCameraTransform=nil i.LastSubjectCFrame=nil t.UserPanningTheCamera=false t.RotateInput=Vector2.new()t.GamepadPanningCamera=Vector2.new(0,0)M=nil B=nil H=nil P=false Y=false p={}m=0 i6=nil o6=nil if UserInputService.MouseBehavior~=Enum.MouseBehavior.LockCenter then UserInputService.MouseBehavior=Enum.MouseBehavior.Default end end function t.ResetInputStates(i)P=false Y=false t.TurningRight=false t.TurningLeft=false r6()if UserInputService.TouchEnabled then for t,i in pairs(p)do p[t]=nil end H=nil M=nil B=nil t.UserPanningTheCamera=false i6=nil o6=nil m=0 end end function t.getGamepadPan(i,o,x)if o==Enum.UserInputState.Cancel then t.GamepadPanningCamera=ZERO_VECTOR2 return end if x.UserInputType==t.activeGamepad and x.KeyCode==Enum.KeyCode.Thumbstick2 then local i=Vector2.new(x.Position.X,-x.Position.Y)if i.magnitude>THUMBSTICK_DEADZONE then t.GamepadPanningCamera=a(x.Position.X,-x.Position.Y)else t.GamepadPanningCamera=ZERO_VECTOR2 end end end function t.doGamepadZoom(i,o,x)if x.UserInputType==t.activeGamepad and(x.KeyCode==Enum.KeyCode.ButtonR3 and o==Enum.UserInputState.Begin)then if t.ZoomEnabled then if t:GetCameraZoom()>.5 then t:ZoomCamera(0)else t:ZoomCamera(10)end end end end function t.BindGamepadInputActions(i)ContextActionService:BindAction("\082\111\111\116\067\097\109\071\097\109\101\112\097\100\080\097\110",t.getGamepadPan,false,Enum.KeyCode.Thumbstick2)ContextActionService:BindAction("\082\111\111\116\067\097\109\071\097\109\101\112\097\100\090\111\111\109",t.doGamepadZoom,false,Enum.KeyCode.ButtonR3)end function t.ConnectInputEvents(i)X6=UserInputService.InputBegan:connect(function(t,i)if t.UserInputType==Enum.UserInputType.Touch then x6(t,i)elseif t.UserInputType==Enum.UserInputType.MouseButton2 then n6(t,i)elseif t.UserInputType==Enum.UserInputType.MouseButton3 then A6(t,i)end if t.UserInputType==Enum.UserInputType.Keyboard then j6(t,i)end end)h6=UserInputService.InputChanged:connect(function(t,i)if t.UserInputType==Enum.UserInputType.Touch then P6(t,i)elseif t.UserInputType==Enum.UserInputType.MouseMovement then R6(t,i)elseif t.UserInputType==Enum.UserInputType.MouseWheel then y6(t,i)end end)u6=UserInputService.InputEnded:connect(function(t,i)if t.UserInputType==Enum.UserInputType.Touch then l6(t,i)elseif t.UserInputType==Enum.UserInputType.MouseButton2 then G6(t,i)elseif t.UserInputType==Enum.UserInputType.MouseButton3 then I6(t,i)end if t.UserInputType==Enum.UserInputType.Keyboard then T6(t,i)end end)W6=UserInputService.WindowFocusReleased:connect(b6)c6=GuiService.MenuOpened:connect(function()t:ResetInputStates()end)S=(workspace:GetPropertyChangedSignal("\067\117\114\114\101\110\116\067\097\109\101\114\097")):Connect(d)if workspace.CurrentCamera then d()end N6=ShiftLockController.OnShiftLockToggled.Event:connect(function()t:UpdateMouseBehavior()end)t.RotateInput=Vector2.new()t.activeGamepad=nil local function o()local i=UserInputService:GetConnectedGamepads()if#i>0 then for o=1,#i,1 do if t.activeGamepad==nil then t.activeGamepad=i[o]elseif i[o].Value<t.activeGamepad.Value then t.activeGamepad=i[o]end end end if t.activeGamepad==nil then t.activeGamepad=Enum.UserInputType.Gamepad1 end end g6=UserInputService.GamepadDisconnected:connect(function(i)if t.activeGamepad~=i then return end t.activeGamepad=nil o()end)d6=UserInputService.GamepadConnected:connect(function(i)if t.activeGamepad==nil then o()end end)i:BindGamepadInputActions()o()i:UpdateMouseBehavior()end function t.ProcessTweens(t)for t,i in pairs(U)do local o=math.min(1.0,(tick()-i.start)/i.duration)i.to=i.func(i.from,i.to,o)if math.abs(1-o)<.0001 then U[t]=nil end end end function t.SetEnabled(i,t)if t~=i.Enabled then i.Enabled=t if i.Enabled then i:ConnectInputEvents()i.cframe=workspace.CurrentCamera.CFrame else i:DisconnectInputEvents()end end end local function B6(i)i.Changed:connect(function(i)if t.Enabled then if i=="\067\097\109\101\114\097\077\111\100\101"or i=="\067\097\109\101\114\097\077\097\120\090\111\111\109\068\105\115\116\097\110\099\101"or i=="\067\097\109\101\114\097\077\105\110\090\111\111\109\068\105\115\116\097\110\099\101"then t:ZoomCameraFixedBy(0)end end end)local function o(o)local x=findPlayerHumanoid(i)local P=tick()while tick()-P<.3 and(x==nil or x.Torso==nil)do wait()x=findPlayerHumanoid(i)end if x and(x.Torso and i.Character==o)then local i=(x.Torso.CFrame.lookVector-Vector3.new(0,math.sin(math.rad(DEFAULT_CAMERA_ANGLE)),0)).unit local o=findAngleBetweenXZVectors(i,t:GetCameraLook())local P=math.asin((t:GetCameraLook()).y)-math.asin(i.y)if not IsFinite(o)then o=0 end if not IsFinite(P)then P=0 end t.RotateInput=Vector2.new(o,P)t.LastCameraTransform=nil end wait()t:ZoomCamera(t.DefaultZoom)end i.CharacterAdded:connect(function(i)if t.Enabled or SetCameraOnSpawn then o(i)SetCameraOnSpawn=false end end)if i.Character then spawn(function()o(i.Character)end)end end if PlayersService.LocalPlayer then B6(PlayersService.LocalPlayer)end PlayersService.ChildAdded:connect(function(t)if t and PlayersService.LocalPlayer==t then B6(PlayersService.LocalPlayer)end end)local function H6()hasGameLoaded=true end spawn(function()if game:IsLoaded()then H6()else game.Loaded:wait()H6()end end)local function k6()if UserInputService.TouchEnabled then r=true end end local function p6()r=false end local function m6()if LocalPlayer.DevTouchMovementMode==Enum.DevTouchMovementMode.UserChoice then if GameSettings.TouchMovementMode.Name=="\068\121\110\097\109\105\099\084\104\117\109\098\115\116\105\099\107"then k6()else p6()end end end local function s6()if LocalPlayer.DevTouchMovementMode.Name=="\068\121\110\097\109\105\099\084\104\117\109\098\115\116\105\099\107"then k6()else m6()end end if PlayersService.LocalPlayer then PlayersService.LocalPlayer.Changed:Connect(function(t)if t=="\068\101\118\084\111\117\099\104\077\111\118\101\109\101\110\116\077\111\100\101"then s6()end end)s6()end GameSettings.Changed:Connect(function(t)if t=="\084\111\117\099\104\077\111\118\101\109\101\110\116\077\111\100\101"then m6()end end)m6()GameSettings:SetCameraYInvertVisible()pcall(function()GameSettings:SetGamepadCameraSensitivityVisible()end)return t end return x

local function OnCharacterAdded(character)
	if UserInputService.TouchEnabled then
		for _, child in ipairs(LocalPlayer.Character:GetChildren()) do
			if child:IsA("Tool") then
				IsAToolEquipped = true
			end
		end
		character.ChildAdded:Connect(function(child)
			if child:IsA("Tool") then
				IsAToolEquipped = true
			end
		end)
		character.ChildRemoved:Connect(function(child)
			if child:IsA("Tool") then
				IsAToolEquipped = false
			end
		end)

		if PlayerGui then
			local TouchGui = PlayerGui:FindFirstChild("TouchGui")
			if TouchGui and TouchGui:WaitForChild("GestureArea", 0.5) then
				GestureArea = TouchGui.GestureArea
				GestureAreaManagedByControlScript = true
			else
				GestureAreaManagedByControlScript = false
				local ScreenGui = Instance.new("ScreenGui")
				ScreenGui.Name = "GestureArea"
				ScreenGui.Parent = PlayerGui
				
				GestureArea = Instance.new("Frame")
                GestureArea.BackgroundTransparency = 1.0
				GestureArea.Visible = true
				GestureArea.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
				layoutGestureArea(PortraitMode)
				GestureArea.Parent = ScreenGui
			end
		end
	end
end

if LocalPlayer then
	if LocalPlayer.Character ~= nil then
		OnCharacterAdded(LocalPlayer.Character)
	end
	LocalPlayer.CharacterAdded:connect(function(character)
		OnCharacterAdded(character)
	end)
end
	
	
local function positionIntersectsGuiObject(position, guiObject)
	if position.X < guiObject.AbsolutePosition.X + guiObject.AbsoluteSize.X
		and position.X > guiObject.AbsolutePosition.X
		and position.Y < guiObject.AbsolutePosition.Y + guiObject.AbsoluteSize.Y
		and position.Y > guiObject.AbsolutePosition.Y then
		return true
	end
	return false
end	

local function GetRenderCFrame(part)
	return part:GetRenderCFrame()
end

local function CreateCamera()
	local this = {}
	local R15HeadHeight = R15_HEAD_OFFSET
	function this:GetActivateValue()
		return 0.7
	end
	
	function this:IsPortraitMode()
		return PortraitMode
	end	
	
	function this:GetRotateAmountValue(vrRotationIntensity)
		vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
		if vrRotationIntensity then
			if vrRotationIntensity == "Low" then
				return VR_LOW_INTENSITY_ROTATION
			elseif vrRotationIntensity == "High" then
				return VR_HIGH_INTENSITY_ROTATION
			end
		end
		return ZERO_VECTOR2
	end
	function this:GetRepeatDelayValue(vrRotationIntensity)
		vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
		if vrRotationIntensity then
			if vrRotationIntensity == "Low" then
				return VR_LOW_INTENSITY_REPEAT
			elseif vrRotationIntensity == "High" then
				return VR_HIGH_INTENSITY_REPEAT
			end
		end
		return 0
	end
	
	this.ShiftLock = false
	this.Enabled = false
	local isFirstPerson = false
	local isRightMouseDown = false
	local isMiddleMouseDown = false
	this.RotateInput = ZERO_VECTOR2
	this.DefaultZoom = LANDSCAPE_DEFAULT_ZOOM
	this.activeGamepad = nil
	
	local tweens = {}

	this.lastSubject = nil
	this.lastSubjectPosition = Vector3.new(0, 5, 0)

	local lastVRRotation = 0
	local vrRotateKeyCooldown = {}
	
	local isDynamicThumbstickEnabled = false
	local dynamicThumbstickFrame = nil
	
	local function getDynamicThumbstickFrame()
		if dynamicThumbstickFrame and dynamicThumbstickFrame:IsDescendantOf(game) then
			return dynamicThumbstickFrame
		else
			local touchGui = PlayerGui:FindFirstChild("TouchGui")
			if not touchGui then return nil end
			
			local touchControlFrame = touchGui:FindFirstChild("TouchControlFrame")
			if not touchControlFrame then return nil end
			
			dynamicThumbstickFrame = touchControlFrame:FindFirstChild("DynamicThumbstickFrame")
			return dynamicThumbstickFrame
		end
	end
	
	-- Check for changes in ViewportSize to decide if PortraitMode
	local CameraChangedConn = nil
	local workspaceCameraChangedConn = nil
	local function onWorkspaceCameraChanged()
		if UserInputService.TouchEnabled then
			if CameraChangedConn then
				CameraChangedConn:Disconnect()
				CameraChangedConn = nil
			end
			local newCamera = workspace.CurrentCamera
			if newCamera then
				local size = newCamera.ViewportSize
				PortraitMode = size.X < size.Y
				layoutGestureArea(PortraitMode)
				DefaultZoom = PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM
				CameraChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
					size = newCamera.ViewportSize
					PortraitMode = size.X < size.Y
					layoutGestureArea(PortraitMode)
					DefaultZoom = PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM
				end)
			end
		end
	end
	workspaceCameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onWorkspaceCameraChanged)
	if workspace.CurrentCamera then
		onWorkspaceCameraChanged()
	end
		
	
	function this:GetShiftLock()
		return ShiftLockController:IsShiftLocked()
	end

	function this:GetHumanoid()
		local player = PlayersService.LocalPlayer
		return findPlayerHumanoid(player)
	end

	function this:GetHumanoidRootPart()
		local humanoid = this:GetHumanoid()
		return humanoid and humanoid.Torso
	end

	function this:GetRenderCFrame(part)
		GetRenderCFrame(part)
	end
	
	local STATE_DEAD = Enum.HumanoidStateType.Dead
	
	-- HumanoidRootPart when alive, Head part when dead
	local function getHumanoidPartToFollow(humanoid, humanoidStateType)
		if humanoidStateType == STATE_DEAD then
			local character = humanoid.Parent
			if character then
				return character:FindFirstChild("Head") or humanoid.Torso
			else
				return humanoid.Torso
			end
		else
			return humanoid.Torso
		end
	end

	local HUMANOID_STATE_DEAD = Enum.HumanoidStateType.Dead
	
	function this:GetSubjectPosition()
		local result = nil
		local camera = workspace.CurrentCamera
		local cameraSubject = camera and camera.CameraSubject
		if cameraSubject then
			if cameraSubject:IsA('Humanoid') then
				local humanoidStateType = cameraSubject:GetState()
				if VRService.VREnabled and humanoidStateType == STATE_DEAD and cameraSubject == this.lastSubject then
					result = this.lastSubjectPosition
				else
					local humanoidRootPart = getHumanoidPartToFollow(cameraSubject, humanoidStateType)
					if humanoidRootPart and humanoidRootPart:IsA('BasePart') then
						local subjectCFrame = GetRenderCFrame(humanoidRootPart)
						local heightOffset = ZERO_VECTOR3
						if humanoidStateType ~= STATE_DEAD then
							heightOffset = cameraSubject.RigType == Enum.HumanoidRigType.R15 and R15HeadHeight or HEAD_OFFSET
						end

						if PortraitMode then
							heightOffset = heightOffset + Vector3.new(0, PORTRAIT_MODE_CAMERA_OFFSET, 0)
						end

						result = subjectCFrame.p +
							subjectCFrame:vectorToWorldSpace(heightOffset + cameraSubject.CameraOffset)
					end
				end	
			elseif cameraSubject:IsA('VehicleSeat') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				local offset = SEAT_OFFSET
				if VRService.VREnabled then
					offset = VR_SEAT_OFFSET
				end
				result = subjectCFrame.p + subjectCFrame:vectorToWorldSpace(offset)
			elseif cameraSubject:IsA('SkateboardPlatform') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				result = subjectCFrame.p + SEAT_OFFSET
			elseif cameraSubject:IsA('BasePart') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				result = subjectCFrame.p
			elseif cameraSubject:IsA('Model') then
				result = cameraSubject:GetModelCFrame().p
			end
		end
		this.lastSubject = cameraSubject
		this.lastSubjectPosition = result
		return result
	end

	function this:ResetCameraLook()
	end

	function this:GetCameraLook()
		return workspace.CurrentCamera and workspace.CurrentCamera.CoordinateFrame.lookVector or Vector3.new(0,0,1)
	end

	function this:GetCameraZoom()
		if this.currentZoom == nil then
			local player = PlayersService.LocalPlayer
			this.currentZoom = player and clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, this.DefaultZoom) or this.DefaultZoom
		end
		return this.currentZoom
	end

	function this:GetCameraActualZoom()
		local camera = workspace.CurrentCamera
		if camera then
			return (camera.CoordinateFrame.p - camera.Focus.p).magnitude
		end
	end

	function this:GetCameraHeight()
		if VRService.VREnabled and not this:IsInFirstPerson() then
			local zoom = this:GetCameraZoom()
			return math.sin(VR_ANGLE) * zoom
		end
		return 0
	end

	function this:ViewSizeX()
		local result = 1024
		local camera = workspace.CurrentCamera
		if camera then
			result = camera.ViewportSize.X
		end
		return result
	end

	function this:ViewSizeY()
		local result = 768
		local camera = workspace.CurrentCamera
		if camera then
			result = camera.ViewportSize.Y
		end
		return result
	end

	
	local math_asin = math.asin
	local math_atan2 = math.atan2
	local math_floor = math.floor
	local math_max = math.max
	local math_pi = math.pi
	local Vector2_new = Vector2.new
	local Vector3_new = Vector3.new
	local CFrame_Angles = CFrame.Angles
	local CFrame_new = CFrame.new

	function this:ScreenTranslationToAngle(translationVector)
		local screenX = this:ViewSizeX()
		local screenY = this:ViewSizeY()
		local xTheta = (translationVector.x / screenX)
		local yTheta = (translationVector.y / screenY)
		return Vector2_new(xTheta, yTheta)
	end

	function this:MouseTranslationToAngle(translationVector)
		local xTheta = (translationVector.x / 1920)
		local yTheta = (translationVector.y / 1200)
		return Vector2_new(xTheta, yTheta)
	end

	function this:RotateVector(startVector, xyRotateVector)
		local startCFrame = CFrame_new(ZERO_VECTOR3, startVector)
		local resultLookVector = (CFrame_Angles(0, -xyRotateVector.x, 0) * startCFrame * CFrame_Angles(-xyRotateVector.y,0,0)).lookVector
		return resultLookVector, Vector2_new(xyRotateVector.x, xyRotateVector.y)
	end

	function this:RotateCamera(startLook, xyRotateVector)
		if VRService.VREnabled then
			local yawRotatedVector, xyRotateVector = self:RotateVector(startLook, Vector2.new(xyRotateVector.x, 0))
			return Vector3_new(yawRotatedVector.x, 0, yawRotatedVector.z).unit, xyRotateVector
		else
			local startVertical = math_asin(startLook.y)
			local yTheta = clamp(-MAX_Y + startVertical, -MIN_Y + startVertical, xyRotateVector.y)
			return self:RotateVector(startLook, Vector2_new(xyRotateVector.x, yTheta))
		end
	end

	function this:IsInFirstPerson()
		return isFirstPerson
	end

	-- there are several cases to consider based on the state of input and camera rotation mode
	function this:UpdateMouseBehavior()
		-- first time transition to first person mode or shiftlock
		local camera = workspace.CurrentCamera
		if camera.CameraType == Enum.CameraType.Scriptable then
			return
		end
		
		if isFirstPerson or self:GetShiftLock() then
			pcall(function() GameSettings.RotationType = Enum.RotationType.CameraRelative end)
			if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
			end
		else
			pcall(function() GameSettings.RotationType = Enum.RotationType.MovementRelative end)
			if isRightMouseDown or isMiddleMouseDown then
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
			else
				UserInputService.MouseBehavior = Enum.MouseBehavior.Default
			end
		end
	end

	function this:ZoomCamera(desiredZoom)
		local player = PlayersService.LocalPlayer
		if player then
			if player.CameraMode == Enum.CameraMode.LockFirstPerson then
				this.currentZoom = 0
			else
				this.currentZoom = clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, desiredZoom)
			end
		end

		isFirstPerson = self:GetCameraZoom() < 2

		ShiftLockController:SetIsInFirstPerson(isFirstPerson)
		-- set mouse behavior
		self:UpdateMouseBehavior()
		return self:GetCameraZoom()
	end

	function this:rk4Integrator(position, velocity, t)
		local direction = velocity < 0 and -1 or 1
		local function acceleration(p, v)
			local accel = direction * math_max(1, (p / 3.3) + 0.5)
			return accel
		end

		local p1 = position
		local v1 = velocity
		local a1 = acceleration(p1, v1)
		local p2 = p1 + v1 * (t / 2)
		local v2 = v1 + a1 * (t / 2)
		local a2 = acceleration(p2, v2)
		local p3 = p1 + v2 * (t / 2)
		local v3 = v1 + a2 * (t / 2)
		local a3 = acceleration(p3, v3)
		local p4 = p1 + v3 * t
		local v4 = v1 + a3 * t
		local a4 = acceleration(p4, v4)

		local positionResult = position + (v1 + 2 * v2 + 2 * v3 + v4) * (t / 6)
		local velocityResult = velocity + (a1 + 2 * a2 + 2 * a3 + a4) * (t / 6)
		return positionResult, velocityResult
	end

	function this:ZoomCameraBy(zoomScale)
		local zoom = this:GetCameraActualZoom()
		
		if zoom then
			-- Can break into more steps to get more accurate integration
            zoom = self:rk4Integrator(zoom, zoomScale, 1)
			self:ZoomCamera(zoom)
		end
		return self:GetCameraZoom()
	end

	function this:ZoomCameraFixedBy(zoomIncrement)
		return self:ZoomCamera(self:GetCameraZoom() + zoomIncrement)
	end

	function this:Update()
	end

	----- VR STUFF ------

	function this:ApplyVRTransform()
		if not VRService.VREnabled then
			return
		end
		--we only want this to happen in first person VR
		local player = PlayersService.LocalPlayer
		if not (player and player.Character
				and player.Character:FindFirstChild("HumanoidRootPart")
				and player.Character.HumanoidRootPart:FindFirstChild("RootJoint")) then
			return
		end

		local camera = workspace.CurrentCamera
		local cameraSubject = camera.CameraSubject
		local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')

		if this:IsInFirstPerson() and not isInVehicle then
			local vrFrame = VRService:GetUserCFrame(Enum.UserCFrame.Head)
			local vrRotation = vrFrame - vrFrame.p
			local rootJoint = player.Character.HumanoidRootPart.RootJoint
			rootJoint.C0 = CFrame.new(vrRotation:vectorToObjectSpace(vrFrame.p)) * CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
		else
			local rootJoint = player.Character.HumanoidRootPart.RootJoint
			rootJoint.C0 = CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
		end
	end

	local vrRotationIntensityExists = true
	local lastVrRotationCheck = 0
	function this:ShouldUseVRRotation()
		if not VRService.VREnabled then
			return false
		end
		if not vrRotationIntensityExists and tick() - lastVrRotationCheck < 1 then return false end

		local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
		vrRotationIntensityExists = success and vrRotationIntensity ~= nil
		lastVrRotationCheck = tick()

		return success and vrRotationIntensity ~= nil and vrRotationIntensity ~= "Smooth"
	end

	function this:GetVRRotationInput()
		local vrRotateSum = ZERO_VECTOR2

		local vrRotationIntensity = StarterGui:GetCore("VRRotationIntensity")

		local vrGamepadRotation = self.GamepadPanningCamera or ZERO_VECTOR2
		local delayExpired = (tick() - lastVRRotation) >= self:GetRepeatDelayValue(vrRotationIntensity)

		if math.abs(vrGamepadRotation.x) >= self:GetActivateValue() then
			if (delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]) then
				local sign = 1
				if vrGamepadRotation.x < 0 then
					sign = -1
				end
				vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity) * sign
				vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = true
			end
		elseif math.abs(vrGamepadRotation.x) < self:GetActivateValue() - 0.1 then
			vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = nil
		end
		if self.TurningLeft then
			if delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Left] then
				vrRotateSum = vrRotateSum - self:GetRotateAmountValue(vrRotationIntensity)
				vrRotateKeyCooldown[Enum.KeyCode.Left] = true
			end
		else
			vrRotateKeyCooldown[Enum.KeyCode.Left] = nil
		end
		if self.TurningRight then
			if (delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Right]) then
				vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity)
				vrRotateKeyCooldown[Enum.KeyCode.Right] = true
			end
		else
			vrRotateKeyCooldown[Enum.KeyCode.Right] = nil
		end

		if vrRotateSum ~= ZERO_VECTOR2 then
			lastVRRotation = tick()
		end

		return vrRotateSum
	end

	local cameraTranslationConstraints = Vector3.new(1, 1, 1)
	local humanoidJumpOrigin = nil
	local trackingHumanoid = nil
	local cameraFrozen = false
	local subjectStateChangedConn = nil
	local cameraSubjectChangedConn = nil
	local workspaceChangedConn = nil
	local humanoidChildAddedConn = nil
	local humanoidChildRemovedConn = nil

	local function cancelCameraFreeze(keepConstraints)
		if not keepConstraints then
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, 1, cameraTranslationConstraints.z)
		end
		if cameraFrozen then
			trackingHumanoid = nil
			cameraFrozen = false
		end
	end

	local function startCameraFreeze(subjectPosition, humanoidToTrack)
		if not cameraFrozen then
			humanoidJumpOrigin = subjectPosition
			trackingHumanoid = humanoidToTrack
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, 0, cameraTranslationConstraints.z)
			cameraFrozen = true
		end
	end

	local function rescaleCameraOffset(newScaleFactor)
		R15HeadHeight = R15_HEAD_OFFSET*newScaleFactor
	end

	local function onHumanoidSubjectChildAdded(child)
		if child.Name == "BodyHeightScale" and child:IsA("NumberValue") then
			if heightScaleChangedConn then
				heightScaleChangedConn:disconnect()
			end
			heightScaleChangedConn = child.Changed:connect(rescaleCameraOffset)
			rescaleCameraOffset(child.Value)
		end
	end

	local function onHumanoidSubjectChildRemoved(child)
		if child.Name == "BodyHeightScale" then
			rescaleCameraOffset(1)
			if heightScaleChangedConn then
				heightScaleChangedConn:disconnect()
				heightScaleChangedConn = nil
			end
		end
	end

	local function onNewCameraSubject()
		if subjectStateChangedConn then
			subjectStateChangedConn:disconnect()
			subjectStateChangedConn = nil
		end
		if humanoidChildAddedConn then
			humanoidChildAddedConn:disconnect()
			humanoidChildAddedConn = nil
		end
		if humanoidChildRemovedConn then
			humanoidChildRemovedConn:disconnect()
			humanoidChildRemovedConn = nil
		end
		if heightScaleChangedConn then
			heightScaleChangedConn:disconnect()
			heightScaleChangedConn = nil
		end

		local humanoid = workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject
		if trackingHumanoid ~= humanoid then
			cancelCameraFreeze()
		end
		if humanoid and humanoid:IsA('Humanoid') then
			humanoidChildAddedConn = humanoid.ChildAdded:connect(onHumanoidSubjectChildAdded)
			humanoidChildRemovedConn = humanoid.ChildRemoved:connect(onHumanoidSubjectChildRemoved)
			for _, child in pairs(humanoid:GetChildren()) do
				onHumanoidSubjectChildAdded(child)
			end

			subjectStateChangedConn = humanoid.StateChanged:connect(function(oldState, newState)
				if VRService.VREnabled and newState == Enum.HumanoidStateType.Jumping and not this:IsInFirstPerson() then
					startCameraFreeze(this:GetSubjectPosition(), humanoid)
				elseif newState ~= Enum.HumanoidStateType.Jumping and newState ~= Enum.HumanoidStateType.Freefall then
					cancelCameraFreeze(true)
				end
			end)
		end
	end

 	local function onCurrentCameraChanged()
		if cameraSubjectChangedConn then
			cameraSubjectChangedConn:disconnect()
			cameraSubjectChangedConn = nil
		end
  		local camera = workspace.CurrentCamera
  		if camera then
			cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):connect(onNewCameraSubject)
			onNewCameraSubject()
  		end
  	end

	function this:GetVRFocus(subjectPosition, timeDelta)
		local newFocus = nil

		local camera = workspace.CurrentCamera
		local lastFocus = self.LastCameraFocus or subjectPosition
		if not cameraFrozen then
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, math.min(1, cameraTranslationConstraints.y + 0.42 * timeDelta), cameraTranslationConstraints.z)
		end
		if cameraFrozen and humanoidJumpOrigin and humanoidJumpOrigin.y > lastFocus.y then
			newFocus = CFrame.new(Vector3.new(subjectPosition.x, math.min(humanoidJumpOrigin.y, lastFocus.y + 5 * timeDelta), subjectPosition.z))
		else
			newFocus = CFrame.new(Vector3.new(subjectPosition.x, lastFocus.y, subjectPosition.z):lerp(subjectPosition, cameraTranslationConstraints.y))
		end

		if cameraFrozen then
			-- No longer in 3rd person
			if self:IsInFirstPerson() then -- not VRService.VREnabled
				cancelCameraFreeze()
			end
			-- This case you jumped off a cliff and want to keep your character in view
			-- 0.5 is to fix floating point error when not jumping off cliffs
			if humanoidJumpOrigin and subjectPosition.y < (humanoidJumpOrigin.y - 0.5) then
				cancelCameraFreeze()
			end
		end

		return newFocus
	end

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

	---- Input Events ----
	local startPos = nil
	local lastPos = nil
	local panBeginLook = nil
	local lastTapTime = nil

	local fingerTouches = {}
	local NumUnsunkTouches = 0
	
	local inputStartPositions = {}
	local inputStartTimes = {}

	local StartingDiff = nil
	local pinchBeginZoom = nil

	this.ZoomEnabled = true
	this.PanEnabled = true
	this.KeyPanEnabled = true

	local function OnTouchBegan(input, processed)
		--If isDynamicThumbstickEnabled, then only process TouchBegan event if it starts in GestureArea
		
		local dtFrame = getDynamicThumbstickFrame()
        
        local isDynamicThumbstickUsingThisInput = false
        if isDynamicThumbstickEnabled then
            local ControlScript = CameraScript.Parent:FindFirstChild("ControlScript")
            if ControlScript then
                local MasterControl = ControlScript:FindFirstChild("MasterControl")
                if MasterControl then
                    local DynamicThumbstickModule = MasterControl:FindFirstChild("DynamicThumbstick")
                    if DynamicThumbstickModule then
                        DynamicThumbstickModule = require(DynamicThumbstickModule)
                        local dynamicInputObject = DynamicThumbstickModule:GetInputObject()
                        isDynamicThumbstickUsingThisInput = (dynamicInputObject == input)
                    end
                end
            end
        end
        
		if not isDynamicThumbstickUsingThisInput then
			fingerTouches[input] = processed
			if not processed then
				inputStartPositions[input] = input.Position
				inputStartTimes[input] = tick()
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end
	end

	local function OnTouchChanged(input, processed)
		
		if fingerTouches[input] == nil then
			if isDynamicThumbstickEnabled then
				return
			end
			fingerTouches[input] = processed
			if not processed then
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end

		if NumUnsunkTouches == 1 then
			if fingerTouches[input] == false then
				panBeginLook = panBeginLook or this:GetCameraLook()
				startPos = startPos or input.Position
				lastPos = lastPos or startPos
				this.UserPanningTheCamera = true

				local delta = input.Position - lastPos		
				
				delta = Vector2.new(delta.X, delta.Y * GameSettings:GetCameraYInvertValue())
				
				if this.PanEnabled then
					local desiredXYVector = this:ScreenTranslationToAngle(delta) * TOUCH_SENSITIVTY
					this.RotateInput = this.RotateInput + desiredXYVector
				end

				lastPos = input.Position
			end
		else
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
		end
		if NumUnsunkTouches == 2 then
			local unsunkTouches = {}
			for touch, wasSunk in pairs(fingerTouches) do
				if not wasSunk then
					table.insert(unsunkTouches, touch)
				end
			end
			if #unsunkTouches == 2 then
				local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
				if StartingDiff and pinchBeginZoom then
					local scale = difference / math_max(0.01, StartingDiff)
					local clampedScale = clamp(0.1, 10, scale)
					if this.ZoomEnabled then
						this:ZoomCamera(pinchBeginZoom / clampedScale)
					end
				else
					StartingDiff = difference
					pinchBeginZoom = this:GetCameraActualZoom()
				end
			end
		else
			StartingDiff = nil
			pinchBeginZoom = nil
		end
	end
	
	local function calcLookBehindRotateInput(torso)
		if torso then
			local newDesiredLook = (torso.CFrame.lookVector - Vector3.new(0, math.sin(math.rad(DEFAULT_CAMERA_ANGLE), 0))).unit
			local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
			local vertShift = math.asin(this:GetCameraLook().y) - math.asin(newDesiredLook.y)
			if not IsFinite(horizontalShift) then
				horizontalShift = 0
			end
			if not IsFinite(vertShift) then
				vertShift = 0
			end
			
			return Vector2.new(horizontalShift, vertShift)
		end
		return nil
	end
	
	local function IsTouchTap(input)
		-- We can't make the assumption that the input exists in the inputStartPositions because we may have switched from a different camera type.
		if inputStartPositions[input] then
			local posDelta = (inputStartPositions[input] - input.Position).magnitude
			if posDelta < MAX_TAP_POS_DELTA then
				local timeDelta = inputStartTimes[input] - tick()
				if timeDelta < MAX_TAP_TIME_DELTA then
					return true
				end
			end
		end
		return false
	end
	
	
	local function OnTouchEnded(input, processed)
		if fingerTouches[input] == false then
			if NumUnsunkTouches == 1 then
				panBeginLook = nil
				startPos = nil
				lastPos = nil
				this.UserPanningTheCamera = false
			elseif NumUnsunkTouches == 2 then
				StartingDiff = nil
				pinchBeginZoom = nil
			end
		end

		if fingerTouches[input] ~= nil and fingerTouches[input] == false then
			NumUnsunkTouches = NumUnsunkTouches - 1
		end
		fingerTouches[input] = nil
		inputStartPositions[input] = nil
		inputStartTimes[input] = nil
	end

	local function OnMousePanButtonPressed(input, processed)
		if processed then return end
		this:UpdateMouseBehavior()
		panBeginLook = panBeginLook or this:GetCameraLook()
		startPos = startPos or input.Position
		lastPos = lastPos or startPos
		this.UserPanningTheCamera = true
	end

	local function OnMousePanButtonReleased(input, processed)
		this:UpdateMouseBehavior()
		if not (isRightMouseDown or isMiddleMouseDown) then
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
		end
	end

	local function OnMouse2Down(input, processed)
		if processed then return end

		isRightMouseDown = true
		OnMousePanButtonPressed(input, processed)
	end

	local function OnMouse2Up(input, processed)
		isRightMouseDown = false
		OnMousePanButtonReleased(input, processed)
	end

	local function OnMouse3Down(input, processed)
		if processed then return end

		isMiddleMouseDown = true
		OnMousePanButtonPressed(input, processed)
	end

	local function OnMouse3Up(input, processed)
		isMiddleMouseDown = false
		OnMousePanButtonReleased(input, processed)
	end

	local function OnMouseMoved(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end
		
		local inputDelta = input.Delta
		inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * GameSettings:GetCameraYInvertValue())
		
		if startPos and lastPos and panBeginLook then
			local currPos = lastPos + input.Delta
			local totalTrans = currPos - startPos
			if this.PanEnabled then
				local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
				this.RotateInput = this.RotateInput + desiredXYVector
			end
			lastPos = currPos
		elseif this:IsInFirstPerson() or this:GetShiftLock() then
			if this.PanEnabled then
				local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
				this.RotateInput = this.RotateInput + desiredXYVector
			end
		end
	end

	local function OnMouseWheel(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end
		if not processed then
			if this.ZoomEnabled then
                this:ZoomCameraBy(clamp(-1, 1, -input.Position.Z) * 1.4)
			end
		end
	end

	local function round(num)
		return math_floor(num + 0.5)
	end

	local eight2Pi = math_pi / 4

	local function rotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
		if camLook ~= ZERO_VECTOR3 then
			camLook = camLook.unit
			local currAngle = math_atan2(camLook.z, camLook.x)
			local newAngle = round((math_atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
			return newAngle - currAngle
		end
		return 0
	end

	local function OnKeyDown(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end
		if processed then return end
		if this.ZoomEnabled then
			if input.KeyCode == Enum.KeyCode.I then
				this:ZoomCameraBy(-5)
			elseif input.KeyCode == Enum.KeyCode.O then
				this:ZoomCameraBy(5)
			end
		end
		if panBeginLook == nil and this.KeyPanEnabled then
			if input.KeyCode == Enum.KeyCode.Left then
				this.TurningLeft = true
			elseif input.KeyCode == Enum.KeyCode.Right then
				this.TurningRight = true
			elseif input.KeyCode == Enum.KeyCode.Comma then
				local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), -eight2Pi * (3/4), eight2Pi)
				if angle ~= 0 then
					this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
					this.LastUserPanCamera = tick()
					this.LastCameraTransform = nil
				end
			elseif input.KeyCode == Enum.KeyCode.Period then
				local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), eight2Pi * (3/4), eight2Pi)
				if angle ~= 0 then
					this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
					this.LastUserPanCamera = tick()
					this.LastCameraTransform = nil
				end
			elseif input.KeyCode == Enum.KeyCode.PageUp then
			--elseif input.KeyCode == Enum.KeyCode.Home then
				this.RotateInput = this.RotateInput + Vector2.new(0,math.rad(15))
				this.LastCameraTransform = nil
			elseif input.KeyCode == Enum.KeyCode.PageDown then
			--elseif input.KeyCode == Enum.KeyCode.End then
				this.RotateInput = this.RotateInput + Vector2.new(0,math.rad(-15))
				this.LastCameraTransform = nil
			end
		end
	end

	local function OnKeyUp(input, processed)
		if input.KeyCode == Enum.KeyCode.Left then
			this.TurningLeft = false
		elseif input.KeyCode == Enum.KeyCode.Right then
			this.TurningRight = false
		end
	end

	local function onWindowFocusReleased()
		this:ResetInputStates()
	end

	local lastThumbstickRotate = nil
	local numOfSeconds = 0.7
	local currentSpeed = 0
	local maxSpeed = 6
	local vrMaxSpeed = 4
	local lastThumbstickPos = Vector2.new(0,0)
	local ySensitivity = 0.65
	local lastVelocity = nil

	-- K is a tunable parameter that changes the shape of the S-curve
	-- the larger K is the more straight/linear the curve gets
	local k = 0.35
	local lowerK = 0.8
	local function SCurveTranform(t)
		t = clamp(-1,1,t)
		if t >= 0 then
			return (k*t) / (k - t + 1)
		end
		return -((lowerK*-t) / (lowerK + t + 1))
	end

	-- DEADZONE
	local DEADZONE = 0.1
	local function toSCurveSpace(t)
		return (1 + DEADZONE) * (2*math.abs(t) - 1) - DEADZONE
	end

	local function fromSCurveSpace(t)
		return t/2 + 0.5
	end

	local function gamepadLinearToCurve(thumbstickPosition)
		local function onAxis(axisValue)
			local sign = 1
			if axisValue < 0 then
				sign = -1
			end
			local point = fromSCurveSpace(SCurveTranform(toSCurveSpace(math.abs(axisValue))))
			point = point * sign
			return clamp(-1, 1, point)
		end
		return Vector2_new(onAxis(thumbstickPosition.x), onAxis(thumbstickPosition.y))
	end

	function this:UpdateGamepad()
		local gamepadPan = this.GamepadPanningCamera
		if gamepadPan and (hasGameLoaded or not VRService.VREnabled) then
			gamepadPan = gamepadLinearToCurve(gamepadPan)
			local currentTime = tick()
			if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
				this.userPanningTheCamera = true
			elseif gamepadPan == ZERO_VECTOR2 then
				lastThumbstickRotate = nil
				if lastThumbstickPos == ZERO_VECTOR2 then
					currentSpeed = 0
				end
			end

			local finalConstant = 0

			if lastThumbstickRotate then
				if VRService.VREnabled then
					currentSpeed = vrMaxSpeed
				else
					local elapsedTime = (currentTime - lastThumbstickRotate) * 10
					currentSpeed = currentSpeed + (maxSpeed * ((elapsedTime*elapsedTime)/numOfSeconds))

					if currentSpeed > maxSpeed then currentSpeed = maxSpeed end

					if lastVelocity then
						local velocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
						local velocityDeltaMag = (velocity - lastVelocity).magnitude

						if velocityDeltaMag > 12 then
							currentSpeed = currentSpeed * (20/velocityDeltaMag)
							if currentSpeed > maxSpeed then currentSpeed = maxSpeed end
						end
					end
				end

				local success, gamepadCameraSensitivity = pcall(function() return GameSettings.GamepadCameraSensitivity end)
				finalConstant = success and (gamepadCameraSensitivity * currentSpeed) or currentSpeed
				lastVelocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
			end

			lastThumbstickPos = gamepadPan
			lastThumbstickRotate = currentTime

			return Vector2_new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * ySensitivity * GameSettings:GetCameraYInvertValue())
		end

		return ZERO_VECTOR2
	end

	local InputBeganConn, InputChangedConn, InputEndedConn, WindowUnfocusConn, MenuOpenedConn, ShiftLockToggleConn, GamepadConnectedConn, GamepadDisconnectedConn, TouchActivateConn = nil, nil, nil, nil, nil, nil, nil, nil, nil

	function this:DisconnectInputEvents()
		if InputBeganConn then
			InputBeganConn:disconnect()
			InputBeganConn = nil
		end
		if InputChangedConn then
			InputChangedConn:disconnect()
			InputChangedConn = nil
		end
		if InputEndedConn then
			InputEndedConn:disconnect()
			InputEndedConn = nil
		end
		if WindowUnfocusConn then
			WindowUnfocusConn:disconnect()
			WindowUnfocusConn = nil
		end 
		if MenuOpenedConn then
			MenuOpenedConn:disconnect()
			MenuOpenedConn = nil
		end
		if ShiftLockToggleConn then
			ShiftLockToggleConn:disconnect()
			ShiftLockToggleConn = nil
		end
		if GamepadConnectedConn then
			GamepadConnectedConn:disconnect()
			GamepadConnectedConn = nil
		end
		if GamepadDisconnectedConn then
			GamepadDisconnectedConn:disconnect()
			GamepadDisconnectedConn = nil
		end
		if subjectStateChangedConn then
			subjectStateChangedConn:disconnect()
			subjectStateChangedConn = nil
		end
		if workspaceChangedConn then
			workspaceChangedConn:disconnect()
			workspaceChangedConn = nil
		end
		if TouchActivateConn then
			TouchActivateConn:disconnect()
			TouchActivateConn = nil
		end

		this.TurningLeft = false
		this.TurningRight = false
		this.LastCameraTransform = nil
		self.LastSubjectCFrame = nil
		this.UserPanningTheCamera = false
		this.RotateInput = Vector2.new()
		this.GamepadPanningCamera = Vector2.new(0,0)

		-- Reset input states
		startPos = nil
		lastPos = nil
		panBeginLook = nil
		isRightMouseDown = false
		isMiddleMouseDown = false

		fingerTouches = {}
		NumUnsunkTouches = 0

		StartingDiff = nil
		pinchBeginZoom = nil

		-- Unlock mouse for example if right mouse button was being held down
		if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
			UserInputService.MouseBehavior = Enum.MouseBehavior.Default
		end
	end

	function this:ResetInputStates()
		isRightMouseDown = false
		isMiddleMouseDown = false
		this.TurningRight = false
		this.TurningLeft = false
		OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters

		if UserInputService.TouchEnabled then
			--[[menu opening was causing serious touch issues
			this should disable all active touch events if
			they're active when menu opens.]]
			for inputObject, value in pairs(fingerTouches) do
				fingerTouches[inputObject] = nil
			end
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
			StartingDiff = nil
			pinchBeginZoom = nil
			NumUnsunkTouches = 0
		end
	end

	function this.getGamepadPan(name, state, input)
		if state == Enum.UserInputState.Cancel then
			this.GamepadPanningCamera = ZERO_VECTOR2
			return
		end		

		if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then
			local inputVector = Vector2.new(input.Position.X, -input.Position.Y)
			if inputVector.magnitude > THUMBSTICK_DEADZONE then
				this.GamepadPanningCamera = Vector2_new(input.Position.X, -input.Position.Y)
			else
				this.GamepadPanningCamera = ZERO_VECTOR2
			end
		end
	end

	function this.doGamepadZoom(name, state, input)
		if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonR3 and state == Enum.UserInputState.Begin then
			if this.ZoomEnabled then
				if this:GetCameraZoom() > 0.5 then
					this:ZoomCamera(0)
				else
					this:ZoomCamera(10)
				end
			end
		end
	end
	
	function this:BindGamepadInputActions()
		ContextActionService:BindAction("RootCamGamepadPan", this.getGamepadPan, false, Enum.KeyCode.Thumbstick2)
		ContextActionService:BindAction("RootCamGamepadZoom", this.doGamepadZoom, false, Enum.KeyCode.ButtonR3)
	end

	function this:ConnectInputEvents()	
		InputBeganConn = UserInputService.InputBegan:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchBegan(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
				OnMouse2Down(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
				OnMouse3Down(input, processed)
			end
			-- Keyboard
			if input.UserInputType == Enum.UserInputType.Keyboard then
				OnKeyDown(input, processed)
			end
		end)

		InputChangedConn = UserInputService.InputChanged:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchChanged(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseMovement then
				OnMouseMoved(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseWheel then
				OnMouseWheel(input, processed)
			end
		end)

		InputEndedConn = UserInputService.InputEnded:connect(function(input, processed)
			if input.UserInputType == Enum.UserInputType.Touch then
				OnTouchEnded(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
				OnMouse2Up(input, processed)
			elseif input.UserInputType == Enum.UserInputType.MouseButton3 then
				OnMouse3Up(input, processed)
			end
			-- Keyboard
			if input.UserInputType == Enum.UserInputType.Keyboard then
				OnKeyUp(input, processed)
			end
		end)

		WindowUnfocusConn = UserInputService.WindowFocusReleased:connect(onWindowFocusReleased)

		MenuOpenedConn = GuiService.MenuOpened:connect(function()
			this:ResetInputStates()
		end)

		workspaceChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onCurrentCameraChanged)
		if workspace.CurrentCamera then
			onCurrentCameraChanged()
		end

		ShiftLockToggleConn = ShiftLockController.OnShiftLockToggled.Event:connect(function()
			this:UpdateMouseBehavior()
		end)

		this.RotateInput = Vector2.new()

		this.activeGamepad = nil
		local function assignActivateGamepad()
			local connectedGamepads = UserInputService:GetConnectedGamepads()
			if #connectedGamepads > 0 then
				for i = 1, #connectedGamepads do
					if this.activeGamepad == nil then
						this.activeGamepad = connectedGamepads[i]
					elseif connectedGamepads[i].Value < this.activeGamepad.Value then
						this.activeGamepad = connectedGamepads[i]
					end
				end
			end

			if this.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
				this.activeGamepad = Enum.UserInputType.Gamepad1
			end
		end

		GamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
			if this.activeGamepad ~= gamepadEnum then return end
			this.activeGamepad = nil
			assignActivateGamepad()
		end)

		GamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum)
			if this.activeGamepad == nil then
				assignActivateGamepad()
			end
		end)

		self:BindGamepadInputActions()
		
		assignActivateGamepad()

		-- set mouse behavior
		self:UpdateMouseBehavior()
	end
	
	--Process tweens related to tap-to-recenter and double-tap-to-zoom
	--Needs to be called from specific cameras on each update
	function this:ProcessTweens()
		for name, tween in pairs(tweens) do
			local alpha = math.min(1.0, (tick() - tween.start)/tween.duration)
			tween.to = tween.func(tween.from, tween.to, alpha)
			if math.abs(1 - alpha) < 0.0001 then
				tweens[name] = nil
			end
		end
	end

	function this:SetEnabled(newState)
		if newState ~= self.Enabled then
			self.Enabled = newState
			if self.Enabled then
				self:ConnectInputEvents()
				self.cframe = workspace.CurrentCamera.CFrame
			else
				self:DisconnectInputEvents()
			end
		end
	end

	local function OnPlayerAdded(player)
		player.Changed:connect(function(prop)
			if this.Enabled then
				if prop == "CameraMode" or prop == "CameraMaxZoomDistance" or prop == "CameraMinZoomDistance" then
					 this:ZoomCameraFixedBy(0)
				end
			end
		end)
		
		local function OnCharacterAdded(newCharacter)

			local humanoid = findPlayerHumanoid(player)
			local start = tick()
			while tick() - start < 0.3 and (humanoid == nil or humanoid.Torso == nil) do
				wait()
				humanoid = findPlayerHumanoid(player)
			end

			if humanoid and humanoid.Torso and player.Character == newCharacter then
				local newDesiredLook = (humanoid.Torso.CFrame.lookVector - Vector3.new(0, math.sin(math.rad(DEFAULT_CAMERA_ANGLE)), 0)).unit
				local horizontalShift = findAngleBetweenXZVectors(newDesiredLook, this:GetCameraLook())
				local vertShift = math.asin(this:GetCameraLook().y) - math.asin(newDesiredLook.y)
				if not IsFinite(horizontalShift) then
					horizontalShift = 0
				end
				if not IsFinite(vertShift) then
					vertShift = 0
				end
				this.RotateInput = Vector2.new(horizontalShift, vertShift)
				
				-- reset old camera info so follow cam doesn't rotate us
				this.LastCameraTransform = nil
			end
			
			-- Need to wait for camera cframe to update before we zoom in
			-- Not waiting will force camera to original cframe
			wait()
			this:ZoomCamera(this.DefaultZoom)
		end

		player.CharacterAdded:connect(function(character)
			if this.Enabled or SetCameraOnSpawn then
				OnCharacterAdded(character)
				SetCameraOnSpawn = false
			end
		end)
		if player.Character then
			spawn(function() OnCharacterAdded(player.Character) end)
		end
	end
	if PlayersService.LocalPlayer then
		OnPlayerAdded(PlayersService.LocalPlayer)
	end
	PlayersService.ChildAdded:connect(function(child)
		if child and PlayersService.LocalPlayer == child then
			OnPlayerAdded(PlayersService.LocalPlayer)
		end
	end)

	local function OnGameLoaded()
		hasGameLoaded = true
	end

	spawn(function()
		if game:IsLoaded() then
			OnGameLoaded()
		else
			game.Loaded:wait()
			OnGameLoaded()
		end
	end)
	
	local function OnDynamicThumbstickEnabled()
		if UserInputService.TouchEnabled then
			isDynamicThumbstickEnabled = true
		end
	end
	
	local function OnDynamicThumbstickDisabled()
		isDynamicThumbstickEnabled = false
	end
	
	local function OnGameSettingsTouchMovementModeChanged()
		if LocalPlayer.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
			if GameSettings.TouchMovementMode.Name == "DynamicThumbstick" then
				OnDynamicThumbstickEnabled()
			else
				OnDynamicThumbstickDisabled()
			end
		end
	end
	
	local function OnDevTouchMovementModeChanged()
		if LocalPlayer.DevTouchMovementMode.Name == "DynamicThumbstick" then
			OnDynamicThumbstickEnabled()
		else
			OnGameSettingsTouchMovementModeChanged()
		end
	end
	
	if PlayersService.LocalPlayer then
		PlayersService.LocalPlayer.Changed:Connect(function(prop)
			if prop == "DevTouchMovementMode" then
				OnDevTouchMovementModeChanged()
			end
		end)
		OnDevTouchMovementModeChanged()
	end
	
	GameSettings.Changed:Connect(function(prop)
		if prop == "TouchMovementMode" then
			OnGameSettingsTouchMovementModeChanged()
		end
	end)
	OnGameSettingsTouchMovementModeChanged()
	GameSettings:SetCameraYInvertVisible()
	pcall(function() GameSettings:SetGamepadCameraSensitivityVisible() end)
	
	return this
end

return CreateCamera
