WDFUSE Tutorial - Scripting

From DF21 Wiki

INF Scripting

To make your level geometry interactive you would need to learn the INF programming language. Scripts written in INF tell the JEDI engine to control almost every component in the game. From moving elevators, morphing platforms, opening doors, changing lights, moving water and completing objectives, the possibilities for unique adventures in Dark Forces are endless. One can create some amazing and unique experiences not present in the base game.

This tutorial will gradually introduce you to the basic INF concepts. While it helps to understand programming, it is not required as the INF script is more similar to a state machine than modern object-oriented programming. For this section it is critical that you have the community-made INF documentation open as you follow along. Therefore make sure to open this page and keep it handy. It contains Dark Forces component specifications such as GOBs and LFDs but we will be focusing on the INF section .


Important!

While this guide will take things slowly, remember not to get frustrated at the complexity you may encounter. Take your time and be patient, go do something else and come back. The best way to learn is through practice and experimentation!



With that out of the way, go ahead and start a new project and switch to the Scripting tutorial .

INF Editor

All the script editing you will be doing is done in the INF Editor. There are many ways you can bring it up. You can choose it from the main menu or you use the hotkey I.

thumb

INF scripts are applied to Sector and Wall components in Dark Forces. Every time you open the Sector or Wall Editors you will see the a small INF icon at the top. By clicking this icon you can also bring up the INF editor. Sectors that have any INF code are colored yellow on the map.

thumb

From any Sector or Wall editor you can click the Yellow INF button at the top or press F2 . The hotkey is different from the main map.

Go ahead and click on the sector named ELEV1 and then open the INF editor for that sector using any of the methods described above.

thumb

Here you will see a rather complicated window but we will concentrate on only a few components here. The main one is the Script Text Editing Area at the bottom. This is where you will write all your INF scripting code. When you're asked to edit or write something you will go to that text area and make changes. Just like the other windows at the top is the green check mark box. When you are done editing, press it to commit and save your changes. One important difference with the other editors is you cannot autocommit your work as it is very easy to make a mistake and break everything.

Above the text area you can Search and Replace text. This is useful if you have a very large script. At the bottom you will see a small No Error bar. If you make a syntax mistake this will turn red and warn you about an error. The line in your INF code that has an issue will be highlighted.

Center top you will see the name of the INF code titled ELEV1. Every sector that has INF logic applied to it must have a name. To the left of the sector name you will be able to Change your Font button. And to the left of that is a useful checkmark button that Verifies Syntax. Click it if you want to make sure nothing is broken but you don't want to commit your changes. You can also rollback your changes and exit the editor. To the right is the useful "Stay on Top" button. The blue "Go to Highlighted Sector" button is useful to jump to a sector that is referenced in your code. There are many more components here but this is enough to get started.

INF Structure

Lets look at the code in detail. Take your time to understand everything shown.

seq
  class: elevator move_floor   
    stop: 2 5
    stop: 16 5
seqend 

Every INF script begins with the word SEQ (for Sequence) and ends with the word SEQEND. If you do not have these at the beginning and end your code will not work and you will get errors. Inside the sequence there can be one or more Classes. The Classes are major sequences components that tell the JEDI engine what type of action this sector must take. For a full list of Class Elevators please refer to the DF Specs page .

Class Elevators

All classes in the game are made out of Elevators. In fact, almost every kind of game logic in the game is done with an elevator. That's how Dark Forces keeps track of the level state and makes things interactive. Elevators make sectors and walls dynamic. They can obviously be used to create lifts, platforms, doors etc., but you often also need dummy (i.e. non-accessible, completely beyond the area the player is intended to roam) elevators for level control purposes.

Some of the common ones you will be using are...

  1. Move_floor - Changes the floor altitude of a sector.
  2. Move_ceiling - Changes the ceiling altitude of a sector.
  3. Scroll_floor - Scrolls the floor texture of a sector. Player moves with the floor texture by default
  4. Change_light - Changes the ambient of a sector, i.e. changes the light level in a sector.
  5. Morph_move1 - Moves the vertex positions of any walls in the sector with flag 1 bit 32 (wall morph with sector).
  6. Morph_spin1 - Rotates the vertex positions of any walls in the sector with flag 1 bit 32 (wall morph with sector).
  7. Door_inv - A door that opens downwards. Otherwise, the same as any other door elevator.

Elevators will usually have Stops, which are different values the elevator can arrive at. In the example above, the elevator has two stops.

Class Stops

A Stop is a value that an elevator can arrive at. This value varies depending on the class of elevator, and can be floor altitude, ceiling altitude, ambience, degrees etc. Stops can be used practically, such as different heights a lift stops at, or can be used purely for level control as elevators can also send a message, page a sound, or create an adjoin upon arriving at a stop.

Note: Elevators can have any number of stops. If no stops are given, the elevator will start at value 0 and keep increasing its value throughout the entire level. This may be appropriate for an "elevator scroll_floor", but not for an "elevator move_floor" !!!

usage:

| stop: [value1] [value2]

The first value can be given in three ways:

[num] absolute stop
| @[num]	relative stop
| [sectorname]	equal the value of the sector [sectorname]

The second value can be given in 4 ways:

[time] time in sec that elevator remains at stop
| hold	elevator will remain at stop indefinitely
| terminate	elevator will stay at the stop permanently
| complete	mission will be complete when elev arrives at stop

Elevator Move_Floor

Lets see what this ELEV1 sector is doing. Launch the game (GOB) and take a look at the row of elevators in front of you.

thumb

Our elevator is on the left and you will see that all it does is automatically move up and down by itself. According to the code (INF Text Area) this elevator has only two stops. For an elevator of type move_floor the stops are the heights at which it will stop. In this case, the first stop is at height 2 and the second one stops at height 16. The second number is the delay (in seconds) at how long this elevator should stay at the stop. In this case, both stops will wait for 5 seconds before going to the next stop. This elevator will go in an infinite loop between these two stops.

From this point on, any time you make a change to the script, make sure you exit the game, save and rerun it to see what has happened. We cannot rely on the 3D renderer as much because it doesn't contain INF logic.

Lets start making our first changes! Just like in real life, an elevator can have multiple stops. Lets add one more stop.

seq
  class: elevator move_floor
    stop: 2 1
    stop: 8 1
    stop: 16 1
seqend


Add a stop at height 10 and change all the delays to 1, nobody wants to wait that long between stops! Click the Syntax Checker button at the top to make sure you didn't make a mistake. If you did, the bar at the bottom will to red and the error will be highlighted. Now run the game. What happens?

The elevator moves at the same speed but the stops are much shorter right? Also notice that when the elevator comes down, it doesn't stop in the middle. This is because it runs all the stops from top to bottom. If you wanted it to stop in the middle again you would have to add another stop: 8 1 below the last stop.

Texture Anchoring

Lets look at elevator ELEV2 next to it. The code is identical to ELEV1 but something is really wrong with it...

thumb

The textures aren't stationary, they move along with the elevator! This is because for elevators one needs to tell the textures that they must be anchored as the sector moves up and down. This is done with the Wall Editor's Flag1 section. Click on the elevator wall in front of you and take a look.

thumb

Make the change to the flag by turning on the WALL TX ANCHORED (16) flag.

thumb

You will need to do it to the two side walls and the wall of the top ledge as well. Make sure you multiselect by holding the SHIFT button to do it faster. Use the image above of the highlighted walls to make your change (You can always look at how ELEV1 wall flags are set as well). Once you are done making your changes, launch the game and confirm the textures look aligned now.

Elevator Move_FC

Lets jump to the next elevator ELEV3. You will notice that the class has changed.

seq
  class: elevator move_fc
      speed: 30
    stop: 2 5
    stop: 16 5
seqend


While the stops are the same, the elevator class has changed. FC stands for "FloorCeiling" and this means that both the floor and ceiling will move at the same time. There is also a new keyword called speed. The name is self-explanatory, it just tells the elevator how fast it should be moving, in Dark Forces units per second. Go ahead and change the speed value to 0 (or instant!) . If you run the game you will see that the ceiling moves along with the floor immediately.

thumb

Elevator Audio

The elevator on the right is ELEV4. Here is the code for it, it's back to move_floor but now there are more changes.

seq
  class: elevator move_floor
      sound: 1 ELEV3-1.VOC
      sound: 2 ELEV3-2.VOC
      sound: 3 ELEV3-3.VOC
      speed: 5
    stop: @0 1
    stop: @14 1
seqend


You will see that there are three sound keywords and each one corresponds to a particular action.

  1. Sound: 1 is played when the elevator starts moving.
  2. Sound :2 is played while the elevator is moving.
  3. Sound: 3 is played when the elevator arrives at the final stop.

The Dark Forces team tagged the elevator and door sounds with either 1 , 2 or 3 suffix so you can easily tell if the sound effect is for a starting, moving or an arriving elevator. In this case, the sound ELEV3-1.VOC means it is the sound played when the elevators starts moving.

Lets make a change and play some other sound. Switch sound 2 to ELECTRIC.VOC and the start and end audio to KEY.VOC.

You will also notice that the stops have a strange @ symbol in front of them. All it means is that instead of an absolute stops at 2 or 16 heights, the elevator would stop at relative stops. Since it starts at altitude 2 the bottom relative offset is 0. And it has to get up to height 16 so the relative offset is 16-2 = 14.

Lets change the 5 seconds stop delays to 1 as well. Run the game and see what happens.

seq
  class: elevator move_floor
      sound: 1 KEY.VOC
      sound: 2 ELECTRIC.VOC
      sound: 3 KEY.VOC
      speed: 5
    stop: @0 1
    stop: @14 1
seqend


Now you will hear quite unusual sound effects. instead of the normal grinding elevator. Keep in mind that in the original game you can, at most, hear only 8 sounds. If you are having trouble hearing the new audio because the other elevators are being loud you can mute them by renaming the sound keywords it to something that doesn't exist ( Ex: NOSOUND.VOC ) or leave it blank.

Event Masking

Obviously you don't want the elevators to move by themselves. In order to make the elevators react to what you are doing you must understand event masking. An Event Mask determines what event will operate an elevator or trigger. The event, when carried out, will move an elevator to its next stop, or trigger a trigger. From the DF Specs site we can see the full definition of event mask actions.

event_mask values
1 Cross line from front side
2 Cross line from back side
4 Enter sector
8 Leave sector
16 Nudge line from front side / Nudge sector from inside
32 Nudge line from back side / Nudge sector from outside
64 Explosion
256 Shoot or punch line (see entity_mask )
512 Land on floor of sector


The values above are flags and can be added to combine multiple actions at once. So if you want an event to occur if you enter or leave a sector you would add 4+8 and use 12 as your event mask.

Lets go to the next room and see event masks in action.

thumb

You will notice that all the elevators here are static. They are waiting for you to perform an action.

Entering and Leaving

Look at Elev5 on the left-hand side of the new room.

thumb
seq
  class: elevator move_floor
      event_mask: 4
    stop: 2 hold
    stop: 16 hold
seqend


Notice it has a new field event_mask with a value 4. This maps to triggering the elevator when you enter the sector. Go ahead and run the game with these settings. Notice that it doesn't move and when you enter the elevator it goes up automatically.

Go ahead and change the value of event_mask from 4 to 12. This will make it so when you Enter (4) or Leave (8) the sector the elevator will go to the next stop.

User Interaction

Look at ELEV6 's code on the right. You will see that the event_mask is 16.

seq
  class: elevator move_floor
      event_mask: 16
    stop: 2 hold
    stop: 16 hold
seqend

This maps to the user pressing the Use or Nudge key (typically E ) from inside the sector. This means that it will move when you activate it yourself. Go ahead and try it out.

seq
  class: elevator move_floor
      event_mask: 48
    stop: 2 hold
    stop: 16 hold
seqend

Now change the event_mask to 48 . Now you can activate the elevator from being INSIDE and OUTSIDE the sector. So if you fall off while the elevator is on top you can just use the elevator's wall and it will come down. The event_mask of 48 is very common in Dark Forces missions.

Explosions

What is pretty fun is that you can trigger elevators by various explosions. Go to ELEV7 and notice that the event_mask is set to 64.

seq
  class: elevator move_floor
      event_mask: 64
    stop: 2 hold
    stop: 16 hold
seqend

This maps to explosive triggers. Any time something explodes (like a thermal detonator or a fuel canister) the elevator will trigger. Go ahead and run the game. Try shooting the canister when your pistol - notice after it explodes the elevator moves up. This is not just a random curiosity, but can be cleverly used to make explodable walls, etc.

thumb

You can also pick-up the nearby concussion rifle and try shooting the elevator to test this mask.

Switches

Proceed to the third room. Here the elevators are all the same, except that now there are switches near them.

thumb

The switches themselves are Sign textures you've seen earlier.

thumb

When you are looking at the Texture Resource picker you will see that Switches consist of multiple BM images. The switches will alternate between the textures when the switches are pressed.

Now look at the map overview.

thumb

If you look at at the editor you will notice a few things.

  1. Every wall with a switch is colored teal - this means the wall has a trigger (in this case a Switch).
  2. The Sector highlighted by the switch is highlighted in Red. Meaning activating the trigger switch will affect the highlighted elevator sector.
  3. The Switch is a sign with a -4 altitude offset. This is the perfect height (usually) for switches. Heights are recorded backwards for sign textures

Triggers

Look at the Wall Editor INF (It's the same yellow button at the top). Note: if the icon is greyed out it means it does not have any INF code applied to it.

thumb

Notice that just like with elevators there is a new class called Trigger. Triggers send a message to a client sector when triggered. They can be used to create switches, tripwires etc. Triggers can also be used to display text ― in fact, the only way to display custom text is to use triggers.

Some common triggers classes are..

  1. Trigger - This can be applied to a sector (entering, leaving, nudging it etc.) or a line (crossing, nudging it etc.). Can't be used with switches.
  2. Trigger switch1 - Used by switches. When pressed, the first texture will change to the second texture in the multiple bm switch.
  3. Trigger single - A trigger than can only be pressed once.
  4. Trigger toggle - A switch that will toggle between the ON and OFF states.

For a full list of trigger types check out DF Specs.

seq
  class: trigger switch1
    client: ELEV8
seqend

Also note that each trigger needs a client value. It tells the trigger which Sector this trigger will affect. In this case the trigger is the sector Elev8.

thumb

If you are in a Wall Editor at the bottom you can also see the client sector that is affected by the trigger. Above you can see that the class is T.switch1 and the client is ELEV8 .

Trigger Messages

Go ahead and run the game and press the first switch. The trigger activates ELEV8 and it goes to the next stop.

thumb

But something is wrong. The elevator goes up and stays at the top. You cannot interact with the switch anymore. This is because the switch is stuck waiting for a message from the client (in this case elevator ELEV8) that it completed the passage. At the moment the code for ELEV8 is the same as all the other elevators and it doesn't interact back with the calling trigger.

Look at the elevator next to it called ELEV9. The trigger next to it is the same as the one to the left but the only difference is the client is ELEV9 .

seq
  class: trigger switch1
    client: ELEV9
seqend

But this time the elevator's code has a new message field.

seq
  class: elevator move_floor
    stop: 2 hold
      message: 0 ELEVATORS3(8) done  
    stop: 16 hold
      message: 1 ELEVATORS3(8) done  
seqend

Messages are sent from triggers when they are triggered and elevators when they arrive at their corresponding stops. They are sent to other triggers and elevators, and in some cases just regular sectors and lines (except message: lights, which is sent to the system). They do various things to their recipients. Messages are placed in the sequence of elevators and triggers.

The syntax differs depending on where the message comes from.

(sent from an elevator)
message: [stop number] [receiver] [message] [parameters]
(sent from a trigger)
client: [receiver]
| message: [message] [parameters]


Here are the definitions of the options above.

[receiver]  is the receiver of a message. Can be one of the following:
[sectorname] 				receiver is a sector
[sectorname([wallnum])] 		receiver is a wall
SYSTEM 				receiver is the SYSTEM (message: lights only)
[parameters] are parameters specific to the type of message.

Lets look at the message in our elevator ELEV9 that is called every time it arrives at a stop.

message: 0 ELEVATORS3(8) done  

Here it tells that when it arrives at stop 0 (and 1) a message is sent to the Sector ELEVATORS3 and Wall 8 . The ELEVATORS3 is the big room you are in right now. And wall 8 is the wall of the switch. The message to the switch is that the elevator is done moving. This tells the switch to flip back to the original texture and make it ready for additional presses.

thumb

In game, you can see the switch reverts back after the elevator reaches the top.

Switch Types

Look at ELEV10 and the corresponding switch. Notice that the class has changed to trigger toggle.

seq
  class: trigger toggle
    client: ELEV10
seqend


The elevator ELEV10 itself does not have any messages. It does not notify the switch that it has reached a stop.

seq
  class: elevator move_floor
      speed: 50
    stop: 2 hold
    stop: 16 hold
seqend

This is because it is a toggle switch. You can press it as much as you want. Go ahead and try it in game.

Now lets combine what we've learned. You can add event types to trigger classes just like elevators.

Go ahead and add an event_mask of 256 (Shooting) to the trigger.

seq
  class: trigger toggle
    event_mask: 256 
    client: ELEV10
seqend

Run the game and try shooting the switch! BUT WAIT... nothing happens. How come? This is because some actions require an entity_mask value.

Entity Masks

An Entity Mask defines the entity that triggers a trigger. An entity is just a way to identify an game actor that can trigger an event. 99% of the time it is the player using E to activate a switch, a door or an elevator but that is not always the case. Here are the possible values for Entity Masks.

entity_mask values
1 Enemy
8 Weapon
2147483648 Player


By default the Entity Mask is the Player ( 2147483648 ). However, since in our case we want to shoot the switch we cannot activate it with that value (Because when you fire your weapon you are not shooting instances of yourself - you are shooting with a bullet). So you need to add an entity_mask with a value of 8 (Weapon) . You can also use the value of * (entity_mask: *) for all the possible entity_mask values!

seq
  class: trigger toggle
      entity_mask: 8
      event_mask: 256
    client: ELEV10
seqend


Give this a try and shoot the switch again.

thumb

It works! You can blast that switch all you want and it will insta-trigger the elevator. Remember, INF scripting is all about trying various effects and combining them to see what happens.

Important: This tutorial cannot possibly cover ever single combination of classes. If you are wondering how something is done in the game - just load up the mission and look at the original code.

With that lets proceed to Scripting Part II where you can learn more about Doors and other triggers.


WDFUSE Tutorials

  1. Introduction
  2. WDFUSE Tutorial - Configuration
  3. WDFUSE Tutorial - Basic Geometry
  4. WDFUSE Tutorial - Basic Geometry II
  5. WDFUSE Tutorial - 3D Renderer
  6. WDFUSE Tutorial - Advanced Geometry
  7. WDFUSE Tutorial - Advanced Geometry II
  8. WDFUSE Tutorial - Advanced Geometry III
  9. WDFUSE Tutorial - Objects
  10. WDFUSE Tutorial - Scripting
  11. WDFUSE Tutorial - Scripting II
  12. WDFUSE Tutorial - Scripting III