Companions stuck in corners

I have a situation in a module I’ve recently started, that in an outdoor area (but it behaves more like an indoor area, I guess) where there are “ramps” that the party is walking on, companions often get stuck in corners which is quite annoying. I saw @travus Gather Party item: Gather Party item | The Neverwinter Vault and I’m wondering if there’s something you can do about this behaviour automatically? Like, tell the game that, if the characters/companions get stuck and can’t follow the PC, get unstuck and try another route? I think this should be posslbe to achieve with scripting, but I don’t know. Like, maybe with a heartbeat script of the area checking this? Problem is, how would one write something like that? Are there functions checking for “companions not being able to get where they want to go”?

Maybe one should use “check distance” or something like that? Or maybe that’s too vague? Is there some good way to do this?

Alright, so…I have tried making two scripts. Not sure this will work. I will test ingame. I might have forgotten something important.

I’ve placed a few triggers on the corners where the companions use to get stuck. I have one OnEnter script and one OnExit script. Here’s how they look at the moment:

OnEnter:

// s_pr_partystuckenter

const float  COMPANION_DIST = 7.f;

const string sROSTER_KELLIE = "kellie";
const string sROSTER_BACHAR = "bachar";
const string sROSTER_ASTRID = "astrid";
const string sROSTER_GENEVIEVE = "genevieve";

void HB(object oPC, object oEnter, object oKellie, object oBachar, object oAstrid, object oGenevieve)
{

	if(GetLocalInt(OBJECT_SELF,"nonestuck")) return;
	
	object oPC1 = GetFirstPC();
	
	int iKellie = GetLocalInt(oKellie,"stuck");
	int iBachar = GetLocalInt(oBachar,"stuck");
	int iAstrid = GetLocalInt(oAstrid,"stuck");
	int iGenevieve = GetLocalInt(oGenevieve,"stuck");
	
	if(!iKellie && !iBachar && iAstrid && !iGenevieve) 
	{
		SetLocalInt(OBJECT_SELF,"nonestuck",TRUE);
	}
	
	if (iKellie && GetFactionEqual(oPC,oKellie) && GetDistanceToObject(oPC) > COMPANION_DIST)
	{
		SetLocalInt(oKellie,"stuck",FALSE);
		SendMessageToPC(oPC1,"Kellie NOT stuck");
		AssignCommand(oKellie, ClearAllActions());
		AssignCommand(oKellie, ActionJumpToObject(oPC));
	}
	
	if (iBachar && GetFactionEqual(oPC,oBachar) && GetDistanceToObject(oPC) > COMPANION_DIST)
	{
		SetLocalInt(oBachar,"stuck",FALSE);
		SendMessageToPC(oPC1,"Bachar NOT stuck");
		AssignCommand(oBachar, ClearAllActions());
		AssignCommand(oBachar, ActionJumpToObject(oPC));
	}
	
	if (iAstrid && GetFactionEqual(oPC,oAstrid) && GetDistanceToObject(oPC) > COMPANION_DIST)
	{		
		SetLocalInt(oAstrid,"stuck",FALSE);
		SendMessageToPC(oPC1,"Astrid NOT stuck");
		AssignCommand(oAstrid, ClearAllActions());
		AssignCommand(oKellie, ActionJumpToObject(oPC));
	}
	
	if (iGenevieve && GetFactionEqual(oPC,oGenevieve) && GetDistanceToObject(oPC) > COMPANION_DIST)
	{
		SetLocalInt(oGenevieve,"stuck",FALSE);
		SendMessageToPC(oPC1,"Genenieve NOT stuck");
		AssignCommand(oGenevieve, ClearAllActions());
		AssignCommand(oGenevieve, ActionJumpToObject(oPC));
	}
	
	DelayCommand(2.0,HB(oPC,oEnter,oKellie,oBachar,oAstrid,oGenevieve));

}


void main()
{
	object oEnter = GetEnteringObject();
	object oPC = GetFirstPC(FALSE);
	object oPC1 = GetFirstPC();

	object oKellie = GetObjectFromRosterName(sROSTER_KELLIE);
	object oBachar = GetObjectFromRosterName(sROSTER_BACHAR);
	object oAstrid = GetObjectFromRosterName(sROSTER_ASTRID);
	object oGenevieve = GetObjectFromRosterName(sROSTER_GENEVIEVE);
	
	if(!GetIsRosterMember(oEnter)) return;
		
	if(oEnter == oKellie) 
	{
		SetLocalInt(oKellie,"stuck",TRUE);
		SetLocalInt(OBJECT_SELF,"nonestuck",FALSE);
		SendMessageToPC(oPC1,"Kellie stuck");
	}
	
	if(oEnter == oBachar) 
	{
		SetLocalInt(oBachar,"stuck",TRUE);
		SetLocalInt(OBJECT_SELF,"nonestuck",FALSE);
		SendMessageToPC(oPC1,"Bachar stuck");
	}
	
	if(oEnter == oAstrid)
	{
		SetLocalInt(oAstrid,"stuck",TRUE);
		SetLocalInt(OBJECT_SELF,"nonestuck",FALSE);
		SendMessageToPC(oPC1,"Astrid stuck");
	} 
	
	if(oEnter == oGenevieve)
	{
		SetLocalInt(oGenevieve,"stuck",TRUE);
		SetLocalInt(OBJECT_SELF,"nonestuck",FALSE);
		SendMessageToPC(oPC1,"Genevieve stuck");
	}
	
	HB(oPC,oEnter,oKellie,oBachar,oAstrid,oGenevieve);

}

OnExit:

// s_pr_partystuckexit

const float  COMPANION_DIST = 7.f;

const string sROSTER_KELLIE = "kellie";
const string sROSTER_BACHAR = "bachar";
const string sROSTER_ASTRID = "astrid";
const string sROSTER_GENEVIEVE = "genevieve";

void main()
{
	object oExit = GetExitingObject();
	object oPC1 = GetFirstPC();

	object oKellie = GetObjectFromRosterName(sROSTER_KELLIE);
	object oBachar = GetObjectFromRosterName(sROSTER_BACHAR);
	object oAstrid = GetObjectFromRosterName(sROSTER_ASTRID);
	object oGenevieve = GetObjectFromRosterName(sROSTER_GENEVIEVE);
	
	if(!GetIsRosterMember(oExit)) return;
		
	if(oExit == oKellie) 
	{
		SetLocalInt(oKellie,"stuck",FALSE);
		SendMessageToPC(oPC1,"Kellie NOT stuck");
	}
	
	if(oExit == oBachar) 
	{
		SetLocalInt(oBachar,"stuck",FALSE);
		SendMessageToPC(oPC1,"Bachar NOT stuck");
	}
	
	if(oExit == oAstrid)
	{
		SetLocalInt(oAstrid,"stuck",FALSE);
		SendMessageToPC(oPC1,"Astrid NOT stuck");	
	} 
	
	if(oExit == oGenevieve)
	{
		SetLocalInt(oGenevieve,"stuck",FALSE);
		SendMessageToPC(oPC1,"Genevieve NOT stuck");	
	}
	

}

EDIT: The scripts don’t seem to work. Hmmm…Guess I’ll go with the SendMessageToPC to see what’s happening.
EDIT2: Tried with some SendMessageToPC. It seems the scripts are working the way they should…I think. However, one of the companions still get stuck. Weird…

Since I couldn’t get this to work properly even though it should have worked, I solved this in a whole other way, by making a trigger on the “ramps” after the corners, by just checking if the PC isn’t in a fight. If he isn’t the party will gather. By the nature of how the area looks like, I think this will work just as fine, altough in a different way.

3 Likes

@andgalf

Just to say I do have both a Gather Party GUI and an automatic fix on a HB in my campaign. Care needs to be taken when such code is allowed to fire as it can also cause issues if not executed properly.

Bottom line… It is doable.

2 Likes