LithTech Python Scripting Interface

Jon Parise <jparise@cmu.edu>
Last updated: 12/13/01
  1. Introduction
  2. Triggers
  3. Script Modules
  4. Callbacks
  5. Functions
  6. Modules

Introduction

The LithTech engine does not include a native scripting engine. An explanation follows:

"After a year of development, we had most of the essentials - an editor, physics, rendering, sound, and networking. We also were using a scripting language called DScript. The engine still needed a LOT of optimization and finishing work at this point (2 years worth!)

At this point, we began development of Blood2 on the same engine. When Blood2 started,it became apparent that we were wasting our time with a scripting language and chose to drop DScript in favor of compiled DLLs."

- Mike Dussault, http://www.planetblood.com/gameinfo/lithtech.shtml

However, our particular application of the LithTech engine (the Earth Theater) requires dynamic scripting capabilities. Instead of writing my own interpreter, I chose to embed the Python language interpreter inside of the LithTech engine.

Triggers

The LithTech Python system is designed to be complete event-driven. To that end, it relies heavily on the use of "trigger" objects. Triggers can either be placed inside the world environment (as objects in DEdit) or artificially created, scheduled and fired from within the engine.

Triggers Objects in DEdit

Trigger Properties

Triggers can be placed directly in the world using DEdit. In order for this to work, the Trigger class must already be defined in the game code itself. Once that is done, the "Trigger" class should be available in all of the appropriate DEdit lists.

Triggers are placed in the world just like any other object. Keep in mind that the trigger won't be fired unless a player object collides with it, though, so placement is important. The size of the trigger can be adjusted using the Dims property.

The name of the trigger is fairly important. A trigger can be named virtually anything, but the trigger is identified in the Python script by its name, so it's important to make the name something meaningful and unambiguous.

The Delay property is also important. Setting the delay to a positive number will delay the trigger's action for the given number of seconds after the collision occurs. In other words, when a player object collides with the trigger, the trigger event won't be fired until the delay has elapsed. The default delay is zero seconds, so the trigger will fire immediately unless this property is modified.

Artificial Triggers

Triggers can also be created artifically. Because they don't exist as objects in the world, though, the name and delay properties become very important.

See ltserver.addevent for details.

Special Triggers

There are a few "special" triggers. All of the special triggers are artificial triggers. The engine will fire these triggers upon certain engine events. The following table describes each of these special triggers:

Name Description
start Fired upon simulation initialized
startShow Fired when the "start show" command key is pressed
restartShow Fired when the "restart show" command key is pressed
stopShow Fired when the "stop show" command key is pressed
testTrigger Fired when the "test trigger" command key is pressed

Script Modules

The actual script exists in its own Python module. The module can be named nearly anything, so it's probably most convenient to name the module after the name of the show. We'll use arrg in our examples, so the name of the module would be arrg.py inside of the rez/python/lithtech/ directory.

Callbacks

The LithTech Python interface works on a callback principle. That means that each trigger event is directly associated with some sort of action (in the form of a function call). The list of callbacks is defined as a Python dictionary. It might look something like this for a script module named arrg:

callbacks = {
    "start":            "arrg.start",
    "startShow":        "arrg.startShow",
    "exitBase":         "arrg.exitBase",
    "safetyTalk":       "arrg.safetyTalk",
    "cruising":         "arrg.cruising"
}

In the above example, the dictionary keys (in the left column) represent trigger names. The values in the right column represent function names.

Note that not all of the triggers in the world need to be listed in the callbacks dictionary. Only those triggers that require an action (and have that action defined in a function) need to be listed here.

Functions

Individual functions are fairly simple. All callback functions only receive one parameter: the object handle of the calling engine object. A sample function might look like this:

def startShow(o):
    ltsound.play(o, "sounds/effects/radioconvo.wav", 70)
    ltsound.play(o, "sounds/effects/sship_dclose.wav", 100, 30)
    ltobject.followpath(o,
        (("Waypoint1", 2000, 60, 60, 1.14),
         ("Waypoint2", 2000, 60, 60, 1.14),
         ("Waypoint3", 2000, 60, 60, 1.14)))

This example declares a new function named startShow. It receives one paramter, o, which is the object handle mentioned above.

Based on the callbacks dictionary introduced in the previous section, this function would be called upon the "startShow" event, which is one of the special triggers mentioned earlier.

Modules

There are a number of special modules used in the LithTech Python interface. These modules exposed core engine functionality to the Python scripting environment. Nearly all of the functionality of the scripting system is derived from the functions exported by these modules.

ltserver

The ltserver module provides an interface to the functionality of the LithTech server shell.

ltserver.cprint(string msg)

ltserver.addevent(string trigger, float delay)

ltserver.hudstate(int screen, byte state)

ltobject

The ltobject module provides an functions for working with individual objects.

handle ltobject.getobject(string name)

ltobject.setposition(handle object, float x, float y, float z)

ltobject.setrotation(handle object, float ax, float ay, float az)

ltobject.setvelocity(handle object, float dx, float dy, float dz)

ltobject.setangularvelocity(handle object, float dax, float day, float daz)

ltobject.moveto(handle object, string target, float max_velocity, float max_accel, float max_braking)

ltobject.followpath(handle object, tuple path)
    ltobject.followpath(o,
        (("Waypoint1", 2000, 60, 60, 1.14, 5),
         ("Waypoint2", 2000, 60, 60, 1.14, 5),
         ("Waypoint3", 2000, 60, 60, 1.14, 5)))

ltobject.startanim(handle object, string anim, int looping)

ltobject.stopanim(handle object)

ltsound

The ltsound provides functions for working with sounds.

ltobject.play(handle object, string filename, int volume, float delay)

handle ltsound.loop(handle object, string filename, int volume)

ltsound.kill(handle sound)