Skip to content
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
153 changes: 153 additions & 0 deletions MainModule/Client/Core/Functions.luau
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ return function(Vargs, GetEnv)
end

local function RunLast()
-- Initialize camera stabilization to prevent unwanted movement during window resizing
if Functions.InitCameraStabilization then
Functions.InitCameraStabilization()
end
Functions.RunLast = nil;
end

Expand Down Expand Up @@ -264,6 +268,155 @@ return function(Vargs, GetEnv)
end
end;

-- Camera stabilization to prevent unwanted camera movement during window resizing
InitCameraStabilization = function()
local cam = workspace.CurrentCamera
local lastCameraState = {
CFrame = cam.CFrame,
CameraType = cam.CameraType,
CameraSubject = cam.CameraSubject,
FieldOfView = cam.FieldOfView,
ViewportSize = cam.ViewportSize,
LastUpdate = os.clock()
}
local isResizing = false
local stabilizationEnabled = true
local resizeDebounce = false
local lastUserCameraChange = 0

-- Function to check if camera change was likely user-initiated
local function isUserCameraChange()
local now = os.clock()
-- If camera changed recently (within 2 seconds), it might be user input
return (now - lastUserCameraChange) < 2
end

-- Function to save current camera state (only if not user-initiated)
local function saveCameraState()
if cam and cam.Parent and not isResizing and stabilizationEnabled then
local now = os.clock()
-- Only save if camera type is Custom (normal gameplay) and enough time has passed
if cam.CameraType == Enum.CameraType.Custom and not isUserCameraChange() then
lastCameraState.CFrame = cam.CFrame
lastCameraState.CameraType = cam.CameraType
lastCameraState.CameraSubject = cam.CameraSubject
lastCameraState.FieldOfView = cam.FieldOfView
lastCameraState.LastUpdate = now
end
end
end

-- Function to restore camera state (with additional safety checks)
local function restoreCameraState()
if cam and cam.Parent and stabilizationEnabled and isResizing then
local now = os.clock()
-- Only restore if the saved state is recent (within 5 seconds) and camera is in Custom mode
if lastCameraState.LastUpdate and (now - lastCameraState.LastUpdate) < 5
and cam.CameraType == Enum.CameraType.Custom then
cam.CFrame = lastCameraState.CFrame
if lastCameraState.CameraType then
cam.CameraType = lastCameraState.CameraType
end
if lastCameraState.CameraSubject then
cam.CameraSubject = lastCameraState.CameraSubject
end
if lastCameraState.FieldOfView then
cam.FieldOfView = lastCameraState.FieldOfView
end
end
end
end

-- Track user input that might affect camera
service.UserInputService.InputBegan:Connect(function(input, gameProcessed)
if not gameProcessed then
-- Track mouse and touch input that could move camera
if input.UserInputType == Enum.UserInputType.MouseMovement or
input.UserInputType == Enum.UserInputType.Touch or
input.UserInputType == Enum.UserInputType.MouseButton2 then
lastUserCameraChange = os.clock()
end
end
end)

-- Monitor viewport size changes (window resizing)
cam:GetPropertyChangedSignal("ViewportSize"):Connect(function()
if stabilizationEnabled and not resizeDebounce then
local newViewportSize = cam.ViewportSize
local oldViewportSize = lastCameraState.ViewportSize

-- Check if this is actually a resize (size changed significantly)
if oldViewportSize and
(math.abs(newViewportSize.X - oldViewportSize.X) > 10 or
math.abs(newViewportSize.Y - oldViewportSize.Y) > 10) then
Copy link

Copilot AI Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Extract the pixel threshold (10) and timing parameters (0.15, 2, 5) into named constants or configuration entries so their purpose is clear and easily adjustable.

Suggested change
(math.abs(newViewportSize.X - oldViewportSize.X) > 10 or
math.abs(newViewportSize.Y - oldViewportSize.Y) > 10) then
(math.abs(newViewportSize.X - oldViewportSize.X) > PIXEL_THRESHOLD or
math.abs(newViewportSize.Y - oldViewportSize.Y) > PIXEL_THRESHOLD) then

Copilot uses AI. Check for mistakes.
resizeDebounce = true
isResizing = true
lastCameraState.ViewportSize = newViewportSize

-- Use task.defer to avoid blocking the event handler
task.defer(function()
-- Wait for resize to settle
task.wait(0.15)
-- Only restore if no recent user camera input
if not isUserCameraChange() then
restoreCameraState()
end
task.wait(0.15)
isResizing = false
resizeDebounce = false
end)
else
lastCameraState.ViewportSize = newViewportSize
end
end
end)

-- Periodically save camera state when stable (less frequent)
service.StartLoop("CameraStabilization", 1.0, function()
Copy link

Copilot AI Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The periodic loop continues running even when stabilization is disabled. Consider stopping the loop in Disable to reduce unnecessary work when the feature is turned off.

Copilot uses AI. Check for mistakes.
saveCameraState()
end)

-- Return functions to control stabilization
Variables.CameraStabilization = {
Enable = function()
stabilizationEnabled = true
end,
Disable = function()
stabilizationEnabled = false
end,
IsEnabled = function()
return stabilizationEnabled
end,
Cleanup = function()
service.StopLoop("CameraStabilization")
stabilizationEnabled = false
Copy link

Copilot AI Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Cleanup function stops the loop but does not disconnect the InputBegan and ViewportSize event connections. Store and disconnect those connections to avoid memory leaks or duplicate handlers on re-initialization.

Suggested change
stabilizationEnabled = false
stabilizationEnabled = false
-- Disconnect event connections to avoid memory leaks
if Variables.InputBeganConnection then
Variables.InputBeganConnection:Disconnect()
Variables.InputBeganConnection = nil
end
if Variables.ViewportSizeConnection then
Variables.ViewportSizeConnection:Disconnect()
Variables.ViewportSizeConnection = nil
end

Copilot uses AI. Check for mistakes.
end,
-- Debug function to check current state
GetState = function()
return {
enabled = stabilizationEnabled,
isResizing = isResizing,
lastUpdate = lastCameraState.LastUpdate,
viewportSize = lastCameraState.ViewportSize
}
end
}
end;

CameraStabilization = function(action)
if Variables.CameraStabilization then
if action == "Enable" then
Variables.CameraStabilization.Enable()
elseif action == "Disable" then
Variables.CameraStabilization.Disable()
elseif action == "IsEnabled" then
return Variables.CameraStabilization.IsEnabled()
elseif action == "GetState" then
return Variables.CameraStabilization.GetState()
end
end
end;

-- Thanks to Tiffany352 for this base64 implementation!

Base64Encode = function(str)
Expand Down
1 change: 1 addition & 0 deletions MainModule/Client/Core/Variables.luau
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ return function(Vargs, GetEnv)
KeyBinds = {};
Aliases = {};
Capes = {};
CameraStabilization = {};
savedUI = {};
localSounds = {};
ESPObjects = {};
Expand Down
37 changes: 37 additions & 0 deletions MainModule/Server/Commands/Players.luau
Original file line number Diff line number Diff line change
Expand Up @@ -1238,5 +1238,42 @@ return function(Vargs, env)
return args
end
};

CameraStabilization = {
Prefix = Settings.PlayerPrefix;
Commands = {"camerastab", "camerastabilization", "fixcamera", "stabilizecamera"};
Args = {"on/off/status"};
Copy link

Copilot AI Jun 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Args documentation lists only on/off/status but the handler also accepts enable/disable/true/false. Consider updating Args or normalizing inputs to avoid user confusion.

Copilot uses AI. Check for mistakes.
Description = "Toggle camera stabilization to prevent unwanted movement during window resizing";
AdminLevel = "Players";
Function = function(plr: Player, args: {string})
local action = string.lower(args[1] or "status")

if action == "on" or action == "enable" or action == "true" then
Remote.Send(plr, "Function", "CameraStabilization", "Enable")
Remote.MakeGui(plr, "Output", {
Color = Color3.new(0, 1, 0);
Message = "Camera stabilization enabled";
Title = "Camera Stabilization";
})
elseif action == "off" or action == "disable" or action == "false" then
Remote.Send(plr, "Function", "CameraStabilization", "Disable")
Remote.MakeGui(plr, "Output", {
Color = Color3.new(1, 0.5, 0);
Message = "Camera stabilization disabled";
Title = "Camera Stabilization";
})
else
-- Show status
Remote.LoadCode(plr, [[
local status = Variables.CameraStabilization and Variables.CameraStabilization.IsEnabled and Variables.CameraStabilization.IsEnabled() or false
Remote.MakeGui(service.Player, "Output", {
Color = status and Color3.new(0, 1, 0) or Color3.new(1, 0.5, 0);
Message = "Camera stabilization is currently " .. (status and "enabled" or "disabled");
Title = "Camera Stabilization Status";
})
]])
end
end
};
};
end
Loading