local function b(b,y,V,S,X)return((b-y)*(X-S))/(V-y)+S end local function y(b)b.TimePosition=0 b.Playing=true end local function V(b)b.Playing=false b.TimePosition=0 end local function S(b)local y={}for b,V in pairs(b)do y[b]=V end return y end local function X(V,X,U)local G={}for b,y in pairs(SOUND_DATA)do local V=Instance.new("\083\111\117\110\100")V.Name=b V.Archivable=false V.EmitterSize=5 V.MaxDistance=150 V.Volume=.65 for b,y in pairs(y)do V[b]=y end V.Parent=U G[b]=V end local L={}local function D(b)for y in pairs(S(L))do if y~=b then y.Playing=false L[y]=nil end end end local g={[Enum.HumanoidStateType.FallingDown]=function()D()end;[Enum.HumanoidStateType.GettingUp]=function()D()y(G.GettingUp)end;[Enum.HumanoidStateType.Jumping]=function()D()y(G.Jumping)end;[Enum.HumanoidStateType.Swimming]=function()local V=math.abs(U.Velocity.Y)if V>.1 then G.Splash.Volume=math.clamp(b(V,100,350,.28,1),0,1)y(G.Splash)end D(G.Swimming)G.Swimming.Playing=true L[G.Swimming]=true end,[Enum.HumanoidStateType.Freefall]=function()G.FreeFalling.Volume=0 D(G.FreeFalling)L[G.FreeFalling]=true end;[Enum.HumanoidStateType.Landed]=function()D()local V=math.abs(U.Velocity.Y)if V>75 then G.Landing.Volume=math.clamp(b(V,50,100,0,1),0,1)y(G.Landing)end end,[Enum.HumanoidStateType.Running]=function()D(G.Running)G.Running.Playing=true L[G.Running]=true end;[Enum.HumanoidStateType.Climbing]=function()local b=G.Climbing if math.abs(U.Velocity.Y)>.1 then b.Playing=true D(b)else D()end L[b]=true end,[Enum.HumanoidStateType.Seated]=function()D()end;[Enum.HumanoidStateType.Dead]=function()D()y(G.Died)end}local F={[G.Climbing]=function(b,y,V)y.Playing=V.Magnitude>.1 end;[G.FreeFalling]=function(b,y,V)if V.Magnitude>75 then y.Volume=math.clamp(y.Volume+.9*b,0,1)else y.Volume=0 end end;[G.Running]=function(b,y,V)y.Playing=V.Magnitude>.5 and X.MoveDirection.Magnitude>.5 end}local E={[Enum.HumanoidStateType.RunningNoPhysics]=Enum.HumanoidStateType.Running}local k=E[X:GetState()]or X:GetState()local m={}local C=X.StateChanged:Connect(function(b,y)y=E[y]or y if y~=k then local b=g[y]if b then b()end k=y end end)local f=SetState.Event:Connect(function(b)b=E[b]or b if b~=k then local y=g[b]if y then y()end k=b end end)local s=RunService.Stepped:Connect(function(b,y)for b in pairs(L)do local V=F[b]if V then V(y,b,U.Velocity)end end end)local N local i local a local function o()C:Disconnect()f:Disconnect()s:Disconnect()N:Disconnect()i:Disconnect()a:Disconnect()end N=X.AncestryChanged:Connect(function(b,y)if not y then o()end end)i=U.AncestryChanged:Connect(function(b,y)if not y then o()end end)a=V.CharacterAdded:Connect(o)end local function U(b)local function y(y)if not y.Parent then waitForFirst(y.AncestryChanged,b.CharacterAdded)end if b.Character~=y or not y.Parent then return end local V=y:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")while y:IsDescendantOf(game)and not V do waitForFirst(y.ChildAdded,y.AncestryChanged,b.CharacterAdded)V=y:FindFirstChildOfClass("\072\117\109\097\110\111\105\100")end if b.Character~=y or not y:IsDescendantOf(game)then return end local S=y:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")while y:IsDescendantOf(game)and not S do waitForFirst(y.ChildAdded,y.AncestryChanged,V.AncestryChanged,b.CharacterAdded)S=y:FindFirstChild("\072\117\109\097\110\111\105\100\082\111\111\116\080\097\114\116")end if S and(V:IsDescendantOf(game)and(y:IsDescendantOf(game)and b.Character==y))then X(b,V,S)end end if b.Character then y(b.Character)end b.CharacterAdded:Connect(y)end Players.PlayerAdded:Connect(U)for b,y in ipairs(Players:GetPlayers())do U(y)end
-- map a value from one range to another
local function map(x, inMin, inMax, outMin, outMax)
	return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin
end

local function playSound(sound)
	sound.TimePosition = 0
	sound.Playing = true
end

local function stopSound(sound)
	sound.Playing = false
	sound.TimePosition = 0
end

local function shallowCopy(t)
	local out = {}
	for k, v in pairs(t) do
		out[k] = v
	end
	return out
end

local function initializeSoundSystem(player, humanoid, rootPart)
	local sounds = {}

	-- initialize sounds
	for name, props in pairs(SOUND_DATA) do
		local sound = Instance.new("Sound")
		sound.Name = name

		-- set default values
		sound.Archivable = false
		sound.EmitterSize = 5
		sound.MaxDistance = 150
		sound.Volume = 0.65

		for propName, propValue in pairs(props) do
			sound[propName] = propValue
		end

		sound.Parent = rootPart
		sounds[name] = sound
	end

	local playingLoopedSounds = {}

	local function stopPlayingLoopedSounds(except)
		for sound in pairs(shallowCopy(playingLoopedSounds)) do
			if sound ~= except then
				sound.Playing = false
				playingLoopedSounds[sound] = nil
			end
		end
	end

	-- state transition callbacks
	local stateTransitions = {
		[Enum.HumanoidStateType.FallingDown] = function()
			stopPlayingLoopedSounds()
		end,

		[Enum.HumanoidStateType.GettingUp] = function()
			stopPlayingLoopedSounds()
			playSound(sounds.GettingUp)
		end,

		[Enum.HumanoidStateType.Jumping] = function()
			stopPlayingLoopedSounds()
			playSound(sounds.Jumping)
		end,

		[Enum.HumanoidStateType.Swimming] = function()
			local verticalSpeed = math.abs(rootPart.Velocity.Y)
			if verticalSpeed > 0.1 then
				sounds.Splash.Volume = math.clamp(map(verticalSpeed, 100, 350, 0.28, 1), 0, 1)
				playSound(sounds.Splash)
			end
			stopPlayingLoopedSounds(sounds.Swimming)
			sounds.Swimming.Playing = true
			playingLoopedSounds[sounds.Swimming] = true
		end,

		[Enum.HumanoidStateType.Freefall] = function()
			sounds.FreeFalling.Volume = 0
			stopPlayingLoopedSounds(sounds.FreeFalling)
			playingLoopedSounds[sounds.FreeFalling] = true
		end,

		[Enum.HumanoidStateType.Landed] = function()
			stopPlayingLoopedSounds()
			local verticalSpeed = math.abs(rootPart.Velocity.Y)
			if verticalSpeed > 75 then
				sounds.Landing.Volume = math.clamp(map(verticalSpeed, 50, 100, 0, 1), 0, 1)
				playSound(sounds.Landing)
			end
		end,

		[Enum.HumanoidStateType.Running] = function()
			stopPlayingLoopedSounds(sounds.Running)
			sounds.Running.Playing = true
			playingLoopedSounds[sounds.Running] = true
		end,

		[Enum.HumanoidStateType.Climbing] = function()
			local sound = sounds.Climbing
			if math.abs(rootPart.Velocity.Y) > 0.1 then
				sound.Playing = true
				stopPlayingLoopedSounds(sound)
			else
				stopPlayingLoopedSounds()
			end
			playingLoopedSounds[sound] = true
		end,

		[Enum.HumanoidStateType.Seated] = function()
			stopPlayingLoopedSounds()
		end,

		[Enum.HumanoidStateType.Dead] = function()
			stopPlayingLoopedSounds()
			playSound(sounds.Died)
		end,
	}

	-- updaters for looped sounds
	local loopedSoundUpdaters = {
		[sounds.Climbing] = function(dt, sound, vel)
			sound.Playing = vel.Magnitude > 0.1
		end,

		[sounds.FreeFalling] = function(dt, sound, vel)
			if vel.Magnitude > 75 then
				sound.Volume = math.clamp(sound.Volume + 0.9 * dt, 0, 1)
			else
				sound.Volume = 0
			end
		end,

		[sounds.Running] = function(dt, sound, vel)
			sound.Playing = vel.Magnitude > 0.5 and humanoid.MoveDirection.Magnitude > 0.5
		end,
	}

	-- state substitutions to avoid duplicating entries in the state table
	local stateRemap = {
		[Enum.HumanoidStateType.RunningNoPhysics] = Enum.HumanoidStateType.Running,
	}

	local activeState = stateRemap[humanoid:GetState()] or humanoid:GetState()
	local activeConnections = {}

	local stateChangedConn = humanoid.StateChanged:Connect(function(_, state)
		state = stateRemap[state] or state

		if state ~= activeState then
			local transitionFunc = stateTransitions[state]

			if transitionFunc then
				transitionFunc()
			end

			activeState = state
		end
	end)

	local customStateChangedConn = SetState.Event:Connect(function(state)
		state = stateRemap[state] or state

		if state ~= activeState then
			local transitionFunc = stateTransitions[state]

			if transitionFunc then
				transitionFunc()
			end

			activeState = state
		end
	end)

	local steppedConn = RunService.Stepped:Connect(function(_, worldDt)
		-- update looped sounds on stepped
		for sound in pairs(playingLoopedSounds) do
			local updater = loopedSoundUpdaters[sound]

			if updater then
				updater(worldDt, sound, rootPart.Velocity)
			end
		end
	end)

	local humanoidAncestryChangedConn
	local rootPartAncestryChangedConn
	local characterAddedConn

	local function terminate()
		stateChangedConn:Disconnect()
		customStateChangedConn:Disconnect()
		steppedConn:Disconnect()
		humanoidAncestryChangedConn:Disconnect()
		rootPartAncestryChangedConn:Disconnect()
		characterAddedConn:Disconnect()
	end

	humanoidAncestryChangedConn = humanoid.AncestryChanged:Connect(function(_, parent)
		if not parent then
			terminate()
		end
	end)

	rootPartAncestryChangedConn = rootPart.AncestryChanged:Connect(function(_, parent)
		if not parent then
			terminate()
		end
	end)

	characterAddedConn = player.CharacterAdded:Connect(terminate)
end

local function playerAdded(player)
	local function characterAdded(character)
		-- Avoiding memory leaks in the face of Character/Humanoid/RootPart lifetime has a few complications:
		-- * character deparenting is a Remove instead of a Destroy, so signals are not cleaned up automatically.
		-- ** must use a waitForFirst on everything and listen for hierarchy changes.
		-- * the character might not be in the dm by the time CharacterAdded fires
		-- ** constantly check consistency with player.Character and abort if CharacterAdded is fired again
		-- * Humanoid may not exist immediately, and by the time it's inserted the character might be deparented.
		-- * RootPart probably won't exist immediately.
		-- ** by the time RootPart is inserted and Humanoid.RootPart is set, the character or the humanoid might be deparented.

		if not character.Parent then
			waitForFirst(character.AncestryChanged, player.CharacterAdded)
		end

		if player.Character ~= character or not character.Parent then
			return
		end

		local humanoid = character:FindFirstChildOfClass("Humanoid")
		while character:IsDescendantOf(game) and not humanoid do
			waitForFirst(character.ChildAdded, character.AncestryChanged, player.CharacterAdded)
			humanoid = character:FindFirstChildOfClass("Humanoid")
		end

		if player.Character ~= character or not character:IsDescendantOf(game) then
			return
		end

		-- must rely on HumanoidRootPart naming because Humanoid.RootPart does not fire changed signals
		local rootPart = character:FindFirstChild("HumanoidRootPart")
		while character:IsDescendantOf(game) and not rootPart do
			waitForFirst(
				character.ChildAdded,
				character.AncestryChanged,
				humanoid.AncestryChanged,
				player.CharacterAdded
			)
			rootPart = character:FindFirstChild("HumanoidRootPart")
		end

		if
			rootPart
			and humanoid:IsDescendantOf(game)
			and character:IsDescendantOf(game)
			and player.Character == character
		then
			initializeSoundSystem(player, humanoid, rootPart)
		end
	end

	if player.Character then
		characterAdded(player.Character)
	end
	player.CharacterAdded:Connect(characterAdded)
end

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