NorseDude - Part IV: What Did We Just Do?
NorseDude's Cut-Scene Tutorial
Note: these tutorials were written prior to the new cut-scene functionality added by BioWare in patch 1.30.
The purpose of these tutorials: To help the average NWN module maker create his or her own cut-scenes in NWN. Simple as that.
Requirements: Not much, but I do expect you to know at least the basics in module-creation. When I say "Create a NPC named 'Bob' with the Tag 'bob'", I will not tell you how to do just that since I expect you to know that already.
My e-mail: norsedude@tiscali.no
Part IV: What Did We Just Do?
If you did the last three steps correctly, you should now have a module where the PC talks to a guard, the guard forces you to follow him to their leader, the guard tells the leader he has a prisoner, and the leader replies. You can only take control over yourself again when this is done. So how did we do it? Time for some explanation.
Let's take a look at the first script (cutscene_1_1):
// cutscene_1_1 void main() { object oGuard = GetObjectByTag("guard"); object oLeader = GetObjectByTag("leader"); object oPC = GetLastPerceived(); object oDoor = GetObjectByTag("guard_door"); if (GetIsPC(oPC)) { SetCommandable(FALSE, oPC); DelayCommand(10.0, SetCommandable(TRUE, oPC)); AssignCommand(oPC, SpeakString("Lead the way!")); AssignCommand(oPC, ActionForceFollowObject(oGuard)); AssignCommand(oGuard, ActionOpenDoor(oDoor)); AssignCommand(oGuard, ActionForceMoveToObject(oLeader, FALSE)); AssignCommand(oPC, ClearAllActions()); } }
If you are familiar to module making, you will recognize more or less all of the above code. It's very simple, actually. The first four lines (starting with "object") tells the game what variables you need for the script. To be short, a variable is akin to putting a new name on a object in the game so you can access in a script. The object can be anything like a player, creature, or even a waypoint or trap. With those set, the actual script first checks if we are a player instead of an NPC, and if we are a player, makes us just another NPC for the time being. Don't worry, this just means we can't control the player but that was sort of the point, right?
If we can't control the player during the cut-scene, how can we control him afterwards? I have tried many, many ways to do this, and so far the only way I have found out how to really get control again is if we use a DelayCommand to set the control back. What this does is when the script starts, it runs through the commands and executes all at once. However if the commands start with "Action", they are put "on hold" and are executed one at a time. The SetCommandable does not start with Action, so when the script starts it first turns the player into a NPC and then back again to a player before it even starts with the Action-commands. This is pointless, so we use a DelayCommand instead. The Action-commands wait for their turn to launch, while the DelayCommand starts right away - but don't execute the sub-command until the timer runs out. In this time we start the DelayCommand when the script starts, but it waits for ten seconds before it launches the sub-command SetCommandable. This will give us plenty of time to run the cut-scene first. This is a bit tricky, since we need to time it correct. We can't give the player control of the character during the cut-scene, and we really shouldn't give him control back too late either. A handy work-around is to start a conversation with the player after the cut-scene, so he will be talking when he can finally move again. You can run another cut-scene with another SetCommandable after the conversation, but who can tell how long the player will be talking?
The next line (the first one starting with AssignCommand) is just to demonstrate that it is fully possible to make the player speak in the cut-scene, so you can have fun with him as well. After that we follow the guard, and this is the fun part: The "Force" in ActionForceFollowObject actually forces the player to follow the NPC - even through areas. Now why would we want to do that? Well, what if we are thrown in jail, and the jail is in the basement? What if we are talking to some guy inside a inn, and we hear a strange noise outside? Everyone would be running out to check - including the player.
While we're on the subject, the AssignCommand simply gives a command to someone or something. Many commands can't be given directly, so we need to assign the command to someone first. I know it's a bit complicated, but once you get the hang of it, it really isn't that annoying. Sort of like the semicolon (";") at the end of every single line, you know?
The ActionOpenDoor part is kinda useless right now, but keep it in mind anyway.
ActionForceMoveToObject makes the guard walk up to the leader, but take a second to read the line. ActionForceMove... Force... the Force-part is a lot like the Force-part in the ForceFollow line we had earlier. Basically it forces the NPC to move to the leader - no matter where the leader is. All that's required is a free path to him. Not only can this path lead to a different area, but it can even span multiple areas if you want. I haven't tested the maximum limit, but I have made a NPC walk from one area, walk through a crowded street, enter a house, walk past two more NPC's and up the stairs - all with a single ForceMove-command. And again, if you force the player to follow...
Now, let's take a look at the second script while we're at it (cutscene_1_2):
// cutscene_1_2 int StartingConditional() { int l_iResult; l_iResult = !GetIsObjectValid(GetPCSpeaker()) && GetLocalInt(OBJECT_SELF, "TalkingToGuard") == 10; return l_iResult; }
A bit tougher, this one. Basically it makes the leader check if there is a player nearby (no point in a cut-scene if there isn't anyone there to see it), and then checks if the player has talked to the guard already.
So, the third script (cutscene_1_3):
// cutscene_1_3 void main() { object oGuard=GetObjectByTag("guard"); if (GetLocalInt(OBJECT_SELF,"TalkingToGuard") == 0 && CanSeePlayer() && GetObjectSeen(oGuard)) { SetLocalInt(OBJECT_SELF, "TalkingToGuard", 10); ActionStartConversation(oGuard); } }
It's really not as hard as it looks. You do recognize the object oGuard-part? We had the same one in the first script. This time we don't need to make the player a variable, and since it is the leader who will be running the script, so we don't need him either. So what does it do?
"if (GetLocalInt(OBJECT_SELF, "TalkingToGuard") == 0 &&" checks if we have talked to the guard. If we have, no need to restart the conversation. The && might confuse you at first, but think of it as AND. The second line, "CanSeePlayer() &&" should be obvious, and so the next line, "GetObjectSeen(oGuard))". Let's put it all together:
"We have not talked to the guard yet" AND "We can see the player" AND "We can see the guard".
(When you don't end a line with a semicolon, then that line is assumed to continue onto the next actual line in the script - this is a way to make your code more "readible" - Ed.).
So if we haven't talked to the guard, we can see him but we can not see the player, don't do anything. If we can see the guard and the player but we have already talked to them, don't do anything. If we can see both but we haven't talked to them yet, let's talk to them now. We perform these tests because this script goes into the OnHeartbeat event, which fires every six seconds. When all three conditions are met, then we can continue on with the cut-scene.
author: Michael Kenyon, editor: Charles Feduke