SyntaxGameServer/RCCService2018/content/scripts/PlayerScripts/StarterPlayerScripts/CameraScript/PopperCam.lua

195 lines
5.0 KiB
Lua

-- PopperCam Version 16
-- OnlyTwentyCharacters
local PopperCam = {} -- Guarantees your players won't see outside the bounds of your map!
-----------------
--| Constants |--
-----------------
local POP_RESTORE_RATE = 0.3
local MIN_CAMERA_ZOOM = 0.5
local VALID_SUBJECTS = {
'Humanoid',
'VehicleSeat',
'SkateboardPlatform',
}
local portraitPopperFixFlagExists, portraitPopperFixFlagEnabled = pcall(function()
return UserSettings():IsUserFeatureEnabled("UserPortraitPopperFix")
end)
local FFlagUserPortraitPopperFix = portraitPopperFixFlagExists and portraitPopperFixFlagEnabled
-----------------
--| Variables |--
-----------------
local PlayersService = game:GetService('Players')
local Camera = nil
local CameraSubjectChangeConn = nil
local SubjectPart = nil
local PlayerCharacters = {} -- For ignoring in raycasts
local VehicleParts = {} -- Also just for ignoring
local LastPopAmount = 0
local LastZoomLevel = 0
local PopperEnabled = false
local CFrame_new = CFrame.new
-----------------------
--| Local Functions |--
-----------------------
local math_abs = math.abs
local function OnCameraSubjectChanged()
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
if FFlagUserPortraitPopperFix then
if newSubject:IsA("BasePart") then
SubjectPart = newSubject
elseif newSubject:IsA("Model") then
SubjectPart = newSubject.PrimaryPart
elseif newSubject:IsA("Humanoid") then
SubjectPart = newSubject.Torso
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 CameraSubjectChangeConn then
CameraSubjectChangeConn:disconnect()
end
CameraSubjectChangeConn = Camera:GetPropertyChangedSignal("CameraSubject"):connect(OnCameraSubjectChanged)
OnCameraSubjectChanged()
end
end
end
-------------------------
--| Exposed Functions |--
-------------------------
function PopperCam:Update(EnabledCamera)
if PopperEnabled then
-- First, prep some intermediate vars
local cameraCFrame = Camera.CFrame
local focusPoint = Camera.Focus.p
if FFlagUserPortraitPopperFix and SubjectPart then
focusPoint = SubjectPart.CFrame.p
end
local ignoreList = {}
for _, character in pairs(PlayerCharacters) do
ignoreList[#ignoreList + 1] = character
end
for i = 1, #VehicleParts do
ignoreList[#ignoreList + 1] = VehicleParts[i]
end
-- Get largest cutoff distance
local largest = Camera:GetLargestCutoffDistance(ignoreList)
-- 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 = largest
if LastPopAmount > popAmount then
popAmount = LastPopAmount
end
if popAmount > 0 then
Camera.CFrame = cameraCFrame + (cameraCFrame.lookVector * popAmount)
LastPopAmount = popAmount - POP_RESTORE_RATE -- Shrink it for the next frame
if LastPopAmount < 0 then
LastPopAmount = 0
end
end
LastZoomLevel = zoomLevel
-- Stop shift lock being able to see through walls by manipulating Camera focus inside the wall
if EnabledCamera and EnabledCamera:GetShiftLock() and not EnabledCamera:IsInFirstPerson() then
if EnabledCamera:GetCameraActualZoom() < 1 then
local subjectPosition = EnabledCamera.lastSubjectPosition
if subjectPosition then
Camera.Focus = CFrame_new(subjectPosition)
Camera.CFrame = CFrame_new(subjectPosition - MIN_CAMERA_ZOOM*EnabledCamera:GetCameraLook(), subjectPosition)
end
end
end
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