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