Forcegreet tutorial

Post » Thu Jun 21, 2012 5:16 pm

I've seen a few requests for help with Forcegreet here, so I have written up an example in the hopes it will help. I am not sure where the problems usually arise, so I am basically just rambling. All comments and questions welcome. :smile:


Forcegreet example.

OK, for this example I'm going to assume you know how to make an NPC and a quest, with an alias, can record your own dialogue, and are comfortable with the sandboxing options of the forcegreet package. The NPC in this example has ID TESTHilda and the name Hilda and is a Unique Actor with the FemaleNord voicetype, and the quest (ID = TESTQuest) has a 'Unique actor' alias for her named AlHilda. Hilda lives in Riverwood and hangs about on the bridge there.

Setting up the dialogue
====================
So, the first thing to do is make the topic Hilda will greet the player with. In the quest's Dialogue views tab, rightclick under EditorID, select New, and give the new dialogue view a name; then rightclick in the blank window, select Create branch, and give the new dialogue branch a name; then give the new dialogue topic the name TESTHildaGreetingTopic, and click OK.

Doubleclick on the Branch (the outer box) in the dialogue view, and set the topic to Normal. This prevents it from appearing as dialogue when you activate NPCs, unless you explicitly link to it from other dialogue topics. Then doubleclick on the Topic (the inner box), rightclick in the Info box and choose New, then enter "Hello dear! I'm Hilda!" as the Response text. Then, to help with testing, set the Idle animation for this Response to IdleGetAttention. Click OK. In the Topic Info window add a Condition: GetIsVoiceType FemaleNord == 1. Doubleclick on the Response Text again, Record yourself saying "Hello dear! I'm Hilda!", and don't forget to save the recording.

Now click OK and get back to the Dialogue Views tab. Rightclick inside the Branch (outer, purple) box and select Add topic. This time we'll give the topic some Topic text, which is what the player will say: "Hello. Have we met before?" Then add a new Response info in the same way as before, with the text "No. But we shall meet again!", and the Idle animation IdleApplaud2. This time, also, make sure the Goodbye box for this info is checked in the Topic info window.

Back in the Dialogue view, click on TESTHildaGreetingTopic so that you get a hand cursor, then drag from that topic to the second topic. That's the dialogue all done: click OK and save.

Setting up the package
======================
In the Object window, go to Character\Package, rightclick under EditorID, and select New. Give the package the ID TESTHildaForceGreet. For the time being, leave Owner quest at None - we'll change that later. Set the package template dropdown to ForceGreet.

Go to the Package's Flags tab and make sure 'Preferred speed' is checked and set to Run, so that Hilda will run up to the player. (I inevitably forget to set Flags if I don't do it right away.)

Now go back to the Package tab, to set the options in the Public package data window. Click on Topic, then set the Value dropdown on the right to TESTHildaGreetingTopic.

That was straightforward, but the next options in this window - NPC wait location, Trigger location, Forcegreet distance - can be a bit more confusing. So let's take a moment just to look at how the package is going to use them. The procedure tree is all greyed out at the moment, so change that by setting the Package Type dropdown to Package Template - we'll change it back later.

Now you can see the Procedure tree, you'll probably notice straight away that it contains two forcegreet procedures. But we only want the player to be forcegreeted once, so what's that about? Well, if you click on the first Sequence in the procedure tree, you'll see it has several conditions, and the first of these is that the player must be detected. Click on the second sequence, and instead of that you'll see a GetNumericPackageData condition, specifying that the package data for 'Player must be detected' must be 0, i.e., false. These conditions (which are on the procedures inside the sequences as well as on the sequences themselves) are the only difference between these two sequences.

What this means is that, if you set the package data ''Player must be detected' to true, only the first sequence will ever run. If you set it to false, the first sequence will run when the player is detected, and the second sequence will run whether the player is detected or not. To see what stops the second sequence running after the first sequence has run, click on the ForceGreet procedure in the first sequence. You'll see that the 'Success completes package' box is checked. Whenever Hilda manages to ForceGreet the player, the package will end and she'll re-evaluate her packages. It's then up to us to make sure she doesn't decide to run TESTHildaForceGreet package again - but more on this later.

The other conditions on the sequences boil down to the requirement that the player (not Hilda) must be within the Trigger location. The condition 'GetWithinPackageLocation', which is run on the player, means that the location is taken from the package data: in this case the Trigger Location item you can see in the Public package data box. Click on that and you'll see that by default it is 'Near package start location, radius 500'. So this sequence will only start when the player is within 500 distance units of - well, of wherever Hilda was when she started this package.

For a simple example, that's fine. But Skyrim's NPCs can move around, and start their packages at all sorts of times, and you won't always know where the Trigger location ends up being. So in most cases you'll probably want to specify a less moveable location for your trigger. Don't make the radius too small, though, or the player may have difficulty getting to the exact spot.

Now let's suppose the player has got to the Trigger location and this sequence can start. The first procedure for Hilda is to Travel. Her destination will be the package data location 'Forcegreet Distance' - and there's a 'GetWithinPackageLocation'condition on this procedure telling her not to bother if she is already within the location ForceGreet Distance. So let's have a quick peep at 'ForceGreet Distance' in the Public package data box. You'll see it's set to 'PlayerRef in (none) radius 300' - in other words, it is the location 'within 300 distance units of the player'. Since forcegreeting only applies to NPCs approaching the player, you should never change the PlayerRef location here, but you can change the radius. The smaller you make it, the closer Hilda will have to get to you before she can go on to deliver her line. If you set it to 0 she may find it impossible to get close enough; if you set it to 4000 she'll speak to you at an absurd distance. Most of the time, it's fine to leave Forcegreet Distance unchanged.

Next in this sequence, after Hilda has Travelled to the player, we tell her to forcegreet - that is, to start dialogue with the player by saying her line. If you look at the conditions on this procedure, you'll see that there are two GetWithinPackageLocation conditions, one run on the player and one on Hilda herself, checking that they are both still in suitable locations (in the Trigger location, and in the Forcegreet Distance location, respectively).

You may be a little puzzled at this point by the NPCWaitLocation and TargetTriggerLocation items in the Selected procedure's data box. These are both set to 'ForceGreetLoc'. Because we changed our package type to Package Template, you can see ForceGreetLoc in the Public package data box, but notice that it is not public - in a normal package you cannot change this. ForceGreetLoc is set to 'PlayerRef in (none) radius 5000'. This is what is passed to the ForceGreet procedure, which takes three arguments: Topic, NPCWaitLocation, and TargetTriggerLocation. Don't muddle this procedure up with the ForceGreet package that we're busy making. The package takes care of making the NPC travel to within forcegreet distance of the player and to the NPC wait location (as we'll see later), and sets up all the conditions, like the player trigger location, in which they'll attempt the ForceGreet procedure. So the package has ensured that the values passed to the ForceGreet procedure are OK - the player is always within 5000 of themselves. But if you changed these values in a package template you might get strange results.

That's the sequences done. But if the conditions for them aren't met - the player isn't in the Trigger location - Hilda will go on to the next procedure: Sandbox while waiting. This has a condition, which is that the package data 'Sandbox while waiting?' is true, and a location, which is set to the package data for NPC wait location. If Sandbox while waiting is set to false, Hilda will go onto the last procedure, Travel, which tells her to go to NPC wait location. So, either way, Hilda will go to the NPC wait location.

Now the reason for putting the sequences at the top is apparent: we wouldn't want Hilda to travel to the NPC wait location when the player is already at the trigger location. At that point, Hilda should go up to the player instead. But until that happens, Hilda will either Sandbox at, or Travel to, the NPC wait location, whenever this package is evaluated.

So now we understand how the package works, let's set the rest of the package data.

We'll leave Forcegreet Distance as it is, as I suggested earlier. For this example, we can also leave NPC wait location as it is, since Hilda's going to stay on the bridge where I put her; but I could set NPC wait location to a marker in the Sleeping Giant Inn to have her travel there and wait for me there instead. Bear in mind, though, that if I met her while she was still travelling to the Inn, she would not greet me with the topic unless I was within the trigger location (and even then, only when she re-evaluated her package).

Trigger Location, however, we'll change. If we leave it as it is, the player will have to go up to within 500 of Hilda, and then she barely moves before she delivers her line. To get her to run up to the player, we need the trigger location to be further from her. You could set this to be a completely different place, so that she would start running after the player when the player gets to Whiterun, for example. But we'll just make the Trigger Location radius a bit bigger - say, 4000.

Assigning the package to the NPC
=============================
Now click on OK, and let's try to put this package on Hilda. We can either add a package to her directly, or to her quest alias, AlHilda. In both cases you rightclick in the package list, either in the NPC's AI data tab or in the Reference Alias window's Alias Package data box, select Add, and choose the package to add. But what you should notice this time is that you can only add TESTHildaForceGreet to the alias - it does not appear in the list of possible packages for the NPC.

Now you might think that that does not matter, because you want to assign this package to the alias anyway; but if you do that and run the game, you'll find that Hilda does not greet you.

The problem here is that we didn't change the package type back: it's still a Package template. So go back and edit TESTHildaForceGreet and change it back to being a package. (Notice that, when you do, the Flags tab reappears - package templates don't have one. And what this means is that, by changing to a package template, we lost our 'Preferred speed - Run' flag. Go back and check that again, if you want to.) Click OK and save. Now in the Creation Kit you'll be able to add the package to NPCs, as well as aliases, and Hilda will greet you properly in Skyrim.

So now I always like to make sure that I can add my new packages to an NPC, rather than an alias, when I first create them - even if I only intend to assign them to aliases. It saves much hair-pulling later.

Now you've got your package assignable to an NPC, let's go back and specify that our package has TESTQuest as its Owner quest. This would let us use the quest's aliases when we are setting the package data. "But we don't need to!", you may protest; but do it anyway.

Now go back and try to assign the package to an NPC again. All seems well, you can see TESTHildaForceGreet in the list of possible packages - but when you select it and click OK, it does not appear in the NPC's package list. Packages with Owner quests can only be assigned to aliases. So if a package you put on an NPC stops working, ask yourself whether you might have set it to have an Owner quest.


Stopping the forcegreet
========================
If you run the game with this package as it is, Hilda will greet you, you have the chat, and then you leave. But shortly afterwards, Hilda will come up and greet you again. It can become wearing. So we need to stop Hilda running this package after she has forcegreeted you once.

There are various ways to do this, but perhaps the commonest is to set a quest stage when Hilda says her line, and put a condition on the package checking that that stage has not been reached.
User avatar
BaNK.RoLL
 
Posts: 3451
Joined: Sun Nov 18, 2007 3:55 pm

Post » Thu Jun 21, 2012 5:30 pm

Great tutorial! If there was a thumbs up button i would give you one. Great job man.
User avatar
Danel
 
Posts: 3417
Joined: Tue Feb 27, 2007 8:35 pm

Post » Thu Jun 21, 2012 7:46 pm

Thanks for this. I won't get a chance to test it out until wednesday, but this could be very useful :)
User avatar
Laura Cartwright
 
Posts: 3483
Joined: Mon Sep 25, 2006 6:12 pm

Post » Thu Jun 21, 2012 7:31 pm

I have a question. In the game I'm making I frequently need characters to interrupt the player's dialogue with another character. Is there a way to do this?
For an example, I walk up to a character Dhall and say "Greetings." instead of Dhall responding, my companion, Morte pipes up with "Woah, chief, what are you doing?"
Problem: Basically I can set the conditions on all the packages such that it SHOULD work, but I need to tab out of dialogue with one person before the other will talk. I'd like some way of them actually changing the target of dialogue from one person to the next. Any ideas?
TL;DR How do I get one person to interrupt another mid sentence?
User avatar
Laura Cartwright
 
Posts: 3483
Joined: Mon Sep 25, 2006 6:12 pm

Post » Thu Jun 21, 2012 6:50 pm

You can prevent the need to tab out by making the interrupted sentence (which could be short and silent) a Goodbye, but you can't (as far as I know) get a perfectly seamless dialogue UI in such a scene - it will be obvious that you've left that dialogue. However you can get a new one to start pretty quickly afterwards, if another NPC is standing by waiting to Forcegreet you. But for this to work you must sprinkle the procedures in the Forcegreet package with 'IsTalking Player == 0' conditions. Otherwise when they greet you the dialogue UI won't appear (I assume because it was still showing when the code to make it appear should have run), and so you won't be able to continue the dialogue.

If the gap is still too long I think you'd have to use a scene for the interruption and then pop a forcegreet package at the end of the scene.
User avatar
An Lor
 
Posts: 3439
Joined: Sun Feb 18, 2007 8:46 pm

Post » Thu Jun 21, 2012 5:57 pm

For an example, I walk up to a character Dhall and say "Greetings." instead of Dhall responding, my companion, Morte pipes up with "Woah, chief, what are you doing?"

Does the player then enter a conversation with Morte, or does the conversation with Dhall begin? (It's clearly been too long since I played Torment!)

And do you mean the player actually chooses a "Greetings" topic, or should Morte interrupt as soon as you activate Dhall?

If you just want Morte to interject the one line before the conversation with Dhall begins, then I wonder if you could use the http://www.creationkit.com/Say_-_ObjectReference command to do this. You could disable activation on Dhall, then when his OnActivate event is triggered for the first time, use Say to have Morte say the line before really activating Dhall and beginning the conversation.

You could also perhaps do some things with topic fragment scripts...in a topic info's Begin Fragment, you could put the Say command to have Morte say his interjection, and then you could pad the start of Dhall's lines with that length of silence.

Anyway, to get more on-topic, a few quick comments on force-greets to complement Ingenue's excellent tutorial:

1) Basic information about debugging quests applies here. Always remember, when using packages attached to quest aliases, that a ) the quest won't start if an alias not marked as optional isn't filled and b ) the Priority of the quest (the number on its Quest Data tab) influences which quest's packages will be at the top of the actor's package stack. So if a forcegreet isn't working, make sure its quest is starting, and that it has the highest priority of any running quest that uses that actor in an alias.

2) Also, basic techniques for debugging packages apply. Packages can run script fragments when they start and end, and it can be useful to put debug commands in these fragments that will tell you if the package is being run correctly. Also actors (and thus Reference Aliases filled by actors) have an OnPackageChange event, which likewise can be useful in determining whether a forcegreet package is being switched to and from at the appropriate times.
User avatar
Céline Rémy
 
Posts: 3443
Joined: Sat Apr 07, 2007 12:45 am

Post » Thu Jun 21, 2012 11:33 pm

So if a forcegreet isn't working, make sure its quest is starting, and that it has the highest priority of any running quest that uses that actor in an alias.

Very good points, thanks. I'm thinking of posting this as an example on the wiki in a few days, when more people have had a chance to comment - I'd like to incorporate additions like this and any corrections people want to make.
User avatar
Crystal Clarke
 
Posts: 3410
Joined: Mon Dec 11, 2006 5:55 am


Return to V - Skyrim