<roblox version="4">
	<Item class="Workspace" referent="RBXDB84F286281869208FB639E1D4DDFDDE">
		<Properties>
			<string name="Name">Workspace</string>
			<float name="Gravity">196.2</float>
			<float name="FallenPartsDestroyHeight">-500</float>
			<Ref name="CurrentCamera">RBX954E8B47B46A3F2B34DF83ECC3D3C294</Ref>
		</Properties>
		<Item class="Camera" referent="RBX954E8B47B46A3F2B34DF83ECC3D3C294">
			<Properties>
				<string name="Name">Camera</string>
				<CoordinateFrame name="CFrame"><X>0</X><Y>62</Y><Z>95</Z><R00>1</R00><R01>0</R01><R02>0</R02><R10>0</R10><R11>1</R11><R12>0</R12><R20>0</R20><R21>0</R21><R22>1</R22></CoordinateFrame>
				<float name="FieldOfView">70</float>
				<token name="CameraType">5</token>
			</Properties>
		</Item>
		<Item class="Terrain" referent="RBX81FD49D945AADA2E40529EFFC5A08808">
			<Properties>
				<string name="Name">Terrain</string>
			</Properties>
		</Item>
		<Item class="Part" referent="RBXD5FA9714C74DC02E1BCFB7C94CF588C0">
			<Properties>
				<string name="Name">Baseplate</string>
				<bool name="Anchored">true</bool>
				<bool name="CanCollide">true</bool>
				<bool name="CastShadow">true</bool>
				<bool name="Locked">false</bool>
				<CoordinateFrame name="CFrame"><X>0</X><Y>-10</Y><Z>0</Z><R00>1</R00><R01>0</R01><R02>0</R02><R10>0</R10><R11>1</R11><R12>0</R12><R20>0</R20><R21>0</R21><R22>1</R22></CoordinateFrame>
				<Color3uint8 name="Color3uint8">4284702562</Color3uint8>
				<token name="Material">256</token>
				<Vector3 name="size"><X>512</X><Y>20</Y><Z>512</Z></Vector3>
				<token name="TopSurface">0</token>
				<token name="BottomSurface">0</token>
				<float name="Transparency">0</float>
				<token name="shape">1</token>
			</Properties>
		</Item>
		<Item class="Folder" referent="RBX6CFC3EF69D2771BD4910BD61B373A5D1">
			<Properties>
				<string name="Name">Lobby</string>
			</Properties>
			<Item class="Part" referent="RBX959E412DAA7F5406F8A8383C9AC301ED">
				<Properties>
					<string name="Name">LobbyFloor</string>
					<bool name="Anchored">true</bool>
					<bool name="CanCollide">true</bool>
					<bool name="CastShadow">true</bool>
					<bool name="Locked">false</bool>
					<CoordinateFrame name="CFrame"><X>0</X><Y>0</Y><Z>110</Z><R00>1</R00><R01>0</R01><R02>0</R02><R10>0</R10><R11>1</R11><R12>0</R12><R20>0</R20><R21>0</R21><R22>1</R22></CoordinateFrame>
					<Color3uint8 name="Color3uint8">4294956664</Color3uint8>
					<token name="Material">272</token>
					<Vector3 name="size"><X>60</X><Y>1</Y><Z>60</Z></Vector3>
					<token name="TopSurface">0</token>
					<token name="BottomSurface">0</token>
					<float name="Transparency">0</float>
					<token name="shape">1</token>
				</Properties>
			</Item>
			<Item class="SpawnLocation" referent="RBX59D9C16D8B71D282A6CA4952AEF52C47">
				<Properties>
					<string name="Name">LobbySpawn</string>
					<bool name="Anchored">true</bool>
					<bool name="CanCollide">true</bool>
					<bool name="CastShadow">true</bool>
					<bool name="Locked">false</bool>
					<CoordinateFrame name="CFrame"><X>0</X><Y>1</Y><Z>110</Z><R00>1</R00><R01>0</R01><R02>0</R02><R10>0</R10><R11>1</R11><R12>0</R12><R20>0</R20><R21>0</R21><R22>1</R22></CoordinateFrame>
					<Color3uint8 name="Color3uint8">4283463920</Color3uint8>
					<token name="Material">272</token>
					<Vector3 name="size"><X>12</X><Y>1</Y><Z>12</Z></Vector3>
					<token name="TopSurface">0</token>
					<token name="BottomSurface">0</token>
					<float name="Transparency">0</float>
					<int name="Duration">0</int>
					<bool name="Enabled">true</bool>
					<bool name="Neutral">true</bool>
				</Properties>
			</Item>
		</Item>
		<Item class="Part" referent="RBXFC1B95736452B9282B246BA843B3E963">
			<Properties>
				<string name="Name">Sign</string>
				<bool name="Anchored">true</bool>
				<bool name="CanCollide">true</bool>
				<bool name="CastShadow">true</bool>
				<bool name="Locked">false</bool>
				<CoordinateFrame name="CFrame"><X>0</X><Y>66</Y><Z>-39</Z><R00>1</R00><R01>0</R01><R02>0</R02><R10>0</R10><R11>1</R11><R12>0</R12><R20>0</R20><R21>0</R21><R22>1</R22></CoordinateFrame>
				<Color3uint8 name="Color3uint8">4294637030</Color3uint8>
				<token name="Material">272</token>
				<Vector3 name="size"><X>30</X><Y>12</Y><Z>1</Z></Vector3>
				<token name="TopSurface">0</token>
				<token name="BottomSurface">0</token>
				<float name="Transparency">0</float>
				<token name="shape">1</token>
			</Properties>
			<Item class="SurfaceGui" referent="RBX089D6EB92A44C1F7231522E6D93A2623">
				<Properties>
					<string name="Name">SurfaceGui</string>
					<token name="Face">2</token>
					<token name="SizingMode">1</token>
					<float name="LightInfluence">0</float>
				</Properties>
				<Item class="TextLabel" referent="RBX98AE02F13145D721F076F3C86F24B61D">
					<Properties>
						<string name="Name">TextLabel</string>
						<UDim2 name="Size"><XS>1</XS><XO>0</XO><YS>1</YS><YO>0</YO></UDim2>
						<float name="BackgroundTransparency">1</float>
						<string name="Text">DO NOT PRESS</string>
						<bool name="TextScaled">true</bool>
						<token name="Font">26</token>
						<Color3 name="TextColor3"><R>0.7843137</R><G>0.0980392</G><B>0.0980392</B></Color3>
					</Properties>
				</Item>
			</Item>
		</Item>
		<Item class="Model" referent="RBX8E486BBFF217626B48297C82AED4B4A2">
			<Properties>
				<string name="Name">TheButton</string>
				<Ref name="PrimaryPart">RBX463A8BCEBCD11FC2326A69CDE3C29B3C</Ref>
			</Properties>
			<Item class="Part" referent="RBX463A8BCEBCD11FC2326A69CDE3C29B3C">
				<Properties>
					<string name="Name">Pedestal</string>
					<bool name="Anchored">true</bool>
					<bool name="CanCollide">true</bool>
					<bool name="CastShadow">true</bool>
					<bool name="Locked">false</bool>
					<CoordinateFrame name="CFrame"><X>0</X><Y>51.5</Y><Z>0</Z><R00>0</R00><R01>-1</R01><R02>0</R02><R10>1</R10><R11>0</R11><R12>0</R12><R20>0</R20><R21>0</R21><R22>1</R22></CoordinateFrame>
					<Color3uint8 name="Color3uint8">4294297640</Color3uint8>
					<token name="Material">272</token>
					<Vector3 name="size"><X>3</X><Y>14</Y><Z>14</Z></Vector3>
					<token name="TopSurface">0</token>
					<token name="BottomSurface">0</token>
					<float name="Transparency">0</float>
					<token name="shape">2</token>
				</Properties>
			</Item>
			<Item class="Part" referent="RBX6A84378776FD5DEB388EA925A7744A3B">
				<Properties>
					<string name="Name">Cap</string>
					<bool name="Anchored">true</bool>
					<bool name="CanCollide">true</bool>
					<bool name="CastShadow">true</bool>
					<bool name="Locked">false</bool>
					<CoordinateFrame name="CFrame"><X>0</X><Y>54.5</Y><Z>0</Z><R00>0</R00><R01>-1</R01><R02>0</R02><R10>1</R10><R11>0</R11><R12>0</R12><R20>0</R20><R21>0</R21><R22>1</R22></CoordinateFrame>
					<Color3uint8 name="Color3uint8">4293601320</Color3uint8>
					<token name="Material">272</token>
					<Vector3 name="size"><X>3</X><Y>8</Y><Z>8</Z></Vector3>
					<token name="TopSurface">0</token>
					<token name="BottomSurface">0</token>
					<float name="Transparency">0</float>
					<token name="shape">2</token>
				</Properties>
				<Item class="ProximityPrompt" referent="RBXF44C111F42D939712905864A379B9A02">
					<Properties>
						<string name="Name">ProximityPrompt</string>
						<string name="ActionText">PRESS IT?</string>
						<string name="ObjectText">DO NOT PRESS</string>
						<float name="HoldDuration">0</float>
						<float name="MaxActivationDistance">12</float>
						<bool name="RequiresLineOfSight">false</bool>
						<bool name="Enabled">false</bool>
					</Properties>
				</Item>
			</Item>
		</Item>
	</Item>
	<Item class="Players" referent="RBXCDE6F48B97C2240CD8A5362A8E05C99D">
		<Properties>
			<string name="Name">Players</string>
		</Properties>
	</Item>
	<Item class="Lighting" referent="RBXB8573135967349761EDF3E8E9C3CC2E7">
		<Properties>
			<string name="Name">Lighting</string>
			<float name="ClockTime">14</float>
			<float name="Brightness">3</float>
			<Color3 name="Ambient"><R>0.4313725</R><G>0.4313725</G><B>0.4901961</B></Color3>
			<Color3 name="OutdoorAmbient"><R>0.5490196</R><G>0.5882353</G><B>0.6666667</B></Color3>
			<float name="EnvironmentDiffuseScale">0.6</float>
			<float name="EnvironmentSpecularScale">0.3</float>
			<token name="LightingStyle">1</token>
			<bool name="PrioritizeLightingQuality">false</bool>
		</Properties>
		<Item class="Atmosphere" referent="RBX4AED0B7E54ED4A43DFA352E949A1A59E">
			<Properties>
				<string name="Name">Atmosphere</string>
				<float name="Density">0.25</float>
				<float name="Offset">0.1</float>
				<Color3 name="Color"><R>0.7803922</R><G>0.8823529</G><B>1</B></Color3>
				<Color3 name="Decay"><R>1</R><G>0.9019608</G><B>0.7843137</B></Color3>
				<float name="Haze">0.8</float>
				<float name="Glare">0</float>
			</Properties>
		</Item>
		<Item class="BloomEffect" referent="RBX29C0E0D03EBF0536AD359FA847EA4614">
			<Properties>
				<string name="Name">Bloom</string>
				<float name="Intensity">0.6</float>
				<float name="Size">24</float>
				<float name="Threshold">1.6</float>
				<bool name="Enabled">true</bool>
			</Properties>
		</Item>
		<Item class="ColorCorrectionEffect" referent="RBXBD1DE842515FA780E76095779718EFC2">
			<Properties>
				<string name="Name">ColorCorrection</string>
				<float name="Contrast">0.15</float>
				<float name="Saturation">0.25</float>
				<bool name="Enabled">true</bool>
			</Properties>
		</Item>
	</Item>
	<Item class="ReplicatedFirst" referent="RBX946880D3E635690A6E7C8F0211AF2D1A">
		<Properties>
			<string name="Name">ReplicatedFirst</string>
		</Properties>
	</Item>
	<Item class="ReplicatedStorage" referent="RBX15AE8986B8AB63D9247F71914BFC09B0">
		<Properties>
			<string name="Name">ReplicatedStorage</string>
		</Properties>
		<Item class="ModuleScript" referent="RBXFB858CE3BF3676E0C2585068B0CEB9F5">
			<Properties>
				<string name="Name">GameConfig</string>
				<ProtectedString name="Source"><![CDATA[-- GameConfig (ModuleScript in ReplicatedStorage)
-- Every number you might want to tweak lives here. Change a value, press Play, see what happens.
-- You never need to touch the other scripts just to rebalance the game.

local GameConfig = {}

---------------------------------------------------------------------
-- ROUND TIMING (seconds)
---------------------------------------------------------------------
GameConfig.MinPlayers = 1            -- 1 lets you test alone. Use 2+ when you publish.
GameConfig.IntermissionTime = 10     -- lobby countdown before each round
GameConfig.ButtonPhaseTime = 20      -- how long players have to decide
GameConfig.ResultsTime = 5           -- how long the results screen stays up
GameConfig.AutoPressWhenTimeUp = true -- if nobody presses, the button presses itself

---------------------------------------------------------------------
-- DIFFICULTY
-- "Escalating" = starts Chill, becomes Normal, then Chaos as rounds go on.
-- Or lock it to one level: "Chill", "Normal" or "Chaos".
---------------------------------------------------------------------
GameConfig.DifficultyMode = "Escalating"
GameConfig.EscalationRounds = {
	Normal = 4, -- from round 4
	Chaos = 9,  -- from round 9
}

GameConfig.Difficulties = {
	Chill = {
		Order = 1,
		EventTime = 20,
		RewardMultiplier = 1,
		Flood = { RiseTime = 18, MaxHeight = 16, DamagePerSecond = 15 },
		Floor = { TilesPerWave = 3, WaveInterval = 2, WarnTime = 1.5, KeepTiles = 30 },
		Ball = { Size = 14, Speed = 22, Count = 1 },
	},
	Normal = {
		Order = 2,
		EventTime = 25,
		RewardMultiplier = 1.5,
		Flood = { RiseTime = 15, MaxHeight = 21, DamagePerSecond = 25 },
		Floor = { TilesPerWave = 5, WaveInterval = 1.5, WarnTime = 1, KeepTiles = 14 },
		Ball = { Size = 20, Speed = 30, Count = 1 },
	},
	Chaos = {
		Order = 3,
		EventTime = 30,
		RewardMultiplier = 2.5,
		Flood = { RiseTime = 11, MaxHeight = 26, DamagePerSecond = 40 },
		Floor = { TilesPerWave = 8, WaveInterval = 1, WarnTime = 0.6, KeepTiles = 6 },
		Ball = { Size = 26, Speed = 40, Count = 2 },
	},
}

---------------------------------------------------------------------
-- EVENTS
-- Name must match a ModuleScript inside ServerScriptService > Events.
-- Settings: which table in each difficulty (Flood / Floor / Ball) the event reads.
-- UnlockRound: the event can't be picked before this round number.
-- Weight: bigger = picked more often.
---------------------------------------------------------------------
GameConfig.Events = {
	{ Name = "DisappearingFloor", Settings = "Floor", DisplayName = "THE FLOOR IS LEAVING!", UnlockRound = 1, Weight = 1 },
	{ Name = "RisingFlood", Settings = "Flood", DisplayName = "FLOOD! CLIMB!", UnlockRound = 1, Weight = 1 },
	{ Name = "RollingBall", Settings = "Ball", DisplayName = "GIANT BALL INCOMING!", UnlockRound = 3, Weight = 1 },
	{ Name = "MoonFloor", Settings = "Floor", DisplayName = "MOON MODE! (the floor is STILL leaving)", UnlockRound = 5, Weight = 1 },
}

---------------------------------------------------------------------
-- SOUNDS
-- Paste a Roblox audio asset ID (just the number) between the quotes. Empty = silent, which is fine.
-- Find free audio in the Creator Store (create.roblox.com/store/audio, or Toolbox > Audio in Studio),
-- open a sound, and copy the number from its page address or its "Copy Asset ID" button.
-- Only use audio that is marked free to use. No IDs are included, so the game is silent until you add some.
---------------------------------------------------------------------
GameConfig.Sounds = {
	ButtonPress = "",  -- a click or buzzer when somebody presses the button
	EventStart = "",   -- an alarm when the chaos event begins
	Survived = "",     -- a happy sound for survivors
	Volume = 0.6,      -- 0 (silent) to 1 (loud)
}

---------------------------------------------------------------------
-- REWARDS (coins, before the difficulty multiplier)
---------------------------------------------------------------------
GameConfig.Rewards = {
	Participation = 1, -- everyone who was in the arena
	Survive = 10,      -- still alive when the event ends
	PresserBonus = 5,  -- pressed the button AND survived
}

-- Cosmetic trails unlock automatically when your coin total reaches the number.
GameConfig.Cosmetics = {
	{ Name = "Bronze Trail", Coins = 50, Color = Color3.fromRGB(205, 127, 50) },
	{ Name = "Silver Trail", Coins = 150, Color = Color3.fromRGB(200, 210, 225) },
	{ Name = "Gold Trail", Coins = 400, Color = Color3.fromRGB(255, 200, 40) },
	{ Name = "Rainbow Trail", Coins = 1000, Rainbow = true },
}

---------------------------------------------------------------------
-- SAVING
-- Saving only works in Studio after you publish, then: File > Experience Settings >
-- Security > "Enable Studio Access to API Services". The game still runs without it.
-- Careful: once it's on, coins you earn while testing in Studio go into the REAL saves.
---------------------------------------------------------------------
GameConfig.SaveData = true
GameConfig.DataStoreName = "DontPressTheButton_v1"

---------------------------------------------------------------------
-- ARENA (built by ArenaBuilder, so events always line up)
---------------------------------------------------------------------
GameConfig.Arena = {
	Center = Vector3.new(0, 50, 0), -- floats in the sky; lava waits below
	TileSize = 8,
	TilesPerSide = 10,              -- 10 x 10 tiles = 80 x 80 studs
	WallHeight = 40,
	LobbySpawn = Vector3.new(0, 1, 110),
	ApplyLighting = true,           -- set false if you want to design the Lighting yourself
}

---------------------------------------------------------------------
-- COLOURS (cartoony "toy diorama": calm colours everywhere, RED means danger)
---------------------------------------------------------------------
GameConfig.Colors = {
	ButtonCap = Color3.fromRGB(235, 40, 40),
	Pedestal = Color3.fromRGB(245, 200, 40),
	SignBoard = Color3.fromRGB(250, 245, 230),
	SignText = Color3.fromRGB(200, 25, 25),
	TileA = Color3.fromRGB(120, 205, 190),
	TileB = Color3.fromRGB(150, 225, 210),
	TileWarning = Color3.fromRGB(255, 160, 40),
	Wall = Color3.fromRGB(255, 255, 255),
	Water = Color3.fromRGB(40, 150, 240),
	Ball = Color3.fromRGB(255, 120, 30),
	Lava = Color3.fromRGB(255, 90, 20),
	LobbyFloor = Color3.fromRGB(255, 214, 120),
	Steps = Color3.fromRGB(80, 120, 240),
}

return GameConfig
]]></ProtectedString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBXADB425FA079A6B566FF6D821AD02BBD6">
			<Properties>
				<string name="Name">RoundLogic</string>
				<ProtectedString name="Source"><![CDATA[-- RoundLogic (ModuleScript in ReplicatedStorage)
-- Small "thinking" functions with no parts or players in them.
-- Keeping them separate means they are easy to read and easy to test.

local RoundLogic = {}

-- Which difficulty should this round use?
function RoundLogic.GetDifficultyName(config, roundNumber)
	local mode = config.DifficultyMode
	if mode ~= "Escalating" then
		return mode
	end
	if roundNumber >= config.EscalationRounds.Chaos then
		return "Chaos"
	elseif roundNumber >= config.EscalationRounds.Normal then
		return "Normal"
	end
	return "Chill"
end

-- Events that are unlocked by this round.
function RoundLogic.GetAvailableEvents(config, roundNumber)
	local available = {}
	for _, event in ipairs(config.Events) do
		if roundNumber >= event.UnlockRound then
			table.insert(available, event)
		end
	end
	return available
end

-- Pick a random unlocked event, avoiding a repeat of last round when possible.
-- randomNumber is a function returning a number from 0 to 1 (math.random works).
function RoundLogic.PickEvent(config, roundNumber, lastEventName, randomNumber)
	randomNumber = randomNumber or math.random
	local choices = {}
	for _, event in ipairs(RoundLogic.GetAvailableEvents(config, roundNumber)) do
		if event.Name ~= lastEventName then
			table.insert(choices, event)
		end
	end
	if #choices == 0 then
		choices = RoundLogic.GetAvailableEvents(config, roundNumber)
	end
	if #choices == 0 then
		return nil
	end

	local totalWeight = 0
	for _, event in ipairs(choices) do
		totalWeight = totalWeight + event.Weight
	end
	local roll = randomNumber() * totalWeight
	for _, event in ipairs(choices) do
		roll = roll - event.Weight
		if roll < 0 then
			return event
		end
	end
	return choices[#choices]
end

-- How many coins does one player get this round?
function RoundLogic.CalculateReward(config, difficultyName, survived, pressedButton)
	local rewards = config.Rewards
	local coins = rewards.Participation
	if survived then
		coins = coins + rewards.Survive
		if pressedButton then
			coins = coins + rewards.PresserBonus
		end
	end
	local multiplier = config.Difficulties[difficultyName].RewardMultiplier
	return math.floor(coins * multiplier + 0.5)
end

-- The best cosmetic a coin total has unlocked (or nil).
function RoundLogic.GetBestCosmetic(config, coins)
	local best = nil
	for _, cosmetic in ipairs(config.Cosmetics) do
		if coins >= cosmetic.Coins and (best == nil or cosmetic.Coins > best.Coins) then
			best = cosmetic
		end
	end
	return best
end

-- Checks the config for typos and impossible values. Returns a list of problems.
function RoundLogic.ValidateConfig(config)
	local problems = {}
	local function check(condition, message)
		if not condition then
			table.insert(problems, message)
		end
	end

	check(config.MinPlayers >= 1, "MinPlayers must be at least 1")
	check(config.IntermissionTime > 0, "IntermissionTime must be above 0")
	check(config.ButtonPhaseTime > 0, "ButtonPhaseTime must be above 0")
	check(config.ResultsTime >= 0, "ResultsTime can't be negative")

	local mode = config.DifficultyMode
	check(mode == "Escalating" or config.Difficulties[mode] ~= nil, "Unknown DifficultyMode: " .. tostring(mode))
	if mode == "Escalating" then
		check(config.EscalationRounds.Normal < config.EscalationRounds.Chaos, "EscalationRounds.Normal must come before Chaos")
		check(config.Difficulties.Chill and config.Difficulties.Normal and config.Difficulties.Chaos,
			"Escalating mode needs Chill, Normal and Chaos difficulties")
	end

	for name, difficulty in pairs(config.Difficulties) do
		check(difficulty.EventTime > 0, name .. ".EventTime must be above 0")
		check(difficulty.RewardMultiplier > 0, name .. ".RewardMultiplier must be above 0")
		check(difficulty.Flood.RiseTime > 0, name .. ".Flood.RiseTime must be above 0")
		check(difficulty.Flood.MaxHeight > 0 and difficulty.Flood.MaxHeight < config.Arena.WallHeight,
			name .. ".Flood.MaxHeight must be between 0 and the wall height")
		check(difficulty.Floor.WaveInterval > difficulty.Floor.WarnTime,
			name .. ".Floor.WaveInterval must be longer than WarnTime")
		check(difficulty.Floor.KeepTiles >= 1, name .. ".Floor.KeepTiles must be at least 1")
		check(difficulty.Ball.Size > 0 and difficulty.Ball.Size < config.Arena.TileSize * config.Arena.TilesPerSide / 2,
			name .. ".Ball.Size is too big for the arena")
		check(difficulty.Ball.Count >= 1, name .. ".Ball.Count must be at least 1")
	end

	local seen = {}
	local hasRoundOneEvent = false
	for _, event in ipairs(config.Events) do
		check(not seen[event.Name], "Event listed twice: " .. tostring(event.Name))
		seen[event.Name] = true
		check(event.Weight > 0, event.Name .. " needs a Weight above 0")
		check(event.UnlockRound >= 1, event.Name .. " UnlockRound must be at least 1")
		for difficultyName, difficulty in pairs(config.Difficulties) do
			check(difficulty[event.Settings] ~= nil,
				event.Name .. " reads settings '" .. tostring(event.Settings) .. "' but " .. difficultyName .. " has none")
		end
		if event.UnlockRound <= 1 then
			hasRoundOneEvent = true
		end
	end
	check(hasRoundOneEvent, "At least one event must unlock on round 1")

	for i, cosmetic in ipairs(config.Cosmetics) do
		check(cosmetic.Coins > 0, cosmetic.Name .. " needs a coin price above 0")
		check(cosmetic.Rainbow or cosmetic.Color, cosmetic.Name .. " needs a Color (or Rainbow = true)")
		if i > 1 then
			check(cosmetic.Coins > config.Cosmetics[i - 1].Coins, "Cosmetics must be listed cheapest first")
		end
	end

	if config.Sounds then
		for key, value in pairs(config.Sounds) do
			if key == "Volume" then
				check(type(value) == "number" and value >= 0 and value <= 1, "Sounds.Volume must be a number from 0 to 1")
			else
				check(type(value) == "string" and (value == "" or value:match("^%d+$") ~= nil),
					"Sounds." .. tostring(key) .. " must be empty or just the number of an audio asset ID")
			end
		end
	end

	return problems
end

return RoundLogic
]]></ProtectedString>
			</Properties>
		</Item>
	</Item>
	<Item class="ServerScriptService" referent="RBXAAFDAC95666B85126F02800CB8642545">
		<Properties>
			<string name="Name">ServerScriptService</string>
		</Properties>
		<Item class="ModuleScript" referent="RBX67F65E0A36B65CE269D05A7F5A8DD52B">
			<Properties>
				<string name="Name">ArenaBuilder</string>
				<ProtectedString name="Source"><![CDATA[-- ArenaBuilder (ModuleScript in ServerScriptService)
-- Builds the arena floor, walls, lava and lobby from GameConfig, so the events always line up.
-- If you built your own button ("TheButton") or lobby ("Lobby"), it keeps yours.

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

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))

local ArenaBuilder = {}

local settings = GameConfig.Arena
local colors = GameConfig.Colors
local arenaWidth = settings.TileSize * settings.TilesPerSide

-- Handy numbers other scripts use
ArenaBuilder.Center = settings.Center
ArenaBuilder.FloorY = settings.Center.Y -- the top of the floor tiles
ArenaBuilder.Width = arenaWidth

local arenaFolder, tilesFolder, eventFolder
local tiles = {}
local buttonHomePivot
local liftCount = 0 -- goes up on every Reset so an old lift stops moving the button

local function makePart(properties)
	local part = Instance.new("Part")
	part.Anchored = true
	part.TopSurface = Enum.SurfaceType.Smooth
	part.BottomSurface = Enum.SurfaceType.Smooth
	part.Material = Enum.Material.SmoothPlastic
	for key, value in pairs(properties) do
		if key ~= "Parent" then
			part[key] = value
		end
	end
	part.Parent = properties.Parent -- set Parent last (faster and avoids flicker)
	return part
end

local function getOrMakeFolder(parent, name)
	local folder = parent:FindFirstChild(name)
	if not folder then
		folder = Instance.new("Folder")
		folder.Name = name
		folder.Parent = parent
	end
	return folder
end

local function buildFloor()
	local half = arenaWidth / 2
	local size = settings.TileSize
	for row = 1, settings.TilesPerSide do
		for column = 1, settings.TilesPerSide do
			local x = settings.Center.X - half + (column - 0.5) * size
			local z = settings.Center.Z - half + (row - 0.5) * size
			local isEven = (row + column) % 2 == 0
			local tile = makePart({
				Name = "Tile_" .. row .. "_" .. column,
				Size = Vector3.new(size, 2, size),
				Position = Vector3.new(x, ArenaBuilder.FloorY - 1, z),
				Color = isEven and colors.TileA or colors.TileB,
				CastShadow = false, -- cheaper on phones
				Parent = tilesFolder,
			})
			tile:SetAttribute("HomeColor", tile.Color)
			table.insert(tiles, tile)
		end
	end
end

local function buildWalls()
	local wallsFolder = getOrMakeFolder(arenaFolder, "Walls")
	local half = arenaWidth / 2
	local height = settings.WallHeight
	local y = ArenaBuilder.FloorY + height / 2 - 2
	local c = settings.Center
	local wallData = {
		{ Vector3.new(c.X, y, c.Z - half - 1), Vector3.new(arenaWidth + 4, height, 2) },
		{ Vector3.new(c.X, y, c.Z + half + 1), Vector3.new(arenaWidth + 4, height, 2) },
		{ Vector3.new(c.X - half - 1, y, c.Z), Vector3.new(2, height, arenaWidth) },
		{ Vector3.new(c.X + half + 1, y, c.Z), Vector3.new(2, height, arenaWidth) },
	}
	for i, data in ipairs(wallData) do
		makePart({
			Name = "Wall" .. i,
			Position = data[1],
			Size = data[2],
			Color = colors.Wall,
			Transparency = 0.15,
			Parent = wallsFolder,
		})
	end
end

local function buildLava()
	local lava = makePart({
		Name = "Lava",
		Size = Vector3.new(arenaWidth + 60, 2, arenaWidth + 60),
		Position = Vector3.new(settings.Center.X, ArenaBuilder.FloorY - 25, settings.Center.Z),
		Color = colors.Lava,
		Material = Enum.Material.Neon,
		Parent = arenaFolder,
	})
	lava.Touched:Connect(function(hit)
		local humanoid = hit.Parent and hit.Parent:FindFirstChildOfClass("Humanoid")
		if humanoid then
			humanoid.Health = 0
		end
	end)
end

local function buildLobbyIfMissing()
	if workspace:FindFirstChild("Lobby") then
		return
	end
	local lobby = Instance.new("Folder")
	lobby.Name = "Lobby"
	makePart({
		Name = "LobbyFloor",
		Size = Vector3.new(60, 1, 60),
		Position = settings.LobbySpawn - Vector3.new(0, 1, 0),
		Color = colors.LobbyFloor,
		Parent = lobby,
	})
	local lobbySpawn = Instance.new("SpawnLocation")
	lobbySpawn.Name = "LobbySpawn"
	lobbySpawn.Anchored = true
	lobbySpawn.Size = Vector3.new(12, 1, 12)
	lobbySpawn.Position = settings.LobbySpawn
	lobbySpawn.Color = colors.Steps
	lobbySpawn.Duration = 0 -- no force-field bubble (it would block flood damage)
	lobbySpawn.Parent = lobby
	lobby.Parent = workspace
end

-- The Baseplate template has its own SpawnLocation right under the arena. Switch off
-- every spawn that isn't in the Lobby, so players always (re)spawn in the lobby.
local function disableOtherSpawns()
	local lobby = workspace:FindFirstChild("Lobby")
	for _, item in ipairs(workspace:GetDescendants()) do
		if item:IsA("SpawnLocation") and not (lobby and item:IsDescendantOf(lobby)) then
			item.Enabled = false
		end
	end
end

local function buildSignIfMissing()
	if workspace:FindFirstChild("Sign") then
		return
	end
	local c = settings.Center
	local sign = makePart({
		Name = "Sign",
		Size = Vector3.new(30, 12, 1),
		Position = Vector3.new(c.X, ArenaBuilder.FloorY + 16, c.Z - arenaWidth / 2 + 1),
		Color = colors.SignBoard,
		Parent = workspace,
	})
	local gui = Instance.new("SurfaceGui")
	gui.Face = Enum.NormalId.Back -- the side facing into the arena
	gui.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
	gui.LightInfluence = 0
	local label = Instance.new("TextLabel")
	label.Size = UDim2.fromScale(1, 1)
	label.BackgroundTransparency = 1
	label.Text = "DO NOT PRESS"
	label.TextScaled = true
	label.Font = Enum.Font.FredokaOne
	label.TextColor3 = colors.SignText
	label.Parent = gui
	gui.Parent = sign
end

local function buildButtonIfMissing()
	local button = workspace:FindFirstChild("TheButton")
	if not button then
		warn("[ArenaBuilder] No 'TheButton' model found, building a basic one for you.")
		button = Instance.new("Model")
		button.Name = "TheButton"
		local c = settings.Center
		local pedestal = makePart({
			Name = "Pedestal",
			Shape = Enum.PartType.Cylinder,
			Size = Vector3.new(3, 14, 14),
			CFrame = CFrame.new(c.X, ArenaBuilder.FloorY + 1.5, c.Z) * CFrame.Angles(0, 0, math.rad(90)),
			Color = colors.Pedestal,
			Parent = button,
		})
		makePart({
			Name = "Cap",
			Shape = Enum.PartType.Cylinder,
			Size = Vector3.new(3, 8, 8),
			CFrame = CFrame.new(c.X, ArenaBuilder.FloorY + 4.5, c.Z) * CFrame.Angles(0, 0, math.rad(90)),
			Color = colors.ButtonCap,
			Parent = button,
		})
		button.PrimaryPart = pedestal
		button.Parent = workspace
	end

	local cap = button:FindFirstChild("Cap")
	if not cap then
		error("[ArenaBuilder] Your TheButton model needs a Part named 'Cap' (the red part you press).")
	end
	for _, part in ipairs(button:GetDescendants()) do
		if part:IsA("BasePart") then
			part.Anchored = true -- in case one was missed: unanchored parts fall when Play starts
			part:SetAttribute("HomeCanCollide", part.CanCollide)
		end
	end

	local prompt = cap:FindFirstChildOfClass("ProximityPrompt")
	if not prompt then
		prompt = Instance.new("ProximityPrompt")
		prompt.Parent = cap
	end
	prompt.ActionText = "PRESS IT?"
	prompt.ObjectText = "DO NOT PRESS"
	prompt.HoldDuration = 0
	prompt.MaxActivationDistance = 12
	prompt.RequiresLineOfSight = false
	prompt.Enabled = false

	buttonHomePivot = button:GetPivot()
	return button
end

local function applyLighting()
	-- LightingStyle / PrioritizeLightingQuality can't be set from a script:
	-- set them by hand in Lighting's Properties (Soft + off is best for phones).
	Lighting.ClockTime = 14
	Lighting.Brightness = 3
	Lighting.Ambient = Color3.fromRGB(110, 110, 125)
	Lighting.OutdoorAmbient = Color3.fromRGB(140, 150, 170)
	Lighting.EnvironmentDiffuseScale = 0.6
	Lighting.EnvironmentSpecularScale = 0.3

	local atmosphere = Lighting:FindFirstChildOfClass("Atmosphere") or Instance.new("Atmosphere")
	atmosphere.Density = 0.25
	atmosphere.Offset = 0.1
	atmosphere.Color = Color3.fromRGB(199, 225, 255)
	atmosphere.Decay = Color3.fromRGB(255, 230, 200)
	atmosphere.Haze = 0.8
	atmosphere.Glare = 0
	atmosphere.Parent = Lighting

	local bloom = Lighting:FindFirstChildOfClass("BloomEffect") or Instance.new("BloomEffect")
	bloom.Intensity = 0.6
	bloom.Size = 24
	bloom.Threshold = 1.6
	bloom.Parent = Lighting

	local grade = Lighting:FindFirstChildOfClass("ColorCorrectionEffect") or Instance.new("ColorCorrectionEffect")
	grade.Contrast = 0.15
	grade.Saturation = 0.25
	grade.Parent = Lighting
end

function ArenaBuilder.Build()
	arenaFolder = getOrMakeFolder(workspace, "Arena")
	tilesFolder = getOrMakeFolder(arenaFolder, "FloorTiles")
	eventFolder = getOrMakeFolder(arenaFolder, "EventStuff")
	if #tilesFolder:GetChildren() == 0 then
		buildFloor()
		buildWalls()
		buildLava()
	else
		for _, tile in ipairs(tilesFolder:GetChildren()) do
			table.insert(tiles, tile)
		end
	end
	buildLobbyIfMissing()
	disableOtherSpawns()
	buildSignIfMissing()
	local button = buildButtonIfMissing()
	if settings.ApplyLighting then
		applyLighting()
	end
	return button
end

function ArenaBuilder.GetTiles()
	return tiles
end

-- Where event scripts put the things they create, so cleanup is one line.
function ArenaBuilder.GetEventFolder()
	return eventFolder
end

-- Put every tile back and delete everything the last event created.
function ArenaBuilder.Reset()
	for _, tile in ipairs(tiles) do
		tile.Transparency = 0
		tile.CanCollide = true
		tile.Material = Enum.Material.SmoothPlastic
		tile.Color = tile:GetAttribute("HomeColor") or colors.TileA
	end
	eventFolder:ClearAllChildren()
	liftCount = liftCount + 1
	local button = workspace:FindFirstChild("TheButton")
	if button and buttonHomePivot then
		button:PivotTo(buttonHomePivot)
		for _, part in ipairs(button:GetDescendants()) do
			if part:IsA("BasePart") and part:GetAttribute("HomeCanCollide") ~= nil then
				part.CanCollide = part:GetAttribute("HomeCanCollide")
			end
		end
	end
end

-- A random standing spot on the arena floor, not too close to the button in the middle.
function ArenaBuilder.GetRandomSpot(heightAboveFloor)
	local c = settings.Center
	local limit = arenaWidth / 2 - 6
	local x, z
	repeat
		x = (math.random() * 2 - 1) * limit
		z = (math.random() * 2 - 1) * limit
	until math.abs(x) > 10 or math.abs(z) > 10
	return Vector3.new(c.X + x, ArenaBuilder.FloorY + (heightAboveFloor or 3), c.Z + z)
end

-- The button floats up out of the way during events. It turns non-solid first,
-- so nobody can stand on it and ride it up to safety.
function ArenaBuilder.LiftButton(height)
	local button = workspace:FindFirstChild("TheButton")
	if not (button and buttonHomePivot) then
		return
	end
	for _, part in ipairs(button:GetDescendants()) do
		if part:IsA("BasePart") then
			part.CanCollide = false
		end
	end
	liftCount = liftCount + 1
	local myLift = liftCount
	task.spawn(function()
		for i = 1, 20 do
			if liftCount ~= myLift then
				return -- the arena was reset, stop moving the button
			end
			button:PivotTo(buttonHomePivot + Vector3.new(0, height * i / 20, 0))
			task.wait(0.05)
		end
	end)
end

function ArenaBuilder.GetLobbySpot()
	local offset = Vector3.new((math.random() * 2 - 1) * 15, 3, (math.random() * 2 - 1) * 15)
	return settings.LobbySpawn + offset
end

return ArenaBuilder
]]></ProtectedString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX4091706C11AA4882A4BDB44B6EABA7A5">
			<Properties>
				<string name="Name">CoinManager</string>
				<ProtectedString name="Source"><![CDATA[-- CoinManager (ModuleScript in ServerScriptService)
-- Shows Coins and Survived on the leaderboard and saves them between visits.

local DataStoreService = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))

local CoinManager = {}

local AUTOSAVE_SECONDS = 120
local LOAD_TRIES = 3

local store = nil
local loadedOk = {} -- [player] = true once their saved data loaded safely
local saving = {} -- [player] = true while a save is running (so we never save twice at once)

local function keyFor(player)
	return "player_" .. player.UserId
end

local function getStat(player, statName)
	local leaderstats = player:FindFirstChild("leaderstats")
	return leaderstats and leaderstats:FindFirstChild(statName)
end

local function loadData(player)
	local ok, data
	for try = 1, LOAD_TRIES do
		ok, data = pcall(function()
			return store:GetAsync(keyFor(player))
		end)
		if ok then
			return true, data
		end
		task.wait(try) -- wait a little longer each time
	end
	return false, data
end

local function onPlayerAdded(player)
	-- Load first, THEN show the stats, so the numbers never jump from 0 to the saved amount.
	local saved = nil
	if store then
		local ok, data = loadData(player)
		if ok then
			saved = data
			loadedOk[player] = true
		else
			-- Never save over data we couldn't read, or we might wipe someone's coins.
			warn("[CoinManager] Couldn't load data for " .. player.Name .. ", progress won't save this visit: " .. tostring(data))
		end
	end
	if player.Parent == nil then
		loadedOk[player] = nil
		return -- they left while we were loading
	end

	-- "leaderstats" is a special name: Roblox shows these values on the player list.
	local leaderstats = Instance.new("Folder")
	leaderstats.Name = "leaderstats"
	local coins = Instance.new("IntValue")
	coins.Name = "Coins"
	coins.Parent = leaderstats
	local survived = Instance.new("IntValue")
	survived.Name = "Survived"
	survived.Parent = leaderstats
	if type(saved) == "table" then
		coins.Value = tonumber(saved.Coins) or 0
		survived.Value = tonumber(saved.Survived) or 0
	end
	leaderstats.Parent = player
end

local function save(player)
	if not (store and loadedOk[player]) or saving[player] then
		return
	end
	local coins = getStat(player, "Coins")
	local survived = getStat(player, "Survived")
	if not (coins and survived) then
		return
	end
	local data = { Coins = coins.Value, Survived = survived.Value }
	saving[player] = true
	local ok, err = pcall(function()
		store:UpdateAsync(keyFor(player), function(old)
			-- Coins only ever go up. If the stored number is already higher (another server
			-- saved more recently), keep it: returning nil means "don't change anything".
			-- (If you add a shop where coins get SPENT, this check must change. See Level 3.)
			if type(old) == "table" and (tonumber(old.Coins) or 0) > data.Coins then
				return nil
			end
			return data
		end)
	end)
	saving[player] = nil
	if not ok then
		warn("[CoinManager] Save failed for " .. player.Name .. ": " .. tostring(err))
	end
end

function CoinManager.Setup()
	if GameConfig.SaveData then
		local ok, result = pcall(function()
			return DataStoreService:GetDataStore(GameConfig.DataStoreName)
		end)
		if ok then
			store = result
		else
			warn("[CoinManager] Saving is off (turn on API access in Experience Settings > Security): " .. tostring(result))
		end
	end

	Players.PlayerAdded:Connect(onPlayerAdded)
	for _, player in ipairs(Players:GetPlayers()) do
		task.spawn(onPlayerAdded, player)
	end

	Players.PlayerRemoving:Connect(function(player)
		while saving[player] do
			task.wait(0.1) -- an autosave is running; wait, then save the final numbers
		end
		save(player)
		loadedOk[player] = nil -- so the shutdown save below skips them
	end)

	-- When the server shuts down, save everyone still here, then wait for every save to finish.
	game:BindToClose(function()
		for _, player in ipairs(Players:GetPlayers()) do
			task.spawn(save, player)
		end
		while next(saving) ~= nil do
			task.wait(0.1)
		end
	end)

	task.spawn(function()
		while true do
			task.wait(AUTOSAVE_SECONDS)
			for _, player in ipairs(Players:GetPlayers()) do
				task.spawn(save, player)
			end
		end
	end)
end

function CoinManager.AddCoins(player, amount)
	local coins = getStat(player, "Coins")
	if coins then
		coins.Value = coins.Value + amount
	end
end

function CoinManager.AddSurvived(player)
	local survived = getStat(player, "Survived")
	if survived then
		survived.Value = survived.Value + 1
	end
end

function CoinManager.GetCoins(player)
	local coins = getStat(player, "Coins")
	return coins and coins.Value or 0
end

return CoinManager
]]></ProtectedString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX2B9B8918EC006FB4A1FF005F6B33A07A">
			<Properties>
				<string name="Name">Cosmetics</string>
				<ProtectedString name="Source"><![CDATA[-- Cosmetics (ModuleScript in ServerScriptService)
-- Gives each player the best trail their coins have unlocked (see GameConfig.Cosmetics).

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

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))
local RoundLogic = require(ReplicatedStorage:WaitForChild("RoundLogic"))

local Cosmetics = {}

local equipped = {} -- [player] = name of the trail they have on

local function getCoins(player)
	local leaderstats = player:FindFirstChild("leaderstats")
	local coins = leaderstats and leaderstats:FindFirstChild("Coins")
	return coins and coins.Value or 0
end

local function makeColorSequence(cosmetic)
	if cosmetic.Rainbow then
		return ColorSequence.new({
			ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 60, 60)),
			ColorSequenceKeypoint.new(0.25, Color3.fromRGB(255, 200, 40)),
			ColorSequenceKeypoint.new(0.5, Color3.fromRGB(60, 220, 90)),
			ColorSequenceKeypoint.new(0.75, Color3.fromRGB(60, 140, 255)),
			ColorSequenceKeypoint.new(1, Color3.fromRGB(190, 80, 255)),
		})
	end
	return ColorSequence.new(cosmetic.Color)
end

local function applyTrail(character, cosmetic)
	local root = character:WaitForChild("HumanoidRootPart", 5)
	if not root then
		return
	end
	local old = root:FindFirstChild("CosmeticTrail")
	if old then
		old:Destroy()
	end
	if not cosmetic then
		return
	end

	-- A Trail draws between two Attachments as the character moves.
	local top = root:FindFirstChild("TrailTop") or Instance.new("Attachment")
	top.Name = "TrailTop"
	top.Position = Vector3.new(0, 1, 0)
	top.Parent = root
	local bottom = root:FindFirstChild("TrailBottom") or Instance.new("Attachment")
	bottom.Name = "TrailBottom"
	bottom.Position = Vector3.new(0, -1, 0)
	bottom.Parent = root

	local trail = Instance.new("Trail")
	trail.Name = "CosmeticTrail"
	trail.Attachment0 = top
	trail.Attachment1 = bottom
	trail.Color = makeColorSequence(cosmetic)
	trail.Transparency = NumberSequence.new(0.2, 1)
	trail.Lifetime = 0.6
	trail.Parent = root
end

-- quiet = true: equip without the "unlocked" banner (used when someone joins with old coins)
local function refresh(player, fxRemote, quiet)
	local best = RoundLogic.GetBestCosmetic(GameConfig, getCoins(player))
	local bestName = best and best.Name or nil
	if bestName == equipped[player] then
		return
	end
	equipped[player] = bestName
	if player.Character then
		task.spawn(applyTrail, player.Character, best)
	end
	if best and fxRemote and not quiet then
		fxRemote:FireClient(player, "Banner", "TRAIL UNLOCKED: " .. best.Name)
	end
end

local function onPlayerAdded(player, fxRemote)
	player.CharacterAdded:Connect(function(character)
		local best = RoundLogic.GetBestCosmetic(GameConfig, getCoins(player))
		applyTrail(character, best)
	end)
	local leaderstats = player:WaitForChild("leaderstats", 10)
	local coins = leaderstats and leaderstats:WaitForChild("Coins", 10)
	if not coins then
		return
	end
	refresh(player, fxRemote, true) -- coins are already loaded here (CoinManager loads before showing stats)
	coins.Changed:Connect(function()
		refresh(player, fxRemote)
	end)
end

function Cosmetics.Setup(fxRemote)
	Players.PlayerAdded:Connect(function(player)
		onPlayerAdded(player, fxRemote)
	end)
	for _, player in ipairs(Players:GetPlayers()) do
		task.spawn(onPlayerAdded, player, fxRemote)
	end
	Players.PlayerRemoving:Connect(function(player)
		equipped[player] = nil
	end)
end

return Cosmetics
]]></ProtectedString>
			</Properties>
		</Item>
		<Item class="Script" referent="RBX74DFB46E3A5B2E4C7015B35EEE6174D3">
			<Properties>
				<string name="Name">RoundManager</string>
				<bool name="Disabled">false</bool>
				<token name="RunContext">0</token>
				<ProtectedString name="Source"><![CDATA[-- RoundManager (Script in ServerScriptService)
-- The "brain" of the game. It runs this loop forever:
--   lobby countdown -> teleport into arena -> 20s "don't press" -> random event -> rewards -> back to lobby

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local ServerScriptService = game:GetService("ServerScriptService")
local TweenService = game:GetService("TweenService")

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))
local RoundLogic = require(ReplicatedStorage:WaitForChild("RoundLogic"))
local ArenaBuilder = require(ServerScriptService:WaitForChild("ArenaBuilder"))
local CoinManager = require(ServerScriptService:WaitForChild("CoinManager"))
local Cosmetics = require(ServerScriptService:WaitForChild("Cosmetics"))
local SoundPlayer = require(ServerScriptService:WaitForChild("SoundPlayer"))
local eventsFolder = ServerScriptService:WaitForChild("Events")

-- Catch config typos right away, with a clear message in the Output window.
local problems = RoundLogic.ValidateConfig(GameConfig)
if #problems > 0 then
	for _, problem in ipairs(problems) do
		warn("[GameConfig] " .. problem)
	end
	error("GameConfig has " .. #problems .. " problem(s). Fix them (see the warnings above) and press Play again.")
end

-- The server uses this RemoteEvent to tell every player's screen to shake, flash or show a banner.
local fxRemote = ReplicatedStorage:FindFirstChild("GameFX")
if not fxRemote then
	fxRemote = Instance.new("RemoteEvent")
	fxRemote.Name = "GameFX"
	fxRemote.Parent = ReplicatedStorage
end

local button = ArenaBuilder.Build()
local cap = button:FindFirstChild("Cap")
local prompt = cap:FindFirstChildOfClass("ProximityPrompt")
CoinManager.Setup()
Cosmetics.Setup(fxRemote)

local FALL_DEPTH = 15 -- this far below the floor = you fell, you're out

local roundNumber = 0
local lastEventName = nil
local participants = {} -- [player] = true while they are alive in the arena
local diedConnections = {}
local presser = nil
local buttonArmed = false
local stopEvent = nil -- the running event's stop function (endRound always calls it)
local PRESS_DISTANCE = 15 -- studs; stops cheaters pressing from across the arena

---------------------------------------------------------------------
-- Helpers
---------------------------------------------------------------------
-- Players read these attributes to update their screen (see the HUD LocalScript).
local function setStatus(phase, text, timeLeft)
	ReplicatedStorage:SetAttribute("Phase", phase)
	ReplicatedStorage:SetAttribute("Status", text)
	ReplicatedStorage:SetAttribute("TimeLeft", timeLeft or 0)
end

local function anyoneLeft()
	return next(participants) ~= nil
end

local function eliminate(player)
	if participants[player] then
		participants[player] = nil
		if player.Parent then
			fxRemote:FireClient(player, "Banner", "ELIMINATED!")
		end
	end
end

-- Everyone still alive in the arena, with the parts events usually need.
local function getAlive()
	local alive = {}
	for player in pairs(participants) do
		local character = player.Character
		local humanoid = character and character:FindFirstChildOfClass("Humanoid")
		local root = character and character:FindFirstChild("HumanoidRootPart")
		if humanoid and root and humanoid.Health > 0 then
			table.insert(alive, { Player = player, Character = character, Humanoid = humanoid, Root = root })
		end
	end
	return alive
end

-- Counts down to a fixed finish time (so it never drifts). Stops early (and returns true) if stopWhen() becomes true.
-- makeText(timeLeft) returns the status text, e.g. "Next round in 5..."
local function countdown(seconds, phase, makeText, stopWhen)
	local finish = os.clock() + seconds
	local shown = nil
	while true do
		local timeLeft = math.ceil(finish - os.clock())
		if timeLeft <= 0 then
			break
		end
		if timeLeft ~= shown then
			shown = timeLeft
			setStatus(phase, makeText(timeLeft), timeLeft)
		end
		if stopWhen and stopWhen() then
			return true
		end
		task.wait(0.1)
	end
	return stopWhen ~= nil and stopWhen() == true
end

local function playPressAnimation()
	local home = cap.CFrame
	local down = TweenService:Create(cap, TweenInfo.new(0.12, Enum.EasingStyle.Quad, Enum.EasingDirection.In), {
		CFrame = home - Vector3.new(0, 1.8, 0),
	})
	down:Play()
	task.delay(0.4, function()
		TweenService:Create(cap, TweenInfo.new(0.3, Enum.EasingStyle.Back, Enum.EasingDirection.Out), { CFrame = home }):Play()
	end)
	local sound = cap:FindFirstChildOfClass("Sound") -- add one from the Toolbox if you like
	if sound then
		sound:Play()
	end
	SoundPlayer.Play("ButtonPress", cap)
end

local function endRound()
	if stopEvent then
		pcall(stopEvent)
		stopEvent = nil
	end
	buttonArmed = false
	prompt.Enabled = false
	for _, connection in ipairs(diedConnections) do
		connection:Disconnect()
	end
	diedConnections = {}
	participants = {}
	presser = nil
	ArenaBuilder.Reset()
end

---------------------------------------------------------------------
-- Things that can happen at any moment
---------------------------------------------------------------------
prompt.Triggered:Connect(function(player)
	local root = player.Character and player.Character:FindFirstChild("HumanoidRootPart")
	local closeEnough = root and (root.Position - cap.Position).Magnitude <= PRESS_DISTANCE
	if buttonArmed and participants[player] and closeEnough then
		buttonArmed = false
		prompt.Enabled = false
		presser = player
	end
end)

Players.PlayerRemoving:Connect(function(player)
	participants[player] = nil
end)

-- Anyone who falls off the arena is out. (The lava kills too, but this check usually gets there first.)
task.spawn(function()
	while true do
		task.wait(0.25)
		for _, info in ipairs(getAlive()) do
			if info.Root.Position.Y < ArenaBuilder.FloorY - FALL_DEPTH then
				info.Humanoid.Health = 0
				eliminate(info.Player)
			end
		end
	end
end)

---------------------------------------------------------------------
-- One full round
---------------------------------------------------------------------
local function runRound()
	while #Players:GetPlayers() < GameConfig.MinPlayers do
		setStatus("Waiting", "Waiting for players (" .. #Players:GetPlayers() .. "/" .. GameConfig.MinPlayers .. ")", 0)
		task.wait(1)
	end
	countdown(GameConfig.IntermissionTime, "Intermission", function(timeLeft)
		return "Next round in " .. timeLeft .. "..."
	end)

	-- 1. Teleport everyone with a living character into the arena
	ArenaBuilder.Reset()
	local roundPlayers = {}
	for _, player in ipairs(Players:GetPlayers()) do
		local character = player.Character
		local humanoid = character and character:FindFirstChildOfClass("Humanoid")
		if humanoid and humanoid.Health > 0 and character:FindFirstChild("HumanoidRootPart") then
			character:PivotTo(CFrame.new(ArenaBuilder.GetRandomSpot(3)))
			participants[player] = true
			table.insert(roundPlayers, player)
			table.insert(diedConnections, humanoid.Died:Connect(function()
				eliminate(player)
			end))
		end
	end
	if not anyoneLeft() then
		return
	end
	roundNumber = roundNumber + 1 -- only rounds somebody actually played count
	local difficultyName = RoundLogic.GetDifficultyName(GameConfig, roundNumber)
	local difficulty = GameConfig.Difficulties[difficultyName]
	ReplicatedStorage:SetAttribute("Round", roundNumber)
	ReplicatedStorage:SetAttribute("Difficulty", difficultyName)

	-- 2. The button phase: 20 seconds of temptation
	buttonArmed = true
	prompt.Enabled = true
	countdown(GameConfig.ButtonPhaseTime, "Button", function(timeLeft)
		return "DON'T PRESS THE BUTTON! " .. timeLeft
	end, function()
		return presser ~= nil or not anyoneLeft()
	end)
	buttonArmed = false
	prompt.Enabled = false
	if not anyoneLeft() then
		for _, player in ipairs(roundPlayers) do
			if player.Parent then
				CoinManager.AddCoins(player, GameConfig.Rewards.Participation)
			end
		end
		return
	end

	if presser == nil and not GameConfig.AutoPressWhenTimeUp then
		setStatus("Results", "Nobody pressed it. Boring... but safe.", 0)
		for _, player in ipairs(roundPlayers) do
			if player.Parent then
				CoinManager.AddCoins(player, GameConfig.Rewards.Participation)
			end
		end
		task.wait(GameConfig.ResultsTime)
		return
	end

	-- 3. Somebody pressed it (or the button got impatient)
	local presserName = presser and presser.Name
	playPressAnimation()
	fxRemote:FireAllClients("Flash")
	fxRemote:FireAllClients("Shake", 0.8, 0.6)
	local event = RoundLogic.PickEvent(GameConfig, roundNumber, lastEventName)
	lastEventName = event.Name
	if presserName then
		setStatus("Pressed", presserName .. " PRESSED THE BUTTON!", 0)
	else
		setStatus("Pressed", "Nobody pressed it... so it pressed ITSELF!", 0)
	end
	task.wait(2)
	fxRemote:FireAllClients("Banner", event.DisplayName)
	SoundPlayer.Play("EventStart", cap)
	ArenaBuilder.LiftButton(30)

	-- 4. Run the event
	local eventModule = eventsFolder:FindFirstChild(event.Name)
	if not eventModule then
		error("No ModuleScript named '" .. event.Name .. "' inside ServerScriptService > Events")
	end
	local context = {
		Settings = difficulty[event.Settings],
		Arena = ArenaBuilder,
		Folder = ArenaBuilder.GetEventFolder(),
		GetAlive = getAlive,
		FX = function(...)
			fxRemote:FireAllClients(...)
		end,
	}
	local ok, result = pcall(function()
		return require(eventModule).Start(context)
	end)
	if ok then
		stopEvent = result
	else
		warn("[RoundManager] " .. event.Name .. " crashed when starting: " .. tostring(result))
	end
	countdown(difficulty.EventTime, "Event", function(timeLeft)
		return event.DisplayName .. "  " .. timeLeft
	end, function()
		return not anyoneLeft()
	end)
	if stopEvent then
		pcall(stopEvent)
		stopEvent = nil
	end

	-- 5. Rewards
	local survivorCount = 0
	for _, player in ipairs(roundPlayers) do
		if player.Parent then
			local survived = participants[player] == true
			local coins = RoundLogic.CalculateReward(GameConfig, difficultyName, survived, player == presser)
			CoinManager.AddCoins(player, coins)
			if survived then
				survivorCount = survivorCount + 1
				CoinManager.AddSurvived(player)
				fxRemote:FireClient(player, "Banner", "YOU SURVIVED! +" .. coins .. " coins")
			else
				fxRemote:FireClient(player, "Banner", "Better luck next time. +" .. coins .. " coins")
			end
		end
	end
	setStatus("Results", survivorCount .. " of " .. #roundPlayers .. " survived!", 0)
	if survivorCount > 0 then
		SoundPlayer.Play("Survived", cap)
	end
	task.wait(GameConfig.ResultsTime)

	-- 6. Survivors go back to the lobby (eliminated players already respawned there)
	for player in pairs(participants) do
		if player.Character then
			player.Character:PivotTo(CFrame.new(ArenaBuilder.GetLobbySpot()))
		end
	end
end

---------------------------------------------------------------------
-- Run rounds forever. If a round ever crashes, log it and start a fresh one.
---------------------------------------------------------------------
while true do
	local ok, err = pcall(runRound)
	if not ok then
		warn("[RoundManager] Round crashed: " .. tostring(err))
		setStatus("Waiting", "Oops! Restarting the round...", 0)
		task.wait(2)
	end
	endRound()
end
]]></ProtectedString>
			</Properties>
		</Item>
		<Item class="ModuleScript" referent="RBX57709C1662D30D49405830975D957301">
			<Properties>
				<string name="Name">SoundPlayer</string>
				<ProtectedString name="Source"><![CDATA[-- SoundPlayer (ModuleScript in ServerScriptService)
-- Plays the sounds listed in GameConfig.Sounds. If an ID is empty it quietly does nothing,
-- so the game works fine with no audio at all.

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

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))

local SoundPlayer = {}

local LIFETIME = 10 -- seconds before the little Sound object is cleaned up

-- Returns the Sound it started, or nil when there is nothing to play.
function SoundPlayer.Play(soundName, parent)
	local sounds = GameConfig.Sounds
	local id = sounds and sounds[soundName]
	if type(id) ~= "string" or id == "" or parent == nil then
		return nil
	end
	local sound = Instance.new("Sound")
	sound.SoundId = "rbxassetid://" .. id
	sound.Volume = sounds.Volume or 0.6
	sound.Parent = parent
	sound:Play()
	Debris:AddItem(sound, LIFETIME)
	return sound
end

return SoundPlayer
]]></ProtectedString>
			</Properties>
		</Item>
		<Item class="Folder" referent="RBX5E6EFE4CD42413D976131E257DD12D9F">
			<Properties>
				<string name="Name">Events</string>
			</Properties>
			<Item class="ModuleScript" referent="RBX93E57D0975D168B6D86A82F283274B00">
				<Properties>
					<string name="Name">DisappearingFloor</string>
					<ProtectedString name="Source"><![CDATA[-- DisappearingFloor (ModuleScript inside ServerScriptService > Events)
-- Tiles flash orange, then vanish. Don't be standing on them. The lava is waiting.

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))

local DisappearingFloor = {}

function DisappearingFloor.Start(context)
	local settings = context.Settings
	local remaining = {}
	for _, tile in ipairs(context.Arena.GetTiles()) do
		table.insert(remaining, tile)
	end

	local running = true
	task.spawn(function()
		while running do
			local count = math.min(settings.TilesPerWave, #remaining - settings.KeepTiles)
			if count > 0 then
				local wave = {}
				for _ = 1, count do
					local tile = table.remove(remaining, math.random(1, #remaining))
					tile.Color = GameConfig.Colors.TileWarning
					tile.Material = Enum.Material.Neon
					table.insert(wave, tile)
				end
				task.wait(settings.WarnTime)
				if not running then
					break
				end
				for _, tile in ipairs(wave) do
					tile.Transparency = 1
					tile.CanCollide = false
				end
			end
			task.wait(settings.WaveInterval - settings.WarnTime)
		end
	end)

	return function()
		running = false
	end
end

return DisappearingFloor
]]></ProtectedString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX4FD3121C7F5F63716383374A3C9C99DB">
				<Properties>
					<string name="Name">MoonFloor</string>
					<ProtectedString name="Source"><![CDATA[-- MoonFloor (Level 2 bonus event: ModuleScript inside ServerScriptService > Events)
-- Low gravity AND the floor disappears. Shows how a new event can reuse an old one.
-- It is already switched on in GameConfig.Events (it unlocks on round 5):
--   { Name = "MoonFloor", Settings = "Floor", DisplayName = "MOON MODE! (the floor is STILL leaving)", UnlockRound = 5, Weight = 1 },

local DisappearingFloor = require(script.Parent:WaitForChild("DisappearingFloor"))

local MoonFloor = {}

local MOON_GRAVITY = 50 -- normal Roblox gravity is 196.2

function MoonFloor.Start(context)
	local normalGravity = workspace.Gravity
	workspace.Gravity = MOON_GRAVITY
	context.FX("Banner", "Gravity is broken!")

	-- If the floor part breaks, put gravity back before passing the error on.
	local started, stopFloor = pcall(DisappearingFloor.Start, context)
	if not started then
		workspace.Gravity = normalGravity
		error(stopFloor)
	end

	return function()
		stopFloor()
		workspace.Gravity = normalGravity
	end
end

return MoonFloor
]]></ProtectedString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX3090D13E2A39C77A9A18C82E60063B65">
				<Properties>
					<string name="Name">RisingFlood</string>
					<ProtectedString name="Source"><![CDATA[-- RisingFlood (ModuleScript inside ServerScriptService > Events)
-- Water fills the arena. Two staircases appear: climb before the water reaches you!

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

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))

local RisingFlood = {}

local STEP_HEIGHTS = { 4, 8, 12, 16, 20, 24, 28 } -- a 7-stud jump easily goes up 4

local function makeStep(folder, position)
	local step = Instance.new("Part")
	step.Name = "Step"
	step.Anchored = true
	step.Size = Vector3.new(8, 1, 8)
	step.Position = position
	step.Color = GameConfig.Colors.Steps
	step.Material = Enum.Material.SmoothPlastic
	step.Parent = folder
end

function RisingFlood.Start(context)
	local settings = context.Settings
	local arena = context.Arena
	local center = arena.Center
	local floorY = arena.FloorY
	local half = arena.Width / 2

	-- One staircase along the north wall, a mirrored one along the south wall.
	for i, height in ipairs(STEP_HEIGHTS) do
		local offsetX = -half + 6 + (i - 1) * 10
		local y = floorY + height - 0.5
		makeStep(context.Folder, Vector3.new(center.X + offsetX, y, center.Z - half + 6))
		makeStep(context.Folder, Vector3.new(center.X - offsetX, y, center.Z + half - 6))
	end

	local water = Instance.new("Part")
	water.Name = "FloodWater"
	water.Anchored = true
	water.CanCollide = false
	water.Material = Enum.Material.Glass
	water.Color = GameConfig.Colors.Water
	water.Transparency = 0.35
	water.Size = Vector3.new(arena.Width, 1, arena.Width)
	water.Position = Vector3.new(center.X, floorY + 0.5, center.Z)
	water.Parent = context.Folder

	local rise = TweenService:Create(water, TweenInfo.new(settings.RiseTime, Enum.EasingStyle.Linear), {
		Size = Vector3.new(arena.Width, settings.MaxHeight, arena.Width),
		Position = Vector3.new(center.X, floorY + settings.MaxHeight / 2, center.Z),
	})
	rise:Play()

	local running = true
	task.spawn(function()
		while running do
			local elapsed = task.wait(0.25)
			if not running then
				break
			end
			local waterTop = water.Position.Y + water.Size.Y / 2
			for _, info in ipairs(context.GetAlive()) do
				-- the root part is about waist height, so this means "waist-deep or worse"
				if info.Root.Position.Y < waterTop then
					info.Humanoid:TakeDamage(settings.DamagePerSecond * elapsed)
				end
			end
		end
	end)

	return function()
		running = false
		rise:Cancel()
	end
end

return RisingFlood
]]></ProtectedString>
				</Properties>
			</Item>
			<Item class="ModuleScript" referent="RBX85A02DF613DE29BFCE900A1B7107D323">
				<Properties>
					<string name="Name">RollingBall</string>
					<ProtectedString name="Source"><![CDATA[-- RollingBall (ModuleScript inside ServerScriptService > Events)
-- A giant ball picks a victim, pauses, then charges in a straight line. Side-step it!

local ReplicatedStorage = game:GetService("ReplicatedStorage")

local GameConfig = require(ReplicatedStorage:WaitForChild("GameConfig"))

local RollingBall = {}

local AIM_PAUSE = 0.6 -- seconds the ball "thinks" before each charge (your chance to dodge)

local function runBall(context, ball, isRunning)
	local settings = context.Settings
	local arena = context.Arena
	local radius = settings.Size / 2
	local limit = arena.Width / 2 - radius
	local center = arena.Center
	local y = arena.FloorY + radius
	local position = ball.Position
	local spin = CFrame.new()

	while isRunning() do
		-- 1. pick a target and aim
		local alive = context.GetAlive()
		local direction
		if #alive > 0 then
			local target = alive[math.random(1, #alive)].Root.Position
			direction = Vector3.new(target.X - position.X, 0, target.Z - position.Z)
		end
		if direction == nil or direction.Magnitude < 1 then
			local angle = math.random() * math.pi * 2
			direction = Vector3.new(math.cos(angle), 0, math.sin(angle))
		end
		direction = direction.Unit
		task.wait(AIM_PAUSE)

		-- 2. charge until we hit a wall
		local hitWall = false
		while isRunning() and not hitWall do
			local elapsed = task.wait()
			local nextPosition = position + direction * settings.Speed * elapsed
			local offsetX = nextPosition.X - center.X
			local offsetZ = nextPosition.Z - center.Z
			if math.abs(offsetX) > limit or math.abs(offsetZ) > limit then
				hitWall = true
				offsetX = math.clamp(offsetX, -limit, limit)
				offsetZ = math.clamp(offsetZ, -limit, limit)
			end
			position = Vector3.new(center.X + offsetX, y, center.Z + offsetZ)

			-- make it look like it's rolling
			local rollAxis = Vector3.new(direction.Z, 0, -direction.X)
			spin = CFrame.fromAxisAngle(rollAxis, settings.Speed * elapsed / radius) * spin
			ball.CFrame = CFrame.new(position) * spin

			for _, info in ipairs(context.GetAlive()) do
				if (info.Root.Position - position).Magnitude < radius + 1.5 then
					info.Humanoid.Health = 0
				end
			end
		end
		if hitWall then
			context.FX("Shake", 0.6, 0.4)
		end
	end
end

function RollingBall.Start(context)
	local settings = context.Settings
	local running = true
	local function isRunning()
		return running
	end

	for i = 1, settings.Count do
		local ball = Instance.new("Part")
		ball.Name = "GiantBall" .. i
		ball.Shape = Enum.PartType.Ball
		ball.Size = Vector3.new(settings.Size, settings.Size, settings.Size)
		ball.Anchored = true -- the script moves it, so physics can't fling it out of the arena
		ball.CanCollide = false
		ball.Color = GameConfig.Colors.Ball
		ball.Material = Enum.Material.SmoothPlastic
		-- start in a corner, away from the middle
		local corner = (i % 2 == 1) and 1 or -1
		local start = context.Arena.Center + Vector3.new(corner, 0, corner) * (context.Arena.Width / 2 - settings.Size / 2)
		ball.Position = Vector3.new(start.X, context.Arena.FloorY + settings.Size / 2, start.Z)
		ball.Parent = context.Folder
		task.spawn(runBall, context, ball, isRunning)
	end

	return function()
		running = false
	end
end

return RollingBall
]]></ProtectedString>
				</Properties>
			</Item>
		</Item>
	</Item>
	<Item class="ServerStorage" referent="RBX2C68708FE17C45421CA1FF760A120B33">
		<Properties>
			<string name="Name">ServerStorage</string>
		</Properties>
	</Item>
	<Item class="StarterGui" referent="RBXCAFEF993210AFC8A021D814CBD7DFAFB">
		<Properties>
			<string name="Name">StarterGui</string>
		</Properties>
	</Item>
	<Item class="StarterPack" referent="RBX5F9E15DF072227F85D173ACEFA0DE1B2">
		<Properties>
			<string name="Name">StarterPack</string>
		</Properties>
	</Item>
	<Item class="StarterPlayer" referent="RBXA0FC70D274055A22C351D7BAB15B01E2">
		<Properties>
			<string name="Name">StarterPlayer</string>
		</Properties>
		<Item class="StarterPlayerScripts" referent="RBX52B6F111CD21E3914C5A96BA5CCDA455">
			<Properties>
				<string name="Name">StarterPlayerScripts</string>
			</Properties>
			<Item class="LocalScript" referent="RBX642CE0F4EA14C5C23E1C6C44C90DB593">
				<Properties>
					<string name="Name">HUD</string>
					<bool name="Disabled">false</bool>
					<ProtectedString name="Source"><![CDATA[-- HUD (LocalScript in StarterPlayer > StarterPlayerScripts)
-- Runs on each player's own computer/phone. It builds the on-screen text and plays screen effects.
-- It only DISPLAYS things; the server decides everything (so nobody can cheat coins from here).

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

local player = Players.LocalPlayer
local fxRemote = ReplicatedStorage:WaitForChild("GameFX")

local FONT = Enum.Font.FredokaOne
local SHAKE_ENABLED = true -- set false if screen shake makes anyone feel sick

---------------------------------------------------------------------
-- Build the screen (Scale sizes so it fits phones and big monitors)
---------------------------------------------------------------------
local gui = Instance.new("ScreenGui")
gui.Name = "HUD"
gui.ResetOnSpawn = false -- keep it when your character respawns

local function makeLabel(name, position, size)
	local label = Instance.new("TextLabel")
	label.Name = name
	label.AnchorPoint = Vector2.new(0.5, 0.5)
	label.Position = position
	label.Size = size
	label.BackgroundTransparency = 1
	label.Font = FONT
	label.TextScaled = true
	label.TextColor3 = Color3.fromRGB(255, 255, 255)
	label.Text = ""
	local stroke = Instance.new("UIStroke")
	stroke.Thickness = 3
	stroke.Color = Color3.fromRGB(20, 24, 36)
	stroke.Parent = label
	local limit = Instance.new("UITextSizeConstraint")
	limit.MinTextSize = 16
	limit.MaxTextSize = 64
	limit.Parent = label
	label.Parent = gui
	return label
end

local status = makeLabel("Status", UDim2.fromScale(0.5, 0.06), UDim2.fromScale(0.6, 0.07))
status.BackgroundTransparency = 0.35
status.BackgroundColor3 = Color3.fromRGB(20, 24, 36)
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0.3, 0)
corner.Parent = status

local timer = makeLabel("Timer", UDim2.fromScale(0.5, 0.16), UDim2.fromScale(0.2, 0.1))
local info = makeLabel("Info", UDim2.fromScale(0.5, 0.96), UDim2.fromScale(0.4, 0.04))
local banner = makeLabel("Banner", UDim2.fromScale(0.5, 0.4), UDim2.fromScale(0.8, 0.12))
banner.TextColor3 = Color3.fromRGB(255, 220, 60)
banner.Visible = false

local flash = Instance.new("Frame")
flash.Name = "Flash"
flash.Size = UDim2.fromScale(1, 1)
flash.BackgroundColor3 = Color3.fromRGB(255, 40, 40)
flash.BackgroundTransparency = 1
flash.BorderSizePixel = 0
flash.ZIndex = 0
flash.Parent = gui

gui.Parent = player:WaitForChild("PlayerGui")

---------------------------------------------------------------------
-- Show what the server is telling us
---------------------------------------------------------------------
local function updateStatus()
	status.Text = ReplicatedStorage:GetAttribute("Status") or "Loading..."
	local timeLeft = ReplicatedStorage:GetAttribute("TimeLeft") or 0
	timer.Text = timeLeft > 0 and tostring(timeLeft) or ""
	-- the last 5 seconds of the button phase turn red
	local phase = ReplicatedStorage:GetAttribute("Phase")
	if phase == "Button" and timeLeft <= 5 then
		timer.TextColor3 = Color3.fromRGB(255, 70, 70)
	else
		timer.TextColor3 = Color3.fromRGB(255, 255, 255)
	end
end

local function updateInfo()
	local round = ReplicatedStorage:GetAttribute("Round")
	local difficulty = ReplicatedStorage:GetAttribute("Difficulty")
	if round then
		info.Text = "Round " .. round .. "  |  " .. tostring(difficulty)
	end
end

ReplicatedStorage:GetAttributeChangedSignal("Status"):Connect(updateStatus)
ReplicatedStorage:GetAttributeChangedSignal("TimeLeft"):Connect(updateStatus)
ReplicatedStorage:GetAttributeChangedSignal("Round"):Connect(updateInfo)
ReplicatedStorage:GetAttributeChangedSignal("Difficulty"):Connect(updateInfo)
updateStatus()
updateInfo()

---------------------------------------------------------------------
-- Screen effects
---------------------------------------------------------------------
local bannerToken = 0
local function showBanner(text)
	bannerToken = bannerToken + 1
	local myToken = bannerToken
	banner.Text = text
	banner.Visible = true
	task.delay(2.5, function()
		if bannerToken == myToken then -- a newer banner replaces this one
			banner.Visible = false
		end
	end)
end

local function playFlash()
	flash.BackgroundTransparency = 0.4
	TweenService:Create(flash, TweenInfo.new(0.6), { BackgroundTransparency = 1 }):Play()
end

local function shake(strength, duration)
	if not SHAKE_ENABLED then
		return
	end
	local character = player.Character
	local humanoid = character and character:FindFirstChildOfClass("Humanoid")
	if not humanoid then
		return
	end
	local started = os.clock()
	local connection
	connection = RunService.RenderStepped:Connect(function()
		local progress = (os.clock() - started) / duration
		if progress >= 1 or humanoid.Parent == nil then
			humanoid.CameraOffset = Vector3.new(0, 0, 0)
			connection:Disconnect()
			return
		end
		local amount = strength * (1 - progress) -- fades out
		humanoid.CameraOffset = Vector3.new((math.random() * 2 - 1) * amount, (math.random() * 2 - 1) * amount, 0)
	end)
end

fxRemote.OnClientEvent:Connect(function(kind, a, b)
	if kind == "Banner" then
		showBanner(a)
	elseif kind == "Flash" then
		playFlash()
	elseif kind == "Shake" then
		shake(a or 0.5, b or 0.5)
	end
end)
]]></ProtectedString>
				</Properties>
			</Item>
		</Item>
		<Item class="StarterCharacterScripts" referent="RBX25BE83129013A708851B36D8DB4D0449">
			<Properties>
				<string name="Name">StarterCharacterScripts</string>
			</Properties>
		</Item>
	</Item>
	<Item class="Teams" referent="RBX970B660BF35FB00091175667DFF207BD">
		<Properties>
			<string name="Name">Teams</string>
		</Properties>
	</Item>
	<Item class="SoundService" referent="RBX9C866156D7A9FCC59C2D9992510FA4BE">
		<Properties>
			<string name="Name">SoundService</string>
		</Properties>
	</Item>
	<Item class="Chat" referent="RBX86D722661FC1C95EDB145992130EC857">
		<Properties>
			<string name="Name">Chat</string>
		</Properties>
	</Item>
	<Item class="TextChatService" referent="RBX173409CCFDCB3901B4712D0412F2A47C">
		<Properties>
			<string name="Name">TextChatService</string>
		</Properties>
	</Item>
	<Item class="LocalizationService" referent="RBX6E638EA1CE169B8599FFB6DF554D164D">
		<Properties>
			<string name="Name">LocalizationService</string>
		</Properties>
	</Item>
	<Item class="TestService" referent="RBX11488AD625AC0A8C94C35B18277BF3A9">
		<Properties>
			<string name="Name">TestService</string>
		</Properties>
	</Item>
</roblox>
