I have a particular scene where I teleport the player and an NPC to a certain place. Then there’s a generic trigger where if the PC enters the NPC moves towards the PC and starts a conversation by ActionStartConversation. This conversation also has a delay of 1.5 seconds before starting. I also enter cutscene mode to prevent the player from interrupting the conversation. Now, here’s the thing. I have the X2_L_SPAWN_USE_AMBIENT variable on this NPC which makes it walk around randomly, and I want to keep that. I’ve noticed that on a few occations the conversation won’t start, and my guess is that’s because of the ambient animation. I do a clearallactions first on the NPC, but I think that when moving towards the PC it is perhaps interrupted by that ambient animation.
So, my question is, is there some way to keep the ambient stuff and still be certain that the conversation starts? My own thoughts are to just switch to CreateIpSpeaker, but I would rather avoid that (even though I’ve used that way countless times before) and use ActionStartConversation instead.
Not sure that will have any effect, since it’s used upon spawning the NPC.
I’d add a slight delay for the second AssignCommand, to make sure it’s fired after the ClearAllActions() line.
Indirectly, that may be the case if the NPC wanders too far away from the PC when the conversation is supposed to start (remember the cutscenes in ASW3, which didn’t start because the companion was too far behind the main PC?)
The weird thing about this, is that the NPC is not spawned into the world/area. The NPC already exists in the area and it still works (but maybe I’ve misunderstood the spawn thing here). I’ve just found that this excellent variable make the NPCs wander around a bit aimlessly.
Wasn’t that ASW2 or ASW1? Nevermind… The PC and the NPC are quite close to one another when this scene starts so I really wonder if that’s the case here. It’s hard to test this too since most of the times it works. That’s why I’m thinking of switching to CreateIpSpeaker here again, since that thing fires over and over 'till the conversation starts but…well, I don’t know.
Good idea to do a slight delay for the second AssignCommand.
To me, if I remember correctly, the time it failed it looked like the NPC was moving towards the PC but was interrupted and then wandered off, but I’m certainly not sure about this.
So, a script like this then?
void main()
{
object oPC1 = GetEnteringObject();
object oPC = GetFirstPC();
if (!GetIsPC(oPC1)) return;
if (!GetLocalInt(OBJECT_SELF, "Done") && GetGlobalInt("global1"))
{
object oNPC = GetObjectByTag("commio9");
SetLocalInt(OBJECT_SELF, "Done", TRUE);
SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0);
AssignCommand(oPC, ClearAllActions());
AssignCommand(oNPC , ClearAllActions());
DelayCommand(0.1,AssignCommand(oNPC , ActionForceMoveToObject(oPC)));
SetCutsceneMode(oPC); // this needs to be cleared by the dialog's End/Abort dialog handlers.
DelayCommand(1.5,AssignCommand(oNPC, ActionStartConversation(oPC, "c_io_npc", FALSE, FALSE, TRUE, FALSE)));
}
}
I’m not sure why I did it like that. It’s just my way of being over-meticulous maybe. In this case I could have used just oPC for GetEnteringObject since the companions are elsewhere.
Ok…I’m not sure how I would implement that here though. I’ll wait 'till you get back to your computer, maybe you can explain to me better. I find X2_L_SPAWN_USE_AMBIENT to be a really easy way to do this random walk thing.
If you make the conversation owned by another npc not involved in the conversation then whatever the others are doing doesn’t matter.
Here’s the no new script solution… If there’s nobody else there then spawn in a tiny invisible kobold ( I am serious and have done this ) that owns the conversation on a standard speak trigger and gets deleted when it’s over. So long as you set the speaker and listener on the conversation it wont zoom in on the kobold.
In places that have lots of npc’s you can use the ga_create object script in the conversations to bring in a random npc to the area and have them owning a conversation on a generic speak trigger that fires when the PC hits it because now the owner has arrived.
eg. You’re in a house, kill the baddie after a quick chat that spawns a peasant outside, now when you leave you’ll hit the trigger ( owned by the peasant ) and it will work because the peasant’s there so you can have a chat about how horrible the baddie was. This gets rid of the need for complicated on enter scripts that fire at different points in the story. So long as it’s a cutscene the peasant can be on the other side of the town, it doesn’t matter.
ps. standard encounter spawns make things roam around randomly too and can be good for chickens etc. in villages.
I’m not sure about that in this case. I want this particular NPC to move towards the PC and then begin the conversation. If the NPC is interrupted by whatever I’m not sure that them not being the owner of the conversation matters, but I don’t know. I have lots of NPCs in this outdoors area, so sure, I could do that, but I’m not 100 % sure that the will run when then NPC is interrupted, but as I said, I don’t know. Do anybody else have thoughts about doing it like this?
Yes, I remember you speaking about the micro-kobolds before, lol.
If you stop this action, you can restart a random walk action for the NPC by using an AssignCommand for the NPC starting this “action”.
i.e. Use the ClearAllActions for both the NPC and PC to ensure your conversation starts as you say. This will also stop the NPC random walk you started via the spawn code you mention. So, all you need to do after the conversation has finished is to ensure you start their random walk on the last node … or even using the end conversation script.
If the conversation owner is the NPC, then simply using ActionRandomWalk(); will likely be all you need. If, however, you need to redirect to the NPC due to the conversation owner being somebody else, then just Get the NPC using their tag and assign this ActionRandomWalk to them:
AssignCommand(oNPC, ActionRandomWalk());
You know how to do this …
The ActionRandomWalk() function does exactly this. Just assign it to the target you wish to do it as normal.
Ok, but what I want to know is: by using SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0); as I did in my script, will that actually stop the X2_L_SPAWN_USE_AMBIENT? 4760 wasn’t sure that it would.
In this particular case I don’t actually think I will need to restart this random walk, but I do need it to be on the character before this scene happens so again, will SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0); stop the ambient walk?
If you see my second post here I specifically asked if my script would work, and 4760 wasn’t sure, and you didn’t answer that question, just said that ActionRandomWalk would do what I needed.
Edit: Or do you mean that ClearAllActions actually do that? Because I’m not convinced of that. Actually I’m pretty sure it doesn’t since if that were the case I wouldn’t have this problem.
Edit2: To be even more clear, here’s how it looks in the toolset:
Basically, spawn integers (which this is) are normally used when the creature first enters the world. Therefore, they are (as far as I was aware) only really referenced this once. I did a search and was unable to find their “usage” anywhere else. So, assuming they are only referenced from their spawn script, I imagine that changing them after spawning is unlikely to have any bearing thereafter.
So, you need a different way to restart what X2_L_SPAWN_USE_AMBIENT did when they spawned. i.e. As far as I am aware just resetting it will do nothing (unless it is referenced somehow from their heartbeat script), which only testing such will prove. (I imagine you test this already and found it not to work.)
So, my suggestion was that to restart this random walk action that you achieved with the spawn integer was to simply restart it via the action command instead. Without testing, I suppose it may even be possible that the action sets the same integer and somehow “restarts” them like the spawn integer does all in one function. That’s purely speculation on my part and highly unlikely I guess, but my point is, I don’t think it matters, as the same result is achieved.
To be more specific, I don’t think you will have any bearing on the integer at all. As far as I am aware it serves a one off spawn action, That’s it. Thereafter, if you need to do more with the NPC, it’s back to script and actions - the X2_L_SPAWN_USE_AMBIENT integer (no matter what state) is (as far as I am aware) now irrelevant.
By all means test deleting it and resetting it, but I don’t think it will serve any purpose moving forward as the spawn event is over and done with - unless you destroy the NPC and respawn them elsewhere.
Come back to me if I have not answered something more specific for you.
EDIT: Another way of saying it, is that the variable is only ever checked when the creature SPAWNS (part of the integer name). It is NEVER (as far as I can see) referenced ever again.
As I say in my last post , you can as far (as I am aware) correctly stop this walk with CearAllActions, which you used in your script if I recall correctly.
What do you mean by “but I do need it to be on the character before this scene happens so again”? Are you saying you are using the same integer to check for something else other than have them start a random walk?
EDIT: OK, I delved deeper and notice this sets a bitwise integer on the creature a spawn time using NW_FLAG_AMBIENT_ANIMATIONS. (0x00080000)
I am unfamiliar with what animations these are exactly, but from what you described earlier, it sounded like a random walk, Now, I am not so certain. However, I do not understand why you cannot simply call the ClearAllActions on the creature in question to stop any actions (including any animations start with this integer set at spawn time).
And as for clearing it down (or reinstating), I still do not think that would have any bearing (unless it is picked up again in a heartbeat script).
And if the effect is one of random walk only (rather than a selection of animations), then ActionRandomWalk would start that again, although I hear you say that was not required.
What happens when you try this?
void main()
{
object oPC1 = GetEnteringObject();
object oPC = GetFirstPC();
if (!GetIsPC(oPC1)) return;
if (!GetLocalInt(OBJECT_SELF, "Done") && GetGlobalInt("global1"))
{
object oNPC = GetObjectByTag("commio9");
SetLocalInt(OBJECT_SELF, "Done", TRUE);
//SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0);
AssignCommand(oPC, ClearAllActions());
AssignCommand(oNPC , ClearAllActions());
//DelayCommand(0.1,AssignCommand(oNPC , ActionForceMoveToObject(oPC)));
//SetCutsceneMode(oPC); // this needs to be cleared by the dialog's End/Abort dialog handlers.
AssignCommand(oNPC, ActionStartConversation(oPC, "c_io_npc", FALSE, FALSE, TRUE, FALSE));
}
}
Also, I think you would be better moving some of the movements of the NPC into the first nodes of the conversation. i.e. Consider commenting out the force movement line in this script and having the NPC move on the opening conversation node. EDIT: In fact I commented that line out anyway and recommend adding it to the first node in the conversation now.
Hmmm, ok. Maybe move the character after the conversation starts then.
So, SetLocalInt(oNPC,"X2_L_SPAWN_USE_AMBIENT",0); probably does nothing then.
So, if what you say is true, then the ambient walk thing shouldn’t have anything to do with this since I used ClearAllActions. But I am fairly certain, even though it is very hard to prove this, since I would have to test countless times to get the error about the not starting of the conversation again, that the character was indeed doing some random walking when the conversation didn’t start and cutscene mode was on, and thus you couldn’t do anything in the game. Since Cutscene mode was initiated, ClearAllAction must have run, and if that was the case, that proves that ClearAllActions didn’t work taking away the X2_L_SPAWN_USE_AMBIENT.
So we’re back at square one. Maybe doing the moving thing on the first node would solve things, even if I really don’t want to do that in this case. I want the walk to happen before the dialogue begins.
I think I’ll just do some testing and hope that the error occurs again so that I can study it in detail.
But I’ll do that later tonight. I don’t feel like going on a testing spree at the moment. We’ll see what happens. Maybe I’ll just do what I always did before, doing the CreateIpSpeaker thing. That way I know everthing will work. Sigh
@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.
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):
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 :)
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?
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.
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.
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! )
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’ …