WDFUSE Tutorial - Scripting

## 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.**

**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.

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.

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 S**tops**, 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:**

The first value can be given in three ways:

```

```

The second value can be given in 4 ways:

```

```

### 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.

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...

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.

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

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.

### 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.

 2
 4
 8
 16
 32
 64
 256
 512

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.

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.

```
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.

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.

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

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.

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.

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**.

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.

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.

 message: [stop number] [receiver] [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.

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.

 8
 2147483648

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.

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