Well, I managed to do something like that. Maybe not very beautiful, but at least it works
// 'tr_party_convo'
//
/*******************************************************************************/
#include "ginc_cutscene"
#include "x0_i0_assoc"
//__________________
// ** 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 and the speaker face oFacer
void PartyFace(object oListener, object oSpeaker);
// The wrapper for SetOwnersControlledCompanion() function
void SetOwnersControlledCompanionWrapper(object oCurrentCreature, object oTargetCreature=OBJECT_INVALID);
// Makes the listener move to the speaker and start a coversation
void StartConversation(object oListener, object oSpeaker, float fTime);
object oTrigger = OBJECT_SELF;
/*******************************************************************************/
//____________
// ** MAIN ***
void main()
{
if (!GetLocalInt(oTrigger, "bCondition") && !GetLocalInt(oTrigger, "bDone"))
{
object oPC = GetEnteringObject();
if (GetIsPC(oPC) && getConversable(oPC, TRUE))
{
object oSpeaker = GetNearestObjectByTag(GetLocalString(oTrigger, "sSpeaker"));
if (GetIsObjectValid(oSpeaker) && GetFactionEqual(oPC, oSpeaker))
{
SetLocalInt(oTrigger, "bDone", TRUE);
SetLocalInt(oSpeaker, "NodeIndex", GetLocalInt(oTrigger, "NodeIndex"));
SetLocalInt(oSpeaker, "bConvoPending", 1);
ClearPartyActions(oPC);
SetCutsceneMode(oPC, TRUE);
effect eParalyze = EffectCutsceneParalyze();
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eParalyze, oPC);
SetLocalString(oTrigger, "sActivatedBy", GetTag(oPC));
object oMain = GetFirstPC();
if(GetAssociateType(oPC) != ASSOCIATE_TYPE_NONE && !GetAssociateState(NW_ASC_MODE_STAND_GROUND, oPC))
{
SetLocalInt(oPC, "NeedStandGround", 1); //Otherwise it will run to the master, even if paralyzed
SetAssociateState(NW_ASC_MODE_STAND_GROUND, TRUE, oPC);
}
if(GetAssociateState(NW_ASC_MODE_PUPPET, oMain))
{
SetLocalInt(oMain, "NeedHeartbeat", 1);
SetEventHandler(oMain, CREATURE_SCRIPT_ON_HEARTBEAT, ""); // To prevent problems with PuppetMode and AI
}
if(GetAssociateState(NW_ASC_MODE_PUPPET, oSpeaker))
{
SetLocalInt(oSpeaker, "NeedHeartbeat", 1);
SetEventHandler(oSpeaker, CREATURE_SCRIPT_ON_HEARTBEAT, "");
}
PartyFace(oMain, oSpeaker);
DelayCommand(0.25f, StartConversation(oMain, oSpeaker, 0.0f));
}
}
}
}
//_________________
// ** FUNCTIONS ***
// The wrapper for SetOwnersControlledCompanion() function.
//
// This will find the player controlling oCurrentCreature, and set
// their currently controlled companion to oTargetCreature. If it
// canât find oTargetCreature, then the player is reassigned to
// whoever their original, owned character is.
void SetOwnersControlledCompanionWrapper(object oCurrentCreature, object oTargetCreature=OBJECT_INVALID)
{
oCurrentCreature = SetOwnersControlledCompanion(oCurrentCreature, oTargetCreature);
}
// Makes the listener (oListener) and the speaker (oSpeaker) move to
// each other and start the coversation.
void StartConversation(object oListener, object oSpeaker, float fTime)
{
string sTag = GetLocalString(oTrigger, "sActivatedBy");
string sConvo = GetLocalString(oTrigger, "sConvo");
object oPC = GetObjectByTag(sTag);
if (!GetLocalInt(oSpeaker, "bConvoPending"))
return;
ClearPartyActions(oListener);
if (fTime >= 10.0f)
AssignCommand(oListener, ActionStartConversation(oSpeaker, sConvo, FALSE, FALSE, TRUE));
if(sTag == "")
{
AssignCommand(oSpeaker, ActionStartConversation(oListener, sConvo, FALSE, FALSE));
}
else if (sTag == GetLocalString(oTrigger, "sSpeaker"))
{
AssignCommand(oListener, ActionForceMoveToObject(oSpeaker, TRUE, 0.5f));
AssignCommand(oListener, ActionDoCommand(SetOwnersControlledCompanionWrapper(oSpeaker)));
AssignCommand(oListener, ActionStartConversation(oSpeaker, sConvo, FALSE, FALSE));
}
else
{
if((GetDistanceBetween(oListener, oPC) < 3.0f)
&& (GetDistanceBetween(oSpeaker, oPC) < 3.0f))
{
oListener = SetOwnersControlledCompanion(oPC);
AssignCommand(oSpeaker, ActionStartConversation(oListener, sConvo, FALSE, FALSE));
}
else
{
AssignCommand(oListener, ActionForceMoveToObject(oPC, TRUE));
AssignCommand(oSpeaker, ActionForceMoveToObject(oPC, TRUE));
}
}
if (GetLocalInt(oSpeaker, "bConvoPending"))
DelayCommand(2.0f, StartConversation(oListener, oSpeaker, fTime + 2.0f));
}
// Set the facing of the party to the speaker.
void PartyFace(object oListener, object oSpeaker)
{
string sTag = GetLocalString(oTrigger, "sActivatedBy");
object oFacer, oFaced;
object oPC = GetObjectByTag(sTag);
if(sTag == "" || sTag == GetLocalString(oTrigger, "sSpeaker"))
{
if (sTag == "")
{
oFacer = oSpeaker;
oFaced = oListener;
}
else
{
oFacer = oListener;
oFaced = oSpeaker;
}
object oFM = oFacer;
vector vTarget = GetPosition(oFaced);
AssignCommand(oFaced, ActionDoCommand(SetFacingPoint(GetPosition(oFacer), FALSE)));
AssignCommand(oFaced, ActionWait(0.5f));
oFacer = GetFirstFactionMember(oFM, FALSE);
while(GetIsObjectValid(oFacer))
{
AssignCommand(oFacer, ActionDoCommand(SetFacingPoint(vTarget, FALSE)));
AssignCommand(oFacer, ActionWait(0.5f));
oFacer = GetNextFactionMember(oFM, FALSE);
}
}
else
{
AssignCommand(oPC, ActionDoCommand(SetFacingPoint(GetPosition(oSpeaker), 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 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;
}
Dialogâs Abort/End event script:
#include "ginc_cutscene"
void main()
{
SetLocalInt(OBJECT_SELF, "NodeIndex", 0);
object oPC = GetFirstPC();
object oFactionMember = GetFirstFactionMember(oPC, FALSE);
effect eEffect;
while(GetIsObjectValid(oFactionMember))
{
RemoveEffectCutsceneParalyze(oFactionMember);
SetCutsceneMode(oFactionMember, FALSE);
if(GetLocalInt(oFactionMember, "NeedHeartbeat"))
SetEventHandler(oFactionMember, CREATURE_SCRIPT_ON_HEARTBEAT, "gb_comp_heart");
if(GetLocalInt(oFactionMember, "NeedStandGround"))
SetAssociateState(NW_ASC_MODE_STAND_GROUND, FALSE, oFactionMember);
oFactionMember = GetNextFactionMember(oPC, FALSE);
}
}