--============================================================* -- Prison Life - Persistent ORIGINAL Gun Sounds --============================================================* -- -- SHOOTING: -- • Keeps the original shot playing after weapon switch -- • One shot = one sound -- • No double sounds -- • No looping -- -- RELOAD: -- • Keeps the original reload playing after weapon switch -- • Does not restart -- • Does not loop -- -- AUDIO: -- • Uses the ORIGINAL game's SoundId -- • Preserves the original sound's 3D position -- • Does NOT play as a background/global sound -- --============================================================* local Players = game:GetService("Players") local Workspace = game:GetService("Workspace") local Player = Players.LocalPlayer --============================================================* -- PERSISTENT FOLDER --============================================================* local PersistentFolder = Workspace:FindFirstChild("PrisonLife_PersistentGunSounds") if not PersistentFolder then PersistentFolder = Instance.new("Folder") PersistentFolder.Name = "PrisonLife_PersistentGunSounds" PersistentFolder.Parent = Workspace end --============================================================* -- SOUND DETECTION --============================================================* local function getSoundType(sound) if not sound or not sound:IsA("Sound") then return nil end local name = string.lower(sound.Name) ------------------------------------------------------------ -- RELOAD ------------------------------------------------------------ if string.find(name, "reload", 1, true) then return "reload" end ------------------------------------------------------------ -- SHOOTING ------------------------------------------------------------ if string.find(name, "shoot", 1, true) then return "shoot" end if string.find(name, "fire", 1, true) then return "shoot" end if string.find(name, "shot", 1, true) then return "shoot" end return nil end --============================================================* -- TRACKING --============================================================* local hooked = {} local lastPreserved = {} --============================================================* -- GET WORLD POSITION --============================================================* local function getSoundPosition(sound) local parent = sound.Parent if parent and parent:IsA("Attachment") then return parent.WorldPosition end if parent and parent:IsA("BasePart") then return parent.Position end local part = sound:FindFirstAncestorWhichIsA("BasePart") if part then return part.Position end return nil end --============================================================* -- CREATE 3D SOUND CARRIER --============================================================* local function createCarrier(position) if not position then return nil end local carrier = Instance.new("Part") carrier.Name = "PersistentGunSoundEmitter" carrier.Size = Vector3.new(0.1, 0.1, 0.1) carrier.Transparency = 1 carrier.Anchored = true carrier.CanCollide = false carrier.CanTouch = false carrier.CanQuery = false carrier.CastShadow = false carrier.CFrame = CFrame.new(position) carrier.Parent = PersistentFolder return carrier end --============================================================* -- PRESERVE SOUND --============================================================* local function preserveSound(sound) if not sound or not sound.Parent or not sound:IsA("Sound") then return end local soundType = getSoundType(sound) if not soundType then return end ------------------------------------------------------------ -- Prevent duplicate event handling ------------------------------------------------------------ local now = os.clock() if lastPreserved[sound] and now - lastPreserved[sound] < 0.08 then return end lastPreserved[sound] = now ------------------------------------------------------------ -- Remember the original world position ------------------------------------------------------------ local position = getSoundPosition(sound) ------------------------------------------------------------ -- Clone ORIGINAL sound ------------------------------------------------------------ local clone local success = pcall(function() clone = sound:Clone() end) if not success or not clone then return end ------------------------------------------------------------ -- Never loop ------------------------------------------------------------ clone.Looped = false ------------------------------------------------------------ -- Create a proper 3D emitter ------------------------------------------------------------ local carrier = createCarrier(position) if carrier then clone.Parent = carrier else -- Fallback if the original sound has no -- detectable world position. clone.Parent = PersistentFolder end ------------------------------------------------------------ -- Continue from EXACT current playback position ------------------------------------------------------------ pcall(function() clone.TimePosition = sound.TimePosition end) ------------------------------------------------------------ -- Play the persistent copy ------------------------------------------------------------ pcall(function() clone:Play() end) ------------------------------------------------------------ -- Stop the original. -- -- The clone is now independent and survives -- weapon switching / unequipping. ------------------------------------------------------------ task.defer(function() if sound and sound.Parent then pcall(function() sound:Stop() end) end end) --========================================================* -- CLEANUP --========================================================* local cleaned = false local function cleanup() if cleaned then return end cleaned = true pcall(function() if clone then clone:Destroy() end end) pcall(function() if carrier then carrier:Destroy() end end) end ------------------------------------------------------------ -- Normal sound completion ------------------------------------------------------------ clone.Ended:Connect(function() cleanup() end) ------------------------------------------------------------ -- Safety cleanup ------------------------------------------------------------ task.spawn(function() local startTime = os.clock() local length = 0 pcall(function() length = clone.TimeLength end) if length <= 0 then task.wait(0.25) pcall(function() length = clone.TimeLength end) end local timeout = math.max( length + 1, 5 ) while not cleaned and clone and clone.Parent and os.clock() - startTime < timeout do task.wait(0.1) end cleanup() end) end --============================================================* -- HOOK SOUND --============================================================* local function hookSound(sound) if not sound:IsA("Sound") then return end local soundType = getSoundType(sound) if not soundType then return end if hooked[sound] then return end hooked[sound] = true ------------------------------------------------------------ -- SHOOTING -- -- ONLY Played is used. -- This prevents the double-shot problem. ------------------------------------------------------------ if soundType == "shoot" then sound.Played:Connect(function() preserveSound(sound) end) ------------------------------------------------------------ -- RELOAD ------------------------------------------------------------ elseif soundType == "reload" then sound.Played:Connect(function() preserveSound(sound) end) -------------------------------------------------------- -- Backup for reload sounds that change Playing -- without properly firing Played. -------------------------------------------------------- sound:GetPropertyChangedSignal("Playing"):Connect( function() if not sound.Playing then return end local now = os.clock() if not lastPreserved[sound] or now - lastPreserved[sound] > 0.08 then preserveSound(sound) end end ) end ------------------------------------------------------------ -- Cleanup tracking ------------------------------------------------------------ sound.Destroying:Connect(function() hooked[sound] = nil lastPreserved[sound] = nil end) end --============================================================* -- SCAN CONTAINER --============================================================* local function scanContainer(container) if not container then return end ------------------------------------------------------------ -- Existing sounds ------------------------------------------------------------ for _, object in ipairs( container:GetDescendants() ) do if object:IsA("Sound") then hookSound(object) end end ------------------------------------------------------------ -- Newly-created sounds ------------------------------------------------------------ container.DescendantAdded:Connect( function(object) if object:IsA("Sound") then hookSound(object) end end ) end --============================================================* -- BACKPACK --============================================================* local Backpack = Player:WaitForChild("Backpack") scanContainer( Backpack ) --============================================================* -- CHARACTER / EQUIPPED WEAPON --============================================================* local function setupCharacter(character) scanContainer( character ) end if Player.Character then setupCharacter( Player.Character ) end Player.CharacterAdded:Connect( function(character) setupCharacter( character ) end ) --============================================================* -- PLAYERGUI --============================================================* local PlayerGui = Player:WaitForChild("PlayerGui") scanContainer( PlayerGui ) --============================================================* -- DONE --============================================================*