Conversation failing to fire

Hi all,

a problem has come up in my campaign and 3 people reported that they are stuck because of it and I need some help solving it.

The facts:

Conversation trigger doesn’t fire on ongoing game
Same trigger fires if area is loaded from toolset
Same trigger never malfunctioned before.

The script which is used is this

// 'dialog_tr'
//
/*******************************************************************************
	Andy for Return of the Exile
	22.1.2016

	kevL rework
	28.1.2016

	To be used by a trigger's onEnter event.
	Starts a dialog. See TRIGGER VARIABLES below.

	Works with both SoZ-style.
	Works with an i-point placeable as owner as well.
	
	-------------
	Additions
    -------------
	30.1.2016 : Added a party face target. Note that this adresses the start of the dialogue
            	If the party moves or the NPC moves, we still need to adjust the facing using the ga_party_face
	30.1.2016 : Added a bPCTalk option. For cutscenes that NEED to happen, 
	            but where the NPC only needs to talk to the PC. Instead of the idiotic warp,
				the main PC stays where he is but control switches to him.
	4.2.2016  : Added a method for required creatures in order to start the dialogue. Good to have
	4.2.2016  : Commented out the required creatures because of too much complexity. Perhaps in the future another trigger could
	            be made for it, and all these function in a library.
				If this is ever added, remember to change the 
				if (!GetLocalInt(oTrig, "bCondition")) with
				if (!GetLocalInt(oTrig, "bCondition") && checkRequiredTalkers(GetLocalString(oTrig, "sTalkers")))
	            
	-------------
	TODO
	-------------
	Add a bForce option that makes party/oSpeaker conversable.
	Add an option to use fade-in/fade-outs.
	Add an option to delay the conversation start.
	Possibly implement a heartbeat like CreateIPSpeaker() uses.
	Add an option to disable cutscene bars.
	Add a variable to check for item

	-------------
	TRIGGER VARIABLES
	---------
	bCondition		- Default 0. Set it to 1 so that the trigger won't fire if
					  we go inside. If it needs to fire further in the game,
					  set a local int on the trigger called bCondition = 0.

	sQuestCond		- Default "". The tag of the quest if a QuestCondition is
					  needed.
	iQuestCond		- Default 0. The ID of the quest if a QuestCondition is
					  needed.

	sSpeaker		- Default "". The tag of the creature to be speaking to the
					  party.
	sConvo			- Default "". Name of the conversation file.
	bDestroySelf	- Default 0. Set to 1 if the trigger should get destroyed
					  after it's done its job.

	bParty			- Default 0. Set to 1 if the conversation is an internal
					  party dialog. Checks if the companions needed (speaker) is in party.
	bPCTalk         - Default 0. Set to 1 if Speaker is supposed to talk to the PC only. 
	                  Note: Leave bParty default FALSE.	
	sTalkers        - Default "". A list of tags for required participants excluding PC and Speaker (comma-separated, no spaces).
	                  This is not used at the moment.				  			  

*******************************************************************************/
//__________________
// ** PROTOTYPES ***

// Checks if target can talk.
int getConversable(object oTarget, int bFaction = FALSE);
// Checks if an effect prevents dialog.
int hasEffectSpeechless(object oTarget);
// Makes party face the speaker
void PartyFace(object oFacer, object oFaced);
// Checks if required talkers are present and conversable.
//int checkRequiredTalkers(string sTalkers);

//____________
// ** MAIN ***
void main()
{
	object oTrig = OBJECT_SELF;

	if (!GetLocalInt(oTrig, "bCondition"))
	{
		object oPlayer = GetEnteringObject();
		//SendMessageToPC(oPlayer, "Script has fired upon entering the trigger");
		if (GetIsPC(oPlayer) && getConversable(oPlayer, TRUE))
		{
			string sQuestCond = GetLocalString(oTrig, "sQuestCond");
			if (sQuestCond == ""
				|| GetJournalEntry(sQuestCond, GetOwnedCharacter(oPlayer)) == GetLocalInt(oTrig, "iQuestCond"))
			{
				object oSpeaker = GetNearestObjectByTag(GetLocalString(oTrig, "sSpeaker"));
				if (GetIsObjectValid(oSpeaker))
				{
					int bParty = GetLocalInt(oTrig, "bParty");
					if (bParty || getConversable(oSpeaker))
					{
						if (bParty || GetLocalInt(oTrig, "bPCTalk"))
							oPlayer = SetOwnersControlledCompanion(oPlayer);

						if (!bParty || GetFactionEqual(oPlayer, oSpeaker))
						{
							AssignCommand(oSpeaker, ClearAllActions());
							AssignCommand(oPlayer, ClearAllActions());
							
                            PartyFace(oPlayer, oSpeaker);
							DelayCommand(0.25f, AssignCommand(oSpeaker,
									ActionStartConversation(
											oPlayer,
											GetLocalString(oTrig, "sConvo"),
											FALSE, FALSE, TRUE, FALSE)));

							if (GetLocalInt(oTrig, "bDestroySelf"))
								DelayCommand(3.0f, DestroyObject(oTrig));
						}
					}
				}
			}
		}
	}
}



//_________________
// ** FUNCTIONS ***

// Set the facing of the party to the speaker.
// oFacer - the PC or companion
// oFaced - the speaker
// It happens by default. No need for any trigger variable here.
void PartyFace(object oFacer, object oFaced)
{
	object oFM = oFacer;
	vector vTarget = GetPosition(oFaced);
	
	oFacer = GetFirstFactionMember(oFM, FALSE);
	while(GetIsObjectValid(oFacer))
	{
		AssignCommand(oFacer, ActionDoCommand(SetFacingPoint(vTarget, FALSE)));
		AssignCommand(oFacer, ActionWait(0.5f));
		oFacer = GetNextFactionMember(oFM, FALSE);
	}
}
// Checks if target can talk.
// oTarget	- creature to check
// bFaction	- true to check target's faction (default FALSE)
int getConversable(object oTarget, int bFaction = FALSE)
{
	if (bFaction)
	{
		object oFaction = GetFirstFactionMember(oTarget, FALSE);
		while (GetIsObjectValid(oFaction))
		{
			if (GetIsDead(oFaction)
				|| GetIsInCombat(oFaction)
				|| IsInConversation(oFaction)
				|| hasEffectSpeechless(oFaction))
			{
				return FALSE;
			}
			oFaction = GetNextFactionMember(oTarget, FALSE);
		}
	}
	else if (GetIsDead(oTarget)
		|| GetIsInCombat(oTarget)
		|| IsInConversation(oTarget)
		|| hasEffectSpeechless(oTarget))
	{
		return FALSE;
	}

	return TRUE;
}

// Checks if required talkers are present and conversable.
// sTalkers - a comma-separated list of creature-tags (spaces *not* allowed)
/*int checkRequiredTalkers(string sTalkers)
{
    string sTalker;
    object oTalker;

    int iSeparator = 0;
    while (GetStringLength(sTalkers) > 0 && iSeparator != -1)
    {
        iSeparator = FindSubString(sTalkers, ",");
        if (iSeparator == -1)
            sTalker = sTalkers;
        else
            sTalker = GetStringLeft(sTalkers, iSeparator);

        oTalker = GetNearestObjectByTag(sTalker);
        if (!GetIsObjectValid(oTalker) || !getConversable(oTalker))
            return FALSE;

        sTalkers = GetStringRight(sTalkers, GetStringLength(sTalkers) - iSeparator - 1);
    }

    return TRUE;
}*/
// Checks if an effect prevents dialog.
// oTarget - creature to check
int hasEffectSpeechless(object oTarget)
{
	int iEffect;

	effect eEffect = GetFirstEffect(oTarget);
	while (GetIsEffectValid(eEffect))
	{
		iEffect = GetEffectType(eEffect);
		if (   iEffect == EFFECT_TYPE_CONFUSED
			|| iEffect == EFFECT_TYPE_CUTSCENE_PARALYZE
			|| iEffect == EFFECT_TYPE_CUTSCENEGHOST
//			|| iEffect == EFFECT_TYPE_CUTSCENEIMMOBILIZE
			|| iEffect == EFFECT_TYPE_DAZED
			|| iEffect == EFFECT_TYPE_DEAF
			|| iEffect == EFFECT_TYPE_DOMINATED
//			|| iEffect == EFFECT_TYPE_ENTANGLE
//			|| iEffect == EFFECT_TYPE_FRIGHTENED
//			|| iEffect == EFFECT_TYPE_GREATERINVISIBILITY
			|| iEffect == EFFECT_TYPE_INSANE
//			|| iEffect == EFFECT_TYPE_INVISIBILITY
//			|| iEffect == EFFECT_TYPE_JARRING
			|| iEffect == EFFECT_TYPE_MESMERIZE
			|| iEffect == EFFECT_TYPE_PARALYZE
			|| iEffect == EFFECT_TYPE_PETRIFY
//			|| iEffect == EFFECT_TYPE_POLYMORPH
			|| iEffect == EFFECT_TYPE_SILENCE
			|| iEffect == EFFECT_TYPE_SLEEP
			|| iEffect == EFFECT_TYPE_STUNNED)
//			|| iEffect == EFFECT_TYPE_TURNED
		{
			return TRUE;
		}
		eEffect = GetNextEffect(oTarget);
	}

	return FALSE;
}

The same script is used throughout the whole campaign to initiate convos and it only fails in this area, and only recently. People have finished the game by actually having the conversation needed.

The parameters of the trigger have been extensively checked, and not just on my game instance but also on the uploaded instance and are correct… in other words, tags for speakers etc match.

I’ve tried initiating the conversation for the players, so that they can continue playing, via console.
rs ga_start_convo(“trial_enc_owner”, “enc_trial_begin”, 0, 0, 1, 0)
rs ga_conversation_self(“enc_trial_begin”)

and

rs debug_script

for this script

#include "ginc_ipspeaker"

void main()
{
	CreateIPSpeaker("trial_enc_owner", "enc_trial_begin", GetLocation(GetFirstPC()), 0.5);	
}

have all failed.

The conversation does not have any “Once only” or conditions. There are action scripts attached but things like
ga_move
ga_global_int
etc.
In other words, nothing that should break the convo or cause it to fail beginning.

At this point I am thinking of jumping the players who have reported it to a different area in order to bypass the issue, but this is not a general solution. Something is weird here. If anyone who has downloaded my campaign and feels like looking at the area for reference the details are:

module name: 14_Randomencounter
convo name: enc_trial begin
area name: enc_trial_begin

Thanks a lot for any possible help :slight_smile: .

Small update. I noticed the NPCs who are supposed to initiate conversation are not placed, but spawned instead. I just asked the players if the NPCs spawned there and I am waiting for an answer.

There are a few things that don’t convince me about that script, from a logical point of view (although I do not know the setting in which this is implemented), especially the parts with the bParty and bPCtalkto variables.

I have attempted to refactor the code, it may not fix anything, but perhaps it will help you understand better why and what is not making it fire in certain conditions.

From what I could see the things that do not make the script fire are:

  • The triggerer is not being controlled by a player
  • The triggerer is dead, in combat, affected by certain status effects that do not allow him to speak, ecc…
  • The object to speak to is not found (not identified by tag)
  • Speaker and object to talk to are not of the same faction if the script is party based
  • The bParty or bPCTalk vars are set up in a way that they make the script return.

EDIT2: Made it, apparently the bullet points were messing up the preformatted text.

// 'dialog_tr'
//
/*******************************************************************************
	Andy for Return of the Exile
	22.1.2016

	kevL rework
	28.1.2016
	
	Clangeddin rework
	17.9.2019

	To be used by a trigger's onEnter event.
	Starts a dialog. See TRIGGER VARIABLES below.

	Works with both SoZ-style.
	Works with an i-point placeable as owner as well.
	
	-------------
	Additions
    -------------
	30.1.2016 : Added a party face target. Note that this adresses the start of the dialogue
            	If the party moves or the NPC moves, we still need to adjust the facing using the ga_party_face
	30.1.2016 : Added a bPCTalk option. For cutscenes that NEED to happen, 
	            but where the NPC only needs to talk to the PC. Instead of the idiotic warp,
				the main PC stays where he is but control switches to him.
	4.2.2016  : Added a method for required creatures in order to start the dialogue. Good to have
	4.2.2016  : Commented out the required creatures because of too much complexity. Perhaps in the future another trigger could
	            be made for it, and all these function in a library.
				If this is ever added, remember to change the 
				if (!GetLocalInt(oTrig, "bCondition")) with
				if (!GetLocalInt(oTrig, "bCondition") && checkRequiredTalkers(GetLocalString(oTrig, "sTalkers")))
	            
	-------------
	TODO
	-------------
	Add a bForce option that makes party/oSpeaker conversable.
	Add an option to use fade-in/fade-outs.
	Add an option to delay the conversation start.
	Possibly implement a heartbeat like CreateIPSpeaker() uses.
	Add an option to disable cutscene bars.
	Add a variable to check for item

	-------------
	TRIGGER VARIABLES
	---------
	bCondition		- Default 0. Set it to 1 so that the trigger won't fire if
					  we go inside. If it needs to fire further in the game,
					  set a local int on the trigger called bCondition = 0.

	sQuestCond		- Default "". The tag of the quest if a QuestCondition is
					  needed.
	iQuestCond		- Default 0. The ID of the quest if a QuestCondition is
					  needed.

	sSpeaker		- Default "". The tag of the creature to be speaking to the
					  party.
	sConvo			- Default "". Name of the conversation file.
	bDestroySelf	- Default 0. Set to 1 if the trigger should get destroyed
					  after it's done its job.

	bParty			- Default 0. Set to 1 if the conversation is an internal
					  party dialog. Checks if the companions needed (speaker) is in party.
	bPCTalk         - Default 0. Set to 1 if Speaker is supposed to talk to the PC only. 
	                  Note: Leave bParty default FALSE.	
	sTalkers        - Default "". A list of tags for required participants excluding PC and Speaker (comma-separated, no spaces).
	                  This is not used at the moment.				  			  

*******************************************************************************/
//__________________
// ** PROTOTYPES ***

// Checks if target can talk.
int getConversable(object oTarget, int bFaction = FALSE);
// Checks if an effect prevents dialog.
int hasEffectSpeechless(object oTarget);
// Makes party face the speaker
void PartyFace(object oFacer, object oFaced);

//____________
// ** MAIN ***
void main()
{
	object oTrig = OBJECT_SELF;
	if (GetLocalInt(oTrig, "bCondition") == 1) return;
	
	object oPlayer = GetEnteringObject();
	if (GetIsPC(oPlayer) == FALSE) return;
	
	//SendMessageToPC(oPlayer, "Script has fired upon entering the trigger");
	string sQUEST = GetLocalString(oTrig, "sQuestCond");
	if (sQUEST != "")
	{
		int nQUEST = GetLocalInt(oTrig, "iQuestCond");
		if (GetJournalEntry(sQUEST, GetOwnedCharacter(oPlayer)) != nQUEST) return;
	}
	
	object oSpeaker = GetNearestObjectByTag(GetLocalString(oTrig, "sSpeaker"));
	if (oSpeaker == OBJECT_INVALID) return;
	
	int nPARTY = GetLocalInt(oTrig, "bParty");
	if ((nPARTY == TRUE) && (GetFactionEqual(oPlayer, oSpeaker) == FALSE)) return;
	
	if (GetLocalInt(oTrig, "bPCTalk") != 1) nPARTY = TRUE;
	if (getConversable(oSpeaker) == FALSE) return;
	if (getConversable(oPlayer, nPARTY) == FALSE) return;
	
	PartyFace(oPlayer, oSpeaker);
	DelayCommand(0.25f, AssignCommand(oSpeaker,	ActionStartConversation(oPlayer, GetLocalString(oTrig, "sConvo"), FALSE, FALSE, TRUE, FALSE)));
	if (GetLocalInt(oTrig, "bDestroySelf"))	DestroyObject(oTrig, 3.0);
}

//_________________
// ** FUNCTIONS ***

// Set the facing of the party to the speaker.
// oFacer - the PC or companion
// oFaced - the speaker
// It happens by default. No need for any trigger variable here.
void PartyFace(object oFacer, object oFaced)
{
	AssignCommand(oFacer, ClearAllActions());
	AssignCommand(oFaced, ClearAllActions());
	vector vTarget = GetPosition(oFaced);
	object oFM = GetFirstFactionMember(oFacer, FALSE);
	while (oFM != OBJECT_INVALID)
	{
		AssignCommand(oFM, ActionDoCommand(SetFacingPoint(vTarget, FALSE)));
		AssignCommand(oFM, ActionWait(0.5f));
		oFM = GetNextFactionMember(oFacer, FALSE);
	}
}

// Checks if target can talk.
// oTarget	- creature to check
// bFaction	- true to check target's faction (default FALSE)
int getConversable(object oTarget, int bFaction = FALSE)
{
	if (GetIsDead(oTarget)) return FALSE;
	if (GetIsInCombat(oTarget)) return FALSE;
	if (IsInConversation(oTarget)) return FALSE;
	if (hasEffectSpeechless(oTarget)) return FALSE;
	
	if (bFaction == TRUE)
	{
		object oFaction = GetFirstFactionMember(oTarget, FALSE);
		while (oFaction != OBJECT_INVALID)
		{
			if (oFaction != oTarget)
			{
				if (GetIsDead(oFaction)) return FALSE;
				if (GetIsInCombat(oFaction)) return FALSE;
				if (IsInConversation(oFaction)) return FALSE;
				if (hasEffectSpeechless(oFaction)) return FALSE;
			}
			oFaction = GetNextFactionMember(oTarget, FALSE);
		}
	}

	return TRUE;
}

// Checks if an effect prevents dialog.
// oTarget - creature to check
int hasEffectSpeechless(object oTarget)
{
	int nFX;
	effect eFX = GetFirstEffect(oTarget);
	while (GetIsEffectValid(eFX))
	{
		nFX = GetEffectType(eFX);
		switch (nFX)
		{
			case EFFECT_TYPE_CONFUSED:
			case EFFECT_TYPE_CUTSCENE_PARALYZE:
			case EFFECT_TYPE_CUTSCENEGHOST:
			// case EFFECT_TYPE_CUTSCENEIMMOBILIZE:
			case EFFECT_TYPE_DAZED:
			case EFFECT_TYPE_DEAF:
			case EFFECT_TYPE_DOMINATED:
			// case EFFECT_TYPE_ENTANGLE:
			// case EFFECT_TYPE_FRIGHTENED:
			// case EFFECT_TYPE_GREATERINVISIBILITY:
			case EFFECT_TYPE_INSANE:
			// case EFFECT_TYPE_INVISIBILITY:
			// case EFFECT_TYPE_JARRING:
			case EFFECT_TYPE_MESMERIZE:
			case EFFECT_TYPE_PARALYZE:
			case EFFECT_TYPE_PETRIFY:
			// case EFFECT_TYPE_POLYMORPH:
			case EFFECT_TYPE_SILENCE:
			case EFFECT_TYPE_SLEEP:
			case EFFECT_TYPE_STUNNED:
			// case EFFECT_TYPE_TURNED:
				return TRUE;
		}
		eFX = GetNextEffect(oTarget);
	}

	return FALSE;
}
1 Like

Thanks for the help. Apparently one of the NPCs (who has the first node in the convo) doesn’t spawn when the party enters the area.

So nothing wrong with the trigger script. Also, I am sorry but the script works so I will not be using the changes. I am just afraid that it will mess something up, since as you also said is quite setting specific. Thanks again though :slight_smile:

1 Like