NPC in a cutscene turns to the side when speaking with the PC

Hey guys.

So as the title said, I’m having some trouble with a cutscene where an NPC that’s supposed to address the player character, sharply turns to the right (the player’s right, the NPC’s left) without having any commands to do so.

Furthermore, my attempted workaround of adding a command that forces the NPC to turn back to the player - didn’t work. The NPC turned to face the player, but then turned sideways again on the next line of dialog.

What’s even more confusing is the fact that this is new. That scene worked perfectly fine before, and I didn’t change anything in it. Is it possible that some of the new updates to NWN:EE have created some contradiction?

If anyone has any ideas on what could be causing the problem and how to fix it, I’d be very grateful.

I’m not sure since I only work with NWN2, but could it be that the dialogue is owned by another object/character than the NPC? In that case I think you need to script it so that the NPC looks at the PC on every node. At least that’s the way it’s handled in NWN2.

Post your script so we can see what’s going on. Try to limit it to the problematic situation allowing easier testing of it outside your module’s environment.

You can also upload screenshot(s) showing expected and incorrect behavior.

You’re right, my bad.

Gotta give credit where it’s due: It’s not ‘my’ script per se. This is from Steve B’s Blackguard series, which I’m still in the process of remastering. I’m hoping to release it soon, but this is pretty immersion-breaking. I’ve tested this same cutscene with the original, unedited mod, and the same bug still happens.

Anyway, the full script is below, and I’ve also provided screenshots.

The problem starts at this line:

GestaltTagSpeak(24.0, “NatureSpirit”, “Again I warn you: the dark side of nature shall never…”, ANIMATION_LOOPING_TALK_NORMAL, 3.0);

Which is her second line. That’s when she suddenly turns sideways. However, this also happens in every subsequent line of hers (as tested by adding a command to look directly at the PC after the second line). The first line works fine, possibly because she’s still moving when it starts.

Thanks in advance!

//:: Run the nature warning 2

// Gestalt cutscene scripts Lib
#include “lib_cutscene”

void main()
    object oPC = GetEnteringObject();
    if (GetIsPC(oPC) == FALSE)  return;

    object Loc1 = GetObjectByTag("WP_SpriteSpawn");
    object Loc2 = GetObjectByTag("WP_Sprite_01");
    object Loc3 = GetObjectByTag("WP_NatureSpirit_01");
    object Loc4 = GetObjectByTag("WP_Sprite2_01");

    effect ePouf = EffectVisualEffect(VFX_FNF_SUMMON_MONSTER_1);
    effect eHoly1 = EffectVisualEffect(VFX_IMP_HOLY_AID);
    effect eHoly2 = EffectVisualEffect(VFX_IMP_HEALING_X);


    GestaltStartCutscene(oPC, "Nature's Warning 2", TRUE, TRUE, TRUE, TRUE, FALSE);
    GestaltCameraFade(0.0, oPC, FADE_IN, FADE_SPEED_MEDIUM, 2.0, 0);
    DelayCommand(1.0, AssignCommand(oPC, SetCameraFacing(90.0, 6.0, 62.0, CAMERA_TRANSITION_TYPE_SNAP)));

// Step 1 - Sprite
    GestaltCreate(5.0, Loc1, OBJECT_TYPE_CREATURE,"sprite");

    // Step 2 - Pouf
    DelayCommand(15.0, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, ePouf, GetLocation(Loc2)));
    GestaltDestroy(15.0, oPC, "Sprite");
    GestaltCreate(15.0, Loc2, OBJECT_TYPE_CREATURE,"nymph001","",FALSE,0,FALSE,"");

    GestaltSpeak(13.0, oPC, "Oh, no. Not that winged bitch again...", ANIMATION_LOOPING_TALK_NORMAL, 3.0);

    // Step 3 - Warning
    GestaltTagSpeak(20.0, "NatureSpirit", "We meet once more, dark one.", ANIMATION_LOOPING_TALK_NORMAL, 3.0);
    GestaltTagSpeak(24.0, "NatureSpirit", "Again I warn you: the dark side of nature shall never...", ANIMATION_LOOPING_TALK_NORMAL, 3.0);
    GestaltTagSpeak(28.0, "NatureSpirit", "... rule over this land.", ANIMATION_LOOPING_TALK_NORMAL, 3.0);
    GestaltTagSpeak(32.0, "NatureSpirit", "Return from whence you came.", ANIMATION_LOOPING_TALK_NORMAL, 3.0);

    GestaltSpeak(36.0, oPC, "Arrgh!", ANIMATION_LOOPING_TALK_FORCEFUL, 2.0);
    GestaltSpeak(39.0, oPC, "Shut...", ANIMATION_LOOPING_TALK_FORCEFUL, 2.0);
    GestaltSpeak(42.0, oPC, "... UP!!", ANIMATION_LOOPING_TALK_FORCEFUL, 2.0);
    GestaltSpeak(45.0, oPC, "Enough of your warnings, you fool sprite!", ANIMATION_LOOPING_TALK_FORCEFUL, 3.0);
    GestaltSpeak(49.0, oPC, "Last time, they weren't enough to save your friends!", ANIMATION_LOOPING_TALK_FORCEFUL, 3.0);
    GestaltSpeak(53.0, oPC, "And they won't be this time, either!", ANIMATION_LOOPING_TALK_FORCEFUL, 3.0);
    GestaltSpeak(57.0, oPC, "NOW BEGONE!!", ANIMATION_LOOPING_TALK_FORCEFUL, 3.0);

    GestaltTagSpeak(65.0, "NatureSpirit", "Fine.", ANIMATION_LOOPING_TALK_NORMAL, 3.0);

    GestaltTagSpeak(69.0, "NatureSpirit", "O', Mielikki... your servants are in need of your aid.", ANIMATION_LOOPING_MEDITATE, 4.0);
    GestaltTagSpeak(73.0, "NatureSpirit", "Help me protect your land from Talos's will!", ANIMATION_LOOPING_TALK_PLEADING, 3.0);
    GestaltTagSpeak(77.0, "NatureSpirit", "Grant me your power!!", ANIMATION_FIREFORGET_VICTORY2, 3.0);
    DelayCommand(78.5, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eHoly2, GetLocation(Loc3)));
    DelayCommand(79.0, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, eHoly1, GetLocation(Loc3)));

    GestaltTagSpeak(85.0, "NatureSpirit", "[Infused with Mielikki's power, she levels a piercing glare at you] I'll be waiting, dark one.", ANIMATION_LOOPING_TALK_NORMAL, 2.0);

    // Step 4 - Goes away
    DelayCommand(92.0, ApplyEffectAtLocation(DURATION_TYPE_INSTANT, ePouf, GetLocation(Loc3)));
    GestaltDestroy(92.0, oPC, "NatureSpirit");
    GestaltCreate(92.0, Loc3, OBJECT_TYPE_CREATURE,"sprite", "sprite2");
    GestaltTagActionMove(92.1, "sprite2", Loc4);

    DelayCommand(95.0, DestroyObject(GetObjectByTag("sprite2")));

    GestaltCameraFade(94.9, oPC, FADE_OUT, FADE_SPEED_MEDIUM, 0.0);
    GestaltStopCutscene(95.0, oPC, "", TRUE, TRUE, TRUE, TRUE, TRUE, FALSE);
    DelayCommand(95.0, FadeFromBlack(oPC));

    // enleve le trigger
    DelayCommand(96.1, DestroyObject(OBJECT_SELF));

If you can, try to put the whole script inside preformatted text so we can read the script easier. (Or use ```C in the beginning of the script and ``` at the end ) Also, I think you need to put the include script here as well, as I imagine the GestaltTagSpeak function is explained how it’s set up there.

EDIT: What I meant with the include script is the “lib_cutscene” script if you can find it.

Sure thing.

I edited the script to have formatting like you asked.

lib_cutscene seems to be a default script that comes with the toolset, not a custom script that Steve B created for this mod. Did a little searching, and it does appear that each cutscene in the mod starts with #include “lib_cutscene”

At any rate, the full lib_cutscene script is extremely large. So much so, that it can’t fit in a single post: Body is limited to 32000 characters; you entered 124976.

Would you like me to split it into 5 parts and post them?

Maybe see if you can find the GestaltTagSpeak function and write what it says about that. It has to do something with how the NPC is facing like SetFacingPoint or something.

From what I can see it seems like there’s no actual dialogue tree for this, just this script, right? I mean, all the dialogue lines are there and since they are run with the GestaltTagSpeak, the bug (this is my guess) ought to be found somewhere there…

Here’s the function description for GestaltTagSpeak:

// Tell the selected actor to speak a line
    // fDelay           how many seconds to wait before speaking line
    // sActor           the tag of the character you want to speak the line - MAKE SURE THIS IS UNIQUE!
    // sLine            the line you want them to speak
    // iAnim            the animation you want them to play whilst speaking the line (leave as ANIMATION_NONE for no animation)
    // fDuration        how long the animation should last (leave at 0.0 for fire-and-forget animations)
    // fSpeed           the speed of the animation (defaults to 1.0)
void GestaltTagSpeak(float fDelay, string sActor, string sLine, int iAnimation = ANIMATION_NONE, float fDuration = 0.0, float fSpeed = 1.0)

Ok, that’s weird. Nowhere is it telling the actor where to look…

Since I work only in NWN2, this is just a guess, but maybe you could do something like this before the spoken lines start (before Step 3 or something - I hope these functions exist in NWN1):

object oSpeaker = GetObjectByTag("NatureSpirit");
vector vTarget = GetPosition(oPC);
AssignCommand(oSpeaker, ClearAllActions(TRUE));
AssignCommand(oSpeaker, SetFacingPoint(vTarget));

EDIT: Removed the “1” after vTarget in SetFacingPoint.

Sure, let’s give it a shot. I’ll let you know how it goes :smiley:

Well, it wouldn’t compile the script. Got an error saying this: ERROR: DECLARATION DOES NOT MATCH PARAMETERS

The problem seems to be with the last line.

Ah, darn it. Then you don’t have the SetFacingPoint function, I guess. Could you search in your toolset for “Facing” or something like that. There ought to be a similar function but with another name.

EDIT: Ah, just as you said… I now fired up the NWN Toolset (I only have an old install of the NWN Deluxe Edition, don’t have the EE). I still have it on my computer even if I hardly ever use it or play NWN1 anymore, and you do have SetFacingPoint so…eh…I don’t get it.

The plot thickens: apparently NWN 1 does have the SetFacingPoint function. Its description in the toolset is as follows:

// Cause the caller to face vTarget
void SetFacingPoint(vector vTarget)

So it might just be the vTarget or the 1 being the problem, though I wouldn’t know why.

Yes, “1” is the problem. There is not supposed to be a “1” or TRUE in your version of SetFacingPoint apparently, only in NWN2 (or at least it was like that in one of my scripts I had for one of my own modules). Try without the “1” in your script then.

It successfully compiled the script, and upon testing, your addition nearly worked. She said the second line, turned away as usual, then turned back to face the PC again… and then turned away again and stayed like that.

I think I’m gonna try to switch to an earlier version of NWN:EE and see if it makes a difference. That’ll at least let us know if one of the latest updates caused the problem.

Hmmm, ok. Yeah, ok, though I suspect there’s something else going on, but I don’t know…I’m wondering if it has to do with the animation perhaps? What happens if you use ANIMATION_NONE on her lines with GestaltTagSpeak?

EDIT: With GestaltTagSpeak, my guess is that the function is in its entirety further down in the include script? Could that be the case? If so, what does it say there?

EDIT2: I don’t have the lib_cutscene in my NWN toolset, so my guess is this is a NWN:EE thing.

I’ll check in a bit, but first: lo and behold, reverting to version 8193.27 (the latest version is 8193.30) has indeed solved the problem.

I’ll need to check .28 and .29 as well, to see exactly where the problem begins. Then I’ll consult the patch notes for that particular version and see if any changes affected these particular scripts.

Also, my bad about lib_cutscene. Apparently it is a Blackguard module script. Found it on the list. If you’re interested in checking it out, you’ll need to download the module called “Blackguard II - The Innocents’ Blood”


I searched and found Gestalt Cutscene Scripting System on the vault. Apparently this scripting system should work with NWN2 too. I might download and check it out:

Okay, so I tracked the problem down to patch 8193.29. That’s where the bug starts happening.

I went through the patch notes, but unfortunately, I can’t see anything that would create this problem. That said, it’s highly likely that the information is there and I just failed to spot it, since I couldn’t script my way out of a paper bag (otherwise we wouldn’t be here right now ^^)

I hate to ask this, but could you go over the patch notes (below) and see if anything seems likely to be the root of the problem? The new script commands at the bottom is probably the most relevant bit, but I figured I should post everything to be thorough.

Game, Logic and Rules

  • Shields: Now take their AC from the BaseAC column, no longer hardcoded. Still only works for the three base shield types!

  • Game: Reverted the MoveToObject fRange change; fRange is now once again ignored for that specific breakage. This fixes various issues in custom content modules, notably the elusive wizard in Aielund III Pt 3.

  • Game/baseitems: New column IsMonkWeapon replaces all hardcoded checks against BASETYPE_KAMA.

  • Game/baseitems: Custom content can now make Flurry Of Blows also work with ranged weapons (when IsMonkWeapon is enabled).

  • Game/baseitems: New column WeaponFinesseMinimumCreatureSize. Takes 0/**** or the minimum creature size type needed to finesse.

  • Game: Other stealthed/concealed creatures no longer pop up briefly when transitioning into a new area or into a doored-off room.

  • Game: Fixed creatures doing a weird shimmy/turny when coming to a stop.

  • Game data: Added “Neverwinter Chess” module from old Bioware demo modules.


  • Game/Config: You can now hide player quickchat lines from the chat window (Config->UI->Feedback)

  • Config: Exposing codepage setting under Game->Advanced; this should make it easier for Russian language players to set the correct codepage without having to edit settings.tml.

  • Config: New option to enable SQLite API tracing to the server log: Show all queries, or just show queries running longer than N msec.


  • Core: Upgraded static openssl to 1.1.1k.

  • Core: Upgraded libyuv to latest master upstream.

  • Core: Upgraded SQLite to version 3.36.0.

  • Renderer: VDO optimisations in preparation for OpenGL 3.3.

  • Renderer: The fallback envmap is now a globally shared object, not tied to the scene.

  • Renderer: Fixed most visual oddities related to water refraction (fringing, edge gaps, highlight strangeness).

  • Renderer: Fixed a regression that prevented custom shader params being set in scripting.

  • Renderer: Improved TSB generation, doing away with some visual glitches when adjacent faces had opposing hardness.

  • Renderer: Fixed a crash when setting hilite color of party members clients haven’t seen yet.

  • Renderer: Fixed a crash when setting a texture override for a creature object that the client hasn’t seen yet.

  • Nk/Nui: Image renderer can now render webm.

  • Nk/Nui: Fix key repeat not working.

  • Nk/Nui/json: NWSync Repository CExoLocStr fields now convert to the local game encoding; should fix various special characters. This means you now need to have the json once again properly encoded as UTF-8. (e.g. module descriptions in Polish)

  • Nk/Nui: Copy/Paste now works with Cmd on Mac, instead of Ctrl.

  • Game Launcher: Repositories can now advertise websites (such as the Vault on the Curated repo).

  • Game Launcher: Modules can now have a list of web links.

  • Game Launcher: Repository Info now shows the origin URL.

  • NWSync Management UI: Text clarity improvements to the disk space effect of deleting manifests.

  • NWSync UI: Sped up manifest loading. Game Launcher and Storage UI should now appear much faster.

  • NWSync UI: Fixed the NWSync housekeeping UI no longer popping up when deleting multiple manifests in a row.

  • NWSync UI: NWSync Storage UI now remembers selected checkboxes and sorting.

  • NWSync: Game no longer crashes when running out of disk space while downloading manifest, shows error instead.

  • NWSync/SQLite: We now use soft limits on shard size, not hard limits. This fixes a very rare “out of disk” error when trying to write metadata even though plenty of free space is available.

  • VM: Fixed a crash in reconciling automap data when trying to load a player TURD. #309

  • VM: Fixed CopyObject not triggering OnAreaEnter (fixes HotU Beholder caves anti-magic restore)

  • VM: Fixed SetCampaignString(, “”) resulting in a SQL error. Now it deletes the variable from the database instead. It now also deletes entries when other types are “empty”.

  • VM: GetCurrentlyRunningEvent now has param bInheritParent for cases such as CreatureOnDeath/Damaged returning the wrong event ID otherwise.

  • VM: Fixed Area Of Effect sometimes not returning the expected objects due to local object positions in memory not updating correctly. #52

  • VM: SQLite: soundex() now available to scripting. Built-In Scalar SQL Functions

  • VM: SQLite: Math functions now available to scripting. Built-In Mathematical SQL Functions

  • VM: Cassowary: Fixed a crash when calling AddConstraint with invalid math formula. #293

  • VM: We had to change how Create/Copy/DestroyArea works. Most prominently:

  • Areas you create via Create/Copy now have their own auto-generated resref in the nw_ namespace; they no longer have the same resref as the originating area/template. You cannot influence this. nw_ is also the same namespace that the toolset reserves for game-builtin content, so you shouldn’t be able to clobber into it. Instanced areas go into CURRENTGAME:, until they are saved into a .sav. (This also means you can CreateArea with the new template, if you really wanted, though why would you? You’d get the area as it looked like at original instantiation time)

  • Areas are now saved to savegames correctly and savegames no longer corrupt when the area count changes.

  • Savegames now also store a serialised image of the .are data.

  • This means that CreateArea(“originalresref”) in savegames will now instance the area as saved in the savegame, NOT as designed in the toolset. This is because modules don’t actually load anymore after a savegame; the savegame replaces the original module.

  • CopyArea now supports assigning a new tag and name directly on instantiation.

  • This is a high-risk change, as it touches significant parts of the area load codepath. We’d appreciate thorough testing.

  • Movies: We no longer show the Bioware credits for all modules; only the last module in each main campaign.

  • Movies: Fixed error/crash when calling EndGame(“”). Now the game shows no movie instead, and just quits the main menu.

New Script Commands

// Create a RunScript effect.
// Notes: When applied as instant effect, only sOnAppliedScript will fire.
//        In the scripts, OBJECT_SELF will be the object the effect is applied to.
// * sOnAppliedScript: An optional script to execute when the effect is applied.
// * sOnRemovedScript: An optional script to execute when the effect is removed.
// * sOnIntervalScript: An optional script to execute every fInterval seconds.
// * fInterval: The interval in seconds, must be >0.0f if an interval script is set.
//              Very low values may have an adverse effect on performance.
// * sData: An optional string of data saved in the effect, retrievable with GetEffectString() at index 0.
effect EffectRunScript(string sOnAppliedScript = "", string sOnRemovedScript = "", string sOnIntervalScript = "", float fInterval = 0.0f, string sData = "");

// Get the effect that last triggered an EffectRunScript() script.
// Note: This can be used to get creator or tag, among others, of the EffectRunScript() in one of its scripts.
// * Returns an effect of type EFFECT_TYPE_INVALIDEFFECT when called outside of an EffectRunScript() script.
effect GetLastRunScriptEffect();

// Get the script type (RUNSCRIPT_EFFECT_SCRIPT_TYPE_*) of the last triggered EffectRunScript() script.
// * Returns 0 when called outside of an EffectRunScript() script.
int GetLastRunScriptEffectScriptType();

// Hides the effect icon of eEffect and of all effects currently linked to it.
effect HideEffectIcon(effect eEffect);

// Create an Icon effect.
// * nIconID: The effect icon (EFFECT_ICON_*) to display.
//            Using the icon for Poison/Disease will also color the health bar green/brown, useful to simulate custom poisons/diseases.
// Returns an effect of type EFFECT_TYPE_INVALIDEFFECT when nIconID is < 1 or > 255.
effect EffectIcon(int nIconID);