[SOLVED][ZSCRIPT] Pause Every Sound In Game: HELP

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
Post Reply
Kan3x
Posts: 67
Joined: Tue Nov 08, 2022 4:19 pm

[SOLVED][ZSCRIPT] Pause Every Sound In Game: HELP

Post by Kan3x »

I would like to make a monster that can deafen the player and knowing about S_PauseSound that's used in the TimeFreezer powerup, I decided to make a simple power that just pause all the sounds and music, although it seems that is not that immediate...

At first, I just thought that calling for S_PauseSound in InitEffect would have done the trick, but apparently I still have to give the player the timefreezer flag for the sound pausing function to work :/
Not only that, but if I don't have this bit

Code: Select all

for(int i = 0; i < MAXPLAYERS; ++i) {
	if (playeringame[i] && players[i].timefreezer != 0) {
		return;
	}
}
in my EndEffect, the game crashes whenever the powerup ends and this is making me even more confused...

What is going on here???

Noticing that when the player has this powerup active every sound is paused (good) except for when he picks up items (?), I started to think of a way to play a specific sound when the powerup activates (or even when it's active until it ends), but for now I got no luck.
Is there a way?

This is the code I'm using right now:

Code: Select all

Class Deafen : Powerup
{
	Default
	{
		Powerup.Duration -3;
	}
	
	override void InitEffect() {
		Super.InitEffect();
		if (Owner == null || Owner.player == null) {
			return;
		}
		
		S_PauseSound(false, false);
		Owner.player.timefreezer = 1;
	}
	
	override void EndEffect() {
		Super.EndEffect();
		if (Owner != null && Owner.player != null) {
			Owner.player.timefreezer = 0;
		}
		for(int i = 0; i < MAXPLAYERS; ++i) {
			if (playeringame[i] && players[i].timefreezer != 0) {
				return;
			}
		}
		S_ResumeSound(false);
	}
}
Any help in understanding what is going on and how to play a specific sound with this powerup is highly appreciated! thx
Last edited by Kan3x on Sun Jan 28, 2024 11:00 am, edited 1 time in total.
User avatar
Player701
 
 
Posts: 1710
Joined: Wed May 13, 2009 3:15 am
Graphics Processor: nVidia with Vulkan support
Contact:

Re: [ZSCRIPT] Pause Every Sound In Game: HELP

Post by Player701 »

Kan3x wrote: Tue Jan 16, 2024 2:12 pm At first, I just thought that calling for S_PauseSound in InitEffect would have done the trick, but apparently I still have to give the player the timefreezer flag for the sound pausing function to work :/
This is because the engine is coded to automatically resume sound if none of the in-game players have timefreezer set to a non-zero value.
Kan3x wrote: Tue Jan 16, 2024 2:12 pm Not only that, but if I don't have this bit

Code: Select all

for(int i = 0; i < MAXPLAYERS; ++i) {
	if (playeringame[i] && players[i].timefreezer != 0) {
		return;
	}
}
in my EndEffect, the game crashes whenever the powerup ends and this is making me even more confused...
I could not reproduce the crash. Are you by chance using an outdated release of GZDoom? Try 4.11.3.
Kan3x wrote: Tue Jan 16, 2024 2:12 pm Noticing that when the player has this powerup active every sound is paused (good) except for when he picks up items (?), I started to think of a way to play a specific sound when the powerup activates (or even when it's active until it ends), but for now I got no luck.
Is there a way?
I think you have to use the flag CHANF_NOPAUSE for this to work. Item pickup sounds use this flag as well.
Kan3x wrote: Tue Jan 16, 2024 2:12 pm Any help in understanding what is going on and how to play a specific sound with this powerup is highly appreciated! thx
Well, basically, the timefreezer field is used to track if players are allowed to move when the time-freeze effect is active. If non-zero, the player can move, otherwise they cannot. In multiplayer, when someone activates the time-freezer powerup, they and their teammates have this field set to a non-zero value to allow movement. It also uses different bits for each player to allow multiple powerups to be active simultaneously, as well as properly handle their deactivation.

For your Deafen powerup to work correctly, it is sufficient to set the value for the owning player only since no one actually gets frozen in the first place. Also, unless your intention is to deafen all players, you should only call S_PauseSound for the console player only:

Code: Select all

class Deafen : Powerup
{
    Default
    {
        Powerup.Duration -3;
    }
    
    override void InitEffect()
    {    
        if (Owner == null || Owner.player == null) 
        {
            return;
        }
        
        Super.InitEffect();
        
        if (Owner.PlayerNumber() == consoleplayer)
        {
            S_PauseSound(false, false);
        }

        Owner.player.timefreezer = 1;
    }
    
    override void EndEffect() 
    {
        if (Owner == null || Owner.player == null) 
        {
            return;
        }
        
        Owner.player.timefreezer = 0;
        
        if (Owner.PlayerNumber() == consoleplayer)
        {
            S_ResumeSound(false);
        }

        Super.EndEffect();
    }
}
Note, however, that one way or another, you will lose compatibility with the actual time-freezer powerup, so I don't see a need to overcomplicate the code. IMO, it'd be better to suggest a feature that would allow modders to alter the player's hearing range via ZScript.
Kan3x
Posts: 67
Joined: Tue Nov 08, 2022 4:19 pm

Re: [ZSCRIPT] Pause Every Sound In Game: HELP

Post by Kan3x »

Player701 wrote: Wed Jan 24, 2024 4:40 am This is because the engine is coded to automatically resume sound if none of the in-game players have timefreezer set to a non-zero value.
That's the missing piece, thanks for the point!
Player701 wrote: Wed Jan 24, 2024 4:40 am I could not reproduce the crash. Are you by chance using an outdated release of GZDoom? Try 4.11.3.
I was using 4.11.0 and I can't re-reproduce the crash anymore, now the "sounds" just don't get muted. Maybe I was tripping and did also something else for the crash to happen.
Player701 wrote: Wed Jan 24, 2024 4:40 am I think you have to use the flag CHANF_NOPAUSE for this to work. Item pickup sounds use this flag as well.
And that just did the trick! Thank you again
Player701 wrote: Wed Jan 24, 2024 4:40 am Well, basically, the timefreezer field is used to track if players are allowed to move when the time-freeze effect is active. If non-zero, the player can move, otherwise they cannot. In multiplayer, when someone activates the time-freezer powerup, they and their teammates have this field set to a non-zero value to allow movement. It also uses different bits for each player to allow multiple powerups to be active simultaneously, as well as properly handle their deactivation.
I see, multiplayer compatibility is one of the key here.
Player701 wrote: Wed Jan 24, 2024 4:40 am For your Deafen powerup to work correctly, it is sufficient to set the value for the owning player only since no one actually gets frozen in the first place. Also, unless your intention is to deafen all players, you should only call S_PauseSound for the console player only:
I didn't know about the existence of the wiki page about the available global variables, quite the game changer (dumb me that I didn't search for them I guess).
The code works wonderfully! Thanks a lot for your help
Player701 wrote: Wed Jan 24, 2024 4:40 am Note, however, that one way or another, you will lose compatibility with the actual time-freezer powerup, so I don't see a need to overcomplicate the code. IMO, it'd be better to suggest a feature that would allow modders to alter the player's hearing range via ZScript.
Couldn't I just check if the level is frozen (.IsFrozen()>0) in Tick() for example directly in this powerup and end the effect or directly destroy it if it is, this way I won't restore the sound for the player with this power?
User avatar
Player701
 
 
Posts: 1710
Joined: Wed May 13, 2009 3:15 am
Graphics Processor: nVidia with Vulkan support
Contact:

Re: [ZSCRIPT] Pause Every Sound In Game: HELP

Post by Player701 »

Kan3x wrote: Thu Jan 25, 2024 11:44 am Couldn't I just check if the level is frozen (.IsFrozen()>0) in Tick() for example directly in this powerup and end the effect or directly destroy it if it is, this way I won't restore the sound for the player with this power?
The compatibility issue here stems from hijacking the timefreezer field for your powerup. It is used both to:

a) allow movement for players while the level is frozen, and
b) to resume sound automatically if no players have it currently set to a non-zero value.

Since you want to keep the sound paused, you need to set a non-zero timefreezer value in order from prevent b) from taking place, but that would also automatically unblock the player's movement via a) if the level is currently frozen (this part is important for competitive multiplayer).

The only way out of this that I can see is indeed the same one you have suggested: to check for Level.isFrozen() every tick while the effect is active, cancelling it immediately if the result is true. You can avoid a conflict this way, but it still won't allow both effects to coexist.

Below is the modified code:

Code: Select all

class Deafen : Powerup
{
    Default
    {
        Powerup.Duration -3;
    }

    // Using this value should avoid conflicts with actual time-freeze powerups
    // (they use 1 shifted by 0..MAXPLAYERS-1 inclusive)
    const TIMEFREEZER_VAL = 1 << MAXPLAYERS;

    override void InitEffect()
    {    
        if (Owner == null || Owner.player == null) 
        {
            return;
        }

        Super.InitEffect();

        if (!Level.isFrozen() && Owner.PlayerNumber() == consoleplayer)
        {
            S_PauseSound(false, false);
        }

        Owner.player.timefreezer |= TIMEFREEZER_VAL;
    }

    override void DoEffect()
    {
        if (Level.isFrozen())
        {
            // This will also call EndEffect
            Destroy();
        }
    }

    override void EndEffect() 
    {
        if (Owner == null || Owner.player == null) 
        {
            return;
        }

        Owner.player.timefreezer &= ~TIMEFREEZER_VAL;

        if (!Level.isFrozen() && Owner.PlayerNumber() == consoleplayer)
        {
            S_ResumeSound(false);
        }

        Super.EndEffect();
    }
}
User avatar
Chris
Posts: 2978
Joined: Thu Jul 17, 2003 12:07 am
Graphics Processor: ATI/AMD with Vulkan/Metal Support

Re: [ZSCRIPT] Pause Every Sound In Game: HELP

Post by Chris »

It's important to note that pausing sounds does just that: it pauses them. They will resume exactly where they left off when the effect is over. So if you start a sound like an explosion, pause the sounds for a bit, then when you unpause the sounds sometime later, the explosion sound will continue immediately at normal volume from where it stopped. All pausable sounds started during that time will also start simultaneously. In effect, sounds are just delayed as if waiting for the player to hear them. If the idea is to simulate the player being deafened, that's not what should happen. You instead want sounds to play uninterrupted, but turn their volume way down, and slowly bring their volume back up to normal as the effect wears off. I don't know if there's any script functions that can adjust the volume of sounds dynamically like that, but trying to use a pause effect to simulate it will have apparent side-effects.
User avatar
Player701
 
 
Posts: 1710
Joined: Wed May 13, 2009 3:15 am
Graphics Processor: nVidia with Vulkan support
Contact:

Re: [ZSCRIPT] Pause Every Sound In Game: HELP

Post by Player701 »

Good point, Chris. I'm not aware of any ZScript APIs that allow manipulating sound playback globally, either; that'd be another reason to post a feature suggestion.
Kan3x
Posts: 67
Joined: Tue Nov 08, 2022 4:19 pm

Re: [ZSCRIPT] Pause Every Sound In Game: HELP

Post by Kan3x »

Player701 wrote: Thu Jan 25, 2024 12:09 pm The only way out of this that I can see is indeed the same one you have suggested: to check for Level.isFrozen() every tick while the effect is active, cancelling it immediately if the result is true. You can avoid a conflict this way, but it still won't allow both effects to coexist.
That little step of setting and resetting the timefreezer variable using MAXPLAYERS makes sense to me now and the "updated" code works exactly as it should even when I activate a TimeFreeze powerup now.
Since I don't think I will be able to make my mod multiplayer compatible nor I think I will ever release that monster as a standalone mod, I should hopefully be fine, so thank you very much again!
Chris wrote: Thu Jan 25, 2024 12:33 pm It's important to note that pausing sounds does just that: it pauses them. They will resume exactly where they left off when the effect is over. So if you start a sound like an explosion, pause the sounds for a bit, then when you unpause the sounds sometime later, the explosion sound will continue immediately at normal volume from where it stopped. All pausable sounds started during that time will also start simultaneously. In effect, sounds are just delayed as if waiting for the player to hear them. If the idea is to simulate the player being deafened, that's not what should happen. You instead want sounds to play uninterrupted, but turn their volume way down, and slowly bring their volume back up to normal as the effect wears off. I don't know if there's any script functions that can adjust the volume of sounds dynamically like that, but trying to use a pause effect to simulate it will have apparent side-effects.
Yeah, I already thought about this inconvenient and I decided it is an acceptable nuisance and the probability to "stack" multiple loud sounds that will be played all together when effect wears off should be quite low (I hope D:)
The only other thing that comes to mind is to get every actor in a certain radius, get all of the channel slots they use (which it can be done if I know them, but the thing won't be compatible with mods that uses different channel slots of course) and stop all of the possible sounds they could make, also stop the music. Doable, but quite annoying to pull it off x)

I'll probably try to see if I'll ask for something to actually interrupt sounds to be implemented, even if I don't know if it's possible.

Thank you both!
Post Reply

Return to “Scripting”