Help with a script I thought would work

In the module I’m working on right now there are quite a number of companions in your party. When doing dialog the companions and the PC oftentimes don’t face the one the should be facing. Before I used ga_face_target which works well, but I thought I would do a script where all the potiential companions would face the right way. I thought it would work but when testing ingame it doesn’t seem to work that well:

#include "ginc_actions"
#include "ginc_param_const"

void main(string sTarget)
{

object oPC = GetPCSpeaker();
object oTarget = GetTarget(sTarget);
vector vTarget = GetPosition(oTarget);
int bLockOrientation = 0;
object oElvera = GetNearestObjectByTag("elvera",oPC);
object oTaik = GetNearestObjectByTag("taik",oPC);
object oAnto = GetNearestObjectByTag("anto",oPC);
object oCelestine = GetNearestObjectByTag("celestine",oPC);
object oLoreen = GetNearestObjectByTag("loreen",oPC);

	//TAG OBJECT OVERRIDE PC Speaker
   	if(sTarget == "$PC")
   	{
    	object oSpeaker = GetPrimaryPlayer();
	vector vTarget = GetPosition(oSpeaker);
        AssignCommand(OBJECT_SELF, SetFacingPoint(vTarget,1));
        }

	if (GetIsObjectValid(oAnto) && (GetObjectByTag(sTarget)!= oAnto) && GetFactionEqual(oPC,oAnto))
	{
	AssignCommand(oAnto, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oAnto, ActionWait(0.5f));
	}

	if (GetIsObjectValid(oElvera) && (GetObjectByTag(sTarget)!= oElvera) && GetFactionEqual(oPC,oElvera))
	{
	AssignCommand(oElvera, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oElvera, ActionWait(0.5f));	
	}

	if (GetTarget(sTarget)!= oPC)
	{
	AssignCommand(oPC, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oPC, ActionWait(0.5f));
	}
	
	if (GetTarget(sTarget)!= oTaik)
	{
	AssignCommand(oTaik, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oTaik, ActionWait(0.5f));
	}
	
	if (GetTarget(sTarget)!= oCelestine)
	{
	AssignCommand(oCelestine, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oCelestine, ActionWait(0.5f));
	}
	
	if (GetTarget(sTarget)!= oLoreen)
	{
	AssignCommand(oLoreen, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oLoreen, ActionWait(0.5f));
	}
	
}

They may be busy. Try assigning a ClearAllActions to the object before assigning the facing command.

I actually thought of that. However, if they are running behind the PC when the PC initiates a conversation with the NPC, what happens then? Then they stop (I would gather) instead of running there course, as it is when you don’t do anything in the dialog node.

It’s very strange. Tried with clearallactions (but that didn’t change anything) and then I changed my script to mimic the ga_face_target almost exactly (like the script below). Still the companions won’t quite look at what they should when in dialog. Urrgh. I guess I’ll have to go back to writing 6 different ga_face_target on every node instead:

#include "ginc_actions"
#include "ginc_param_const"

void main(string sTarget)
{

object oPC = GetPCSpeaker();
object oTarget = GetTarget(sTarget);
vector vTarget = GetPosition(oTarget);
int bLockOrientation = 0;
object oElvera = GetNearestObjectByTag("elvera",oPC);
object oTaik = GetNearestObjectByTag("taik",oPC);
object oAnto = GetNearestObjectByTag("anto",oPC);
object oCelestine = GetNearestObjectByTag("celestine",oPC);
object oLoreen = GetNearestObjectByTag("loreen",oPC);



	if (GetIsObjectValid(oAnto) && (GetObjectByTag(sTarget)!= oAnto) && GetFactionEqual(oPC,oAnto))
	{
	AssignCommand(oAnto, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oAnto, ActionWait(0.5f));
	}

	if (GetIsObjectValid(oElvera) && (GetObjectByTag(sTarget)!= oElvera) && GetFactionEqual(oPC,oElvera))
	{
	AssignCommand(oElvera, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oElvera, ActionWait(0.5f));	
	}

	if (GetTarget(sTarget)!= oPC)
	{
	AssignCommand(oPC, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oPC, ActionWait(0.5f));
	}
	
	if (GetTarget(sTarget)!= oTaik)
	{
	AssignCommand(oTaik, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oTaik, ActionWait(0.5f));
	}
	
	if (GetTarget(sTarget)!= oCelestine)
	{
	AssignCommand(oCelestine, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oCelestine, ActionWait(0.5f));
	}
	
	if (GetTarget(sTarget)!= oLoreen)
	{
	AssignCommand(oLoreen, ActionDoCommand(SetFacingPoint(vTarget, bLockOrientation)));
	AssignCommand(oLoreen, ActionWait(0.5f));
	}
	
}

Well that’s NWN2 for you, I guess. Quite buggy at times.

Hmmm, as a matter of fact, I’m not sure ga_face_target works as intended either when really checking and testing it out. Sometimes the people look in the right direction but not all the time it seems.

Well you could always run a recursive looping script on the characters using DelayCommand until they get it right.

You could try this at the beginning of the dialog:

void main(string sTarget)
{
	object oPC = GetFirstPC();

	object oFM = GetFirstFactionMember(oPC, FALSE);
	while (GetIsObjectValid(oFM))
	{
		AssignCommand(oFM, ClearAllActions());
		AssignCommand(oFM, SetFacingPoint(GetPosition(GetNearestObjectByTag(sTarget))));
		DelayCommand(1.0f, ApplyEffectToObject(DURATION_TYPE_INSTANT, EffectCutsceneImmobilize(), oFM));
		oFM = GetNextFactionMember(oPC, FALSE);
	}
}

Then use this at the end of the dialog:

void main()
{
	object oPC = GetFirstPC();

	object oFM = GetFirstFactionMember(oPC, FALSE);
	while (GetIsObjectValid(oFM))
	{
		RemoveEffect(oFM, EffectCutsceneImmobilize());
		oFM = GetNextFactionMember(oPC, FALSE);
	}
}

Thanks for the replies. I don’t know how your script, travus, would change anything since the facing point changes with every node, but maybe I could/should use it on every node? I’ve found that the companions will most of the time face the PC if the PC speaks but not always the NPC that the dialog is with. And then when one of the companions say something, and I want all to face that person, that doesn’t always work.
In another dialog which belongs to a character that’s lying down unresponsive, there is the PC, another NPC and the companions. And there it was really apparent that my scripts didn’t work.

Hi,

I have a homebrew function I use:

EDIT: NOTE: Some of the functions I use in the scripts further down may require your own official campaign equivalents, or workarounds. i.e. They won’t compile because you don’t have the function. e.g.
GetNearestControlled could be replaced or commented out for testing. Edit: I have now removed it completely to avoid confusion.

Cheers, Lance.

///////////////////////////////////////////////////////////////////////////////////////////////////
// Set caller to face oTarget
///////////////////////////////////////////////////////////////////////////////////////////////////
void SetFacingObject(object oTarget);
void SetFacingObject(object oTarget)
{
	vector vFace=GetPosition(oTarget);
	SetFacingPoint(vFace, TRUE); 
}

e.g. AssignCommand(oNPC, SetFacingObject(oPC));

ALSO, I have just gone through my own scripts again and found this better suited …

///////////////////////////////////////////////////////////////////////////////////////////////////
// Set all faction members of oPartyMember to face oSpeaker
// E.g. See alb_con_sebbook. It forces all party members to face the speaker (book).
///////////////////////////////////////////////////////////////////////////////////////////////////

void PartyFaceSpeaker(object oPartyMember, object oSpeaker);
void PartyFaceSpeaker(object oPartyMember, object oSpeaker)
{	
	
	vector vTarget = GetPosition(oSpeaker);	

	object oFM = GetFirstFactionMember(oPartyMember, FALSE);
	
	while(oFM != OBJECT_INVALID)
	{			
		SetOrientOnDialog(oFM, TRUE);
		AssignCommand(oFM, SetFacingPoint(vTarget, 1));					
		oFM = GetNextFactionMember(oPartyMember, FALSE);
	}
}

I have it called from (alb_conv_partyfacenpc):-

////////////////////////////////////////////////////////////////////////////////////////////////
// FACE SPEAKER CONVERSATION WRAPPER 
// NB: THE "OWNER" CONV MAY HAVE BEEN RESTARTED USING THE PC AS "OWNER". THEREFORE, MUST USE
// THE "OWNER" TAG TO FORCE THE PC TO LOOK AT THE "OWNWER" OR ELSE WE MAY GET THE 90 DEGREE TURN.
// ADDED iNPCFACEPCs PARAMETER TO FORCE NPC TO FACE PARTY (E.g. Sebastion Rewilder in Jail)
// MUST COME AFTER OTHER SCRIPTS IN CONVERSATION
////////////////////////////////////////////////////////////////////////////////////////////////

#include "alb_functions_unique"

void main(string sObjTag = "", int iNPCFACEPCs = 0)
{
	object oPC = GetPCSpeaker();
	object oSpeaker = OBJECT_SELF;	
	
	// TAG OBJECT OVERRIDE
	if(sObjTag != ""){oSpeaker = GetNearestObjectByTag(sObjTag, oPC, 1);}
	
	// NPC FACE PARTY INTERCEPT
	if(iNPCFACEPCs == 1)
	{
		SetOrientOnDialog(oPC, TRUE);
		vector vTarget = GetPosition(oPC);	
		AssignCommand(oSpeaker, ClearAllActions(TRUE));
		AssignCommand(oSpeaker, SetFacingPoint(vTarget, 1));
	}
		
	// DO NOT ALTER THIS DELAY AS IT TIMES WITH OTHER SCRIPTS LIKE ALB_SPEECH
	DelayCommand(1.1, PartyFaceSpeaker(oPC, oSpeaker));
}

Thank you for your reply, Lance! My head is spinning right now trying to make total sense of your scripts. They are a bit above my understanding at the moment. But if I read them about 20 times maybe I’ll understand eventually.

Since, as you said, I don’t have the function GetNearestControlled, could you maybe write out that function for me, or is it hard to find among your scripts?

The function PartyFaceSpeaker seems neat…The include “alb_functions_unique” I don’t have but I guess I only need the PartyFaceSpeaker from that?

I’m sorry I’m so bad at scripting and after a long day at work my mind feels really slow right now…Sigh Sometimes I just feel so dumb…

1 Like

Ok. I tried to combine your scripts into one script. I don’t understand it fully yet. I’ll have to contemplate it more.

///////////////////////////////////////////////////////////////////////////////////////////////////
// Set all faction members of oPartyMember to face oSpeaker
// E.g. See alb_con_sebbook. It forces all party members to face the speaker (book).
///////////////////////////////////////////////////////////////////////////////////////////////////

void PartyFaceSpeaker(object oPartyMember, object oSpeaker);
void PartyFaceSpeaker(object oPartyMember, object oSpeaker)
{	
	//if(GetIsDead(oPartyMember) || oPartyMember == OBJECT_INVALID)
	//{
	//	oPartyMember = GetNearestControlled(oSpeaker);	
	//}
	
	vector vTarget = GetPosition(oSpeaker);	

	object oFM = GetFirstFactionMember(oPartyMember, FALSE);
	
	while(oFM != OBJECT_INVALID)
	{			
		SetOrientOnDialog(oFM, TRUE);
		AssignCommand(oFM, SetFacingPoint(vTarget, 1));					
		oFM = GetNextFactionMember(oPartyMember, FALSE);
	}
}

////////////////////////////////////////////////////////////////////////////////////////////////
// FACE SPEAKER CONVERSATION WRAPPER 
// NB: THE "OWNER" CONV MAY HAVE BEEN RESTARTED USING THE PC AS "OWNER". THEREFORE, MUST USE
// THE "OWNER" TAG TO FORCE THE PC TO LOOK AT THE "OWNWER" OR ELSE WE MAY GET THE 90 DEGREE TURN.
// ADDED iNPCFACEPCs PARAMETER TO FORCE NPC TO FACE PARTY (E.g. Sebastion Rewilder in Jail)
// MUST COME AFTER OTHER SCRIPTS IN CONVERSATION
////////////////////////////////////////////////////////////////////////////////////////////////



void main(string sObjTag = "", int iNPCFACEPCs = 0)
{
	object oPC = GetPCSpeaker();
	object oSpeaker = OBJECT_SELF;	
	
	// TAG OBJECT OVERRIDE
	if(sObjTag != "")
	{
	oSpeaker = GetNearestObjectByTag(sObjTag, oPC, 1);
	}
	
	// NPC FACE PARTY INTERCEPT
	if(iNPCFACEPCs == 1)
	{
		SetOrientOnDialog(oPC, TRUE);
		vector vTarget = GetPosition(oPC);	
		AssignCommand(oSpeaker, ClearAllActions(TRUE));
		AssignCommand(oSpeaker, SetFacingPoint(vTarget, 1));
	}
		
	// DO NOT ALTER THIS DELAY AS IT TIMES WITH OTHER SCRIPTS LIKE ALB_SPEECH
	DelayCommand(1.1, PartyFaceSpeaker(oPC, oSpeaker));
}

So…as I try to understand this I wonder…oSpeaker is set to OBJECT_SELF. If I put this on a conversation node, what will the script think is the OBJECT_SELF? Is it the one who speaks that particular line (node) or is it the owner of the conversation?

EDIT: Wait a minute…if I put the tag of the one who is speaking into sObjTag it should override the OBJECT_SELF right?

EDIT 2: What happens if the speaker is one of the party members, a companion, will that companion face itself then if I’ve made sObjTag the tag of one of the companions?

1 Like

I tried your script, and though the characters sometimes turn towards the owner of the conversation (the NPC who in this instance is unconscious) “between” the nodes, they seem to actually be facing the right way more often than not now…I still wonder about if the speaker will/can face itself or how that works…Or if I use ga_face_target for the one who speaks in the node, to face the PC for instance, while the others face the speaker (but then as I said, won’t the speaker face itself too once I use this custom script?)?

Hi andgalf,

I’m not at my main computer at the moment, and so I’ll do my best to explain without.

The alb_functions_unique is one of my own include libraries with a load of individual homebrew functions, including the PartyFaceSpeaker function. So the include at the top of the last script points to this library, which uses that function. The “missing” GetNearestControlled function you refer to is in another include, but you could comment it out to be ignored completely. (I’ll edit it above for you.)

Now put the PartyFaceSpeaker in a script called alb_functions_unique, which will now be your own include library with that single function in it. You can add more homebrew functions to it as you write more. :grinning:

Cheers, Lance

You have it about right here.:grinning: (You can provide the homebrew function within the single script as you have done, especially if you don’t intend to call it from anywhere else.)

Not sure if an override tag of a companion would do anything in trying to face itself … Never tried it.:wink:

oSpeaker is the conversation owner by default unless you provide an overriding tag for the party to face instead.

There is also the option to add who is speaking on each node in the conversation tree. Then the pc will face them instead. So, if you just want the pc speaker to face the current speaker, just make sure you have marked the node to the appropriate speaker … Or listener is also available l believe.

Yes, I know about the option of who is speaking and listening on each node.

1 Like

The best solution will probably require a combination of the script for all in party (as provided) and individual node responses .:grin:

I still wonder about the facing “between” the nodes…

What do you mean?

It’s like…When someone speaks most of the party face the right way, then before the next node is triggered, or if you trigger it by responding with your PC, the party would sometimes face the owner of the conversation in between…

My guess, that would be due to the delay I have in the main script before the function call.

Remove the delay (as you do not need it in the way you are using it) and hopefully they won’t do that. Let me know.