Hi, guys! How do I start a separate sound on a trigger? PlaySound () doesn't work

We have articles in the lexicon:
https://nwnlexicon.com/index.php?title=PlaySound
and
https://nwnlexicon.com/index.php?title=SoundObjectPlay

I tried both ways, it doesn’t work and that’s it. You need to play a separate sound from the standard game palette on the trigger. The sound of a bear growling.

AssignCommand(OBJECT_SELF, PlaySound("c_bear_bat2"));

To play a separate sound, we recommend using PlaySound () , but it doesn’t work. Help with advice!

It says that triggers can’t play sounds in the lexicon…

It seems you have to create some kind of sound object. I only work with NWN2 so I don’t particularly know but…

1 Like

But entering objects can:

void main()
{
    object oEnter = GetEnteringObject();
    AssignCommand(oEnter, PlaySound("c_bear_bat2"));
}
Some AssignCommand remarks not really related to the question.

To avoid glitches, always pass a calculated object directly as the first argument of AssignCommand, i.e. don’t do this kind of shortcut:

AssignCommand(GetEnteringObject(), ...

BTW, AssignCommand(OBJECT_SELF, blah()) does exactly the same as: blah(). Caller of any script is referenced by OBJECT_SELF already.

2 Likes

Interesting, and really easy then. I guess this might be the same in NWN2. If I need to use that sometime it’s good to know.

1 Like

In hindsight, PlaySound is an action, so oEnter won’t play the sound when its action queue has something in it. For example, PC is running across the trigger to a point the player clicked.

Therefore to make that code actually work on-trigger-enter, clear actions is needed, which may not be well-received by players:

void main()
{
    object oEnter = GetEnteringObject();
    AssignCommand(oEnter, ClearAllActions());
    AssignCommand(oEnter, PlaySound("c_bear_bat2"));
}

Or just temporarily spawn an invisible placeable nearby, play the sound from it, then delete it. Sounds (hehe) wasteful, but will get the job done.

2 Likes

he-he… now try to do the same in the editor and test it in the game… the result is zero. I’ve tried everything with cleansing actions too =__= tried on the trigger and on a living being…

1st thing to do is to check that the sound you want still exists. You do this by right click/edit copy, select a sound and hit play. There are a number of sounds (at least for me) that seem to have gone awol.

TR

1 Like

Yeah, and maybe check the string name. Could be a spelling error perhaps? I’ve done such mistakes myself, so you never know.

Just for fun, I tried it in NWN2, and it worked splendidly with NWShacker’s script. I think @Tarot_Redhand might be right that your sound may not exist.

Edit: Found my NWN game on one of my old hard drives. Even though it protested (I have not installed it for Win10, this is an old install for Win7) I could actually run the toolset. I checked with running the conversation editor and I found the sound there (“c_bear_bat2”) and could listen to it in the editor, so I don’t know what has gone wrong for you.

Edit2: Found my old module (made from a tutorial for NWN way back probably 10-15 years ago), added a New Generic trigger. Made a script I called s_trigger, where I put NWShacker’s code (the second script he posted here), and attached that script to Properties/Scripts/OnEnter. Tested it ingame and it worked like a charm, so I honestly have no idea why it doesn’t work for you @Victorixxx. Do you use NWN EE perhaps? My game is the old one (Deluxe Edition) with the discs.

2 Likes

Thank you for the possible solution! But I wouldn’t want to add an entire trigger and a separate script for it to play a single sound. Please send the script that you wrote for this, can I really do something wrong?
Yes, I use of course NWN EE! Is that it?

On Enter

void main()
{
    object oPC = GetEnteringObject();
    object oVi = GetNearestObjectByTag("VI");
    object oBear = GetNearestObjectByTag("MM_BEAR3");

    if (GetIsPC(oPC) && GetIsObjectValid(oBear) == FALSE && GetLocalInt(oPC, "brynrip") != TRUE)
    {
    //AddHenchman(oPC, oVi);
        SetCutsceneMode(oPC);
        BlackScreen(oPC);
        SetLocalInt (oPC, "brynrip", TRUE);
        AssignCommand(oVi, ClearAllActions());
        AssignCommand(oVi, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        DelayCommand(0.2, SetCommandable(FALSE, oVi));
    //ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oPC, 2.0);
        AssignCommand(oPC, DelayCommand(0.8, PlaySound("bf_med_flesh")));
        AssignCommand(oPC, ClearAllActions());
        AssignCommand(oPC, ActionStartConversation(oPC, "bryn_rip", FALSE, FALSE));
        AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
    //DelayCommand(20.0, StopFade(oPC));
    //DelayCommand(20.0, SetCutsceneMode(oPC, FALSE));
    }
    if (GetIsObjectValid(oBear) != FALSE && GetLocalInt(oPC, "brynrip") != TRUE)
    {
        BlackScreen(oPC);
        SetLocalInt (oPC, "brynrip", TRUE);
        AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        AssignCommand(OBJECT_SELF, PlaySound("c_bear_bat2"));
        AssignCommand(oBear, ClearAllActions());
        AssignCommand(oBear, PlaySound("c_bear_bat2"));
        AssignCommand(oBear, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oBear, 3.0));
        AssignCommand(oBear, DelayCommand(4.0, ClearAllActions()));
        AssignCommand(oBear, DelayCommand(4.1, ActionAttack(oPC)));
        AssignCommand(oPC, DelayCommand(2.1, FadeFromBlack(oPC, FADE_SPEED_MEDIUM)));
    }
}

As you can see, I tried to apply the effect on the creature (bear) and on the trigger itself, it doesn’t work.

Here’s a (new) script that plays a sound without stopping the player.

I made it into a function, so it’s easier to re-use in other code.

// by NWShacker, 04.2020 - makes oSelf "play" sound sSound
void PlaySoundNow(string sSound, object oSelf=OBJECT_SELF)
{
    // spawn invisible placeable at oSelf's location
    object oSound = CreateObject(OBJECT_TYPE_PLACEABLE,
        "plc_invisobj", GetLocation(oSelf));
    // make oSound play the sound
    // (note: the short delay is needed for the sound)
    DelayCommand(0.25, AssignCommand(oSound,
        PlaySound(sSound)));
    // make oSound destroy itself after playing
    DelayCommand(0.25, AssignCommand(oSound,     
        ActionDoCommand(DestroyObject(OBJECT_SELF))));
}

// OnEnter
void main()
{
    PlaySoundNow("c_bear_bat2");
    // OPTIONAL: uncomment to make the sound one-shot
    //DestroyObject(OBJECT_SELF, 1.0);
}

Try it in your code.

1 Like

Thank you! it work, in my script :slight_smile:

only the time value in DelayCommand(0.25, it is better to put less than DelayCommand(0.05, it works better

1 Like

Yeah, I used quite conservative delay that should work for everyone. If less works for you then great, but it may be a timing issue with lots of stuff going on around (or players with slow CPU/IO).

I’d also make the second delay (with DestroyObject) even 1.0. It doesn’t hurt but keeps the timers at safe distance.

1 Like

Just help me figure this out. The sound for some reason works with a delay, after all the commands in the script, although I put it at the beginning. Why?

// by NWShacker, 04.2020 - makes oSelf "play" sound sSound
void PlaySoundNow(string sSound, object oSelf=OBJECT_SELF)
{
    // spawn invisible placeable at oSelf's location
    object oSound = CreateObject(OBJECT_TYPE_PLACEABLE,
        "plc_invisobj", GetLocation(oSelf));
    // make oSound play the sound
    // (note: the short delay is needed for the sound)
    DelayCommand(0.05, AssignCommand(oSound,
        PlaySound(sSound)));
    // make oSound destroy itself after playing
    DelayCommand(0.5, AssignCommand(oSound,
        ActionDoCommand(DestroyObject(OBJECT_SELF))));
}

void main()
{
    object oPC = GetEnteringObject();
    object oVi = GetNearestObjectByTag("VI");
    object oBear = GetNearestObjectByTag("MM_BEAR3");

    if (GetIsPC(oPC) && GetIsObjectValid(oBear) == FALSE && GetLocalInt(oPC, "brynrip") != TRUE)
    {
    //AddHenchman(oPC, oVi);
        SetCutsceneMode(oPC);
        BlackScreen(oPC);
        SetLocalInt (oPC, "brynrip", TRUE);
        AssignCommand(oVi, ClearAllActions());
        AssignCommand(oVi, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        DelayCommand(0.2, SetCommandable(FALSE, oVi));
    //ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oPC, 2.0);
        AssignCommand(oPC, DelayCommand(0.8, PlaySound("bf_med_flesh")));
        AssignCommand(oPC, ClearAllActions());
        AssignCommand(oPC, ActionStartConversation(oPC, "bryn_rip", FALSE, FALSE));
        AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        DestroyObject(OBJECT_SELF, 1.0);
    //DelayCommand(20.0, StopFade(oPC));
    //DelayCommand(20.0, SetCutsceneMode(oPC, FALSE));
    }
    if (GetIsObjectValid(oBear) != FALSE && GetLocalInt(oPC, "brynrip") != TRUE)
    {
        PlaySoundNow("as_cv_gongring2");
        BlackScreen(oPC);
        SetLocalInt (oPC, "brynrip", TRUE);
        AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        AssignCommand(oBear, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oBear, 3.0));
        AssignCommand(oBear, DelayCommand(4.0, ClearAllActions()));
        AssignCommand(oBear, DelayCommand(4.1, ActionAttack(oPC)));
        AssignCommand(oPC, DelayCommand(2.1, FadeFromBlack(oPC, FADE_SPEED_MEDIUM)));
        DestroyObject(OBJECT_SELF, 1.0);
    }
}

I recorded a video and set the Gong sound for clarity.

To my understanding a function with a delay runs after everything else is done in the script (I think @Lance_Botelle told me that) . However, as your other functions also have delays on them…well, I’m not exactly sure with that.

1 Like

Yes, even delay of 0.0 schedules the function call to happen after current script terminates. If you have multiple delays with 0.0, their order is also preserved (IIRC).

@Victorixxx debug the script by commenting everything and uncommenting the statements one by one. Can you make the sound play during the fade-out?

More AssignCommand remarks.

Also some AssignCommands are superfluous and can be taken out for clarity, like:

AssignCommand(oPC, DelayCommand(2.1, FadeFromBlack(oPC, FADE_SPEED_MEDIUM)));

to simply:

DelayCommand(2.1, FadeFromBlack(oPC, FADE_SPEED_MEDIUM));

AssignCommand is really needed only to change OBJECT_SELF in a call of a nwscript function that doesn’t accept the self as a parameter.

1 Like

I tried putting the Sound command in a separate condition at the very beginning, but nothing changed… the delay remains as in the video.

OnEnter

void main()
{
    object oPC = GetEnteringObject();
    object oVi = GetNearestObjectByTag("VI");
    object oBear = GetNearestObjectByTag("MM_BEAR3");

    if (GetIsObjectValid(oBear) != FALSE && GetLocalInt(oPC, "brynrip") != TRUE)
    {
        PlaySoundNow("as_cv_gongring2");
    }

    if (GetIsPC(oPC) && GetIsObjectValid(oBear) == FALSE && GetLocalInt(oPC, "brynrip") != TRUE)
    {
    //AddHenchman(oPC, oVi);
        SetCutsceneMode(oPC);
        BlackScreen(oPC);
        SetLocalInt (oPC, "brynrip", TRUE);
        AssignCommand(oVi, ClearAllActions());
        AssignCommand(oVi, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        DelayCommand(0.2, SetCommandable(FALSE, oVi));
    //ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oPC, 2.0);
        AssignCommand(oPC, DelayCommand(0.8, PlaySound("bf_med_flesh")));
        AssignCommand(oPC, ClearAllActions());
        AssignCommand(oPC, ActionStartConversation(oPC, "bryn_rip", FALSE, FALSE));
        AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        DestroyObject(OBJECT_SELF, 1.0);
    //DelayCommand(20.0, StopFade(oPC));
    //DelayCommand(20.0, SetCutsceneMode(oPC, FALSE));
    }
    if (GetIsObjectValid(oBear) != FALSE && GetLocalInt(oPC, "brynrip") != TRUE)
    {
        BlackScreen(oPC);
        SetLocalInt (oPC, "brynrip", TRUE);
        //AssignCommand(oPC, ActionPlayAnimation(ANIMATION_LOOPING_SIT_CROSS, 2.0, 9999.9));
        //AssignCommand(oBear, ApplyEffectToObject(DURATION_TYPE_TEMPORARY, EffectKnockdown(), oBear, 3.0));
        //AssignCommand(oBear, DelayCommand(4.0, ClearAllActions()));
        //AssignCommand(oBear, DelayCommand(4.1, ActionAttack(oPC)));
        AssignCommand(oPC, DelayCommand(2.1, FadeFromBlack(oPC, FADE_SPEED_MEDIUM)));
        DestroyObject(OBJECT_SELF, 1.0);
    }
}

is this a limitation of the game engine? Why can’t I run commands in a black screen? Or play sounds and actions?

P.s. this command DelayCommand (2.1, FadeFromBlack(oPC, FADE_SPEED_MEDIUM))); does not work without AssignCommand(oPC,

What if you skip the delay entirely for PlaySoundNow? If that is to be the first thing that happens, do you really need a delay for that function?

1 Like

I figured it out… it’s all the fault of the black screen… until he leaves, the sound doesn’t play, I don’t know why, but this one does.

This works for me:

void main()
{
    object oPC = GetEnteringObject();
    PlaySoundNow("c_bear_bat2");
    BlackScreen(oPC);
    DelayCommand(2.0, FadeFromBlack(oPC));
}

What I suggest is that you check if the above works from and then you recreate the script you want. Step by step: add a command, check if works, add another. It’s possible that your two if’s are mixing things up right now (timing issue).

EDIT: with fade-out, we can also stop the PC and make it play the sound (my first post) since the player won’t notice. Or make the bear do it.

void main()
{
    object oPC = GetEnteringObject();
    BlackScreen(oPC);
    AssignCommand(oPC, ClearAllActions());
    DelayCommand(0.5, AssignCommand(oPC, PlaySound("c_bear_bat2")));
    DelayCommand(2.0, FadeFromBlack(oPC));
}

BTW @Victorixxx, I like your use of knockdown effect on the bear.

1 Like