Turok EX Modding Guide

Scripts

Although some of the game's behavior can be modified by changing things like defs and fx, the scripts are the meat of more complex game logic.

Control Structure

Turok EX's logic is split into three different levels. It's helpful to understand all three and how they fit together:

Native Classes

These are hard-coded in the game engine itself, and cannot be modified or interracted with from user scripts. All dynamic objects in the game (actors) are represented by one of these, which defines their baseline behavior. Native classes are assigned to actors with the classname property in defs/actors/.

By convention, native classes are prefixed with kex.

Internal Scripts

These are part of the scripting engine, but defined inside the game's executable. They can't be modified, but unlike native classes they can be used from user scripts. They act as liasons between user scripts and kex classes (e.g., kActor) or the engine itself (e.g., kGame), or provide some standard functionality (e.g., kVec3).

Internal script classes are conventionally prefixed with k.

User Scripts

This is the stuff in the text files in the scripts folder. We have free reign to modify these, or add our own variables, functions, and classes.

Actors can optionally have a user script class assigned with the scriptClass property in defs/actors/. These classes will be passed a handle to the internal script object associated with their native kex object in their constructor, which is typically stored in a member variable named self.

General

AngelScript

AngelScript is the scripting engine used by Turok EX. It has a fairly familiar C-style syntax.

One feature to be aware of is handles. Handles basically work like C++ references, except they can be modified to refer to different objects after initialization (and therefore also allow null initialization).

obj a, b;
obj@ h;   // null handle
@h = a;   // h now refers to a
@h = @a;  // explicit version of previous line (rarely required)
h = b;    // a is now a copy of b

if ( h == b )     // "a equals b" (true)
if ( h is b )     // "h refers to b" (false)
if ( h !is null ) // "h refers to something" (true)

Includes

Bit Fields

Turok EX uses bit fields for a variety of things, so here's a very quick primer on them.

Bit fields essentially treat each bit in an integer as a boolean value, rather than interpreting all the bits together as a single number. Individual bits can be managed using "bitwise" arithmetic.

Bits can be tested using the "bitwise and" operator, &:

if ( Player.Buttons() & BC_FORWARD != 0 )
	// player is holding "forward"

Bits can be combined or set using the "bitwise or" operator, |:

// combine "loop" and "root motion" flags
self.AnimState().Blend( anim_aiRunning, 4.0f, 8.0f, ANF_LOOP|ANF_ROOTMOTION );

// set the "no interrupt" flag
self.AnimState().flags |= ANF_NOINTERRUPT;

Bits can be cleared with & and the "bitwise not" operator, ~:

// clear the "no interrupt" flag
self.AnimState().flags &= ~ANF_NOINTERRUPT;

Bits can be toggled with the "bitwise exclusive or (xor)" operator, ^:

// toggle the "paused" flag
self.AnimState().flags ^= ANF_PAUSED;

Tick Rate

The basic structure of a main game loop is:

  1. Update game state
  2. Render scene
  3. Repeat

A single iteration of the "update" phase is called a tick.

In some engines, the "update" phase occures once per frame. If the game is designed with a fixed framerate, it will assume a specific, set amount of time has passed since the previous tick. If the game is designed to allow a variable framerate, it will track the amount of time that's passed since the previous tick, and scale its calculations by that time.

Turok EX allows a variable framerate, but its tick rate is fixed at 60 Hz. If the framerate drops below 60 fps, the game will run the update phase multiple times per frame to ensure the overall tick rate remains constant.

The length of a tick is stored in the global constant GAME_DELTA_TIME.

Global

Math Namespace

The Math namespace provides an assortment of common math functions and constants.

common.txt

This file contains a bunch of enums which give names to various constants, like actor IDs.

You should always use these names when referring to such constants in your scripts. Likewise, when modifying the defs files, you should make sure any changes you make are reflected here.

Weapons in particular have a lot of numbers associated with them, so here's a quick reference on those:

turokWeapons TW_WEAPON_[...]
weapon slot numbers
turokActorTypes

AT_WEAPON_[...]
actor IDs of the weapons the player actually uses
(I don't think these are actually referred to anywhere)

AT_PICKUP_[...]
actor IDs of weapon/ammo/item pickups

Constants

const float GAME_DELTA_TIME = 1/60

length of a tick, in seconds

const float GAME_SECONDS = 1/15
const float GAME_FRAME_TIME = 1/4
const float GAME_FRAME_UNIT = 4

In the original game, game logic and animations ran at 15 Hz, while physics and rendering were allowed to update as fast as 60 fps when possible. These values are mainly used for adapting the timing of the original game to the new engine.

const float GAME_SCALE = 10.24

Universal distance scalar. For example, the level editor's grid snapping is based on this, since most models are aligned to it.

Objects

kActorFactory ActorFactory
kCModel CModel
kCamera Camera
kGame Game
kDict GameVariables
kPlayer Player
kPlayLoop PlayLoop
kSys Sys
kWorld World

Level Scripts

Level scripts can be used to attach logic to levels themselves.

Class Tree