null nil false CameraScript ClickToMove y then return x else return y end end end Utility.ViewSizeX = ViewSizeX local function ViewSizeY() local camera = workspace.CurrentCamera local x = camera and camera.ViewportSize.X or 0 local y = camera and camera.ViewportSize.Y or 0 if y == 0 then return 768 else if x > y then return y else return x end end end Utility.ViewSizeY = ViewSizeY local function AspectRatio() return ViewSizeX() / ViewSizeY() end Utility.AspectRatio = AspectRatio local function FindChacterAncestor(part) if part then local humanoid = part:FindFirstChild("Humanoid") if humanoid then return part, humanoid else return FindChacterAncestor(part.Parent) end end end Utility.FindChacterAncestor = FindChacterAncestor local function GetUnitRay(x, y, viewWidth, viewHeight, camera) return camera:ScreenPointToRay(x, y) end Utility.GetUnitRay = GetUnitRay local RayCastIgnoreList = workspace.FindPartOnRayWithIgnoreList local function Raycast(ray, ignoreNonCollidable, ignoreList) local ignoreList = ignoreList or {} local hitPart, hitPos = RayCastIgnoreList(workspace, ray, ignoreList) if hitPart then if ignoreNonCollidable and hitPart.CanCollide == false then table.insert(ignoreList, hitPart) return Raycast(ray, ignoreNonCollidable, ignoreList) end return hitPart, hitPos end return nil, nil end Utility.Raycast = Raycast Utility.Round = function(num, roundToNearest) roundToNearest = roundToNearest or 1 return math.floor((num + roundToNearest/2) / roundToNearest) * roundToNearest end local function AveragePoints(positions) local avgPos = Vector2.new(0,0) if #positions > 0 then for i = 1, #positions do avgPos = avgPos + positions[i] end avgPos = avgPos / #positions end return avgPos end Utility.AveragePoints = AveragePoints local function FuzzyEquals(numa, numb) return numa + 0.1 > numb and numa - 0.1 < numb end Utility.FuzzyEquals = FuzzyEquals local LastInput = 0 UIS.InputBegan:connect(function(inputObject, wasSunk) if not wasSunk then if inputObject.UserInputType == Enum.UserInputType.Touch or inputObject.UserInputType == Enum.UserInputType.MouseButton1 or inputObject.UserInputType == Enum.UserInputType.MouseButton2 then LastInput = tick() end end end) Utility.GetLastInput = function() return LastInput end end local humanoidCache = {} local function findPlayerHumanoid(player) local character = player and player.Character if character then local resultHumanoid = humanoidCache[player] if resultHumanoid and resultHumanoid.Parent == character then return resultHumanoid else humanoidCache[player] = nil -- Bust Old Cache for _, child in pairs(character:GetChildren()) do if child:IsA('Humanoid') then humanoidCache[player] = child return child end end end end end local function CFrameInterpolator(c0, c1) -- (CFrame from, CFrame to) -> (float theta, (float fraction -> CFrame between)) local fromAxisAngle = CFrame.fromAxisAngle local components = CFrame.new().components local inverse = CFrame.new().inverse local v3 = Vector3.new local acos = math.acos local sqrt = math.sqrt local invroot2 = 1 / math.sqrt(2) -- The expanded matrix local _, _, _, xx, yx, zx, xy, yy, zy, xz, yz, zz = components(inverse(c0)*c1) -- The cos-theta of the axisAngles from local cosTheta = (xx + yy + zz - 1)/2 -- Rotation axis local rotationAxis = v3(yz-zy, zx-xz, xy-yx) -- The position to tween through local positionDelta = (c1.p - c0.p) -- Theta local theta; -- Catch degenerate cases if cosTheta >= 0.999 then -- Case same rotation, just return an interpolator over the positions return 0, function(t) return c0 + positionDelta*t end elseif cosTheta <= -0.999 then -- Case exactly opposite rotations, disambiguate theta = math.pi xx = (xx + 1) / 2 yy = (yy + 1) / 2 zz = (zz + 1) / 2 if xx > yy and xx > zz then if xx < 0.001 then rotationAxis = v3(0, invroot2, invroot2) else local x = sqrt(xx) xy = (xy + yx) / 4 xz = (xz + zx) / 4 rotationAxis = v3(x, xy/x, xz/x) end elseif yy > zz then if yy < 0.001 then rotationAxis = v3(invroot2, 0, invroot2) else local y = sqrt(yy) xy = (xy + yx) / 4 yz = (yz + zy) / 4 rotationAxis = v3(xy/y, y, yz/y) end else if zz < 0.001 then rotationAxis = v3(invroot2, invroot2, 0) else local z = sqrt(zz) xz = (xz + zx) / 4 yz = (yz + zy) / 4 rotationAxis = v3(xz/z, yz/z, z) end end else -- Normal case, get theta from cosTheta theta = acos(cosTheta) end -- Return the interpolator return theta, function(t) return c0*fromAxisAngle(rotationAxis, theta*t) + positionDelta*t end end --------------------------------------------------------- local Signal = Utility.Signal local Create = Utility.Create --------------------------CHARACTER CONTROL------------------------------- local function CreateController() local this = {} this.TorsoLookPoint = nil function this:SetTorsoLookPoint(point) local humanoid = findPlayerHumanoid(Player) if humanoid then humanoid.AutoRotate = false end this.TorsoLookPoint = point self:UpdateTorso() delay(2, function() -- this isnt technically correct for detecting if this is the last issue to the setTorso function if this.TorsoLookPoint == point then this.TorsoLookPoint = nil if humanoid then humanoid.AutoRotate = true end end end) end function this:UpdateTorso(point) if this.TorsoLookPoint then point = this.TorsoLookPoint else return end local humanoid = findPlayerHumanoid(Player) local torso = humanoid and humanoid.Torso if torso then local lookVec = (point - torso.CFrame.p).unit local squashedLookVec = Vector3.new(lookVec.X, 0, lookVec.Z).unit torso.CFrame = CFrame.new(torso.CFrame.p, torso.CFrame.p + squashedLookVec) end end return this end local CharacterControl = CreateController() ----------------------------------------------------------------------- --------------------------PC AUTO JUMPER------------------------------- local function GetCharacter() return Player and Player.Character end local function GetTorso() local humanoid = findPlayerHumanoid(Player) return humanoid and humanoid.Torso end local function IsPartAHumanoid(part) return part and part.Parent and (part.Parent:FindFirstChild('Humanoid') ~= nil) end local function doAutoJump() local character = GetCharacter() if (character == nil) then return; end local humanoid = findPlayerHumanoid(Player) if (humanoid == nil) then return; end local rayLength = 1.5; local jumpHeight = 7.0; local torso = GetTorso() if (torso == nil) then return; end local torsoCFrame = torso.CFrame; local torsoLookVector = torsoCFrame.lookVector; local torsoPos = torsoCFrame.p; local torsoRay = Ray.new(torsoPos + Vector3.new(0, -torso.Size.Y/2, 0), torsoLookVector * rayLength); local jumpRay = Ray.new(torsoPos + Vector3.new(0, jumpHeight - torso.Size.Y, 0), torsoLookVector * rayLength); local hitPart, _ = RayCastIgnoreList(workspace, torsoRay, {character}, false) local jumpHitPart, _ = RayCastIgnoreList(workspace, jumpRay, {character}, false) if (hitPart and jumpHitPart == nil and hitPart.CanCollide == true) then -- NOTE: this follow line is not in the C++ impl, but an improvement in Click to Move if not IsPartAHumanoid(hitPart) then humanoid.Jump = true; end end end local NO_JUMP_STATES = { [Enum.HumanoidStateType.FallingDown] = false; [Enum.HumanoidStateType.Flying] = false; [Enum.HumanoidStateType.Freefall] = false; [Enum.HumanoidStateType.GettingUp] = false; [Enum.HumanoidStateType.Ragdoll] = false; [Enum.HumanoidStateType.Running] = false; [Enum.HumanoidStateType.Seated] = false; [Enum.HumanoidStateType.Swimming] = false; -- Special case to detect if we are on a ladder [Enum.HumanoidStateType.Climbing] = false; } local function enableAutoJump() local humanoid = findPlayerHumanoid(Player) local currentState = humanoid and humanoid:GetState() if currentState then return NO_JUMP_STATES[currentState] == nil end return false end local function getAutoJump() return true end local function vec3IsZero(vec3) return vec3.magnitude < 0.05 end -- NOTE: This function is radically different from the engine's implementation local function calcDesiredWalkVelocity() -- TEMP return Vector3.new(1,1,1) end local function preStepSimulatorSide(dt) if getAutoJump() and enableAutoJump() then local desiredWalkVelocity = calcDesiredWalkVelocity(); if (not vec3IsZero(desiredWalkVelocity)) then doAutoJump(); end end end local function AutoJumper() local this = {} local running = false local runRoutine = nil function this:Run() running = true local thisRoutine = nil thisRoutine = coroutine.create(function() while running and thisRoutine == runRoutine do this:Step() wait() end end) runRoutine = thisRoutine coroutine.resume(thisRoutine) end function this:Stop() running = false end function this:Step() preStepSimulatorSide() end return this end ----------------------------------------------------------------------------- -----------------------------------PATHER-------------------------------------- local function CreateDestinationIndicator(pos) local destinationGlobe = Create'Part' { Name = 'PathGlobe'; TopSurface = 'Smooth'; BottomSurface = 'Smooth'; Shape = 'Ball'; CanCollide = false; Size = Vector3.new(2,2,2); BrickColor = BrickColor.new('Institutional white'); Transparency = 0; Anchored = true; CFrame = CFrame.new(pos); } return destinationGlobe end local function Pather(character, point) local this = {} this.Cancelled = false this.Started = false this.Finished = Signal.Create() this.PathFailed = Signal.Create() this.PathStarted = Signal.Create() this.PathComputed = false function this:YieldUntilPointReached(character, point, timeout) timeout = timeout or 10000000 local humanoid = findPlayerHumanoid(Player) local torso = humanoid and humanoid.Torso local start = tick() local lastMoveTo = start while torso and tick() - start < timeout and this.Cancelled == false do local diffVector = (point - torso.CFrame.p) local xzMagnitude = (diffVector * Vector3.new(1,0,1)).magnitude if xzMagnitude < 6 then -- Jump if the path is telling is to go upwards if diffVector.Y >= 2.2 then humanoid.Jump = true end end -- The hard-coded number 2 here is from the engine's MoveTo implementation if xzMagnitude < 2 then return true end -- Keep on issuing the move command because it will automatically quit every so often. if tick() - lastMoveTo > 1.5 then humanoid:MoveTo(point) lastMoveTo = tick() end CharacterControl:UpdateTorso(point) wait() end return false end function this:Cancel() this.Cancelled = true local humanoid = findPlayerHumanoid(Player) local torso = humanoid and humanoid.Torso if humanoid and torso then humanoid:MoveTo(torso.CFrame.p) end end function this:CheckOcclusion(point1, point2, character, torsoRadius) local humanoid = findPlayerHumanoid(Player) local torso = humanoid and humanoid.Torso if torsoRadius == nil then torsoRadius = torso and Vector3.new(torso.Size.X/2,0,torso.Size.Z/2) or Vector3.new(1,0,1) end local diffVector = point2 - point1 local directionVector = diffVector.unit local rightVector = Vector3.new(0,1,0):Cross(directionVector) * torsoRadius local rightPart, _ = Utility.Raycast(Ray.new(point1 + rightVector, diffVector + rightVector), true, {character}) local hitPart, _ = Utility.Raycast(Ray.new(point1, diffVector), true, {character}) local leftPart, _ = Utility.Raycast(Ray.new(point1 - rightVector, diffVector - rightVector), true, {character}) if rightPart or hitPart or leftPart then return false end -- Make sure we have somewhere to stand on local midPt = (point2 + point1) / 2 local studsBetweenSamples = 2 for i = 1, math.floor(diffVector.magnitude/studsBetweenSamples) do local downPart, _ = Utility.Raycast(Ray.new(point1 + directionVector * i * studsBetweenSamples, Vector3.new(0,-7,0)), true, {character}) if not downPart then return false end end return true end function this:SmoothPoints(pathToSmooth) local result = {} local humanoid = findPlayerHumanoid(Player) local torso = humanoid and humanoid.Torso for i = 1, #pathToSmooth do table.insert(result, pathToSmooth[i]) end -- Backwards for safe-deletion for i = #result - 1, 1, -1 do if i + 1 <= #result then local nextPoint = result[i+1] local thisPoint = result[i] local lastPoint = result[i-1] if lastPoint == nil then lastPoint = torso and Vector3.new(torso.CFrame.p.X, thisPoint.Y, torso.CFrame.p.Z) end if lastPoint and Utility.FuzzyEquals(thisPoint.Y, lastPoint.Y) and Utility.FuzzyEquals(thisPoint.Y, nextPoint.Y) then if this:CheckOcclusion(lastPoint, nextPoint, character) then table.remove(result, i) -- Move i back one to recursively-smooth i = i + 1 end end end end return result end function this:CheckNeighboringCells(character) local pathablePoints = {} local humanoid = findPlayerHumanoid(Player) local torso = character and humanoid and humanoid.Torso if torso then local torsoCFrame = torso.CFrame local torsoPos = torsoCFrame.p -- Minus and plus 2 is so we can get it into the cell-corner space and then translate it back into cell-center space local roundedPos = Vector3.new(Utility.Round(torsoPos.X-2,4)+2, Utility.Round(torsoPos.Y-2,4)+2, Utility.Round(torsoPos.Z-2,4)+2) local neighboringCells = {} for x = -4, 4, 8 do for z = -4, 4, 8 do table.insert(neighboringCells, roundedPos + Vector3.new(x,0,z)) end end for _, testPoint in pairs(neighboringCells) do local pathable = this:CheckOcclusion(roundedPos, testPoint, character, Vector3.new(0,0,0)) if pathable then table.insert(pathablePoints, testPoint) end end end return pathablePoints end function this:ComputeDirectPath() local humanoid = findPlayerHumanoid(Player) local torso = humanoid and humanoid.Torso if torso then local startPt = torso.CFrame.p local finishPt = point if (finishPt - startPt).magnitude < 150 then -- move back the destination by 2 studs or otherwise the pather will collide with the object we are trying to reach finishPt = finishPt - (finishPt - startPt).unit * 2 if this:CheckOcclusion(startPt, finishPt, character, Vector3.new(0,0,0)) then local pathResult = {} pathResult.Status = Enum.PathStatus.Success function pathResult:GetPointCoordinates() return {finishPt} end return pathResult end end end end local function AllAxisInThreshhold(targetPt, otherPt, threshold) return math.abs(targetPt.X - otherPt.X) <= threshold and math.abs(targetPt.Y - otherPt.Y) <= threshold and math.abs(targetPt.Z - otherPt.Z) <= threshold end function this:ComputePath() local smoothed = false local humanoid = findPlayerHumanoid(Player) local torso = humanoid and humanoid.Torso if torso then if this.PathComputed then return end this.PathComputed = true -- Will yield the script since it is an Async script (start, finish, maxDistance) -- Try to use the smooth function, but it may not exist yet :( local success = pcall(function() -- 3 is height from torso cframe to ground this.pathResult = PathfindingService:ComputeSmoothPathAsync(torso.CFrame.p - Vector3.new(0,3,0), point, 400) smoothed = true end) if not success then -- 3 is height from torso cframe to ground this.pathResult = PathfindingService:ComputeRawPathAsync(torso.CFrame.p - Vector3.new(0,3,0), point, 400) smoothed = false end this.pointList = this.pathResult and this.pathResult:GetPointCoordinates() local pathFound = false if this.pathResult.Status == Enum.PathStatus.FailFinishNotEmpty then -- Lets try again with a slightly set back start point; it is ok to do this again so the FailFinishNotEmpty uses little computation local diffVector = point - workspace.CurrentCamera.CoordinateFrame.p if diffVector.magnitude > 2 then local setBackPoint = point - (diffVector).unit * 2.1 local success = pcall(function() this.pathResult = PathfindingService:ComputeSmoothPathAsync(torso.CFrame.p, setBackPoint, 400) smoothed = true end) if not success then this.pathResult = PathfindingService:ComputeRawPathAsync(torso.CFrame.p, setBackPoint, 400) smoothed = false end this.pointList = this.pathResult and this.pathResult:GetPointCoordinates() pathFound = true end end if this.pathResult.Status == Enum.PathStatus.ClosestNoPath and #this.pointList >= 1 and pathFound == false then local otherPt = this.pointList[#this.pointList] if AllAxisInThreshhold(point, otherPt, 4) and (torso.CFrame.p - point).magnitude > (otherPt - point).magnitude then local pathResult = {} pathResult.Status = Enum.PathStatus.Success function pathResult:GetPointCoordinates() return {this.pointList} end this.pathResult = pathResult pathFound = true end end if (this.pathResult.Status == Enum.PathStatus.FailStartNotEmpty or this.pathResult.Status == Enum.PathStatus.ClosestNoPath) and pathFound == false then local pathablePoints = this:CheckNeighboringCells(character) for _, otherStart in pairs(pathablePoints) do local pathResult; local success = pcall(function() pathResult = PathfindingService:ComputeSmoothPathAsync(otherStart, point, 400) smoothed = true end) if not success then pathResult = PathfindingService:ComputeRawPathAsync(otherStart, point, 400) smoothed = false end if pathResult and pathResult.Status == Enum.PathStatus.Success then this.pathResult = pathResult if this.pathResult then this.pointList = this.pathResult:GetPointCoordinates() table.insert(this.pointList, 1, otherStart) end break end end end if DirectPathEnabled then if this.pathResult.Status ~= Enum.PathStatus.Success then local directPathResult = this:ComputeDirectPath() if directPathResult and directPathResult.Status == Enum.PathStatus.Success then this.pathResult = directPathResult this.pointList = directPathResult:GetPointCoordinates() end end end end return smoothed end function this:IsValidPath() this:ComputePath() local pathStatus = this.pathResult.Status return pathStatus == Enum.PathStatus.Success end function this:GetPathStatus() this:ComputePath() return this.pathResult.Status end function this:Start() if CurrentSeatPart then return end spawn(function() local humanoid = findPlayerHumanoid(Player) --humanoid.AutoRotate = false local torso = humanoid and humanoid.Torso if torso then if this.Started then return end this.Started = true -- Will yield the script since it is an Async function script (start, finish, maxDistance) local smoothed = this:ComputePath() if this:IsValidPath() then this.PathStarted:fire() -- smooth out zig-zaggy paths local smoothPath = smoothed and this.pointList or this:SmoothPoints(this.pointList) for i, point in pairs(smoothPath) do if humanoid then if this.Cancelled then return end local wayPoint = nil if SHOW_PATH then wayPoint = CreateDestinationIndicator(point) wayPoint.BrickColor = BrickColor.new("New Yeller") wayPoint.Parent = workspace print(wayPoint.CFrame.p) end humanoid:MoveTo(point) local distance = ((torso.CFrame.p - point) * Vector3.new(1,0,1)).magnitude local approxTime = 10 if math.abs(humanoid.WalkSpeed) > 0 then approxTime = distance / math.abs(humanoid.WalkSpeed) end local yielding = true if i == 1 then --local rotatedCFrame = CameraModule:LookAtPreserveHeight(point) if CameraModule then local rotatedCFrame = CameraModule:LookAtPreserveHeight(smoothPath[#smoothPath]) local finishedSignal, duration = CameraModule:TweenCameraLook(rotatedCFrame) end --CharacterControl:SetTorsoLookPoint(point) end ---[[ if (humanoid.Torso.CFrame.p - point).magnitude > 9 then spawn(function() while yielding and this.Cancelled == false do if CameraModule then local look = CameraModule:GetCameraLook() local squashedLook = (look * Vector3.new(1,0,1)).unit local direction = ((point - CameraModule.cframe.p) * Vector3.new(1,0,1)).unit local theta = math.deg(math.acos(squashedLook:Dot(direction))) if tick() - Utility.GetLastInput() > 2 and theta > (workspace.CurrentCamera.FieldOfView / 2) then local rotatedCFrame = CameraModule:LookAtPreserveHeight(point) local finishedSignal, duration = CameraModule:TweenCameraLook(rotatedCFrame) --return end end wait(0.1) end end) end --]] local didReach = this:YieldUntilPointReached(character, point, approxTime * 3 + 1) yielding = false if SHOW_PATH then wayPoint:Destroy() end if not didReach then this.PathFailed:fire() return end end end this.Finished:fire() return end end this.PathFailed:fire() end) end return this end ------------------------------------------------------------------------- local function FlashRed(object) local origColor = object.BrickColor local redColor = BrickColor.new("Really red") local start = tick() local duration = 4 spawn(function() while object and tick() - start < duration do object.BrickColor = origColor wait(0.13) if object then object.BrickColor = redColor end wait(0.13) end end) end --local joystickWidth = 250 --local joystickHeight = 250 local function IsInBottomLeft(pt) local joystickHeight = math.min(Utility.ViewSizeY() * 0.33, 250) local joystickWidth = joystickHeight return pt.X <= joystickWidth and pt.Y > Utility.ViewSizeY() - joystickHeight end local function IsInBottomRight(pt) local joystickHeight = math.min(Utility.ViewSizeY() * 0.33, 250) local joystickWidth = joystickHeight return pt.X >= Utility.ViewSizeX() - joystickWidth and pt.Y > Utility.ViewSizeY() - joystickHeight end local function CheckAlive(character) local humanoid = findPlayerHumanoid(Player) return humanoid ~= nil and humanoid.Health > 0 end local function GetEquippedTool(character) if character ~= nil then for _, child in pairs(character:GetChildren()) do if child:IsA('Tool') then return child end end end end local function ExploreWithRayCast(currentPoint, originDirection) local TestDistance = 40 local TestVectors = {} do local forwardVector = originDirection; for i = 0, 15 do table.insert(TestVectors, CFrame.Angles(0, math.pi / 8 * i, 0) * forwardVector) end end local testResults = {} -- Heuristic should be something along the lines of distance and closeness to the traveling direction local function ExploreHeuristic() for _, testData in pairs(testResults) do local walkDirection = -1 * originDirection local directionCoeff = (walkDirection:Dot(testData['Vector']) + 1) / 2 local distanceCoeff = testData['Distance'] / TestDistance testData["Value"] = directionCoeff * distanceCoeff end end for i, vec in pairs(TestVectors) do local hitPart, hitPos = Utility.Raycast(Ray.new(currentPoint, vec * TestDistance), true, {Player.Character}) if hitPos then table.insert(testResults, {Vector = vec; Distance = (hitPos - currentPoint).magnitude}) else table.insert(testResults, {Vector = vec; Distance = TestDistance}) end end ExploreHeuristic() table.sort(testResults, function(a,b) return a["Value"] > b["Value"] end) return testResults end local TapId = 1 local ExistingPather = nil local ExistingIndicator = nil local PathCompleteListener = nil local PathFailedListener = nil local function CleanupPath() DrivingTo = nil if ExistingPather then ExistingPather:Cancel() end if PathCompleteListener then PathCompleteListener:disconnect() PathCompleteListener = nil end if PathFailedListener then PathFailedListener:disconnect() PathFailedListener = nil end if ExistingIndicator then DebrisService:AddItem(ExistingIndicator, 0) ExistingIndicator = nil end end local function getExtentsSize(Parts) local maxX = Parts[1].Position.X local maxY = Parts[1].Position.Y local maxZ = Parts[1].Position.Z local minX = Parts[1].Position.X local minY = Parts[1].Position.Y local minZ = Parts[1].Position.Z for i = 2, #Parts do maxX = math.max(maxX, Parts[i].Position.X) maxY = math.max(maxY, Parts[i].Position.Y) maxZ = math.max(maxZ, Parts[i].Position.Z) minX = math.min(minX, Parts[i].Position.X) minY = math.min(minY, Parts[i].Position.Y) minZ = math.min(minZ, Parts[i].Position.Z) end return Region3.new(Vector3.new(minX, minY, minZ), Vector3.new(maxX, maxY, maxZ)) end local function inExtents(Extents, Position) if Position.X < (Extents.CFrame.p.X - Extents.Size.X/2) or Position.X > (Extents.CFrame.p.X + Extents.Size.X/2) then return false end if Position.Z < (Extents.CFrame.p.Z - Extents.Size.Z/2) or Position.Z > (Extents.CFrame.p.Z + Extents.Size.Z/2) then return false end --ignoring Y for now return true end local AutoJumperInstance = nil local ShootCount = 0 local FailCount = 0 local function OnTap(tapPositions, goToPoint) -- Good to remember if this is the latest tap event TapId = TapId + 1 local thisTapId = TapId local camera = workspace.CurrentCamera local character = Player.Character if not CheckAlive(character) then return end -- This is a path tap position if #tapPositions == 1 or goToPoint then if camera then local unitRay = Utility.GetUnitRay(tapPositions[1].x, tapPositions[1].y, MyMouse.ViewSizeX, MyMouse.ViewSizeY, camera) local ray = Ray.new(unitRay.Origin, unitRay.Direction*400) local hitPart, hitPt = Utility.Raycast(ray, true, {character}) local hitChar, hitHumanoid = Utility.FindChacterAncestor(hitPart) local torso = character and character:FindFirstChild("Humanoid") and character:FindFirstChild("Humanoid").Torso local startPos = torso.CFrame.p if goToPoint then hitPt = goToPoint hitChar = nil end if hitChar and hitHumanoid and hitHumanoid.Torso and (hitHumanoid.Torso.CFrame.p - torso.CFrame.p).magnitude < 7 then CleanupPath() local myHumanoid = findPlayerHumanoid(Player) if myHumanoid then myHumanoid:MoveTo(hitPt) end ShootCount = ShootCount + 1 local thisShoot = ShootCount -- Do shooot local currentWeapon = GetEquippedTool(character) if currentWeapon then currentWeapon:Activate() LastFired = tick() end elseif hitPt and character and not CurrentSeatPart then local thisPather = Pather(character, hitPt) if thisPather:IsValidPath() then FailCount = 0 -- TODO: Remove when bug in engine is fixed Player:Move(Vector3.new(1, 0, 0)) Player:Move(Vector3.new(0, 0, 0)) thisPather:Start() if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(false) end CleanupPath() local destinationGlobe = CreateDestinationIndicator(hitPt) destinationGlobe.Parent = camera ExistingPather = thisPather ExistingIndicator = destinationGlobe if AutoJumperInstance then AutoJumperInstance:Run() end PathCompleteListener = thisPather.Finished:connect(function() if AutoJumperInstance then AutoJumperInstance:Stop() end if destinationGlobe then if ExistingIndicator == destinationGlobe then ExistingIndicator = nil end DebrisService:AddItem(destinationGlobe, 0) destinationGlobe = nil end if hitChar then local humanoid = findPlayerHumanoid(Player) ShootCount = ShootCount + 1 local thisShoot = ShootCount -- Do shoot local currentWeapon = GetEquippedTool(character) if currentWeapon then currentWeapon:Activate() LastFired = tick() end if humanoid then humanoid:MoveTo(hitPt) end end local finishPos = torso and torso.CFrame.p --hitPt if finishPos and startPos and tick() - Utility.GetLastInput() > 2 then local exploreResults = ExploreWithRayCast(finishPos, ((startPos - finishPos) * Vector3.new(1,0,1)).unit) -- Check for Nans etc.. if exploreResults[1] and exploreResults[1]["Vector"] and exploreResults[1]["Vector"].magnitude >= 0.5 and exploreResults[1]["Distance"] > 3 then if CameraModule then local rotatedCFrame = CameraModule:LookAtPreserveHeight(finishPos + exploreResults[1]["Vector"] * exploreResults[1]["Distance"]) local finishedSignal, duration = CameraModule:TweenCameraLook(rotatedCFrame) end end end end) PathFailedListener = thisPather.PathFailed:connect(function() if AutoJumperInstance then AutoJumperInstance:Stop() end if destinationGlobe then FlashRed(destinationGlobe) DebrisService:AddItem(destinationGlobe, 3) end end) else if hitPt then -- Feedback here for when we don't have a good path local failedGlobe = CreateDestinationIndicator(hitPt) FlashRed(failedGlobe) DebrisService:AddItem(failedGlobe, 1) failedGlobe.Parent = camera if ExistingIndicator == nil then FailCount = FailCount + 1 if FailCount >= 3 then if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(true) end CleanupPath() end end end end elseif hitPt and character and CurrentSeatPart then local destinationGlobe = CreateDestinationIndicator(hitPt) destinationGlobe.Parent = camera ExistingIndicator = destinationGlobe DrivingTo = hitPt local ConnectedParts = CurrentSeatPart:GetConnectedParts(true) while wait() do if CurrentSeatPart and ExistingIndicator == destinationGlobe then local ExtentsSize = getExtentsSize(ConnectedParts) if inExtents(ExtentsSize, destinationGlobe.Position) then DebrisService:AddItem(destinationGlobe, 0) destinationGlobe = nil DrivingTo = nil break end else DebrisService:AddItem(destinationGlobe, 0) if CurrentSeatPart == nil and destinationGlobe == ExistingIndicator then DrivingTo = nil OnTap(tapPositions, hitPt) end destinationGlobe = nil break end end else -- no hit pt end end elseif #tapPositions >= 2 then if camera then ShootCount = ShootCount + 1 local thisShoot = ShootCount -- Do shoot local avgPoint = Utility.AveragePoints(tapPositions) local unitRay = Utility.GetUnitRay(avgPoint.x, avgPoint.y, MyMouse.ViewSizeX, MyMouse.ViewSizeY, camera) local currentWeapon = GetEquippedTool(character) if currentWeapon then currentWeapon:Activate() LastFired = tick() end end end end local function CreateClickToMoveModule() local this = {} local LastStateChange = 0 local LastState = Enum.HumanoidStateType.Running local FingerTouches = {} local NumUnsunkTouches = 0 -- PC simulation local mouse1Down = tick() local mouse1DownPos = Vector2.new() local mouse2Down = tick() local mouse2DownPos = Vector2.new() local mouse2Up = tick() local movementKeys = { [Enum.KeyCode.W] = true; [Enum.KeyCode.A] = true; [Enum.KeyCode.S] = true; [Enum.KeyCode.D] = true; [Enum.KeyCode.Up] = true; [Enum.KeyCode.Down] = true; } local TapConn = nil local InputBeganConn = nil local InputChangedConn = nil local InputEndedConn = nil local HumanoidDiedConn = nil local CharacterChildAddedConn = nil local OnCharacterAddedConn = nil local CharacterChildRemovedConn = nil local RenderSteppedConn = nil local HumanoidSeatedConn = nil local function disconnectEvent(event) if event then event:disconnect() end end local function DisconnectEvents() disconnectEvent(TapConn) disconnectEvent(InputBeganConn) disconnectEvent(InputChangedConn) disconnectEvent(InputEndedConn) disconnectEvent(HumanoidDiedConn) disconnectEvent(CharacterChildAddedConn) disconnectEvent(OnCharacterAddedConn) disconnectEvent(RenderSteppedConn) disconnectEvent(CharacterChildRemovedConn) pcall(function() RunService:UnbindFromRenderStep("ClickToMoveRenderUpdate") end) disconnectEvent(HumanoidSeatedConn) end local function IsFinite(num) return num == num and num ~= 1/0 and num ~= -1/0 end local function findAngleBetweenXZVectors(vec2, vec1) return math.atan2(vec1.X*vec2.Z-vec1.Z*vec2.X, vec1.X*vec2.X + vec1.Z*vec2.Z) end -- Setup the camera CameraModule = ClassicCameraModule() do -- Extend The Camera Module Class function CameraModule:LookAtPreserveHeight(newLookAtPt) local camera = workspace.CurrentCamera local focus = camera.Focus.p local cameraCFrame = CameraModule.cframe local mag = Vector3.new(cameraCFrame.lookVector.x, 0, cameraCFrame.lookVector.z).magnitude local newLook = (Vector3.new(newLookAtPt.x, focus.y, newLookAtPt.z) - focus).unit * mag local flippedLook = newLook + Vector3.new(0, cameraCFrame.lookVector.y, 0) local distance = (focus - cameraCFrame.p).magnitude local newCamPos = focus - flippedLook.unit * distance return CFrame.new(newCamPos, newCamPos + flippedLook) end function CameraModule:TweenCameraLook(desiredCFrame, speed) local e = 2.718281828459 local function SCurve(t) return 1/(1 + e^(-t*1.5)) end local function easeOutSine(t, b, c, d) if t >= d then return b + c end return c * math.sin(t/d * (math.pi/2)) + b; end local theta, interper = CFrameInterpolator(CFrame.new(Vector3.new(), self:GetCameraLook()), desiredCFrame - desiredCFrame.p) theta = Utility.Clamp(0, math.pi, theta) local duration = 0.65 * SCurve(theta - math.pi/4) + 0.15 if speed then duration = theta / speed end local start = tick() local finish = start + duration self.UpdateTweenFunction = function() local currTime = tick() - start local alpha = Utility.Clamp(0, 1, easeOutSine(currTime, 0, 1, duration)) local newCFrame = interper(alpha) local y = findAngleBetweenXZVectors(newCFrame.lookVector, self:GetCameraLook()) if IsFinite(y) and math.abs(y) > 0.0001 then self.RotateInput = self.RotateInput + Vector2.new(y, 0) end return (currTime >= finish or alpha >= 1) end end end --- Done Extending local function OnTouchBegan(input, processed) if FingerTouches[input] == nil and not processed then NumUnsunkTouches = NumUnsunkTouches + 1 end FingerTouches[input] = processed end local function OnTouchChanged(input, processed) if FingerTouches[input] == nil then FingerTouches[input] = processed if not processed then NumUnsunkTouches = NumUnsunkTouches + 1 end end end local function OnTouchEnded(input, processed) --print("Touch tap fake:" , processed) --if not processed then -- OnTap({input.Position}) --end if FingerTouches[input] ~= nil and FingerTouches[input] == false then NumUnsunkTouches = NumUnsunkTouches - 1 end FingerTouches[input] = nil end local function OnCharacterAdded(character) DisconnectEvents() InputBeganConn = UIS.InputBegan:connect(function(input, processed) if input.UserInputType == Enum.UserInputType.Touch then OnTouchBegan(input, processed) -- Give back controls when they tap both sticks local wasInBottomLeft = IsInBottomLeft(input.Position) local wasInBottomRight = IsInBottomRight(input.Position) if wasInBottomRight or wasInBottomLeft then for otherInput, _ in pairs(FingerTouches) do if otherInput ~= input then local otherInputInLeft = IsInBottomLeft(otherInput.Position) local otherInputInRight = IsInBottomRight(otherInput.Position) if otherInput.UserInputState ~= Enum.UserInputState.End and ((wasInBottomLeft and otherInputInRight) or (wasInBottomRight and otherInputInLeft)) then if BindableEvent_OnFailStateChanged then BindableEvent_OnFailStateChanged:Fire(true) end return end end end end end -- Cancel path when you use the keyboard controls. if processed == false and input.UserInputType == Enum.UserInputType.Keyboard and movementKeys[input.KeyCode] then CleanupPath() end if input.UserInputType == Enum.UserInputType.MouseButton1 then mouse1Down = tick() mouse1DownPos = input.Position end if input.UserInputType == Enum.UserInputType.MouseButton2 then mouse2Down = tick() mouse2DownPos = input.Position end end) InputChangedConn = UIS.InputChanged:connect(function(input, processed) if input.UserInputType == Enum.UserInputType.Touch then OnTouchChanged(input, processed) end end) InputEndedConn = UIS.InputEnded:connect(function(input, processed) if input.UserInputType == Enum.UserInputType.Touch then OnTouchEnded(input, processed) end if input.UserInputType == Enum.UserInputType.MouseButton2 then mouse2Up = tick() local currPos = input.Position if mouse2Up - mouse2Down < 0.25 and (currPos - mouse2DownPos).magnitude < 5 then local positions = {currPos} OnTap(positions) end end end) TapConn = UIS.TouchTap:connect(function(touchPositions, processed) if not processed then OnTap(touchPositions) end end) if not UIS.TouchEnabled then -- PC if AutoJumperInstance then AutoJumperInstance:Stop() AutoJumperInstance = nil end AutoJumperInstance = AutoJumper() end local function getThrottleAndSteer(object, point) local lookVector = (point - object.Position) lookVector = Vector3.new(lookVector.X, 0, lookVector.Z).unit local objectVector = Vector3.new(object.CFrame.lookVector.X, 0, object.CFrame.lookVector.Z).unit local dirVector = lookVector - objectVector local mag = dirVector.magnitude local degrees = math.deg(math.acos(lookVector:Dot(objectVector))) local side = (object.CFrame:pointToObjectSpace(point).X > 0) local throttle = 0 if mag < 0.25 then throttle = 1 end if mag > 1.8 then throttle = -1 end local distance = CurrentSeatPart.Position - DrivingTo local velocity = CurrentSeatPart.Velocity if velocity.magnitude*1.5 > distance.magnitude then if velocity.magnitude*0.5 > distance.magnitude then throttle = -throttle else throttle = 0 end end local steer = 0 if degrees > 5 and degrees < 175 then if side then steer = 1 else steer = -1 end end local rotatingAt = math.deg(CurrentSeatPart.RotVelocity.magnitude) local degreesAway = math.max(math.min(degrees, 180 - degrees), 10) if (CurrentSeatPart.RotVelocity.X < 0)== (steer < 0) then if rotatingAt*1.5 > degreesAway then if rotatingAt*0.5 > degreesAway then steer = -steer else steer = 0 end end end return throttle, steer end local function Update() if CameraModule then if CameraModule.UserPanningTheCamera then CameraModule.UpdateTweenFunction = nil else if CameraModule.UpdateTweenFunction then local done = CameraModule.UpdateTweenFunction() if done then CameraModule.UpdateTweenFunction = nil end end end CameraModule:Update() end if CurrentSeatPart then if DrivingTo then local throttle, steer = getThrottleAndSteer(CurrentSeatPart, DrivingTo) CurrentSeatPart.Throttle = throttle CurrentSeatPart.Steer = steer end end end local success = pcall(function() RunService:BindToRenderStep("ClickToMoveRenderUpdate",Enum.RenderPriority.Camera.Value - 1,Update) end) if not success then if RenderSteppedConn then RenderSteppedConn:disconnect() end RenderSteppedConn = RunService.RenderStepped:connect(Update) end local WasAutoJumper = false local WasAutoJumpMobile = false local function onSeated(child, active, currentSeatPart) if active then if BindableEvent_EnableTouchJump then BindableEvent_EnableTouchJump:Fire(true) end if currentSeatPart.ClassName == "VehicleSeat" then CurrentSeatPart = currentSeatPart if AutoJumperInstance then AutoJumperInstance:Stop() AutoJumperInstance = nil WasAutoJumper = true else WasAutoJumper = false end if child.AutoJumpEnabled then WasAutoJumpMobile = true child.AutoJumpEnabled = false end end else CurrentSeatPart = nil if BindableEvent_EnableTouchJump then BindableEvent_EnableTouchJump:Fire(false) end if WasAutoJumper then AutoJumperInstance = AutoJumper() WasAutoJumper = false end if WasAutoJumpMobile then child.AutoJumpEnabled = true WasAutoJumpMobile = false end end end local function OnCharacterChildAdded(child) if UIS.TouchEnabled then if child:IsA('Tool') then child.ManualActivationOnly = true end end if child:IsA('Humanoid') then disconnectEvent(HumanoidDiedConn) HumanoidDiedConn = child.Died:connect(function() DebrisService:AddItem(ExistingIndicator, 1) if AutoJumperInstance then AutoJumperInstance:Stop() AutoJumperInstance = nil end end) local WasAutoJumper = false local WasAutoJumpMobile = false HumanoidSeatedConn = child.Seated:connect(function(active, seat) onSeated(child, active, seat) end) if child.SeatPart then onSeated(child, true, child.SeatPart) end end end CharacterChildAddedConn = character.ChildAdded:connect(function(child) OnCharacterChildAdded(child) end) CharacterChildRemovedConn = character.ChildRemoved:connect(function(child) if UIS.TouchEnabled then if child:IsA('Tool') then child.ManualActivationOnly = false end end end) for _, child in pairs(character:GetChildren()) do OnCharacterChildAdded(child) end end local Running = false function this:Stop() if Running then DisconnectEvents() CleanupPath() if AutoJumperInstance then AutoJumperInstance:Stop() AutoJumperInstance = nil end if CameraModule then CameraModule.UpdateTweenFunction = nil CameraModule:SetEnabled(false) end -- Restore tool activation on shutdown if UIS.TouchEnabled then local character = Player.Character if character then for _, child in pairs(character:GetChildren()) do if child:IsA('Tool') then child.ManualActivationOnly = false end end end end DrivingTo = nil Running = false end end function this:Start() if not Running then if Player.Character then -- retro-listen OnCharacterAdded(Player.Character) end OnCharacterAddedConn = Player.CharacterAdded:connect(OnCharacterAdded) if CameraModule then CameraModule:SetEnabled(true) end Running = true end end return this end return CreateClickToMoveModule ]]> Invisicam originalFade then hit.LocalTransparencyModifier = math.max(originalFade, currentFade - FADE_RATE) else SavedHits[hit] = nil end end end end function Invisicam:SetMode(newMode) AssertTypes(newMode, 'number') for modeName, modeNum in pairs(MODE) do if modeNum == newMode then Mode = newMode return end end error("Invalid mode number") end function Invisicam:SetCustomBehavior(func) AssertTypes(func, 'function') Behaviors[MODE.CUSTOM] = func end -- Want to turn off Invisicam? Be sure to call this after. function Invisicam:Cleanup() for hit, originalFade in pairs(SavedHits) do hit.LocalTransparencyModifier = originalFade end end --------------------- --| Running Logic |-- --------------------- -- Connect to the current and all future cameras workspace.Changed:connect(OnWorkspaceChanged) OnWorkspaceChanged('CurrentCamera') Player.CharacterAdded:connect(OnCharacterAdded) if Player.Character then OnCharacterAdded(Player.Character) end Invisicam:SetMode(STARTING_MODE) Behaviors[MODE.CUSTOM] = function() end -- (Does nothing until SetCustomBehavior) Behaviors[MODE.LIMBS] = LimbBehavior Behaviors[MODE.MOVEMENT] = MoveBehavior Behaviors[MODE.CORNERS] = CornerBehavior Behaviors[MODE.CIRCLE1] = CircleBehavior Behaviors[MODE.CIRCLE2] = CircleBehavior Behaviors[MODE.LIMBMOVE] = LimbMoveBehavior return Invisicam ]]> PopperCam 0.95 or hitPart.CanCollide == false) then table.insert(ignoreList, hitPart) else return hitPart, hitPoint end until false end local function ScreenToWorld(screenPoint, screenSize, pushDepth) local cameraFOV, cameraCFrame = Camera.FieldOfView, Camera.CoordinateFrame local imagePlaneDepth = screenSize.y / (2 * math.tan(math.rad(cameraFOV) / 2)) local direction = Vector3.new(screenPoint.x - (screenSize.x / 2), (screenSize.y / 2) - screenPoint.y, -imagePlaneDepth) local worldDirection = (cameraCFrame:vectorToWorldSpace(direction)).Unit local theta = math.acos(math.min(1, worldDirection:Dot(cameraCFrame.lookVector))) local fixedPushDepth = pushDepth / math.sin((math.pi / 2) - theta) return cameraCFrame.p + worldDirection * fixedPushDepth end local function OnCameraChanged(property) if property == 'CameraSubject' then VehicleParts = {} local newSubject = Camera.CameraSubject if newSubject then -- Determine if we should be popping at all PopperEnabled = false for _, subjectType in pairs(VALID_SUBJECTS) do if newSubject:IsA(subjectType) then PopperEnabled = true break end end -- Get all parts of the vehicle the player is controlling if newSubject:IsA('VehicleSeat') then VehicleParts = newSubject:GetConnectedParts(true) end end end end local function OnCharacterAdded(player, character) PlayerCharacters[player] = character end local function OnPlayersChildAdded(child) if child:IsA('Player') then child.CharacterAdded:connect(function(character) OnCharacterAdded(child, character) end) if child.Character then OnCharacterAdded(child, child.Character) end end end local function OnPlayersChildRemoved(child) if child:IsA('Player') then PlayerCharacters[child] = nil end end local function OnWorkspaceChanged(property) if property == 'CurrentCamera' then local newCamera = workspace.CurrentCamera if newCamera then Camera = newCamera if CameraChangeConn then CameraChangeConn:disconnect() end CameraChangeConn = Camera.Changed:connect(OnCameraChanged) OnCameraChanged('CameraSubject') end end end ------------------------- --| Exposed Functions |-- ------------------------- function PopperCam:Update() if PopperEnabled then -- First, prep some intermediate vars local focusPoint = Camera.Focus.p local cameraCFrame = Camera.CoordinateFrame local cameraFrontPoint = cameraCFrame.p + (cameraCFrame.lookVector * NEAR_CLIP_PLANE_OFFSET) local screenSize = Camera.ViewportSize local ignoreList = {} for _, character in pairs(PlayerCharacters) do table.insert(ignoreList, character) end for _, basePart in pairs(VehicleParts) do table.insert(ignoreList, basePart) end -- Cast rays at the near clip plane, from corresponding points near the focus point, -- and find the direct line that is the most cut off local largest = 0 for _, screenScale in pairs(CAST_SCREEN_SCALES) do local clipWorldPoint = ScreenToWorld(screenSize * screenScale, screenSize, NEAR_CLIP_PLANE_OFFSET) local rayStartPoint = focusPoint + (clipWorldPoint - cameraFrontPoint) local _, hitPoint = PiercingCast(rayStartPoint, clipWorldPoint, ignoreList) local cutoffAmount = (hitPoint - clipWorldPoint).Magnitude if cutoffAmount > largest then largest = cutoffAmount end end -- Then check if the player zoomed since the last frame, -- and if so, reset our pop history so we stop tweening local zoomLevel = (cameraCFrame.p - focusPoint).Magnitude if math.abs(zoomLevel - LastZoomLevel) > 0.001 then LastPopAmount = 0 end -- Finally, zoom the camera in (pop) by that most-cut-off amount, or the last pop amount if that's more local popAmount = math.max(largest, LastPopAmount) if popAmount > 0 then Camera.CoordinateFrame = cameraCFrame + (cameraCFrame.lookVector * popAmount) LastPopAmount = math.max(0, popAmount - POP_RESTORE_RATE) -- Shrink it for the next frame end LastZoomLevel = zoomLevel end end -------------------- --| Script Logic |-- -------------------- -- Connect to the current and all future cameras workspace.Changed:connect(OnWorkspaceChanged) OnWorkspaceChanged('CurrentCamera') -- Connect to all Players so we can ignore their Characters PlayersService.ChildRemoved:connect(OnPlayersChildRemoved) PlayersService.ChildAdded:connect(OnPlayersChildAdded) for _, player in pairs(PlayersService:GetPlayers()) do OnPlayersChildAdded(player) end return PopperCam ]]> RootCamera = 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 then gamepadPan = gamepadLinearToCurve(gamepadPan) local currentTime = tick() if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then this.userPanningTheCamera = true elseif gamepadPan == Vector2.new(0,0) then lastThumbstickRotate = nil if lastThumbstickPos == Vector2.new(0,0) then currentSpeed = 0 end end local finalConstant = 0 if lastThumbstickRotate then 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 finalConstant = thumbstickSensitivity * currentSpeed lastVelocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate) end lastThumbstickPos = gamepadPan lastThumbstickRotate = currentTime return Vector2.new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * ySensitivity) end return Vector2.new(0,0) end local InputBeganConn, InputChangedConn, InputEndedConn, ShiftLockToggleConn, GamepadConnectedConn, GamepadDisconnectedConn = 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 ShiftLockToggleConn then ShiftLockToggleConn:disconnect() ShiftLockToggleConn = nil end if GamepadConnectedConn then GamepadConnectedConn:disconnect() GamepadConnectedConn = nil end if GamepadDisconnectedConn then GamepadDisconnectedConn:disconnect() GamepadDisconnectedConn = 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: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) ShiftLockToggleConn = ShiftLockController.OnShiftLockToggled.Event:connect(function() this:UpdateMouseBehavior() this:GetHumanoid():SetClickToWalkEnabled(false) end) this.RotateInput = Vector2.new() local activateGamepad = nil local function assignActivateGamepad() local connectedGamepads = UserInputService:GetConnectedGamepads() if #connectedGamepads > 0 then for i = 1, #connectedGamepads do if activateGamepad == nil then activateGamepad = connectedGamepads[i] elseif connectedGamepads[i].Value < activateGamepad.Value then activateGamepad = connectedGamepads[i] end end end if activateGamepad == nil then -- nothing is connected, at least set up for gamepad1 activateGamepad = Enum.UserInputType.Gamepad1 end end GamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum) if activateGamepad ~= gamepadEnum then return end activateGamepad = nil assignActivateGamepad() end) GamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum) if activateGamepad == nil then assignActivateGamepad() end end) local getGamepadPan = function(name, state, input) if input.UserInputType == activateGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then if state == Enum.UserInputState.Cancel then this.GamepadPanningCamera = Vector2.new(0,0) return end 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 = Vector2.new(0,0) end end end local doGamepadZoom = function(name, state, input) if input.UserInputType == activateGamepad and input.KeyCode == Enum.KeyCode.ButtonR3 and state == Enum.UserInputState.Begin then if this.ZoomEnabled then if this.currentZoom > 0.5 then this:ZoomCamera(0) else this:ZoomCamera(10) end end end end game.ContextActionService:BindAction("RootCamGamepadPan", getGamepadPan, false, Enum.KeyCode.Thumbstick2) game.ContextActionService:BindAction("RootCamGamepadZoom", doGamepadZoom, false, Enum.KeyCode.ButtonR3) assignActivateGamepad() -- set mouse behavior self:UpdateMouseBehavior() end function this:SetEnabled(newState) if newState ~= self.Enabled then self.Enabled = newState if self.Enabled then self:ConnectInputEvents() self.cframe = workspace.CurrentCamera.CoordinateFrame 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) this:ZoomCamera(12.5) 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 local function setLookBehindCharacter() if humanoid and humanoid.Torso and player.Character == newCharacter then local newDesiredLook = (humanoid.Torso.CFrame.lookVector - Vector3.new(0,0.23,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 end wait() setLookBehindCharacter() 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) return this end return CreateCamera ]]> AttachCamera 1 then module:ResetCameraLook() self.LastCameraTransform = nil end local subjectPosition = self:GetSubjectPosition() if subjectPosition and player and camera then local zoom = self:GetCameraZoom() if zoom <= 0 then zoom = 0.1 end local humanoid = self:GetHumanoid() if lastUpdate and humanoid and humanoid.Torso then -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from local delta = math.min(0.1, now - lastUpdate) local gamepadRotation = self:UpdateGamepad() self.RotateInput = self.RotateInput + (gamepadRotation * delta) local forwardVector = humanoid.Torso.CFrame.lookVector local y = findAngleBetweenXZVectors(forwardVector, self:GetCameraLook()) if IsFinite(y) then -- Preserve vertical rotation from user input self.RotateInput = Vector2.new(y, self.RotateInput.Y) end end local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput) self.RotateInput = Vector2.new() camera.Focus = CFrame.new(subjectPosition) camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * newLookVector), camera.Focus.p) self.LastCameraTransform = camera.CoordinateFrame end lastUpdate = now end return module end return CreateAttachCamera ]]> ClassicCamera 1 then module:ResetCameraLook() self.LastCameraTransform = nil end if lastUpdate then -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from local delta = math.min(0.1, now - lastUpdate) local angle = 0 if not (isInVehicle or isOnASkateboard) then angle = angle + (self.TurningLeft and -120 or 0) angle = angle + (self.TurningRight and 120 or 0) end local gamepadRotation = self:UpdateGamepad() if gamepadRotation ~= Vector2.new(0,0) then userPanningTheCamera = true self.RotateInput = self.RotateInput + (gamepadRotation * delta) end if angle ~= 0 then userPanningTheCamera = true self.RotateInput = self.RotateInput + Vector2.new(math.rad(angle * delta), 0) end end -- Reset tween speed if user is panning if userPanningTheCamera then tweenSpeed = 0 module.LastUserPanCamera = tick() end local userRecentlyPannedCamera = now - module.LastUserPanCamera < timeBeforeAutoRotate local subjectPosition = self:GetSubjectPosition() if subjectPosition and player and camera then local zoom = self:GetCameraZoom() if zoom < 0.5 then zoom = 0.5 end if self:GetShiftLock() and not self:IsInFirstPerson() then -- We need to use the right vector of the camera after rotation, not before local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput) local offset = ((newLookVector * XZ_VECTOR):Cross(UP_VECTOR).unit * 1.75) if IsFiniteVector3(offset) then subjectPosition = subjectPosition + offset end else if self.LastCameraTransform and not userPanningTheCamera then local isInFirstPerson = self:IsInFirstPerson() if (isInVehicle or isOnASkateboard) and lastUpdate and humanoid and humanoid.Torso then if isInFirstPerson then if self.LastSubjectCFrame and (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then local y = -findAngleBetweenXZVectors(self.LastSubjectCFrame.lookVector, cameraSubject.CFrame.lookVector) if IsFinite(y) then self.RotateInput = self.RotateInput + Vector2.new(y, 0) end tweenSpeed = 0 end elseif not userRecentlyPannedCamera then local forwardVector = humanoid.Torso.CFrame.lookVector if isOnASkateboard then forwardVector = cameraSubject.CFrame.lookVector end local timeDelta = (now - lastUpdate) tweenSpeed = clamp(0, tweenMaxSpeed, tweenSpeed + tweenAcceleration * timeDelta) local percent = clamp(0, 1, tweenSpeed * timeDelta) if self:IsInFirstPerson() then percent = 1 end local y = findAngleBetweenXZVectors(forwardVector, self:GetCameraLook()) if IsFinite(y) and math.abs(y) > 0.0001 then self.RotateInput = self.RotateInput + Vector2.new(y * percent, 0) end end end end end local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput) self.RotateInput = Vector2.new() camera.Focus = CFrame.new(subjectPosition) camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * newLookVector), camera.Focus.p) + Vector3.new(0, self:GetCameraHeight(), 0) self.LastCameraTransform = camera.CoordinateFrame if isInVehicle or isOnASkateboard and cameraSubject:IsA('BasePart') then self.LastSubjectCFrame = cameraSubject.CFrame else self.LastSubjectCFrame = nil end end lastUpdate = now end return module end return CreateClassicCamera]]> FixedCamera 1 then module:ResetCameraLook() self.LastCameraTransform = nil end if lastUpdate then -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from local delta = math.min(0.1, now - lastUpdate) local gamepadRotation = self:UpdateGamepad() self.RotateInput = self.RotateInput + (gamepadRotation * delta) end local subjectPosition = self:GetSubjectPosition() if subjectPosition and player and camera then local zoom = self:GetCameraZoom() if zoom <= 0 then zoom = 0.1 end local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput) self.RotateInput = Vector2.new() camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * newLookVector), camera.Focus.p) self.LastCameraTransform = camera.CoordinateFrame end lastUpdate = now end return module end return CreateFixedCamera ]]> FollowCamera 1 then module:ResetCameraLook() self.LastCameraTransform = nil end if lastUpdate then -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from local delta = math.min(0.1, now - lastUpdate) local angle = 0 -- NOTE: Traditional follow camera does not rotate with arrow keys if not (isInVehicle or isOnASkateboard) then angle = angle + (self.TurningLeft and -120 or 0) angle = angle + (self.TurningRight and 120 or 0) end local gamepadRotation = self:UpdateGamepad() if gamepadRotation ~= Vector2.new(0,0) then userPanningTheCamera = true self.RotateInput = self.RotateInput + (gamepadRotation * delta) end if angle ~= 0 then userPanningTheCamera = true self.RotateInput = self.RotateInput + Vector2.new(math.rad(angle * delta), 0) end end -- Reset tween speed if user is panning if userPanningTheCamera then tweenSpeed = 0 module.LastUserPanCamera = tick() end local userRecentlyPannedCamera = now - module.LastUserPanCamera < timeBeforeAutoRotate local subjectPosition = self:GetSubjectPosition() if subjectPosition and player and camera then local zoom = self:GetCameraZoom() if zoom < 0.5 then zoom = 0.5 end if self:GetShiftLock() and not self:IsInFirstPerson() then local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput) local offset = ((newLookVector * XZ_VECTOR):Cross(UP_VECTOR).unit * 1.75) if IsFiniteVector3(offset) then subjectPosition = subjectPosition + offset end else if self.LastCameraTransform and not userPanningTheCamera then local isInFirstPerson = self:IsInFirstPerson() if (isClimbing or isInVehicle or isOnASkateboard) and lastUpdate and humanoid and humanoid.Torso then if isInFirstPerson then if self.LastSubjectCFrame and (isInVehicle or isOnASkateboard) and cameraSubject:IsA('BasePart') then local y = -findAngleBetweenXZVectors(self.LastSubjectCFrame.lookVector, cameraSubject.CFrame.lookVector) if IsFinite(y) then self.RotateInput = self.RotateInput + Vector2.new(y, 0) end tweenSpeed = 0 end elseif not userRecentlyPannedCamera then local forwardVector = humanoid.Torso.CFrame.lookVector if isOnASkateboard then forwardVector = cameraSubject.CFrame.lookVector end local timeDelta = (now - lastUpdate) tweenSpeed = clamp(0, tweenMaxSpeed, tweenSpeed + tweenAcceleration * timeDelta) local percent = clamp(0, 1, tweenSpeed * timeDelta) if not isClimbing and self:IsInFirstPerson() then percent = 1 end local y = findAngleBetweenXZVectors(forwardVector, self:GetCameraLook()) -- Check for NaN if IsFinite(y) and math.abs(y) > 0.0001 then self.RotateInput = self.RotateInput + Vector2.new(y * percent, 0) end end end end end local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput) self.RotateInput = Vector2.new() camera.Focus = CFrame.new(subjectPosition) camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * newLookVector), camera.Focus.p) + Vector3.new(0, self:GetCameraHeight(), 0) self.LastCameraTransform = camera.CoordinateFrame if isInVehicle or isOnASkateboard and cameraSubject:IsA('BasePart') then self.LastSubjectCFrame = cameraSubject.CFrame else self.LastSubjectCFrame = nil end end lastUpdate = now end return module end return CreateFollowCamera ]]> ScriptableCamera TrackCamera 1 then module:ResetCameraLook() self.LastCameraTransform = nil end if lastUpdate then -- Cap out the delta to 0.1 so we don't get some crazy things when we re-resume from local delta = math.min(0.1, now - lastUpdate) local gamepadRotation = self:UpdateGamepad() if gamepadRotation ~= Vector2.new(0,0) then userPanningTheCamera = true self.RotateInput = self.RotateInput + (gamepadRotation * delta) end end local subjectPosition = self:GetSubjectPosition() if subjectPosition and player and camera then local zoom = self:GetCameraZoom() if zoom <= 0 then zoom = 0.1 end local newLookVector = self:RotateCamera(self:GetCameraLook(), self.RotateInput) self.RotateInput = Vector2.new() camera.Focus = CFrame.new(subjectPosition) camera.CoordinateFrame = CFrame.new(camera.Focus.p - (zoom * newLookVector), camera.Focus.p) self.LastCameraTransform = camera.CoordinateFrame end lastUpdate = now end return module end return CreateTrackCamera ]]> WatchCamera 1 then module:ResetCameraLook() self.LastCameraTransform = nil self.LastZoom = nil end local subjectPosition = self:GetSubjectPosition() if subjectPosition and player and camera then local cameraLook = nil if self.LastCameraTransform then local humanoid = self:GetHumanoid() if humanoid and humanoid.Torso then -- TODO: let the paging buttons move the camera but not the mouse/touch -- currently neither do local diffVector = subjectPosition - self.LastCameraTransform.p cameraLook = diffVector.unit if self.LastZoom and self.LastZoom == self:GetCameraZoom() then -- Don't clobber the zoom if they zoomed the camera local zoom = diffVector.magnitude self:ZoomCamera(zoom) end end end local zoom = self:GetCameraZoom() if zoom <= 0 then zoom = 0.1 end local newLookVector = self:RotateVector(cameraLook or self:GetCameraLook(), self.RotateInput) self.RotateInput = Vector2.new() local newFocus = CFrame.new(subjectPosition) local newCamCFrame = CFrame.new(newFocus.p - (zoom * newLookVector), newFocus.p) camera.Focus = newFocus camera.CoordinateFrame = newCamCFrame self.LastCameraTransform = newCamCFrame self.LastZoom = zoom end lastUpdate = now end return module end return CreateWatchCamera ]]> ShiftLockController TransparencyController