Pinkstack/roblox-luau-corpus-text huggingface dataset:
-- ROBLOX NOTE upstream: Added some utilities to support keeping the upstream code consistent


type unknown = any

local CurrentModule = script.Parent
local Packages = CurrentModule.Parent

local LuauPolyfill = require(Packages.LuauPolyfill)
local inspect = LuauPolyfill.util.inspect
type Array<T> = LuauPolyfill.Array<T>

local typesModule = require(CurrentModule.types)
type InspectOptions = typesModule.InspectOptions

function concatRestArgs(restArgs: Array<unknown>)
	local restStr = ""
	if #restArgs > 0 then
		restStr = " " .. table.concat(restArgs, " ")
	end
	return restStr
end

function getFormattedValue(value: unknown, inspectOptions: InspectOptions?)
	return if type(value) == "string" then value else inspect(value, inspectOptions)
end

function format(...)
	return formatter(nil, ...)
end

function formatWithOptions(inspectOptions: InspectOptions, fmt: unknown, ...)
	return formatter(inspectOptions, fmt, ...)
end

function formatter(inspectOptions: InspectOptions?, fmt: unknown, ...)
	local args = { ... }

	local fmtArgs = {}
	local extraArgs = {}

	if type(fmt) == "string" then
		local _, c = fmt:gsub("%%[sdj%%]", "")

		for key, value in pairs(args) do
			local formattedValue = getFormattedValue(value, inspectOptions)
			if key <= c then
				table.insert(fmtArgs, formattedValue)
			else
				table.insert(extraArgs, formattedValue)
			end
		end
	else
		fmt = inspect(fmt, inspectOptions)
		for _, value in pairs(args) do
			table.insert(extraArgs, getFormattedValue(value, inspectOptions))
		end
	end

	return string.format(fmt, table.unpack(fmtArgs)) .. concatRestArgs(extraArgs)
end

return {
	format = format,
	formatWithOptions = formatWithOptions,
	concatRestArgs = concatRestArgs,
}
--Unlock or Lock AR/AF timer if enclosure is locked



function LockButtons()
	ButtonAlert.ProximityPrompt.Enabled = false
	ButtonAttack.ProximityPrompt.Enabled = false
	ButtonCancel.ProximityPrompt.Enabled = false
	ButtonTest.ProximityPrompt.Enabled = false
	ButtonFire.ProximityPrompt.Enabled = false
end

function UnlockButtons()
	ButtonAlert.ProximityPrompt.Enabled = true
	ButtonAttack.ProximityPrompt.Enabled = true
	ButtonCancel.ProximityPrompt.Enabled = true
	ButtonTest.ProximityPrompt.Enabled = true
	ButtonFire.ProximityPrompt.Enabled = true
end

function Check()
	if script.Parent.Locked.Value == true then
		LockButtons()
	else
		UnlockButtons()
	end
end

Check()

script.Parent.Locked.Changed:Connect(function()
	Check()
end)
--[[Front]]

--
	Tune.FSusDamping	= 85 		-- Dampening
	Tune.FSusStiffness	= 2000 		-- Stiffness
	
	Tune.FSusLength		= 1 		-- Suspension length (in studs)
	Tune.FPreComp		= .25 		-- Vehicle height, relative to your suspension settings
	Tune.FExtLimit		= .8 		-- Max Extension Travel (in studs)
	Tune.FCompLimit		= .4 		-- Max Compression Travel (in studs)
	
	Tune.FBaseOffset	= {			-- Suspension (steering point) base
		 --[[Lateral]]		0		,	-- positive = outward
		 --[[Vertical]]		0		,	-- positive = upward
		 --[[Forward]]		0.256		}	-- positive = forward
	Tune.FAxleOffset	= {			-- Suspension (axle point) base
		 --[[Lateral]]		0		,	-- positive = outward
		 --[[Vertical]]		0		,	-- positive = upward
		 --[[Forward]]		0		}	-- positive = forward
	
	Tune.FBricksVisible	= false 	-- Makes the front suspension bricks visible (Debug)
	Tune.FConstsVisible	= false 	-- Makes the front suspension constraints visible (Debug)
--\\Main//--


for Number, Instance in pairs(game.Workspace:GetChildren()) do
	if Instance:IsA("Part") and Instance.Name == "Base" then
		Instance.Touched:Connect(function(Hit)
			local Player = PlayerService:GetPlayerFromCharacter(Hit.Parent)
			if not Player then return end
			Player.Character:FindFirstChild("Humanoid").Sit = true
		end)
	end
end
--[[
	Given a `tweenInfo` and `currentTime`, returns a ratio which can be used to
	tween between two values over time.
]]



local TweenService = game:GetService("TweenService")

local function getTweenRatio(tweenInfo: TweenInfo, currentTime: number): number
	local delay = tweenInfo.DelayTime
	local duration = tweenInfo.Time
	local reverses = tweenInfo.Reverses
	local numCycles = 1 + tweenInfo.RepeatCount
	local easeStyle = tweenInfo.EasingStyle
	local easeDirection = tweenInfo.EasingDirection

	local cycleDuration = delay + duration
	if reverses then
		cycleDuration += duration
	end

	if currentTime >= cycleDuration * numCycles then
		return 1
	end

	local cycleTime = currentTime % cycleDuration

	if cycleTime <= delay then
		return 0
	end

	local tweenProgress = (cycleTime - delay) / duration
	if tweenProgress > 1 then
		tweenProgress = 2 - tweenProgress
	end

	local ratio = TweenService:GetValue(tweenProgress, easeStyle, easeDirection)
	return ratio
end

return getTweenRatio
--[[
	Retrieves at most one child from the children passed to a component.

	If passed nil or an empty table, will return nil.

	Throws an error if passed more than one child.
]]


local function oneChild(children)
	if not children then
		return nil
	end

	local key, child = next(children)

	if not child then
		return nil
	end

	local after = next(children, key)

	if after then
		error("Expected at most child, had more than one child.", 2)
	end

	return child
end

return oneChild
-- (Hat Giver Script - Loaded.)



debounce = true

function onTouched(hit)
	if (hit.Parent:findFirstChild("Humanoid") ~= nil and debounce == true) then
		debounce = false
		h = Instance.new("Hat")
		p = Instance.new("Part")
		h.Name = "Top Hat"
		p.Parent = h
		p.Position = hit.Parent:findFirstChild("Head").Position
		p.Name = "Handle" 
		p.formFactor = 0
		p.Size = Vector3.new(2, 1, 1) 
		p.BottomSurface = 0 
		p.TopSurface = 0 
		p.Locked = true 
		script.Parent.Mesh:clone().Parent = p
		h.Parent = hit.Parent
		h.AttachmentPos = Vector3.new(0,1,-1)
		wait(5)
		debounce = true
	end
end

script.Parent.Touched:connect(onTouched)
--[[
    Module for obtaining the input state for the vehicle, using UserInputService
]]


local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local ClientVehicleBoosts = require(ReplicatedStorage.Source.ClientVehicleBoosts)
local Flags = require(ReplicatedStorage.Source.Common.Flags)

local FlagEnableBoosts = Flags.EnableBoosts

local UserInput = {}

local throttle = 0
local steer = 0

local actions = {
	Forwards = {Enum.KeyCode.W, Enum.KeyCode.Up},
	Backwards = {Enum.KeyCode.S, Enum.KeyCode.Down},
	Left = {Enum.KeyCode.A, Enum.KeyCode.Left},
	Right = {Enum.KeyCode.D, Enum.KeyCode.Right},
	Boost = {Enum.KeyCode.Space}
}

local function getActionFromInput(inputObject)
	for action, bindings in pairs(actions) do
		for _, keyCode in pairs(bindings) do
			if keyCode == inputObject.KeyCode then
				return action
			end
		end
	end
end

local function isActionHeld(action)
	local bindings = actions[action]
	for _, keyCode in pairs(bindings) do
		if UserInputService:IsKeyDown(keyCode) then
			return true
		end
	end
	return false
end
-------------------------------------


equiped=false
sp=script.Parent
RayLength=10000
Spread=0
enabled=true
reloading=false
down=false
r=game:service("RunService")
last=0
last2=0
last3=0
last4=0
last5=0
last6=0

Bullet=Instance.new("Part")
Bullet.Name="Bullet"
Bullet.BrickColor=BrickColor.new("New Yeller")
Bullet.Anchored=true
Bullet.CanCollide=false
Bullet.Locked=true
Bullet.Size=Vector3.new(1,1,1)
--[=[
	@tag Component Class
	`Start` is called when the component is started. At this point in time, it
	is safe to grab other components also bound to the same instance.

	```lua
	local MyComponent = Component.new({Tag = "MyComponent"})
	local AnotherComponent = require(somewhere.AnotherComponent)

	function MyComponent:Start()
		-- e.g., grab another component:
		local another = self:GetComponent(AnotherComponent)
	end
	```
]=]


function Component:Start()
end
--Functions


local function onTouched(hit)
	local Humanoid = hit.Parent:FindFirstChild("Humanoid")
	local player = game.Players:GetPlayerFromCharacter(hit.Parent)

	if Humanoid and Debounce == false then
		local leaderstats = player:FindFirstChild("leaderstats")
		Debounce = true

		if Debounce == true and leaderstats then
			local sound = script.Parent["Game Sound Correct"]

			if not sound.IsPlaying then
				sound:Play()
	
			leaderstats.Guesses.Value = leaderstats.Guesses.Value + AMOUNT_GIVEN
				wait(10)
				
			Debounce = false
		end
	end
end
end
--[[Run]]



	--Print Version
	local ver=require(car["A-Chassis Tune"].README)
	print("//INSPARE: AC6 Loaded - Build "..ver)
	
	--Runtime Loops
	
	-- ~60 c/s
	game["Run Service"].Stepped:connect(function()
		--Steering
		Steering()
		
		--RPM
		RPM()
		
		--Update External Values
		_IsOn = script.Parent.IsOn.Value
		_InControls = script.Parent.ControlsOpen.Value
		script.Parent.Values.Gear.Value = _CGear
		script.Parent.Values.RPM.Value = _RPM
		script.Parent.Values.Horsepower.Value = _HP
		script.Parent.Values.Torque.Value = _HP * _Tune.EqPoint / _RPM
		script.Parent.Values.TransmissionMode.Value = _TMode
		script.Parent.Values.Throttle.Value = _GThrot
		script.Parent.Values.Brake.Value = _GBrake
		script.Parent.Values.SteerC.Value = _GSteerC*(1-math.min(car.DriveSeat.Velocity.Magnitude/_Tune.SteerDecay,1-(_Tune.MinSteer/100)))
		script.Parent.Values.SteerT.Value = _GSteerT
		script.Parent.Values.PBrake.Value = _PBrake
		script.Parent.Values.TCS.Value = _TCS
		script.Parent.Values.TCSActive.Value = _TCSActive
		script.Parent.Values.ABS.Value = _ABS
		script.Parent.Values.ABSActive.Value = _ABSActive
		script.Parent.Values.MouseSteerOn.Value = _MSteer
		script.Parent.Values.Velocity.Value = car.DriveSeat.Velocity
	end)
	
	-- ~15 c/s
	while wait(.0667) do
		--Power
		Engine()
		
		--Flip
		if _Tune.AutoFlip then Flip() end
		if Stats.Fuel == 0 then _IsOn = false end
		
	end
-- Helper function for Determinant of 3x3, not in CameraUtils for performance reasons


local function Det3x3(a: number,b: number,c: number,d: number,e: number,f: number,g: number,h: number,i: number): number
	return (a*(e*i-f*h)-b*(d*i-f*g)+c*(d*h-e*g))
end
--function moveSit()
--	RightShoulder.MaxVelocity = 0.15
--	LeftShoulder.MaxVelocity = 0.15
--	RightShoulder:SetDesiredAngle(3.14 /2)
--	LeftShoulder:SetDesiredAngle(-3.14 /2)
--	RightHip:SetDesiredAngle(3.14 /2)
--	LeftHip:SetDesiredAngle(-3.14 /2)
--end



local lastTick = 0

function move(time)
	local amplitude = 1
	local frequency = 1
  	local deltaTime = time - lastTick
  	lastTick = time

	local climbFudge = 0
	local setAngles = false

  	if (jumpAnimTime > 0) then
  		jumpAnimTime = jumpAnimTime - deltaTime
  	end

	if (pose == "FreeFall" and jumpAnimTime <= 0) then
		playAnimation("fall", fallTransitionTime, Humanoid)
	elseif (pose == "Seated") then
		playAnimation("sit", 0.5, Humanoid)
		return
	elseif (pose == "Running") then
		playAnimation("walk", 0.1, Humanoid)
	elseif (pose == "Dead" or pose == "GettingUp" or pose == "FallingDown" or pose == "Seated" or pose == "PlatformStanding") then
--[==[Documentation uses

	excess information container (titles: tip, warning, info):
	
	:::title
	Information here.
	:::
	
	code example container:
	
	```lua
	
	```
	
	symbol @ examples:
	
	@interface Name
	.Variable objectType
	.functionName () -> ()
	
	@return a, b, ... or DataType | DataType | ... or Class<DataType | DataType | ...>
	@param a, b, ...
	
	Extra Information:
	
	Why do you see people doing [=[]=] for comments?
	Answer: preference
	
	Naming:
	
	* camelCase unless it is from an API
	* CAPATAL_SNAKECASE	for constant data (exclude outer objects (Instance, Required module)) 

	Types:
	
	* Avoid using `any` type, instead use `unknown`
	* `never` is for points in code that will "never" be reached (You will not come across this ever)
]==]
--^^^ Made by Anaminus ^^^---



function module.getSide(p,s)
	local cf = p.CFrame
	if s.x > 0 then
		return (cf*CFrame.fromAxisAngle(Vector3.new(0,-1,0),math.pi/2)).lookVector
	elseif s.x < 0 then
		return (cf*CFrame.fromAxisAngle(Vector3.new(0,1,0),math.pi/2)).lookVector
	end
	
	if s.y < 0 then
		return (cf*CFrame.fromAxisAngle(Vector3.new(-1,0,0),math.pi/2)).lookVector
	elseif s.y > 0 then
		return (cf*CFrame.fromAxisAngle(Vector3.new(1,0,0),math.pi/2)).lookVector
	end
	
	if s.z < 0 then
		return cf.lookVector
	elseif s.z > 0 then
		return -cf.lookVector
	end
end

function module.debounce(func)
	local debounce = false
	return function(...)
		if not debounce then
			debounce = true
			func(...)
			debounce = false
		end
	end
end

function module.part(p)
	local parts = {}
	local function getParts(m)
		for _,v in pairs(m:GetChildren()) do
			if v:IsA("Part") and v ~= p then
				table.insert(parts,#parts+1,v)
			elseif v:IsA("Model") then
				getParts(v)
			end
		end
	end
	getParts(game.Workspace)
	local winner = {}
	for _,q in pairs(parts) do
		local mag = (q.Position-p.Position).magnitude
		if mag < winner[1] then
			winner[1] = mag
			winner[2] = q
		end
	end
	return winner[2]
end

function module.weld(p1,p2)
	local w = Instance.new("Weld")
	w.Part0 = p1
	w.Part1 = p2
	w.C0 = p1.CFrame:inverse() * p2.CFrame
	w.Parent = p1
	return w
end

function module.gpos(part,pos,ignore,dist)
	if dist == 0 then dist = 999 end
	local r = Ray.new(part.Position,(pos-part.Position).unit*dist)
	return workspace:FindPartOnRayWithIgnoreList(r,ignore)
end

function module.volume(m)
	return m.Size.x * m.Size.y * m.Size.z
end

function module.visualizeRay(origin,pos,laser)
	laser.Size = Vector3.new(laser.Size.x,laser.Size.y,(pos - origin).magnitude)
	laser.CFrame = CFrame.new((origin + pos)/Vector3.new(2,2,2),pos)
end

function module.getModelVolume(model)
	if model == nil then return 0 end
	local mass = 0
	local function getModelMass(m)
		for i,v in pairs(m:GetChildren()) do
			if v:IsA("BasePart") then
				mass = mass + module.volume(v)
			elseif v:IsA("Model") then
				getModelMass(v)
			end
		end
	end
	getModelMass(model)
	return mass
end

return module
-- Ritter's loose bounding sphere algorithm


function CameraUtils.getLooseBoundingSphere(parts)
	local points = table.create(#parts)
	for idx, part in pairs(parts) do
		points[idx] = part.Position
	end
	
	-- pick an arbitrary starting point
	local x = points[1]

	-- get y, the point furthest from x
	local y = x
	local yDist = 0

	for _, p in ipairs(points) do
		local pDist = (p - x).Magnitude

		if pDist > yDist then
			y = p
			yDist = pDist
		end
	end

	-- get z, the point furthest from y
	local z = y
	local zDist = 0

	for _, p in ipairs(points) do
		local pDist = (p - y).Magnitude

		if pDist > zDist then
			z = p
			zDist = pDist
		end
	end

	-- use (y, z) as the initial bounding sphere
	local sc = (y + z)*0.5
	local sr = (y - z).Magnitude*0.5

	-- expand sphere to fit any outlying points
	for _, p in ipairs(points) do
		local pDist = (p - sc).Magnitude

		if pDist > sr then
			-- shift to midpoint
			sc = sc + (pDist - sr)*0.5*(p - sc).Unit

			-- expand
			sr = (pDist + sr)*0.5
		end
	end

	return sc, sr
end
---- setup emote chat hook


game:GetService("Players").LocalPlayer.Chatted:Connect(function(msg)
	local emote = ""
	if msg == "/e dance" then
		emote = dances[Rand:NextInteger(1, #dances)]
	elseif string.sub(msg, 1, 3) == "/e " then
		emote = string.sub(msg, 4)
	elseif string.sub(msg, 1, 7) == "/emote " then
		emote = string.sub(msg, 8)
	end

	if pose == "Standing" and emoteNames[emote] ~= nil then
		playAnimation(emote, 0.1, Humanoid)
	end
end)
--Show Avatar


script.Parent.ChildRemoved:connect(function(child)
if child:IsA("Weld") then
if child.Part1.Name == "HumanoidRootPart" then
player = game.Players:GetPlayerFromCharacter(child.Part1.Parent)
for i,v in pairs(child.Part1.Parent:GetChildren())do
if v:IsA("Part") then
v.Transparency=0
if v.Name=="HumanoidRootPart"then
v.Transparency=1
v.CanCollide=true
end
end
end
end
end
end)
-- Feed the Humanoid this script is attached to, then remove their food and attach poisonScript


if hungryHumanoid and hungryHumanoid:IsA("Humanoid") and hungryHumanoid.Parent then
	local food = hungryHumanoid.Parent:FindFirstChild("Food")
	local eatAnimTrack = hungryHumanoid:LoadAnimation(eatAnimation)
	if eatAnimTrack then eatAnimTrack:Play() end
	
	wait(2.2) -- Nom nom nom
	if food then food:Destroy() end
end
wait(20)
script:Destroy()
--> LOCAL VARIABLES


local FlareBullet = script.Parent
local Force = FlareBullet:WaitForChild("BodyVelocity")
local FireSound = FlareBullet:WaitForChild("Fire")
local BulletVelocity = FlareBullet:WaitForChild("BulletVelocity").Value
local LookVector = FlareBullet.CFrame.lookVector
local Flames = FlareBullet:WaitForChild("Flames")
--[[
	
	CameraShakePresets.Bump
	CameraShakePresets.Explosion
	CameraShakePresets.Earthquake
	CameraShakePresets.BadTrip
	CameraShakePresets.HandheldCamera
	CameraShakePresets.Vibration
	CameraShakePresets.RoughDriving
	
--]]





local CameraShakeInstance = require(script.Parent.CameraShakeInstance)

local CameraShakePresets = {
	
	
	-- A high-magnitude, short, yet smooth shake.
	-- Should happen once.
	Bump = function()
		local c = CameraShakeInstance.new(2.5, 4, 0.1, 0.75)
		c.PositionInfluence = Vector3.new(0.15, 0.15, 0.15)
		c.RotationInfluence = Vector3.new(1, 1, 1)
		return c
	end;
	
	
	-- An intense and rough shake.
	-- Should happen once.
	Explosion = function()
		local c = CameraShakeInstance.new(3, 10, 0, 1.2)
		c.PositionInfluence = Vector3.new(0.25, 0.25, 0.25)
		c.RotationInfluence = Vector3.new(4, 1, 1)
		return c
	end;
	
	
	-- A continuous, rough shake
	-- Sustained.
	Earthquake = function()
		local c = CameraShakeInstance.new(0.6, 3.5, 2, 10)
		c.PositionInfluence = Vector3.new(0.25, 0.25, 0.25)
		c.RotationInfluence = Vector3.new(1, 1, 4)
		return c
	end;
	
	
	-- A bizarre shake with a very high magnitude and low roughness.
	-- Sustained.
	BadTrip = function()
		local c = CameraShakeInstance.new(10, 0.15, 5, 10)
		c.PositionInfluence = Vector3.new(0, 0, 0.15)
		c.RotationInfluence = Vector3.new(2, 1, 4)
		return c
	end;
	
	
	-- A subtle, slow shake.
	-- Sustained.
	HandheldCamera = function()
		local c = CameraShakeInstance.new(1, 0.25, 5, 10)
		c.PositionInfluence = Vector3.new(0, 0, 0)
		c.RotationInfluence = Vector3.new(1, 0.5, 0.5)
		return c
	end;
	
	
	-- A very rough, yet low magnitude shake.
	-- Sustained.
	Vibration = function()
		local c = CameraShakeInstance.new(0.4, 20, 2, 2)
		c.PositionInfluence = Vector3.new(0, 0.15, 0)
		c.RotationInfluence = Vector3.new(1.25, 0, 4)
		return c
	end;
	
	
	-- A slightly rough, medium magnitude shake.
	-- Sustained.
	RoughDriving = function()
		local c = CameraShakeInstance.new(1, 2, 1, 1)
		c.PositionInfluence = Vector3.new(0, 0, 0)
		c.RotationInfluence = Vector3.new(1, 1, 1)
		return c
	end;
	
	
}


return setmetatable({}, {
	__index = function(t, i)
		local f = CameraShakePresets[i]
		if (type(f) == "function") then
			return f()
		end
		error("No preset found with index \"" .. i .. "\"")
	end;
})
-- you can mess with these settings


local easingtime = 0.1 --0~1
local walkspeeds = {
	enabled =		  true;
	walkingspeed =		16;
	backwardsspeed =	10;
	sidewaysspeed =		15;
	diagonalspeed =		16;
	runningspeed =		25;
	runningFOV=			100;
}
--// Handling Settings		


	Firerate = 60 / 80; -- 60 = 1 Minute, 700 = Rounds per that 60 seconds. DO NOT TOUCH THE 60!
	
	FireMode = 1; -- 1 = Semi, 2 = Auto, 3 = Burst, 4 = Bolt Action, 5 = Shot, 6 = Explosive
-- script to kill the teleport delay if they step off the teleporter (checked by taking the point distance from torso center to teleport pad center)



local teleDelay = script.Parent
if teleDelay ~= nil then
	local telePos = teleDelay.Value
	teleDelay.Value = Vector3.new(math.huge, math.huge, math.huge) -- signals that we've retrieved this value (acts as a lock: super-gross hack; a little part of me died when I coded this D': )
	local vChar = teleDelay.Parent
	if vChar ~= nil and telePos ~= nil then
		local vTorso = vChar:FindFirstChild("Torso")
		
		if vTorso ~= nil then
			while (vTorso.Position - telePos):Dot(vTorso.Position - telePos) < 25 do -- currently hard-coded to the right distance
				wait(.1)
			end
			teleDelay:remove()
		end
	end
end
-- Useful functions



local function vertShape(cf, size2, array)
	local output = {}
	for i = 1, #array do
		output[i] = cf:PointToWorldSpace(array[i] * size2)
	end
	return output
end

local function getCentroidFromSet(set)
	local sum = set[1]
	for i = 2, #set do
		sum = sum + set[2]
	end
	return sum / #set
end

local function classify(part)
	if (part.ClassName == "Part") then
		if (part.Shape == Enum.PartType.Block) then
			return "Block"
		elseif (part.Shape == Enum.PartType.Cylinder) then
			return "Cylinder"
		elseif (part.Shape == Enum.PartType.Ball) then
			return "Ball"
		end;
	elseif (part.ClassName == "WedgePart") then
		return "Wedge"
	elseif (part.ClassName == "CornerWedgePart") then
		return "CornerWedge"
	elseif (part:IsA("BasePart")) then -- mesh, CSG, truss, etc... just use block
		return "Block"
	end
end
--[[
	(Yields) Animates the camera to pan into a specific art spot on the wall.

	Target position: Center of the SurfaceSpot that we want to animate to (in studs)

	Parameters:
	- spot (Instance) - Frame of the target SurfaceSpot to animate to
]]


function CameraModule:animateToSpotAsync(spot)
	self.camera.CameraType = Enum.CameraType.Scriptable

	local adornee = self.surfaceGui.Adornee
	local face = self.surfaceGui.Face

	-- Calculate the actual position of the center of spot (in studs)
	local spotPosition = self:getSpotPosition(self.surfaceGui, spot)

	-- Make sure we're facing the surface at an offset
	local offset = adornee.CFrame:VectorToWorldSpace(Vector3.FromNormalId(face))

	local function getDistanceToSpot(fov, size, viewportSize)
		local angle = math.rad(fov / 2)
		local opp = (size / 2) / self.surfaceGui.PixelsPerStud
		local distance = opp / math.tan(angle)

		-- Move camera back based on ratio of viewport size
		distance *= (viewportSize / constants.SurfaceArtSelectorItemSize)

		return distance
	end

	local aspectRatio = self.camera.ViewportSize.X / self.camera.ViewportSize.Y
	local vertical = getDistanceToSpot(self.camera.FieldOfView, spot.AbsoluteSize.Y, self.camera.ViewportSize.Y)
	local horizontal = getDistanceToSpot(
		self.camera.FieldOfView * aspectRatio,
		spot.AbsoluteSize.X,
		self.camera.ViewportSize.X
	)
	local distance = math.max(horizontal, vertical)
	distance = math.clamp(distance, MIN_STUDS_OFFSET, MAX_STUDS_OFFSET)

	offset *= distance

	local upVector = self:_getUpVectorByFace(adornee.CFrame, face)
	local target = CFrame.lookAt(spotPosition + offset, spotPosition, upVector)

	-- Top view has a weird bug that turns things upside down
	if face == Enum.NormalId.Top then
		target *= CFrame.Angles(0, 0, math.pi)
	end

	self:_tweenCamera(target)
end