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.
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 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.
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.
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 |
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.
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.
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.
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.
The ltserver module provides an interface to the functionality of the LithTech server shell.
The ltobject module provides an functions for working with individual objects.
ltobject.followpath(o, (("Waypoint1", 2000, 60, 60, 1.14, 5), ("Waypoint2", 2000, 60, 60, 1.14, 5), ("Waypoint3", 2000, 60, 60, 1.14, 5)))
The ltsound provides functions for working with sounds.