UFOAI TD1 Particles Guide

Table of Contents

What is a Particle?

Particles are small, short lived entities which are controlled by a script. UFOAI uses particles to depict bullets, rockets, explosions, smoke, laser beams, and so on. Some effects are a combination of several particles – in fact a particle can spawn more particles.

Particle are local entities, like actors and aliens. Like other entities the particle has an origin, a think function,

Units of Measurement

The quake2 engine measures time in milliseconds. For instance, the cl.time variable is defined as “int time” in the client_state_t struct, which means that its size varies depending on the size of an int on the local system. Specifically, f the local system defines integers as 16 bits, then cl.time has a maximum value of 32767, which means it overflows after about 33 seconds. The gnu-linux-386 architecture defines 32 bit integers, which means the cl.time value is good for about 24 days. We shall assume 32 bit integers and therefore a non-overflowing cl.time value.

The UFOAI TD1 particle system measures time in seconds. Time values are stored in float type variables. Basically, the particle functions multiply the quake2 time by a scaling factor of 0.001.

Distance is measured in texels. A typical male actor is 56 texels tall and 24 texels wide. If the height of an average white man is about 180 cm then a texel is about 3.2 cm, or 5/4 inches. A UFOTD1 “cell” is 32x32x72 texels which is about 1x1x2.3 meters.

Particle Definitions

Every particle in the UFOAI game is an instantiation of a particle definition. Particle definitions are declared by scripts in the base/ufos directory. A particle defintion consists of three functions: a mandatory init function and optional think and run functions. Each function contains commands. A command may or may not have a reference (ie an argument). Here is an example of a particle definition (taken from UFOAI TD1 base/ufos/weaponsfx.ufo).

// ==================
// flamethrower
// ==================

particle fireBall
{
  init
  {
    image  sfx/fireball
    blend  add
    style  facing
    tps    2.5
  }
  run
  {
    push   vector "80 80"
    mul    *t
    add    vector "10 10"
    pop    *size
  }
  think { tfade  out }
}

Particle Commands

In the init and run functions each command is written on a separate line, but that is not required. In fact, the whole script could be written without any line feeds or carriage returns. Line feeds/carriage returns are not required – but whitespace must appear around every token. Brace brackets, function names, commands and arguments are tokens. The fireBall particle only uses nine commands. In fact, there are nineteen particle commands. Commands have a data type in the same way that variables have a data typed. Commands may have a reference (ie an argument). The reference and the command must be of the same type. Here is a table of all the particle commands, the data type of the command, and a description of what the command does.

Particle Command

Command Type

Description

end

no reference

Marks the end of a function. This command is not usually seen in script code – it is automatically added to the end of the function by the parser.

push

untyped

Put reference on the stack. The reference may be any type.

pop

untyped

Pop value from the stack to the indicated variable.

kpop

untyped


add

float, pos, vector or color

Pop value from the stack, add it to reference value, push the sum to the stack.

sub

float, pos, vector or color

Pop value from the stack, subtract reference, push the result to the stack.

mul

float, pos, vector or color

Pop value from the stack, multiply it by reference, push the result to the stack

div

float, pos, vector or color


sin

only float

calculate sine of reference, push result to the stack

cos

only float

calculate cosine of reference, push result to the stack

tan

only float

calculate tangent of reference, push result to the stack

rand

float, pos, vector or color

push a random value between 0 and the argument onto the stack.

crand

float, pos, vector or color

push a random value between -argument and +argument onto the stack.

v2

no reference

pop the last two values from the stack, use these values to create a pos type value, and push the pos value onto the stack

v3

no reference

pop the last three values from the stack, use these values to create a vector type value, and push this vector value onto the stack

v4

no reference

pop the last four values from the stack, use these values to create a color type value, and push this color value onto the stack

kill

no reference

De-activates the particle.

spawn

only string

Instantiates a new particle. The argument must be a defined particle name. The spawned particle inherits the parent's s, v and a values.

nspawn

only string

Instantiates one or more new particles. The argument must be a defined particle name. The number of particles to create is taken from the stack. The spawned particles inherit the parent's s, v and a values.




Particle References

A command may include a target of the operation. This is called the “reference”. The reference may take several forms: a constant, a stack, or a variable. If the reference is a constant and the command accepts more than one type of reference, then the constant will be preceded by a string specifying the type (float, pos, vector, or color). The octalthorpe (#) reference means that the command operates on the top element on the stack. Variables are more complicated ...

Particle Variables

UFOAI TD1 defines twenty-two variables. Each variable has a specific type. Each variable corresponds to a property of the particle (ie a value in its local entity or particle type structure). Most commands can take a variable as an argument. When used as an argument, variables are prefixed with an asterisk “*”. For instance pop *v moves a value from the stack to variable v.

Variable

Data Type

Property

image

string

What the particle looks like; an image from the base/pics directory. This is mostly used for particles which do not have models, ie impact particles.

model

string

The particle's shape; a model from the base/models directory. Models are only rendered if the particle is an entity, which only happens for projectiles.

blend

blend

controls how the particle's image is mixed with the background

style

style


tfade

fade

“think fade”

ffade

fade

“frame fade”

size

position

Typically size[0] is the length of the weapon in the direction in which it is fired. Some particle represent beams from beams weapons – these beams are considered to travel infinitely fast ie they are instantaneous. For these instantaneous particles, the size is equal to the distance between the origin and the target.

scale

vector


color

color

particle color

a

vector

particle acceleration

v

vector

particle velocity

s

vector

particle location

angles

vector

<Pitch, yaw, roll>. Typically this is used for projectiles; it points from the muzzle coordinate towards the impact coordinate.

t

float

time that the particle has been active
MEASURED IN SECONDS!

dt

float

time increment for rendering this particle

life

float

maximum lifetime of the particle
MEASURED IN SECONDS!

tps

float

Number of times per second to run the think function.
Note that this only applies to the particle think function, not the entity think function.

lastthink

float

Time (in seconds) when the think function was last executed.

frame

integer


endframe

integer


fps

float

Number of times to show the image (see above) each second.

lastframe

float

Time (in seconds) when the image was last shown.

You are a coder so you should already know float and integer types. A position type is a set of two positive integers; a vector type is a set of three floats; a color type is a set of four floats, each float having a value between 0 and 1 inclusive (normalized red green blue and alpha channels). Style, fade and blend have specific allowed values defined in source/game/shared.c as shown below:

Blend Values

Style Values

Fade Values

replace

facing

none

blend

rotated

in

add

beam

out

filter

line

sin

invfilter


saw



blend

Assignment Command

The assignment command is simply the name of the variable followed by the value. In the fireBall example image sfx/fireball assigns the string value “sfx/fireball” to the image variable. Note that strings to not require quotation marks. Also, remember that the data types of variable and value must match.

Vector Components

It is possible to specify the individual components of a vector or color. This is done by adding a suffix to the argument. The suffix consists of a period “.” followed by the index of the desired component. For instance, in the particle laserPulse definition:

push float 2.2
pop *size.2

Moves the value 2.2 to size[1].

Projectile Particles

Weapons fire projectiles: beams, bullets, rockets, etc. When a weapons fires, the LE_AddProjectile function creates an entity, sets the s variable to the location of the muzzle of the gun and spawns particles representing the projectiles. The init function runs as soon as the particle spawns. The le->ptl element points to the particle, so projectiles are both entities and particles.

Next, the LE_AddProjectile function sets the particle's angle attribute to point toward the target. As far as I can tell, this does two things: it rotates the particle image so that the particle (eg a beam) is pointed the right way, and similarly for finite speed particle models. AFAIK the angles element has nothing to do with motion because that is accomplished using le->mins and/or the v and a variables.

Infinite speed projectiles

If the speed of a projectile is set to zero, then it is an infinite speed projectile: eg laser beam, bullet, SMG. In this case, LE_AddProjectile sets the length of the projectile (size[0]) to the distance between the muzzle and the target and sets the s variable to the midpoint between the muzzle and the target. If the weapons has impact effects, then LE_AddProjectile spawns the impact particles. Then LE_AddProjectile returns. LE_AddProjectile does not kill the particle – all infinite speed projectiles contain a kill command in the think function, so the particle will disappear when the particle entity performs its first think routine. The lifetime of such a particle can be controlled in the init function by manipulating the tps and lastthink variables.

Finite speed projectiles

If the speed of a projectile is not zero, then it is a finite speed projectile: a rocket, napalm (from the flamethrower), or tachyon burst. These projectiles fly from target to impact. LE_AddProjectile does the following:

The LET_Projectile function implements motion for finite speed projectiles. Remember that the velocity of a projectile is stored in the le->mins vector. Each time the local entity does a think function LET_Projectile which moves the projectile allow the mins vector towards its target. For this reason, the particle definitions for finite speed projectiles never use the v or a variables.

Impact Particles

Impact particles are a little different from projectile particles – most importantly, impact particles are not entities. Since they are not entities, they do not receive an entity think function. Instead, once per frame the CL_Frame function calls the CL_ParticleRun function. For each active particle CL_ParticleRun:

If you want an impact particle to move then you must set the v or a variables in its particle definition.