Question about Speak Triggers

Hi there! As I’m working on my module I have a little problem that I wonder if it can be solved. The PC and his companions are running towards a room. In the room is a Speak Trigger. I’ve set the Speak Trigger variables to Run=1 (integer) and Talk Now=1 (integer) and CutsceneBars=1 (integer). As the companions are quite a bit slower than the PC, the PC arrives to the room much earlier than some of the companions. Still, the conversation starts as it should, but when it does the companion who speaks is not yet in the room and is commenting on how the room looks. The companion who begins the conversation doesn’t run when it speaks but stops, the others however continues to run towards the room.

My question: Can you make it so that the companion who speaks, or all companions for that matter, “jumps” to the room when the PC has arrives there, with the Speak Trigger? Or do you have to make a new script for that kind of thing?

And one thing I’ve noticed in the variables of the Speak Trigger is the field called “Node”. What is that?

Thanks for your time!

I don’t see why the same script that makes the npc comment on a room could not also teleport them to the pc (or a specific waypoint designated for each companion inside the room) before making their comment.

You could even make a bit of a developer joke out of it and have the PC comment something like, “whoa! don’t sneak up on me like that!” or something. :slight_smile: It’s a cutscene right? people are invisible until they have something to say in movies all the time.

If you look into the script gtr_speak_trigger it explains in the comments what every variable does. I would suggest against using this script though because it leads to things like the one you describe. Try writing your own script that does just that but better.

2 Likes

It’s related to a DLG file attached to this speak trigger. Which node to use as a beginning of the conversation.

See also ActionPauseCutscene (http://nwn2.wikia.com/wiki/ActionPauseCutscene) and AssignCutsceneActionToObject (http://nwn2.wikia.com/wiki/AssignCutsceneActionToObject).

1 Like

Ok. Thanks for the replies guys! Looks like I have to create my own script for this.

As 4760 is beta testing my second module now, again I have a question regarding these kind of speak scripts. I have several different versions of different speak scripts created by people here on the forums that I’ve then modified. Many work very well but none of them work every time in all situations, I’m afraid.
A bug that 4760 discovered at a particular moment in my new module is when the PC or the companions enter a trigger where I run a slightly modified version of the gtr_speak_node. The only thing added is SetCutsceneMode. However, in this case the conversation is supposed to be started by temporary henchwoman, and if she is too far away, the game will freeze because of SetCutsceneMode. I therefore made these modifications:

#include "ginc_trigger"

void main ()
{
    object oPC = GetEnteringObject();
	
	if(!GetIsPC(oPC)) return;
	
	if (StandardSpeakTriggerConditions(oPC) == FALSE)
	{
		return;
	}
	
	if (GetIsInCombat(oPC)) return;
	
	
	object oDeivana = GetObjectByTag("Deivana3");
	
	if (!GetFactionEqual (oPC,oDeivana))
	{
		return;
	}
	
	AssignCommand(oDeivana, ClearAllActions());
	AssignCommand(oDeivana, ActionJumpToObject(oPC));

	SetCutsceneMode(oPC);
	
	DoSpeakTrigger(oPC);
}

I think this should work though I haven’t tested it yet. One thing I wonder though: If let’s say oDeivana was a full companion and is the one entering the trigger first, will the game get confused with ActionJumpToObject since she would then be jumping to herself? Or maybe that doesn’t matter.

I also have another start conversation script, written by someone here on the forums (just slightly modified by me), I don’t remember who, that works pretty well but it doesn’t work when the player controls a companion, then the game freezes. When looking at the code I don’t understand why it doesn’t work when controlling a companion. I mean it shouldn’t run in the first place because of (oEnter == GetFactionLeader(oEnter)) so that I don’t understand:

// 'tr_convo'

const string sROSTER_ILIR = "valerie"; // note: this needs to be the *roster* string-id
const string sROSTER_MEAH = "meah";
const string sROSTER_GARDOK = "gardok";
const string sROSTER_SNOFWIN = "snofwin";
const string sROSTER_TYN = "tyn";


void main()
{

object oPC1 = GetEnteringObject();

if (!GetIsPC(oPC1)) return;



if (!GetLocalInt(OBJECT_SELF, "Done"))
    {
        object oEnter = GetEnteringObject();
        if (oEnter == GetFactionLeader(oEnter)) // the party-leader is the entering object (and is controlled) - if player is controlling a companion that's okay
        {
            	object oIlir = GetObjectFromRosterName(sROSTER_ILIR);
		object oGardok = GetObjectFromRosterName(sROSTER_GARDOK);
		object oSnofwin = GetObjectFromRosterName(sROSTER_SNOFWIN);
		object oTyn = GetObjectFromRosterName(sROSTER_TYN);
		object oMeah = GetObjectFromRosterName(sROSTER_MEAH);
			
			
            if (GetIsObjectValid(oIlir) && GetFactionEqual(oIlir, oEnter)) // Ilir is valid and in pc-party.
            {
                SetLocalInt(OBJECT_SELF, "Done", TRUE);

                oEnter = SetOwnersControlledCompanion(oEnter); // shunt oEnter into his/her OwnedPC.

                SetCutsceneMode(oEnter); // this needs to be cleared by the dialog's End/Abort dialog handlers.

                AssignCommand(oEnter, ClearAllActions());

                AssignCommand(oIlir, ClearAllActions());
		AssignCommand(oGardok, ClearAllActions());
		AssignCommand(oSnofwin, ClearAllActions());
		AssignCommand(oTyn, ClearAllActions());
		AssignCommand(oMeah, ClearAllActions());
				
		AssignCommand(oGardok, ActionJumpToObject(oEnter));
		AssignCommand(oSnofwin, ActionJumpToObject(oEnter));
		AssignCommand(oTyn, ActionJumpToObject(oEnter));
		AssignCommand(oMeah, ActionJumpToObject(oEnter)); 				
		AssignCommand(oIlir, ActionJumpToObject(oEnter));

                AssignCommand(oIlir, ActionStartConversation(oEnter, "ignii_room_conv1", FALSE, FALSE, FALSE, FALSE));
            }
        }
    }
}

I tested the first script in my post above. Somehow it doesn’t quite work as intended. oDeivana doesn’t jump to the PC. I tried changing to

AssignCommand(oDeivana, ActionJumpToLocation(GetLocation(oPC)));

but the result was the same. She instead runs to the PC. I wonder if this might be because this is a Speak Trigger with just my modified script. The speak trigger has some variables that maybe get prioritized first, I don’t know.

Since it didn’t work I made a new speakscript for this situation that seems to work fine:

void main()
{

object oPC = GetEnteringObject();

if (!GetIsPC(oPC)) return;

if (GetIsInCombat(oPC)) return;

if (GetLocalInt(OBJECT_SELF, "Done")) return;

	object oDeivana = GetNearestObjectByTag("Deivana3",oPC);
			
	
if (GetIsObjectValid(oDeivana) && GetFactionEqual(oPC, oDeivana))
    {
        
		AssignCommand(oDeivana, ClearAllActions());
		AssignCommand(oDeivana, ActionJumpToLocation(GetLocation(oPC)));
	
		AssignCommand(oPC, ClearAllActions());
	
		SetCutsceneMode(oPC);
		
		SetLocalInt(OBJECT_SELF, "Done", TRUE);
		
		AssignCommand(oDeivana, ActionStartConversation(oPC, "deivanal_conv", FALSE, FALSE, FALSE, FALSE));
		
	}
	
}
```C

Why don’t you just use the fill-in-the-blanks scripts under ‘actions’ in the conversation tool and place one on the first node of the dialogue, Andgalf?

Use an ipoint speaker rather than an NPC to trigger the conversation, then jump your companion on the first dialogue node and assign the node to her / set a camera to watch her.

EDIT:
Actually, since the convo is meant to be a comment on the state of the room, I’d do it like this:

  1. trigger the script with an ipoint speaker, not an NPC
  2. set up the first line of dialogue with a static camera showing off the state of room. Teleport the companion via script and out of sight.
  3. The companion then speaks up on the second line of dialogue, having moved to the right place.
1 Like

I have used ipoint speaker scripts with the first node being like you suggested before (even though I don’t think I’ve used ga_jump to jump a companion to that spot for example). As I said, I have several other conversation scripts. There may be other ways, like yours, to solve things but…

To try and be more clear: I’m talking about two different situations here with two different scripts. (People don’t seem to really read my posts sometimes, or maybe I’m not expressing myself clearly enough)

The first situation is solved with the last script I posted here so I’ll leave that as it is, but I still, I have no answer to my questions:

  1. Why doesn’t ActionJumpToObject work in this script that is OnEnter on a SpeakScript, and would ActionJumpToObject be screwed up if oPC is the same as oDeivana in this case:
#include "ginc_trigger"

void main ()
{
    object oPC = GetEnteringObject();
	
	if(!GetIsPC(oPC)) return;
	
	if (StandardSpeakTriggerConditions(oPC) == FALSE)
	{
		return;
	}
	
	if (GetIsInCombat(oPC)) return;
	
	
	object oDeivana = GetObjectByTag("Deivana3");
	
	if (!GetFactionEqual (oPC,oDeivana))
	{
		return;
	}
	
	AssignCommand(oDeivana, ClearAllActions());
	AssignCommand(oDeivana, ActionJumpToObject(oPC));

	SetCutsceneMode(oPC);
	
	DoSpeakTrigger(oPC);
}
  1. Again to quote myself, I also have another start conversation script, written by someone here on the forums (just slightly modified by me), I don’t remember who, that works pretty well but it doesn’t work when the player controls a companion, then the game freezes. When looking at the code I don’t understand why it doesn’t work when controlling a companion. I mean it shouldn’t run in the first place because of (oEnter == GetFactionLeader(oEnter)) so that I don’t understand:
// 'tr_convo'

const string sROSTER_ILIR = "valerie"; // note: this needs to be the *roster* string-id
const string sROSTER_MEAH = "meah";
const string sROSTER_GARDOK = "gardok";
const string sROSTER_SNOFWIN = "snofwin";
const string sROSTER_TYN = "tyn";


void main()
{

object oPC1 = GetEnteringObject();

if (!GetIsPC(oPC1)) return;



if (!GetLocalInt(OBJECT_SELF, "Done"))
    {
        object oEnter = GetEnteringObject();
        if (oEnter == GetFactionLeader(oEnter)) // the party-leader is the entering object (and is controlled) - if player is controlling a companion that's okay
        {
            	object oIlir = GetObjectFromRosterName(sROSTER_ILIR);
		object oGardok = GetObjectFromRosterName(sROSTER_GARDOK);
		object oSnofwin = GetObjectFromRosterName(sROSTER_SNOFWIN);
		object oTyn = GetObjectFromRosterName(sROSTER_TYN);
		object oMeah = GetObjectFromRosterName(sROSTER_MEAH);
			
			
            if (GetIsObjectValid(oIlir) && GetFactionEqual(oIlir, oEnter)) // Ilir is valid and in pc-party.
            {
                SetLocalInt(OBJECT_SELF, "Done", TRUE);

                oEnter = SetOwnersControlledCompanion(oEnter); // shunt oEnter into his/her OwnedPC.

                SetCutsceneMode(oEnter); // this needs to be cleared by the dialog's End/Abort dialog handlers.

                AssignCommand(oEnter, ClearAllActions());

                AssignCommand(oIlir, ClearAllActions());
		AssignCommand(oGardok, ClearAllActions());
		AssignCommand(oSnofwin, ClearAllActions());
		AssignCommand(oTyn, ClearAllActions());
		AssignCommand(oMeah, ClearAllActions());
				
		AssignCommand(oGardok, ActionJumpToObject(oEnter));
		AssignCommand(oSnofwin, ActionJumpToObject(oEnter));
		AssignCommand(oTyn, ActionJumpToObject(oEnter));
		AssignCommand(oMeah, ActionJumpToObject(oEnter)); 				
		AssignCommand(oIlir, ActionJumpToObject(oEnter));

                AssignCommand(oIlir, ActionStartConversation(oEnter, "ignii_room_conv1", FALSE, FALSE, FALSE, FALSE));
            }
        }
    }
}

All in all ipspeaker is the best method to do basically anything related to conversations. Other than that, I am with Grog here. You will have a much easier time doing it through node action scripts. A fade to black on the first node and that’s all you need.

If there is anything I tried doing in many different ways is convos. I was where you are, and I concluded that nothing is as safe and robust as the ipspeaker and the CreateIPSpeaker function. I remember having talks with Tchos about it and, although it is not the topic, he had said that if the convo fails, the ipspeaker will keep trying to initiate it.

The only place where IPSpeaker loses and it’s the reason I don’t use it in my campaign as extensively as I would like, is that you cannot specify the object the speaker has a conversation with in its parameters. Only speaker, convo and location. For listener I think it takes FirstPC(). At least from what I remember. Correct me if I’m wrong.

the comment stikes me as incorrect/vague… iirc, GetFactionLeader() returns any controlled character of the true faction leader

Similar to GetIsPC() <- returns true if a character is controlled.

i’d change this line

if (oEnter == GetFactionLeader(oEnter))

to

if (GetIsPC(oEnter))

but (unfortunately) i don’t think it’s a substantial change, since oEnter will be redefined as SetOwnersControlledCompanion(oEnter) later … so, it should be okay if a controlled companion triggers the script regardless

If you really want to ensure that oEnter is a true PC, try

if (GetIsPC(oEnter) && GetIsOwnedByPlayer(oEnter)) // is Controlled AND Owned character

( then SetOwnersControlledCompanion() should not be needed )

 
 
@andysks
y, IPSpeaker is really good… not perfect but really good  :)

1 Like

Thank you, kevL_s! Now I understand a lot better! I always thought the FactionLeader in this case was the true faction leader, aka the PC, and not one of the companions, but apparently it can be the PC or one of the companions in this case.

The comment is pretty strange yes, and that comment was copied from the author of the original script (which I don’t remember who it was). It’s taken from a discussion here on the forums probably from about half a year ago or something, but I can’t find it at the moment. That original script’s intention was somewhat different from what I’m trying to do so maybe that’s why the description seems off.

Then the if (GetIsPC(oEnter) && GetIsOwnedByPlayer(oEnter)) is what I would want here. However, when doing some more testing I noticed that in this case it doesn’t matter. When the conversation starts the game automatically switches back to the PC. I found that when controlling a companion and entering the trigger, what made it fail was the oEnter = SetOwnersControlledCompanion(oEnter); (I think) as it then switched back to the PC, and then when the cutscene started a millisecond later the PC wasn’t near the companion to start the conversation, and all was screwed up and the game froze. It also might have been the SetCutsceneMode that should have come later in the script.

I remade the script like this, and now it seems to be working as I want:

// 'tr_convo'

const string sROSTER_ILIR = "valerie"; // note: this needs to be the *roster* string-id
const string sROSTER_MEAH = "meah";
const string sROSTER_GARDOK = "gardok";
const string sROSTER_SNOFWIN = "snofwin";
const string sROSTER_TYN = "tyn";
//const string sROSTER_VALERIE = "valerie";
//
void main()
{

object oPC1 = GetEnteringObject();

if (!GetIsPC(oPC1)) return;

if (GetIsInCombat(oPC1)) return;

if (!GetLocalInt(OBJECT_SELF, "Done"))
    {
        	        object oEnter = GetEnteringObject();
       
                        object oIlir = GetObjectFromRosterName(sROSTER_ILIR);
			object oGardok = GetObjectFromRosterName(sROSTER_GARDOK);
			object oSnofwin = GetObjectFromRosterName(sROSTER_SNOFWIN);
			object oTyn = GetObjectFromRosterName(sROSTER_TYN);
			object oMeah = GetObjectFromRosterName(sROSTER_MEAH);
			//object oValerie = GetObjectFromRosterName(sROSTER_VALERIE);
			
            if (GetIsObjectValid(oIlir) && GetFactionEqual(oIlir, oEnter)) // Ilir is valid and in pc-party.
            {
                SetLocalInt(OBJECT_SELF, "Done", TRUE);

                
                                AssignCommand(oEnter, ClearAllActions());
				AssignCommand(oIlir, ClearAllActions());
				AssignCommand(oGardok, ClearAllActions());
				AssignCommand(oSnofwin, ClearAllActions());
				AssignCommand(oTyn, ClearAllActions());
				AssignCommand(oMeah, ClearAllActions());
				
				AssignCommand(oGardok, ActionJumpToObject(oEnter));
				AssignCommand(oSnofwin, ActionJumpToObject(oEnter));
				AssignCommand(oTyn, ActionJumpToObject(oEnter));
				AssignCommand(oMeah, ActionJumpToObject(oEnter)); 
				
				
				
               
				AssignCommand(oIlir, ActionJumpToObject(oEnter));
				
				SetCutsceneMode(oEnter);  // this needs to be cleared by the dialog's End/Abort dialog handlers.
				
                AssignCommand(oIlir, ActionStartConversation(oEnter, "ignii_room_conv1", FALSE, FALSE, FALSE, FALSE));
            }
        
    }
}

I still find it odd that oDeivana in my other script on the OnEnter of the SpeakTrigger didn’t respond to ActionJumpToObject. Do you have any thoughts about that, kevL_s? Is it because of the variables in the SpeakTrigger that in this case tells the oDeivana to run to the trigger? I thought that the ActionJumpToObject would override that.

Edit: Just to be clear, I do use the CreateIpSpeaker often! It was you who told me about this about a year ago (or something along the line), @andysks, and I’ve used it many times since. When I find that to be the most effective is when the trigger won’t run at all for some reason, then the IpSpeaker tries again and again. So if I have a problem with a trigger not triggering I use that all the time.

1 Like

probly me :clown_face: :smiley_cat:

:smiley: Actually, I don’t think so. I vaguely remember someone wanting to do something very specific with a trigger and a conversation, and I think it was a multiplayer module. You were probably there in the discussion though (And I thought, this I could use, so I just copied the whole thing even without thanking him :flushed: )

I’ll see if I can find it.

no bother /carry on…

I suspect a conjunction of the two : SetOwnersControlledCompanion() and SetCutsceneMode() …

Found it. It actually was you, kevL_s:

Funny thing, in this thread I actually replied that I used IpSpeaker as a way of trying to help (even though I knew even less about scriting at the time).

Ah that would explain it then.

1 Like

/untested  ;)

idk. but I would’ve used

// Jump to oToJumpTo (the action is added to the top of the action queue).
void JumpToObject(object oToJumpTo, int nWalkStraightLineToPoint=1);

instead of the Action* funct. (it’s more “hey do this no matter what”)

And, i spose that the conditions on getting what an ‘oEnter’ really is, could be looked at vis-a-vis your design conditions