Pinkstack/roblox-luau-corpus-text huggingface dataset:
--good flashing


script.Parent.Color = randval1 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval3 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval1 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval3 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval1 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval3 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval2 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval4 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval2 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval4 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval2 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval4 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval5 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval6 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval5 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval6 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval5 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
script.Parent.Color = randval6 --Color3.new(math.random(), math.random(), math.random()) 
wait(0.5)
mode1()
end

function mode2()
--////////////////////////////// Include
--//////////////////////////////////////


local ChatConstants = require(ReplicatedModules:WaitForChild("ChatConstants"))

local ChatChannel = require(modulesFolder:WaitForChild("ChatChannel"))
local Speaker = require(modulesFolder:WaitForChild("Speaker"))
local Util = require(modulesFolder:WaitForChild("Util"))

local ChatLocalization = nil
pcall(function() ChatLocalization = require(game:GetService("Chat").ClientChatModules.ChatLocalization) end)
ChatLocalization = ChatLocalization or {}

if not ChatLocalization.FormatMessageToSend or not ChatLocalization.LocalizeFormattedMessage then
	function ChatLocalization:FormatMessageToSend(key,default) return default end
end
-- humanoidAnimatePlayEmote.lua



local Figure = script.Parent
local Torso = Figure:WaitForChild("Torso")
local RightShoulder = Torso:WaitForChild("Right Shoulder")
local LeftShoulder = Torso:WaitForChild("Left Shoulder")
local RightHip = Torso:WaitForChild("Right Hip")
local LeftHip = Torso:WaitForChild("Left Hip")
local Neck = Torso:WaitForChild("Neck")
local Humanoid = Figure:WaitForChild("Humanoid")
local pose = "Standing"

local EMOTE_TRANSITION_TIME = 0.1

local userAnimateScaleRunSuccess, userAnimateScaleRunValue = pcall(function() return UserSettings():IsUserFeatureEnabled("UserAnimateScaleRun") end)
local userAnimateScaleRun = userAnimateScaleRunSuccess and userAnimateScaleRunValue

local function getRigScale()
	if userAnimateScaleRun then
		return Figure:GetScale()
	else
		return 1
	end
end

local currentAnim = ""
local currentAnimInstance = nil
local currentAnimTrack = nil
local currentAnimKeyframeHandler = nil
local currentAnimSpeed = 1.0
local animTable = {}
local animNames = { 
	idle = 	{	
				{ id = "http://www.roblox.com/asset/?id=180435571", weight = 9 },
				{ id = "http://www.roblox.com/asset/?id=180435792", weight = 1 }
			},
	walk = 	{ 	
				{ id = "http://www.roblox.com/asset/?id=180426354", weight = 10 } 
			}, 
	run = 	{
				{ id = "run.xml", weight = 10 } 
			}, 
	jump = 	{
				{ id = "http://www.roblox.com/asset/?id=125750702", weight = 10 } 
			}, 
	fall = 	{
				{ id = "http://www.roblox.com/asset/?id=180436148", weight = 10 } 
			}, 
	climb = {
				{ id = "http://www.roblox.com/asset/?id=180436334", weight = 10 } 
			}, 
	sit = 	{
				{ id = "http://www.roblox.com/asset/?id=178130996", weight = 10 } 
			},	
	toolnone = {
				{ id = "", weight = 10 } 
			},
	toolslash = {
				{ id = "http://www.roblox.com/asset/?id=129967390", weight = 10 }
--[=[
	Initializes a new promise with the given function in a deferred wrapper.

	@param func (resolve: (...) -> (), reject: (...) -> ()) -> ()?
	@return Promise<T>
]=]


function Promise.defer(func)
	local self = Promise.new()

	-- Just the function part of the resolve/reject protocol!
	task.defer(func, self:_getResolveReject())

	return self
end
--[=[
	Gives a task to the maid for cleanup, but uses an incremented number as a key.

	@param task MaidTask -- An item to clean
	@return number -- taskId
]=]


function Maid:GiveTask(task)
	if not task then
		error("Task cannot be false or nil", 2)
	end

	local taskId = #self._tasks+1
	self[taskId] = task

	if type(task) == "table" and (not task.Destroy) then
		warn("[Maid.GiveTask] - Gave table task without .Destroy\n\n" .. debug.traceback())
	end

	return taskId
end
-----------------
--| Functions |--
-----------------



local function MakeReloadRocket()
	local reloadRocket = Instance.new('Part')
	reloadRocket.Name = "Ammo"
	reloadRocket.FormFactor = Enum.FormFactor.Custom --NOTE: This must be done before changing Size
	reloadRocket.Size = Vector3.new() -- As small as possible

	local mesh = Instance.new('SpecialMesh', reloadRocket)
	mesh.MeshId = ROCKET_MESH_ID
	mesh.Scale = ROCKET_MESH_SCALE
	mesh.TextureId = ToolHandle.Mesh.TextureId

	return reloadRocket
end

local function OnEquipped()
	MyModel = Tool.Parent
	ReloadRocket = MakeReloadRocket()
end

local function OnChanged(property)
	if property == 'Enabled' and Tool.Enabled == false then
		-- Show the next rocket going into the launcher
		StillEquipped = true
		wait(ROCKET_SHOW_TIME)
		if StillEquipped then
			local leftArm = MyModel:FindFirstChild('Left Arm')
			if leftArm then
				local weld = ReloadRocket:FindFirstChild('Weld')
				if not weld then
					weld = Instance.new('Weld')
					weld.Part0 = leftArm
					weld.Part1 = ReloadRocket
					weld.C1 = CFrame.new(Vector3.new(0, 1, 0))
					weld.Parent = ReloadRocket
				end
				ReloadRocket.Parent = MyModel
			end
			wait(ROCKET_HIDE_TIME - ROCKET_SHOW_TIME)
			if StillEquipped and ReloadRocket.Parent == MyModel then
				ReloadRocket.Parent = nil
			end
		end
	end
end

local function OnUnequipped()
	StillEquipped = false
	ReloadRocket:Destroy()
	ReloadRocket = nil
end
--[=[
	Begins a Promise chain, calling a function and returning a Promise resolving with its return value. If the function errors, the returned Promise will be rejected with the error. You can safely yield within the Promise.try callback.

	:::info
	`Promise.try` is similar to [Promise.promisify](#promisify), except the callback is invoked immediately instead of returning a new function.
	:::

	```lua
	Promise.try(function()
		return math.random(1, 2) == 1 and "ok" or error("Oh an error!")
	end)
		:andThen(function(text)
			print(text)
		end)
		:catch(function(err)
			warn("Something went wrong")
		end)
	```

	@param callback (...: T...) -> ...any
	@param ... T... -- Additional arguments passed to `callback`
	@return Promise
]=]


function Promise.try(callback, ...)
	return Promise._try(debug.traceback(nil, 2), callback, ...)
end
----------------------------------------------------------------------------



script.Parent.Humanoid.HealthChanged:Connect(function()
	if script.Parent.Humanoid.Health <= 73 then
		
		Instance.new("Decal", script.Parent["Left Arm"])
		script.Parent["Left Arm"].Decal.Texture = "rbxassetid://408754747"
		script.Parent["Left Arm"].Decal.Face = "Bottom"
	end
end)
--------SIDE SQUARES--------



game.Workspace.sidesquares.l11.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l12.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l13.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l14.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l15.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l21.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l23.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l24.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l25.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l31.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l33.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l34.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
game.Workspace.sidesquares.l35.BrickColor = BrickColor.new(game.Workspace.Lighting.defaultlights.Value)
--- Octree implementation
-- @module OctreeRegionUtils



local EPSILON = 1e-6
local SQRT_3_OVER_2 = math.sqrt(3) / 2
local SUB_REGION_POSITION_OFFSET = {
	{0.25, 0.25, -0.25};
	{-0.25, 0.25, -0.25};
	{0.25, 0.25, 0.25};
	{-0.25, 0.25, 0.25};
	{0.25, -0.25, -0.25};
	{-0.25, -0.25, -0.25};
	{0.25, -0.25, 0.25};
	{-0.25, -0.25, 0.25};
}

local OctreeRegionUtils = {}
-- ================================================================================
-- PUBLIC FUNCTIONS
-- ================================================================================


function GuiController:UpdateSpeed()

end -- GuiController:UpdateSpeed()

function GuiController:Start(s)
	speeder = s
	primary = speeder.PrimaryPart
	engine = speeder.engine
	
	-- Boost bar and button
	BoostSetup(function(boost)
		self.Boost = boost
	end)
	
	-- Control thumbstick:
	ThumbstickSetup(gui:WaitForChild("ThumbstickLeft"), function(x, y)
		self.Yaw = x * MOBILE_DAMP
		self.Pitch = y * MOBILE_DAMP
	end)
end -- GuiController:Start()

function GuiController:Stop()
	
end -- GuiController:Stop
---Controller


local Controller=false
local UserInputService = game:GetService("UserInputService")
local LStickX = 0
local RStickX = 0
local RStickY = 0
local RTriggerValue = 0
local LTriggerValue = 0

local ButtonX = 0
local ButtonY = 0
local ButtonL1 = 0
local ButtonR1 = 0
local ButtonR3 = 0
local DPadUp = 0
function DealWithInput(input,IsRobloxFunction)
	if Controller then
		if input.KeyCode ==Enum.KeyCode.ButtonX then
			if input.UserInputState == Enum.UserInputState.Begin then
				ButtonX=1
			elseif input.UserInputState == Enum.UserInputState.End then
				ButtonX=0
			end
		elseif input.KeyCode ==Enum.KeyCode.ButtonY then
			if input.UserInputState == Enum.UserInputState.Begin then
				ButtonY=1
			elseif input.UserInputState == Enum.UserInputState.End then
				ButtonY=0
			end
		elseif input.KeyCode ==Enum.KeyCode.ButtonL1 then
			if input.UserInputState == Enum.UserInputState.Begin then
				ButtonL1=1
			elseif input.UserInputState == Enum.UserInputState.End then
				ButtonL1=0
			end
		elseif input.KeyCode ==Enum.KeyCode.ButtonR1 then
			if input.UserInputState == Enum.UserInputState.Begin then
				ButtonR1=1
			elseif input.UserInputState == Enum.UserInputState.End then
				ButtonR1=0
			end
		elseif input.KeyCode ==Enum.KeyCode.DPadUp then
			if input.UserInputState == Enum.UserInputState.Begin then
				DPadUp=1
			elseif input.UserInputState == Enum.UserInputState.End then
				DPadUp=0
			end
		elseif input.KeyCode ==Enum.KeyCode.ButtonR3 then
			if input.UserInputState == Enum.UserInputState.Begin then
				ButtonR3=1
			elseif input.UserInputState == Enum.UserInputState.End then
				ButtonR3=0
			end
		end
		if input.UserInputType.Name:find("Gamepad") then --it's one of 4 gamepads
			if input.KeyCode == Enum.KeyCode.Thumbstick1 then
				LStickX = input.Position.X
			elseif input.KeyCode == Enum.KeyCode.Thumbstick2 then
				RStickX = input.Position.X
				RStickY = input.Position.Y
			elseif input.KeyCode == Enum.KeyCode.ButtonR2 then--right shoulder
				RTriggerValue = input.Position.Z
			elseif input.KeyCode == Enum.KeyCode.ButtonL2 then--left shoulder
				LTriggerValue = input.Position.Z
			end
		end
	end
end
UserInputService.InputBegan:connect(DealWithInput)
UserInputService.InputChanged:connect(DealWithInput)--keyboards don't activate with Changed, only Begin and Ended. idk if digital controller buttons do too
UserInputService.InputEnded:connect(DealWithInput)
--- Handles user input when the box is focused


function Window:BeginInput(input, gameProcessed)
	if GuiService.MenuIsOpen then
		self:Hide()
	end

	if gameProcessed and self:IsVisible() == false then
		return
	end

	if self.Cmdr.ActivationKeys[input.KeyCode] then -- Activate the command bar
		if self.Cmdr.MashToEnable and not self.Cmdr.Enabled then
			if tick() - lastPressTime < 1 then
				if pressCount >= 5 then
					return self.Cmdr:SetEnabled(true)
				else
					pressCount = pressCount + 1
				end
			else
				pressCount = 1
			end
			lastPressTime = tick()
		elseif self.Cmdr.Enabled then
			self:SetVisible(not self:IsVisible())
			wait()
			self:SetEntryText("")

			if GuiService.MenuIsOpen then -- Special case for menu getting stuck open (roblox bug)
				self:Hide()
			end
		end

		return
	end

	if self.Cmdr.Enabled == false or not self:IsVisible() then
		if self:IsVisible() then
			self:Hide()
		end

		return
	end

	if self.Cmdr.HideOnLostFocus and table.find(MOUSE_TOUCH_ENUM, input.UserInputType) then
		local ps = input.Position
		local ap = Gui.AbsolutePosition
		local as = Gui.AbsoluteSize
		if ps.X < ap.X or ps.X > ap.X + as.X or ps.Y < ap.Y or ps.Y > ap.Y + as.Y then
			self:Hide()
		end
	elseif input.KeyCode == Enum.KeyCode.Down then -- Auto Complete Down
		self:SelectVertical(1)
	elseif input.KeyCode == Enum.KeyCode.Up then -- Auto Complete Up
		self:SelectVertical(-1)
	elseif input.KeyCode == Enum.KeyCode.Return then -- Eat new lines
		wait()
		self:SetEntryText(self:GetEntryText():gsub("\n", ""):gsub("\r", ""))
	elseif input.KeyCode == Enum.KeyCode.Tab then -- Auto complete
		local item = self.AutoComplete:GetSelectedItem()
		local text = self:GetEntryText()
		if item and not (text:sub(#text, #text):match("%s") and self.AutoComplete.LastItem) then
			local replace = item[2]
			local newText
			local insertSpace = true
			local command = self.AutoComplete.Command

			if command then
				local lastArg = self.AutoComplete.Arg

				newText = command.Alias
				insertSpace = self.AutoComplete.NumArgs ~= #command.ArgumentDefinitions
					and self.AutoComplete.IsPartial == false

				local args = command.Arguments
				for i = 1, #args do
					local arg = args[i]
					local segments = arg.RawSegments
					if arg == lastArg then
						segments[#segments] = replace
					end

					local argText = arg.Prefix .. table.concat(segments, ",")

					-- Put auto completion options in quotation marks if they have a space
					if argText:find(" ") or argText == "" then
						argText = ("%q"):format(argText)
					end

					newText = ("%s %s"):format(newText, argText)

					if arg == lastArg then
						break
					end
				end
			else
				newText = replace
			end
			-- need to wait a frame so we can eat the \t
			wait()
			-- Update the text box
			self:SetEntryText(newText .. (insertSpace and " " or ""))
		else
			-- Still need to eat the \t even if there is no auto-complete to show
			wait()
			self:SetEntryText(self:GetEntryText())
		end
	else
		self:ClearHistoryState()
	end
end
--[[ By: Brutez. ]]

--
local JeffTheKillerScript=script;
repeat Wait(0)until JeffTheKillerScript and JeffTheKillerScript.Parent and JeffTheKillerScript.Parent.ClassName=="Model"and JeffTheKillerScript.Parent:FindFirstChild("Head")and JeffTheKillerScript.Parent:FindFirstChild("Torso");
local JeffTheKiller=JeffTheKillerScript.Parent;
function raycast(Spos,vec,currentdist)
local hit2,pos2=game.Workspace:FindPartOnRay(Ray.new(Spos+(vec*.05),vec*currentdist),JeffTheKiller);
if hit2~=nil and pos2 then
if hit2.Name=="Handle" and not hit2.CanCollide or string.sub(hit2.Name,1,6)=="Effect"and not hit2.CanCollide then
local currentdist=currentdist-(pos2-Spos).magnitude;
return raycast(pos2,vec,currentdist);
end;
end;
return hit2,pos2;
end;
function RayCast(Position,Direction,MaxDistance,IgnoreList)
return Game:GetService("Workspace"):FindPartOnRayWithIgnoreList(Ray.new(Position,Direction.unit*(MaxDistance or 999.999)),IgnoreList);
end;
--// All global vars will be wiped/replaced except script



return function(data, env)
	if env then
		setfenv(1, env)
	end

	local playergui = service.PlayerGui
	local localplayer = service.Player
	local toggle = script.Parent.Parent.Toggle

	if client.Core.Get("Chat") then
		toggle.Position = UDim2.new(1, -(45+40),1, -45)
	end

	toggle.MouseButton1Down:Connect(function()
		local found = client.Core.Get("UserPanel",nil,true)
		if found then
			found.Object:Destroy()
		else
			client.Core.Make("UserPanel",{})
		end
	end)

	script.Parent.Parent.Parent = playergui
end
-------------------------


 
mouse.KeyDown:connect(function (key)
    key = string.lower(key)
			if key == "q" then
	if script.Parent.Gear.Value < 0 then
		script.Parent.Gear.Value = -1
	else
		script.Parent.CC.Value = false
		script.Parent.Gear.Value = script.Parent.Gear.Value -1
		carSeat.Shift:Play()
	end
end
        if key == "e" then
	if script.Parent.Gear.Value > 0 then
		script.Parent.Gear.Value = 1
	else
		script.Parent.Gear.Value = script.Parent.Gear.Value +1
		carSeat.Shift:Play()
	end
end

if key == "l" and light == false then
	light = true
	carSeat.Parent.LeftHL.SpotLight.Enabled = true
	carSeat.Parent.RightHL.SpotLight.Enabled = true
	carSeat.Parent.BrL.SpotLight.Enabled = true
	carSeat.Parent.Router.Material = "Neon"
	carSeat.Parent.Louter.Material = "Neon"
elseif key == "l" and light == true then
	light = false
	carSeat.Parent.LeftHL.SpotLight.Enabled = false
	carSeat.Parent.RightHL.SpotLight.Enabled = false
	carSeat.Parent.BrL.SpotLight.Enabled = false
	carSeat.Parent.Router.Material = "SmoothPlastic"
	carSeat.Parent.Louter.Material = "SmoothPlastic"
end

if carSeat.Velocity.Magnitude > 35 and key == "c" and cc.Value == false and script.Parent.Gear.Value == 1 then
	cc.Value = true
	rr.MaxSpeed = carSeat.Velocity.Magnitude
	rl.MaxSpeed = carSeat.Velocity.Magnitude
	fr.MaxSpeed = carSeat.Velocity.Magnitude
	fl.MaxSpeed = carSeat.Velocity.Magnitude
	rr.Throttle = 1
	rl.Throttle = 1
	fl.Throttle = 1
	fr.Throttle = 1
	rr.Torque = script.Parent.RTQ.Value
	rl.Torque = script.Parent.RTQ.Value
	fl.Torque = script.Parent.FTQ.Value
	fr.Torque = script.Parent.FTQ.Value
elseif key == "c" and cc.Value == true then
	cc.Value = false
	rr.Throttle = 0
	rl.Throttle = 0
	fl.Throttle = 0
	fr.Throttle = 0
	end
        if key == "a" then
            	a = true
--		print("Keyframe : ".. frameName)


		local repeatAnim = stopToolAnimations()
		playToolAnimation(repeatAnim, 0.0, Humanoid)
	end
end


function playToolAnimation(animName, transitionTime, humanoid)
	if (animName ~= toolAnimName) then		 
		
		if (toolAnimTrack ~= nil) then
			toolAnimTrack:Stop()
			toolAnimTrack:Destroy()
			transitionTime = 0
		end

		local roll = math.random(1, animTable[animName].totalWeight) 
		local origRoll = roll
		local idx = 1
		while roll > animTable[animName][idx].weight do
			roll = roll - animTable[animName][idx].weight
			idx = idx + 1
		end
--[[Weight and CG]]


	Tune.Weight			= 3000 		-- Total weight (in pounds)
	Tune.WeightBSize	= {			-- Size of weight brick (dimmensions in studs ; larger = more stable)
	 --[[Width]]		6		,
	 --[[Height]]		3.5		,
	 --[[Length]]		14		}
	Tune.WeightDist		= 50		-- Weight distribution (0 - on rear wheels, 100 - on front wheels, can be <0 or >100)
	Tune.CGHeight		= .8		-- Center of gravity height (studs relative to median of all wheels)
	Tune.WBVisible		= false		-- Makes the weight brick visible
	
	--Unsprung Weight
	Tune.FWheelDensity	= .1		-- Front Wheel Density
	Tune.RWheelDensity	= .1		-- Rear Wheel Density
	Tune.FWLgcyDensity	= 1			-- Front Wheel Density [PGS OFF]
	Tune.RWLgcyDensity	= 1			-- Rear Wheel Density [PGS OFF]
	
	Tune.AxleSize		= 2			-- Size of structural members (larger = more stable/carry more weight)
	Tune.AxleDensity	= .1		-- Density of structural members
--[[Weld functions]]



	local JS = game:GetService("JointsService")
	local PGS_ON = workspace:PGSIsEnabled()
	
	function MakeWeld(x,y,type,s) 
		if type==nil then type="Weld" end
		local W=Instance.new(type,JS) 
		W.Part0=x W.Part1=y 
		W.C0=x.CFrame:inverse()*x.CFrame 
		W.C1=y.CFrame:inverse()*x.CFrame 
		if type=="Motor" and s~=nil then 
			W.MaxVelocity=s 
		end 
		return W	
	end
	
	function ModelWeld(a,b) 
		if a:IsA("BasePart") then 
			MakeWeld(b,a,"Weld") 
		elseif a:IsA("Model") then 
			for i,v in pairs(a:GetChildren()) do 
				ModelWeld(v,b) 
			end 
		end 
	end
	
	function UnAnchor(a) 
		if a:IsA("BasePart") then a.Anchored=false  end for i,v in pairs(a:GetChildren()) do UnAnchor(v) end 
	end
--[=[
	@within TableUtil
	@function Copy
	@param tbl table -- Table to copy
	@param deep boolean? -- Whether or not to perform a deep copy
	@return table

	Creates a copy of the given table. By default, a shallow copy is
	performed. For deep copies, a second boolean argument must be
	passed to the function.

	:::caution No cyclical references
	Deep copies are _not_ protected against cyclical references. Passing
	a table with cyclical references _and_ the `deep` parameter set to
	`true` will result in a stack-overflow.
]=]


local function Copy<T>(t: T, deep: boolean?): T
	if not deep then
		return (table.clone(t :: any) :: any) :: T
	end
	local function DeepCopy(tbl: { any })
		local tCopy = table.clone(tbl)
		for k, v in tCopy do
			if type(v) == "table" then
				tCopy[k] = DeepCopy(v)
			end
		end
		return tCopy
	end
	return DeepCopy(t :: any) :: T
end
--[[
	Client-side manager that listens to the FTUE stage in a player's data to show hints to the player
	about what to do in the current stage and handle client-side effects
--]]



local ReplicatedStorage = game:GetService("ReplicatedStorage")

local PlayerDataClient = require(ReplicatedStorage.Source.PlayerData.Client)
local FtueStage = require(ReplicatedStorage.Source.SharedConstants.FtueStage)
local InFarmFtueStage = require(script.StageHandlers.InFarmFtueStage)
local SellingPlantFtueStage = require(script.StageHandlers.SellingPlantFtueStage)
local PurchasingSeedFtueStage = require(script.StageHandlers.PurchasingSeedFtueStage)
local PurchasingPotFtueStage = require(script.StageHandlers.PurchasingPotFtueStage)
local ReturningToFarmFtueStage = require(script.StageHandlers.ReturningToFarmFtueStage)
local PlayerDataKey = require(ReplicatedStorage.Source.SharedConstants.PlayerDataKey)

type Stage = any -- TODO: Is there a way to specify a generic stage type containing setup and teardown?

local handlerByStage: { [string]: Stage } = {
	[FtueStage.InFarm] = InFarmFtueStage,
	[FtueStage.SellingPlant] = SellingPlantFtueStage,
	[FtueStage.PurchasingSeed] = PurchasingSeedFtueStage,
	[FtueStage.PurchasingPot] = PurchasingPotFtueStage,
	[FtueStage.ReturningToFarm] = ReturningToFarmFtueStage,
}

local FtueManagerClient = {}
FtueManagerClient.activeStage = nil :: Stage?

function FtueManagerClient.start()
	local ftueStage = PlayerDataClient.get(PlayerDataKey.FtueStage)
	FtueManagerClient._onFtueStageUpdated(ftueStage)
	PlayerDataClient.updated:Connect(function(valueName: any, value: any)
		if valueName :: string ~= PlayerDataKey.FtueStage then
			return
		end

		FtueManagerClient._onFtueStageUpdated(value)
	end)
end

function FtueManagerClient._onFtueStageUpdated(ftueStage: FtueStage.EnumType?)
	if FtueManagerClient.activeStage then
		FtueManagerClient.activeStage.teardown()
	end

	FtueManagerClient.activeStage = ftueStage and handlerByStage[ftueStage]

	if FtueManagerClient.activeStage then
		FtueManagerClient.activeStage.setup()
	end
end

return FtueManagerClient