Aller au contenu

Photo

Making NPC speak while walking between waypoints.


  • Veuillez vous connecter pour répondre
16 réponses à ce sujet

#1
Zeke

Zeke
  • Members
  • 48 messages

Hi there,

 

I can't seem to figure out how can I make an NPC waypoint walk and say something.

 

I'm trying to use on heartbeat event but it doesn't work flawlessly.

 

I'm attaching the following script onUserDefined:

#include "X0_INC_HENAI"

int nCalledBy = GetUserDefinedEventNumber();
void main()
{
    switch(nCalledBy)
    {
        case 1001: //OnHeartbeat
            ClearAllActions();
            ActionSpeakString("Ah, I'm bored...");
            ClearAllActions();
            DelayCommand(0.5f, WalkWayPoints());
            break;
    }
}

What is the best way that this can be handled?



#2
Baaleos

Baaleos
  • Members
  • 1 330 messages

If you are focusing on a cinematic appearance - then you need to consider using more controllable means of triggering the dialog.

 

Eg:

Simplify your script - and tell the creature to JUST walk between the waypoints.

 

Then, at the positions where you want the creature to speak -  place triggers.

When the creature walks over the trigger : in the onEnter event.

object oCreature = GetEnteringObejct();

 

then tell oCreature to SpeakString - to say the things you want him to say.

 

This way, he will speak exactly at the right position you need.

The trigger script should be configured to fire just for the creature you intend.



#3
Proleric

Proleric
  • Members
  • 2 352 messages

Also, the second ClearAllActions is unwanted, because it cancels the ActionSpeakString.

 

A more subtle issue is that the default OnHeartbeat script calls WalkWayPoints, which will move on if it finds the NPC is no longer walking. It's possible that this will interrupt other actions you assign to the NPC OnUserDefined. The only safe way I know to fix that is to modify the OnHeartbeat script itself, 



#4
meaglyn

meaglyn
  • Members
  • 808 messages

Baaleos's suggestion can work nicely if you are in a limited area and path.  Proleric is spot on about the ClearAllActions.

 

One thing you could try is to let the heartbeat as is handle the walking part. It already does that, Then in your userdefined you could do something like :

case 1001: //OnHeartbeat
            switch(Random(3)) {
            case 0: DelayCommand(1.5, SpeakString("Ah, I'm bored...")); break;
            case 1: DelayCommand(2.5, SpeakString("La la la...")); break;
            case 2: // left this on blank so it does not speak every time 
            }
            break;


#5
Zeke

Zeke
  • Members
  • 48 messages

Hmm, why 

ActionSpeakString("Ah, I'm bored...");

and

AssignAction();

is not working, but

DelayCommand(0.0, SpeakString("Ah, I'm bored..."));

as @meaglyn said, is working?



#6
meaglyn

meaglyn
  • Members
  • 808 messages

Actions go in the action queue, which can get cleared before they execute. ClearAllActions() for example.

 

SpeakString is not an action. It happens immediately regardless of the state of the action queue.

 

The Delays I put there are just to make it not fire exactly on the HB.  It should work without them as well.



#7
Zeke

Zeke
  • Members
  • 48 messages

Thanks! 

 

And how can I target the bored NPC from an outside script, as SpeakString() is getting the owner of the script?

 

I know about AssignCommand() but as was said, it doesn't seem to work when an NPC is following checkpoints.



#8
meaglyn

meaglyn
  • Members
  • 808 messages

AssignCommand(oNPC, SpeakString("foobar"));  from other scripts should work as well as long as you can get the reference to the right object.  That should not conflict with walking waypoints either. Just don't use  AssignCommand(oNPC, ActionDoCommand(SpeakString("foobar"))); Because that will put it on the action queue.   If that's not working post the code you have that's not working. 



#9
LoA_Tristan

LoA_Tristan
  • Members
  • 40 messages

if this just a question of retrieving the object, you could set the bored npc as a local object on the module (so it can be called up from anywhere without a mess of code)

 

the final line in the outside script would be something like

 

AssignCommand(GetLocalObject(GetModule(), "BORED_GUARD"), SpeakString("Ah, I'm bored..."));



#10
Baaleos

Baaleos
  • Members
  • 1 330 messages

The thing to remember here is that npcs seem to be only able to act on a single action at a time.

(Ala  - the Action Queue)

 

While they are 'walking' this is an action.

When you ask them to speak, that is another action - which goes to the bottom of the queue, or at best, behind the current action.

 

ClearAllActions will allow you to clear out the action queue and put something first in line to be acted upon, but it has the side effect of interrupting the walk action.

Eg: They may stop walking momentarily to deliver their SpeakString.

Im not sure  - but I guess with this logic, even my earlier suggestion of using a trigger, would be at the mercy of the Action Queue.

The trigger would at most be able to add to the end or add a new action behind the current action.

ClearActions would be needed for trigger based scripting too.

 

It might be a little too complex for what is needed, but I am reasonably sure that nwnx would be able to get around this issue. (nwnx_chat ?)

Eg: It could send the chat message - using the NPC as the speaking object.

Because it is bypassing Action Queue - it would not require ClearActions

Since your not relying on the Creature actually speaking, instead its the server speaking, just changing the object ID to the creature.

So the creature would in effect be able to talk and walk at the same time.

 

At least, that is my theory 



#11
meaglyn

meaglyn
  • Members
  • 808 messages

SpeakString is not an action. It should not stop them from doing whatever action they are currently working on.  ActionSpeakString is an action and will go in the queue.



#12
LoA_Tristan

LoA_Tristan
  • Members
  • 40 messages

There are some Actions that are masquerading around as instant commands, though.  Like the function PlaySound().  It gets put in the action queue.



#13
meaglyn

meaglyn
  • Members
  • 808 messages

There are some Actions that are masquerading around as instant commands, though.  Like the function PlaySound().  It gets put in the action queue.

 

True, but SpeakString is not one such. 



#14
Zeke

Zeke
  • Members
  • 48 messages

Thanks for the answers. I'm beginning to understand the Action Queue.

 

Is it global or per character? I mean, if I want to issue a command to an NPC to go and talk to the bored one (Bob) like this

AssignCommand(oJohn, ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL));
AssignCommand(oJohn, ActionSpeakString("Hi there!"));

Bob starts talking the moment that John starts walking towards him.

 

Why ANIMATION_LOOPING_TALK_NORMAL is stopping? Can it continue while the NPCs are interacting?



#15
Baaleos

Baaleos
  • Members
  • 1 330 messages

The action queue is per object.

Is one of those lines meant to be on Bob? they are both on John.

When John clicks/interacts with Bob, it will interrupt any action he is currently doing - I think.

This will then make the next item in the action queue fire.

 

The more absolute way to manage actions, is with DelayCommand - it means you need to worry about delays, and absolute timings.

Opposed to waiting for one object to finish an action then continuing on with another.



#16
Proleric

Proleric
  • Members
  • 2 352 messages

 

...Why ANIMATION_LOOPING_TALK_NORMAL is stopping? Can it continue while the NPCs are interacting?

 

ActionPlayAnimation has a third parameter which defaults to 1 second for looping animations.

 

If you want the animation to play indefinitely, try

const float  FOREVER  = 1000000000000.0;  // 1 trillion seconds = about 5000 years of gameplay
 
void main()
{
  ActionPlayAnimation(ANIMATION_LOOPING_TALK_NORMAL, 1.0, FOREVER);
}

When you want to stop the animation, tell the NPC to ClearAllActions().

 

Getting an NPC to do two things simultaneously is tricky, because they only have one action queue, so, in principle, they only do one thing at a time.

 

There are workarounds, though.

 

If I remember correctly, an NPC can SpeakString while animating (or doing some other action), because SpeakString is not an action, is not queued, and happens immediately.

 

Caveat:

Spoiler

 

Another workaround is to use a conversation. Starting a conversation is an action, so that has to happen first, but once the conversation is running, the NPCs can do almost any action you like, either through script or the PlayAnimation feature of the conversation line, as long as they remain within about 10m of the player.

 

Yet another option is to use invisible objects. You can use an invisible "controller" placeable to assign commands to the NPCs in a scene, to achieve complete control over the sequence of events. Also, an invisible object at the NPC's location can do things while the NPC is busy.

 

As Baaleos said, DelayCommand is a useful way to control timing. The only snag is that it's difficult to know in advance how long actions will take, so you end up tweaking the duration of each delay until the results look right.

 

To avoid that problem, you can make one NPC the "leader". For example, if Jill is the leader, ActionMoveToObject(oJack) followed by ActionDoCommand(AssignCommand(oJack... will cause Jack to do something when, and only when, Jill has finishing walking towards him. This is ideal, because you don't need to know any timings in advance, but if the scenario is very complicated, DelayCommand is probably easier.



#17
Zeke

Zeke
  • Members
  • 48 messages

5000 years of gameplay

That would be enough..  :mellow:

 

I like that "leader" solution. It's genius. ActionDoCommand(AssignCommand()) is something I've never done but I will now, because I really hate tweaking timings, and this idea is very very good.

 

Another workaround is to use a conversation.

So something like AssignCommand(oNPC, SpeakString()) would work parallel to a talking animation?