So I basically wrote a cutscene I am proud of. Everything is working. Everything? No, a small detail while aborting the cutscene withstands my perfectionism.
The issue is this: In this cutscene I wanted to be clever. Since NWN doesn’t allow you to have nice things like a normal camera movement, I made the PC invisible and use him to get the camera around while showing a copy of the PC to the player. So far so normal.
The problem seems to be that aborting the cutscene while the PC is moving (with GestaltActionMove) to get a nice camera transition, the movement action is still completed which of course uncovers to the player what mumbo jumbo I am doing under the hood. I can’t seem to clear the movement, because it is no longer in the action queue (?). Normally you would probably destroy the moving NPC and respawn it, but I can’t do that with the PC without severly changing his OnDeath, can I?
This seems to be solvable by either don’t have the cutscene be skipable or by iterating the movement, by calculating the required distance and issuing multiple Moveactions to the queue. While the first is just not a solution, the latter probably causes the fluid motion needed for the camera to stutter. Also it seems to be incredibly complicated for just moving the PC in a cutscene.
Which brings me to my question: What is the easy solution I am overlooking or should I adopt the rule of thumb to never move the pc with ActionMove in cutscenes?
Cutscene script
#include "in_g_cutscene"
void main()
{
object oPC = GetFirstPC();
object oLester = GetObjectByTag("Lester");
location lPortal = GetLocation(GetWaypointByTag("tp_1p_portal"));
location lPortalOPC = GetLocation(GetWaypointByTag("tp_1p_portal_opc"));
location lPC = GetLocation(GetWaypointByTag("tp_1p_opc"));
location lLester = GetLocation(GetWaypointByTag("tp_1p_lester"));
object oWaypointPortal = GetWaypointByTag("tp_1p_portal");
object oWaypointMagistra = GetWaypointByTag("tp_1p_magistra");
object oWaypointPC = GetWaypointByTag("tp_1p_opc");
object oWaypointLester = GetWaypointByTag("tp_1p_lester");
object oWaypointPCMove = GetWaypointByTag("tp_1p_portal_opc_move");
// start cutscene
float fLength = 45.0;
GestaltStartCutscene(oPC, "cutscene_prison", TRUE, TRUE, TRUE, TRUE, TRUE);
GestaltCameraFade(0.0, oPC, FADE_IN, FADE_SPEED_SLOW, 1.5);
// copy PC and move Lester to lLester
object oNew = CopyObject(oPC, lPC, oPC, "PC_Copy");
AssignCommand(oLester, ActionJumpToLocation(lLester));
// make PC invisble, move-through and send him to the portal
effect eInvisible = EffectVisualEffect(VFX_DUR_CUTSCENE_INVISIBILITY);
effect eGhost = EffectCutsceneGhost();
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eInvisible, oPC, fLength);
ApplyEffectToObject(DURATION_TYPE_TEMPORARY, eGhost, oPC, fLength);
AssignCommand(oPC, ActionJumpToLocation(lPortalOPC));
// camera movement
GestaltCameraFacing (0.0,
90.0, 10.0, 100.0,
oPC);
// animate portal
GestaltCreate(2.0, oWaypointPortal, OBJECT_TYPE_PLACEABLE, "plc_portal", "oPortal");
effect ePortal = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_3);
GestaltApplyLocationEffect(2.0, lPortal, ePortal, INSTANT);
//DelayCommand(2.0, SoundObjectPlay(GetObjectByTag("MagicPortalNeutral")));
GestaltSoundObject(2.0, GetObjectByTag("MagicPortalNeutral"));
// spawn and move magistra
GestaltCreate(2.0, oWaypointPortal, OBJECT_TYPE_CREATURE, "magistra", "oMagistra");
effect eMagistra = EffectVisualEffect(VFX_DUR_PROT_PREMONITION);
GestaltApplyEffect(2.1, OBJECT_INVALID, eMagistra, PERMANENT, 0.0, "oMagistra");
GestaltTagActionMove(2.1, "oMagistra", oWaypointMagistra);
GestaltTagActionFace(3.0, "oMagistra", 270.0);
// zoom camera on Magistra
GestaltCameraMove (4.0,
90.0, 10.0, 100.0,
90.0, 6.0, 150.0,
19.0, 20.0, oPC);
// Magistra monologue
GestaltTagActionSpeak(4.0, "oMagistra", "<cdے>Greetings, prisoners! I am Magistra Aelia, a Pit Master.</c>", NORMAL, 5.0);
GestaltTagActionSpeak(9.0, "oMagistra", "<cdے>I will invoke you sequentially and you will join me at once.</c>", NORMAL, 5.0);
GestaltTagActionSpeak(14.0, "oMagistra", "<cdے>If you do not behave as expected, you will be killed.</c>", NORMAL, 5.0);
GestaltTagActionSpeak(19.0, "oMagistra", "<cdے>Prisoner Number 1374, " + GetName(oPC) + ". Come over here swiftly.</c>", ANIMATION_FIREFORGET_READ, 0.0, 0.5);
GestaltActionOpen(33.0, GetObjectByTag("CellDoor"), GetObjectByTag("CellDoor"));
// Turn Camera on oPC
GestaltCameraMove (25.0,
90.0, 8.0, 150.0,
270.0, 1.0, 80.0,
10.0, 50.0, oPC);
// move invisible oPC to simulate camera movement
GestaltActionMove(25.0, oPC, oWaypointPCMove, FALSE, 0.0, 10.0);
// Lester speaks to oPCand the cell door opens
GestaltActionFace(35.0, oLester, 0.0, 2, oNew);
GestaltActionFace(35.0, oNew, 0.0, 2, oLester);
GestaltActionSpeak(35.0, oLester, "<cŒŒŒ>Hey, you are the first one! That's gotta be lucky!</c>", ANIMATION_FIREFORGET_VICTORY2, 0.0);
// final steps
GestaltActionMove(38.0, oNew, oWaypointPCMove);
GestaltCameraFade(43.0, oPC, FADE_CROSS, FADE_SPEED_SLOW, 1.9);
GestaltDestroy(44.0, oNew);
GestaltActionFace(44.0, oPC, 90.0);
GestaltExecuteScript(44.9, oPC, "_tp_1p_pheartbea");
// close cutscene
GestaltStopCutscene(fLength, oPC);
}
Abort script
#include "in_g_cutscene"
void main()
{
// determine which cutscene is to be cancelled
object oPC = GetLastPCToCancelCutscene();
string sName = GetLocalString(oPC, "cutscene");
if (sName == "cutscene_prison")
{
// cancel prison cutscene
GestaltCameraFade(0.0, oPC, FADE_IN, FADE_SPEED_FASTEST);
GestaltSetSpeed(0.0, oPC, 0.1, 100.0, 0);
GestaltSetSpeed(0.1, oPC, 0.0, 4.0, 0);
DeleteLocalString(GetModule(),"cutscene");
GestaltStopCutscene(0.2, oPC, "tp_1p_portal_opc_move");
AssignCommand(GetObjectByTag("Lester"), ActionJumpToLocation(GetLocation(GetWaypointByTag("tp_1p_lester"))));
DestroyObject(GetObjectByTag("oPortal"));
CreateObject(OBJECT_TYPE_PLACEABLE, "plc_portal", GetLocation(GetWaypointByTag("tp_1p_portal")), FALSE, "oPortal");
SoundObjectPlay(GetObjectByTag("MagicPortalNeutral"));
DestroyObject(GetObjectByTag("oMagistra"));
CreateObject(OBJECT_TYPE_CREATURE, "magistra", GetLocation(GetWaypointByTag("tp_1p_magistra")), FALSE, "oMagistra");
effect eMagistra = EffectVisualEffect(VFX_DUR_PROT_PREMONITION);
ApplyEffectToObject(DURATION_TYPE_PERMANENT, eMagistra, GetObjectByTag("oMagistra"));
AssignCommand(GetObjectByTag("CellDoor"), ActionOpenDoor(GetObjectByTag("CellDoor")));
DestroyObject(GetObjectByTag("PC_Copy"));
//AssignCommand(oPC, ClearAllActions());
//AssignCommand(oPC, ActionJumpToLocation(GetLocation(GetWaypointByTag("tp_1p_portal_opc_move"))));
ExecuteScript("_tp_1p_pheartbea", oPC);
}
// continue with further "else if" for any skiappable cutscene. Final else follows.
else
{
// this should never happen
SendMessageToPC(oPC, "You cannot cancel this cutscene(?)");
}
}