Question about ActionStartConversation

So I have this NPC that talks to the player and the party when the party enters a generic trigger with my own script. The NPC is a bit far away for the conversation to start so he runs to the party. In the ActionStartConversation function there’s nothing about about the NPC running or walking towards the player and the party. How do you set that? I would have liked him to walk instead.

I guess I maybe could just use a SpeakTrigger instead and modify that, since, if I remember correctly, you can set that to walk or run towards the player. I was just curious about this, how to go about that, and how the SpeakTrigger does it.

EDIT: Ah, nevermind. I found in the SpeakTriggers that they use the script gtr_speak_node that has a bunch of functions from ginc_trigger, so I guess I’ll find this there.

Set up a camera pointing at the person approaching, first line delay it and film him arriving but for that…

You could really do with tiny invisible kobolds :crazy_face:

1 Like

:grinning:

I just copied a few lines from the function DoSpeakTrigger, that could be found in ginc_trigger, to my custom trigger’s script. That solved it.

Glad you got it sorted, conversations can be awkward with distances.

Many possibility for that.

You can ignore distance it’s a parameter in ActionStartConversation. The npc won’ t move at all toward the player it will stay in place.

By default the behavior is the one you describe, the NPC will run toward the player.

If you want the NPC to walk toward the player you need a little more :

AssignCommand(oNPC,ActionForceMoveToObject(oPC,FALSE));
AssignCommand(oNPC,ActionStartConversation(oPC blablablablabla

With oNPC as your NPC object and oPC your as player object
When you assign commands, the NPC will do them in the order it recieves them. You need to assign him the command to move in walk mode to the NPC before assigning the conversation for the behavior you are looking for in this case.

As we discussed before if the object giving the 2 commands becommes invalid before giving them, nothing will happens.

If you open the triggers library, you’ll see that Obsidian/Bioware have done it this way, and you ll be able to see how they scripted the others setting as well.

Yeah, looking more at the code inside of DoSpeakTrigger, I realized I didn’t need everything I included at first.
I only really needed just the: AssignCommand(oNPC,ActionForceMoveToObject(oPC,FALSE)); for that. I got a bit blinded by the fact that I thought the ActionStartConversation function would have some things about the moving of the NPC, and then I overcomplicated things.

I also realized a few other things with how about the player could enter the trigger and that it might be too far for the NPC to walk slowly towards the player…And then I realized I have some neat code to count for things like that from 4760 that I’ve used often to great success. I will implement that here too I think.

It’s silly when you realize: “Hey, I’ve solved these kind of situations many times before in my modules. Why did I believe it to be a problem now?”. I guess you just forget simple things like this from time to time.

Alright, so I have another thing I wanted to do now that kind of relates to this. I wanted, for convenience (I’ve done it in other more convoluted ways before) to, at the start of a conversation (in the first conversation node) to jump the party to where the controlled PC or companion is and then set the facing of everyone to the conversation owner, that in these cases where I’m to use it is an NPC. So I did a script that I thought might work, but it doesn’t quite work, so there must be something wrong here:

void FaceConvOwner(string sConversationOwner, object oPC, object oFM)
{

	object oConversationOwner = GetObjectByTag(sConversationOwner);
	
	vector vTarget = GetPosition(oConversationOwner);
	
	while (GetIsObjectValid(oFM))
	{
	
		AssignCommand(oFM, ActionDoCommand(SetFacingPoint(vTarget,FALSE)));
		AssignCommand(oFM, ActionWait(0.5f));
		
		oFM = GetNextFactionMember(oPC,FALSE);
	
	}

}



void main()
{

object oPC = GetFirstPC(FALSE);
object oFM = GetFirstFactionMember(oPC,FALSE);

	while (GetIsObjectValid(oFM))
	{
	
		AssignCommand(oFM, ClearAllActions());
		AssignCommand(oFM, ActionJumpToObject(oPC));
	
		oFM = GetNextFactionMember(oPC,FALSE);
	
	}

	string sConversationOwner = GetNodeSpeaker();

	DelayCommand(0.2,FaceConvOwner(sConversationOwner,oPC,oFM));

	
	
}

What happens at the moment is that the party jumps but won’t face the conversation owner. At first, when testing with two companions, it did work, but when I tried with three companions it all of a sudden didn’t. Really strange, I think.

@andgalf

I have also had some mixed experiences when trying to have main PC and/or companions face a conversation owner … which also appears to not have any obvious reasons for not working.

In the end, I decided to try to take it back to its simplest format. The “best” solution for quite a few situations (not all I hasten to add) was to simply have the Main PC “run” (or jump close to and run if you want) and have the companions follow behind so they end up “facing” correctly because they end their follow facing the Main PC direction who is in conversation.

i.e. I ended up NOT involving every companion to jump on top on the conversation spot, but allow them to jump close (or not jump at all if the conversation owner was close enough) and just force a move to the owner instead.

Now, it all depends on where the conversation owner is in relation to your start point I guess, but the principle is that sometimes using basic (less) code can work for the better. Just something to consider.

Try “move to” instead of “jump to” if possible.

EDIT: Also, is it possible sConversationOwner is OBJECT_SELF by now?

Looking at the code, the loop in main() ends when oFM becomes OBJECT_INVALID.
Then the script calls FaceConvOwner with oPC set to the player character and oFM now OBJECT_INVALID.
So it’s highly likely that the loop in FaceConvOwner wouldn’t start…

Maybe you should remove oFM from the list of parameters in FaceConvOwner and add the object oFM = GetFirstFactionMember(oPC,FALSE); definition at the beginning of the function.

2 Likes

Good point! I did not notice that oFM was not retested in the function! :+1:

Like this …

void FaceConvOwner(string sConversationOwner, object oPC)
{

	object oConversationOwner = GetObjectByTag(sConversationOwner);
	
	vector vTarget = GetPosition(oConversationOwner);

object oFM = GetFirstFactionMember(oPC,FALSE);
	
	while (GetIsObjectValid(oFM))
	{
	
		AssignCommand(oFM, ActionDoCommand(SetFacingPoint(vTarget,FALSE)));
		AssignCommand(oFM, ActionWait(0.5f));
		
		oFM = GetNextFactionMember(oPC,FALSE);
	
	}

}

void main()
{

object oPC = GetFirstPC(FALSE);
object oFM = GetFirstFactionMember(oPC,FALSE);

	while (GetIsObjectValid(oFM))
	{
	
		AssignCommand(oFM, ClearAllActions());
		AssignCommand(oFM, ActionJumpToObject(oPC));
	
		oFM = GetNextFactionMember(oPC,FALSE);
	
	}

	string sConversationOwner = GetNodeSpeaker();

	DelayCommand(0.2,FaceConvOwner(sConversationOwner,oPC));
	
}

Another thing would be to try reducing the delay from 0.2 to 0.1

1 Like

I tend to avoid actionJumptoobject, and use the “jumptoobject” function.

2 Likes

Three companions definitely has issues, I’ve been having problems with my three looking in the wrong place or cameras going through people’s heads. Normally I have two but this time one tagged along and I kept her on the team.

If it’s really bad I move whoever isn’t speaking on the first line to waypoints, it looks strange on the first line as they shuffle about but it’s better later on. I’ve also tried to fix who faces where by making sure everybody says something and has a listener on the node that gets them facing that way.

Thanks for all the answers. Just got back from work and I’m a bit tired and dizzy. At first glance though I can’t see any difference in @Lance_Botelle 's script from mine. I must be missing something. Otherwise I get what you’re saying @4760.

All that said, I mean, I can fix this by doing it the way I’ve done it before but…I just wanted a more neat and simple solution. One script do do it all so to speak, instead of what I’ve done before with first jumping everyone by going by their tag, and then do a ga_face_target on the first node on the conversation. It’s just…well, it’s a lot more to do and I wanted a more efficient solution, I guess.

EDIT: Ok, I’m beginning to slowly comprehend what @4760 means now. I’ll try @Lance_Botelle 's script with those changes and see if that does anything…I just got to eat first so my brain starts working again.

I just tested @4760 (which was what @Lance_Botelle wrote in the new version of the script) and it seems to work now. I’ll do more testing to confirm it.

Now that my head is clear again I understand fully what you meant @4760. I attempted to use oFM when it had become an invalid object, and then of course it no longer worked. I wonder why it worked with two companions though. Could have been that I imagined it did, actually. I did a lot of small tests and I may have altered some code I don’t remember altering. Anyway, thanks again everyone.

@Tsongo Yes, I know that creatures turning the right way can really be a pain in the butt and sometimes in the middle of conversations it doesn’t work like it should at all. However, a thing like this in the beginning of a dialogue usually works, at least that’s my experience.

@Lance_Botelle I already tried with the delay being 0.1 before, and that actually didn’t work as good, so I think I’ll stick with 0.2, at least for now.

EDIT: Why is JumpToObject better than ActionJumpToObject, and what are the differencies between them? When are you supposed to use one or the other? What was the intent of these two functions from Obsidian? Even if JumpToObject is better perhaps, I think I’ll stick with ActionJumpToObject for now since I’ve got it working…

EDIT2: After more testing, I’ve actually decided to alter this script more to akin to what I previously used in my other modules. It’s a much more convoluted code with more steps, but it will look nicer ingame. I won’t go through a while loop to check each faction member, but instead go through each and every companion by their tag, and then with small delays make them turn toward the NPC at somewhat different times. I’ve come to the conclusion that I don’t really like the “whole-party-turn-in-unison” thing. It looks so unnatural.

@andgalf

Sounds good to me. :+1:

Because one adds the “action” while the other does it immediately. An action jump is more likely to be interrupted than the immediate jump. I always use the immediate jump and avoid action functions unless have to. One would use the “action” when we want it to occur among other actions in a queue. As we want an immediate result, we would normally use the one without any “action” connotation.

That is one of the reasons I also used the “follow” action with companions, but it sounds like you have it working now. :+1:

1 Like

Ah, ok. Thanks for the explanation. Maybe I should change to JumpToObject then…

1 Like

ergo …

// nw_i0_spells
float GetRandomDelay(float fMinimumTime = 0.4, float MaximumTime = 1.1)
1 Like

Very promising in testing:

//  ga_jump_face_convo
/*
	Jumps entire party to a waypoint in the FORMATION_SEMICIRCLE_FACE_IN formation and makes the entire party 
	face the sFacee at random intervals. The party will then be immobilized to prevent them from moving around during
	the convo.
	Place this on the very first node of the dialog.
	Use this in conjunction with the remove_csi script to remove the immobilization effect.
	 
	sWaypoint - The tag of the waypoint that the party will jump to. This waypoint should generally be at the feet
			of the sFacee and pointed to where you want the party to be.
	sFacee - The tag of the object that the party will face.
	fRadius - The radius from the sWaypoint to the party. If left at 0, then the default is 3.0.
*/

#include "ginc_group"
#include "nw_i0_spells"

void FaceParty(object oPC, string sFacee)
{
	vector v = GetPosition(GetObjectByTag(sFacee));
	object oFM = GetFirstFactionMember(oPC, FALSE);
	
	while (GetIsObjectValid(oFM))
	{
		AssignCommand(oFM, ClearAllActions());
		DelayCommand(GetRandomDelay(), AssignCommand(oFM, SetFacingPoint(v, TRUE)));
		ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneImmobilize(), oFM);
		oFM = GetNextFactionMember(oPC, FALSE);
	}	
}

void main(string sWaypoint, string sFacee, float fRadius)
{
	object oPC = GetFirstPC();

	if (fRadius == 0.0) fRadius = 3.0f;
	
	ResetGroup(PARTY_GROUP_NAME);
	GroupAddFaction(PARTY_GROUP_NAME, oPC, GROUP_LEADER_FIRST, TRUE);
	GroupSetSemicircleFormation(PARTY_GROUP_NAME, FORMATION_SEMICIRCLE_FACE_IN, fRadius);
	GroupJumpToWP(PARTY_GROUP_NAME, sWaypoint);
	DelayCommand(0.1f, FaceParty(oPC, sFacee));
}
// remove_csi
/*
	Place this on the last node of the dialog to remove the immobilize effect
	from the ga_jump_face_convo script.
*/

#include "x0_i0_petrify"

void main()
{
	object oPC = GetFirstPC();
	object oFM = GetFirstFactionMember(oPC, FALSE);

	while (GetIsObjectValid(oFM))
	{
		RemoveEffectOfType(oFM, EFFECT_TYPE_CUTSCENEIMMOBILIZE);
		oFM = GetNextFactionMember(oPC, FALSE);
	}
}
1 Like

@travus - Hmmm, ok. I’ll try that perhaps. What if I want the party to jump to the PC instead of a waypoint. Could one use a variation of this script for that? Perhaps change the GroupJumpToWP to something like JumpToObject?

Use this version for the party members speaking to the main PC:

//  ga_jump_face_convo_pc
/*
	Jumps companions around the main PC in FORMATION_SEMICIRCLE_FACE_IN formation and makes them 
	face the main PC at random intervals. The party will then be immobilized to prevent them from
	moving around during the convo.
	Place this on the very first node of the dialog.
	Use this in conjunction with the remove_csi script to remove the immobilization effect.
	 
	fRadius - The radius from the sWaypoint to the party. If left at 0, then the default is 3.0.
*/

#include "ginc_group"
#include "nw_i0_spells"

void FaceParty(object oPC)
{
	vector v = GetPosition(oPC);
	object oFM = GetFirstFactionMember(oPC, FALSE);
	
	while (GetIsObjectValid(oFM))
	{
		AssignCommand(oFM, ClearAllActions());	
	
		if (oFM != oPC) DelayCommand(GetRandomDelay(), AssignCommand(oFM, SetFacingPoint(v, TRUE)));

		ApplyEffectToObject(DURATION_TYPE_PERMANENT, EffectCutsceneImmobilize(), oFM);
		oFM = GetNextFactionMember(oPC, FALSE);
	}	
}

void main(float fRadius)
{
	object oPC = GetFirstPC();
	object oFM = GetFirstFactionMember(oPC, FALSE);
	
	if (fRadius == 0.0) fRadius = 3.0f;
	
	ResetGroup(PARTY_GROUP_NAME);
	
	while (GetIsObjectValid(oFM))
	{
		if (oFM != oPC) GroupAddMember(PARTY_GROUP_NAME, oFM, TRUE);
		
		oFM = GetNextFactionMember(oPC, FALSE);
	}	
	
	GroupSetSemicircleFormation(PARTY_GROUP_NAME, FORMATION_SEMICIRCLE_FACE_IN, fRadius);
	GroupMoveToFormationLocation(PARTY_GROUP_NAME, GetLocation(oPC), MOVE_JUMP_INSTANT);
	DelayCommand(0.1f, FaceParty(oPC));
}