Question about conversation trigger

@4760 Could it have been that ActionForceMoveToObject actually overrode ClearAllActions? Because if that’s the case then that would explain the ClearAllActions not working and taking away X2_L_SPAWN_USE_AMBIENT.

Alright, I did some testing anyway. I couldn’t replicate the bug with the conversation not happening. However, I did test something else. After running the script (I used the original one without the SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0);and after doing the whole dialogue with the walking and some other animations. The NPC did return to doing the random walking afterwards. It was clear as day. Therefore, I can be certain that the ClearAllActions doesn’t affect that ambient thing.

I will do another test where I do the delay of ActionForceMoveToObject. See if that makes the ambient animation go away afterwards. And then I think I’ll try the SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0); to see if that stops the ambient thing afterwards.
One test at a time…

EDIT: I tested with the delay of ActionForceMoveToObject. The ambient animation still won’t go away, so now we know for certain ClearAllActions doesn’t affect that. I also tested the SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0); and that didn’t take away the ambient walk either. So nothing seems to be able to take away the ambient movement, if it was indeed that and nothing else that made the bug appear with the conversation not firing.
I have been unable to recreate that bug in about 5 tests now so…

Sure, I could remove the SetCutSceneMode. However, if the bug appears again, it will only mean that the PC can move, but since the conversation doesn’t fire he can’t do much else since the SetLocalInt(OBJECT_SELF, "Done", TRUE);. What perhaps would be a solution is if I do the local int in one of the dialogue nodes instead. That way the conversation can keep firing if the PC moves through the trigger again instead of the game freezing up.

1 Like

I think I have found the perfect solution now. At least the perfect solution for me.

I removed SetCutsceneMode. I moved the local int set to the trigger to the PC instead. The local int is now set inside the dialogue. As it is now, the PC can be stupid and run away from the NPC even if he knows something is clearly about to happen (in this case a dialogue, but I know players think very differently, so there will be those for sure that don’t understand that a conversation is about to start). He can’t run away from the area though, since I’ve made a small patch unwalkable so he can’t escape the scene. If the conversation doesn’t fire he can run around in the small area and the trigger will try to trigger the conversation everytime. When it finally does, I set the local int inside the dialogue, and that way this conversation can’t fire again.

This is approximately the way my script looks now (I’ve replaced some strings 'cause I don’t want to spoil anything):

void main()
{

object oPC1 = GetEnteringObject();
object oPC = GetFirstPC();

if (!GetIsPC(oPC1)) return;

if (!GetLocalInt(oPC, "Donewiththis") && GetGlobalInt("global1"))
    {
		
	object oNPC = GetObjectByTag("commio9");
	
	AssignCommand(oPC, ClearAllActions());
	AssignCommand(oNPC , ClearAllActions());
	
	DelayCommand(0.1,AssignCommand(oNPC , ActionForceMoveToObject(oPC)));
			
    DelayCommand(1.5,AssignCommand(oNPC, ActionStartConversation(oPC, "c_io_npc", FALSE, FALSE, TRUE, FALSE)));
	
	}
	
}
1 Like
X2_L_SPAWN_USE_AMBIENT

is a variable ( CREATURE_VAR_USE_SPAWN_AMBIENT in ‘x2_inc_switches’ ) that sets another variable, usually in a default OnSpawn script …

The variable that it sets is NW_FLAG_AMBIENT_ANIMATIONS in ‘x0_i0_spawncond’ which is used by the heartbeat script to trigger ambient animations. It can be turned off and on again …

#include "x0_i0_spawncond"

int b = ; // TRUE (on) or FALSE (off)
SetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS, b);

ie. Flag it off when PC enters the trigger, and back on when the dialog ends.

[ AssignCommand(oCreature, SetSpawnInCondition(...)); <- required when OBJECT_SELF is not oCreature ]

 

A pre-placed instance Critter still runs its OnSpawn … (or at least it should).

 

that line ought have no effect after the critter has spawned – use SetSpawnInCondition() instead.

The heartbeat script uses the flag like so

if (GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS))
{
    PlayMobileAmbientAnimations(); // #include 'nw_i0_generic'
}

 
ps. ActionRandomWalk() is different than having NW_FLAG_AMBIENT_ANIMATIONS set. eg, ClearAllActions() will stop random walk but it won’t stop ambient animations – the latter is checked every heartbeat, but the random walk is a one-time call … and would need to be called again after a ClearAllActions() /eg

 
@andgalf as usual stick with what works but there it is :)

3 Likes

Thank you, @kevL_s! That was a GREAT explanation! Now I finally understand what this actually is. Sometimes I wonder…are you a teacher perhaps IRL? You are really good at explaining things simply.

I guess I could put SetSpawnInCondition in the script. And it’s really good to know should I need to do this at another time. As it is now, I managed to solve it anyway so I don’t think I need it, but I might need it in the future.

Now that you’ve explained it, it actually makes sense. The NPCs doesn’t just walk around, sometimes they greet each other and looks like having an argument with another NPC if both have the X2_L_SPAWN_USE_AMBIENT variable

If the conversation owner is the oCreature, is it then OBJECT_SELF? No, wait, OBJECT_SELF in this instance has to be the trigger, right?

1 Like

@andgalf

Sorry I left you hanging yesterday, but my wife suddenly hit me with a game-breaking issue in my own module and it pulled me away from this post I was checking for you. (You can see the issue she found in the link below.) I was in the process of responding to you when I had to cancel and deal with that instead. Thankfully, KevL found the answer anyway. (It was a heartbeat recall!)

This is different from just a random walk then. :innocent:

I did wonder if that may be the case. I was in the middle of tracking this down when my wife distracted me with a problem in my module. :woozy_face:

@kevL_s

That’s the bit I found, but was unaware that the heartbeat did recall it. :+1:

@andgalf

If you are ever in doubt, you can always grab the NPC via GetObjectByTag just in case. Your script has the oNPC who starts the conversation, but that script is called via the trigger.

Therefore, depending where you call SetSpawnInCondition determines what you use. i.e. If called from the trigger, use AssignCommand. If called from a conversation node script, then you likely do not need to. HOWEVER, I have witnessed the need to redefine the OBJECT_SELF even via a conversation sometimes. (But that may have been in my bad old days! :wink: )

3 Likes

not a teacher. Explaining things helps me understand and memorize them myself – the simpler things are the easier they are to understand and remember …

hopefully it does some good in a broader sense also

Nwn1 set up a bunch of flags (bitwise) that are held in a variable on a/any creature:

NW_GENERIC_MASTER

The flags along with the Master int are defined in ‘x0_i0_spawncond’ … the ai-scripts in Nwn2 typically carry on the tradition …

If you’re wondering where stuff like NPC arguments happen just browse through ‘x0_i0_anims’ and ‘nw_i0_generic’ …

 

:white_check_mark:  :100:

4 Likes

@Lance_Botelle
it appears that this behavior flag is in fact calling

// 'x0_i0_anims'

ClearAllActions();
ActionRandomWalk();

incognito … and with a probability conditioned by its IG circumstances

3 Likes

@kevL_s,

Thanks for tracking that down - That confirms what I was thinking then. :+1:

I was trying to track down these exact specifics when I got distracted. Well done & Thanks!

This is what I was considering to try both ways here …

1 Like

yes but not technically correct. A master-int in Nwn1/2 can hold up to 32 flags (aka 32 bits).

NW_GENERIC_MASTER

is the int that contains these flags:

const int NW_FLAG_SPECIAL_CONVERSATION        = 0x00000001;
const int NW_FLAG_SHOUT_ATTACK_MY_TARGET      = 0x00000002;
const int NW_FLAG_STEALTH                     = 0x00000004;
const int NW_FLAG_SEARCH                      = 0x00000008;
const int NW_FLAG_SET_WARNINGS                = 0x00000010;
const int NW_FLAG_ESCAPE_RETURN               = 0x00000020;
const int NW_FLAG_ESCAPE_LEAVE                = 0x00000040;
const int NW_FLAG_TELEPORT_RETURN             = 0x00000080;
const int NW_FLAG_TELEPORT_LEAVE              = 0x00000100;
const int NW_FLAG_PERCIEVE_EVENT              = 0x00000200;
const int NW_FLAG_ATTACK_EVENT                = 0x00000400;
const int NW_FLAG_DAMAGED_EVENT               = 0x00000800;
const int NW_FLAG_SPELL_CAST_AT_EVENT         = 0x00001000;
const int NW_FLAG_DISTURBED_EVENT             = 0x00002000;
const int NW_FLAG_END_COMBAT_ROUND_EVENT      = 0x00004000;
const int NW_FLAG_ON_DIALOGUE_EVENT           = 0x00008000;
const int NW_FLAG_RESTED_EVENT                = 0x00010000;
const int NW_FLAG_DEATH_EVENT                 = 0x00020000;
const int NW_FLAG_SPECIAL_COMBAT_CONVERSATION = 0x00040000;
const int NW_FLAG_AMBIENT_ANIMATIONS          = 0x00080000; // <----- aha
const int NW_FLAG_HEARTBEAT_EVENT             = 0x00100000;
const int NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS = 0x00200000;
const int NW_FLAG_DAY_NIGHT_POSTING           = 0x00400000;
const int NW_FLAG_AMBIENT_ANIMATIONS_AVIAN    = 0x00800000;
const int NW_FLAG_APPEAR_SPAWN_IN_ANIMATION   = 0x01000000;
const int NW_FLAG_SLEEPING_AT_NIGHT           = 0x02000000;
const int NW_FLAG_FAST_BUFF_ENEMY             = 0x04000000;

It’s a simple matter for a bitwise check to determine if the flag is on or off … and all that data is stored in a single int instead of 27 separate variables

/the beauty of bitwise code  :) :)

4 Likes

@kevL_s,

Yes, my bitwise knowledge is terrible and so take anything I say with a pinch of salt when I try to describe what I mean I am trying to say. :smile:

I noted it involved bitwise stuff and started to dig deeper but had this faction issue with my own module. I am just glad you stepped in as I did not want to have to try to work out the numbers.

By the way, where did you end up finding the association of the NW_FLAG_AMBIENT_ANIMATIONS with the ActionRandomWalk? That’s the part I was looking for.

2 Likes

well… the heartbeat calls PlayMobileAmbientAnimations(); in ‘nw_i0_generic’
which calls PlayMobileAmbientAnimations_NonAvian() in ‘x0_i0_anims’

which calls AnimActionPlayRandomMobile()

which calls ClearAllActions(); ActionRandomWalk();

ofc That’s only one of many possible paths through that code … several others also seem to end up at ActionRandomWalk()

1 Like

@Lance_Botelle Please excuse me for being cheeky here but why not look at two of my TR’s Basics series -

TR’s Basics - Boolean Algebra and
TR’s Basics - Mostly Operators

They should help you understand bitwise operations. @kevL_s should be able to help with any differences there may be with NwN 2 as they were written with NwN 1 in mind.

TR

4 Likes

Thanks for tracing it out for me. :+1: I think I would have got there eventually, but the faction bomb hit me!

The thing about bitwise, is that I would like to be a “bit wiser” with them!

I believe I have looked at those before actually, thanks for the heads up.

It’s only because I rarely have anything to do with bitwise stuff, that by the time I encounter them again, I normally forget what I had done already. :woozy_face:

I think the only difference is that Nwn2 fixed(?) the bitwise NOT(?) operator … there’s an old thread where you guys were hashing things out … and i seem to remember only that anomaly.

in other words, yours or any tutorial on c-bitwise operations ought be good

1 Like

/lol

lowlevel stuff seems ironically complicated, but once it’s understood it’s ironically simpler than our regular scripting …

2 Likes

I’m totally lost with all that you’re talking about now. Never heard about master-int or bitwise before.

Maybe I should have a look at TR’s stuff but I will surely just be totally confused.

I can understand the concept of 27 varialbes in one int but…

I’m afraid to ask, but one thing I’m curious about (and maybe that’s explained in TR’s Basic series) is the 0x00000001 stuff. I thought an integer was just like 1, 2 or 3 etc. What is that x about?

numbers can be represented in several ways, depending on what “base” is used. We usually use base 10 notation: 1 is 1

0x is the standard for representing numbers in hexadecimal notation: base 16

1 is 0x1 … or 0x01 … or 0x0001 … or 0x00000001 … etc. The preceeding zeros don’t matter, apart from the 0x at the start.

base 16 makes it easier to work with bits. Here’s what NW_GENERIC_MASTER looks like with only the NW_FLAG_AMBIENT_ANIMATIONS flag turned on:

in decimal notation : 524288
in binary notation : 10000000000000000000
in hexadecimal : 80000

it takes getting used to, recognizing exponents of 2 as individual bits : 1, 2, 4, 8 … 10, 20, 40, 80 … 100, 200, 400, 800 … etc.

 
The maximum value of an unsigned 32-bit integer : 4,294,967,295
but in hex that’s more simply 0xFFFFFFFF <- all bits on

1 Like

Brain meltdowns pending … :skull:

1 Like

For numbers, etc. see TR’s Basics - Variables, Types and Functions (TBC). You don’t have to read the whole thing, I deal with the number bases in the first 2 pages. The thing is (to quote the HitchHiker’s Guide to the Galaxy) Don’t Panic. Also if in doubt, ASK! Unlike my seldom used alter-ego (the feline assassin) I don’t bite.

TR

2 Likes