Turok EX Modding Guide

FX

Each kfx file can define multiple particles to spawn when the kfx is spawned.

The number of particle types is specified at the beginning of the file with fx[N] =, followed by a block containing N sub blocks, each defining a particle type to spawn when the kfx is spawned.

The number of particle type definitions must match the value of N, or the game will crash when the kfx is spawned.

General

Key

Walls & Floors

What constitutes a "wall" or a "floor" for the purposes of various collision behaviors isn't entirely intuitive:

A floor is any sector surface, even cliff sectors.

A wall is specifically an unconnected sector edge.

Flags

These can be either 0 (false) or 1 (true).

Display

bDepthBuffer leave at 0 to be visible through walls (e.g., parts of fusion cannon explosion)
bDrawOnBottom render in front of other particles
bLensFlares must be set for lensFlares property to take effect
bOffsetFromFloor sprite is elevated by half its height to avoid clipping into the ground
bScaleLerp

continuously increment rather than multiply scale by scaleDest:

  1. scale *= scaleDest
  2. scale += scaleDest
bStopAnimOnImpact freeze sprite animation after hitting specifically the ground
bTextureWrapMirrorWidth

Mirrors the sprite (horizontally and vertically, contrary to the name and existence of bTextureWrapMirrorHeight) to form a larger image (scaled to fit in the same space).

example:

0: 1:
bTextureWrapMirrorHeight [no effect]
bWeaponView

render with first-person weapon modelview matrix

allows consistent alignment with first-person weapon irrespective of FOV, but causes inconsistent positioning in the world when FOV is not 47.5

bMuzzleEffect

offset (using muzzleOffset) relative to first-person weapon orientation

results in a tighter link than bWeaponView alone (follows waver when turning, won't lag by a frame, etc.)

also causes the effect to last for only a single frame

bFullScreen render in the center of the screen
bFadeout [unused]

Physics

bAddOffset add velocity of actor or particle that spawned this particle onto this particle's initial velocity
bBullet

affects various behavioral properties:

  • Particle is destroyed when speed drops below a certain threshold. Does not call onExpire or onWaterExpire in this case (they will still happen when lifeTime runs out).
  • See notes in Collision.
bDestroyOnWaterSurface destroy when entering/exiting water
bNoDirection use global rather than local coordinates for things like translation and offset
bNoHitSource don't register collision with whatever spawned the particle
bPerPolyCollision

makes the particle qualify for precise projectile collision

may not always be desirable, as it can cause the particle to collide with small details like vines

bProjectile orient toward player when spawned?
bRetainVelocity [unused]

Other/Unknown

bBlood

do not spawn when blood is disabled, or against actors with "no blood" flag (namely animals)

a bBlood particle also indicates that all particles that come after it in the file should only be spawned when blood is disabled (or against "no blood" actors), unless they also have bBlood set

details of the green blood violence setting are untested

bNoSpawnNear
bNoSpawnFar
bNoWallSpawn ignore onImpact contents when hitting wall or floor, respectively
bNoGroundSpawn
bAttachSource
bStickOnTarget
bActorInstance
bCrossFade
bDecalOffset
bImpactEffect [unused]
bRestrictAim [unused]

Physics

offset (v) * where to spawn this particle, relative to where the kfx itself was spawned
muzzleOffset (v)

similar to offset, but relative to first-person weapon orientation

for use with bMuzzleEffect

translationRandomGlobal increment translation by a vector with a random direction and random extent up to this amount?
translation (v) *

initial velocity = forwardSpeed * normal(translation)

speed is in terms of distance per second

forwardSpeed *
gravity

acceleration along global vertical axis, in terms of speed per second

negative values are down

gravityRandomScale works like other randomization properties, just with a different naming convention
mass

fraction of velocity along hit surface normal to retain when bouncing (after velocity is mirrored):

velocity += (1 + mass) * (velocity • normal) * normal

If bBullet is set, this instead controls max deflection angle. The two extremes are:

  • 0.0 - bounce only when hitting completely parallel to surface
  • 1.0 - always bounce, even if hitting head-on
friction fraction of horizontal velocity lost when bouncing specifically off the ground
airFriction

acceleration opposing velocity

seems to be constant based on initial velocity:

  • will cause the particle to move backward if set too high
  • causes path to curve after bouncing (instead of opposing new velocity)
waterFriction

Only affects horizontal velocity. Vertical velocity always seems to be affected by ~0.1 friction (but only when moving down).

Unlike airFriction, this isn't a constant force – the particle will actually come to a stop if waterFriction is high enough, and bouncing won't mess things up.

Existential

instances *

how many copies of this particle to spawn

any randomized properties get randomized independently for each instance (e.g., shotgun pellets don't all get the same randomized trajectory)

a negative instancesRandom can be used to make the particle spawn < 100% of the time

restart delay by a random percentage of this between spawning instances
lifeTime *

how long the particle will exist, in 115 seconds

priority

There's a limit to how many particles can exist at once. Particles with a higher priority are given precedence when that limit is reached.

Things that affect gameplay (like projectiles) should be set to 100.

Display

General

scale * size of the sprite
scaleDest * continuously modifies scale, based on bScaleLerp
shader

shader to render the sprite with

typically worldfx or worldcolorfx (the latter tends to be more colorful for translucent particless)

e.g., shader = "progs/worldcolorfx"

sprite

sprite to represent the particle with

e.g., sprite = "sprites/fx064"

lensFlares

lens flare effect to cause

e.g., lensFlares = "lensflares/turoklens.klf"

bLensFlares must be set for this to take effect

trailDefinition_1
...
trailDefinition_N

leave polygonal trails

e.g.,

trailDefinition_1 = "Trail_TekBow_Crossline_1"
trailDefinition_2 = "Trail_TekBow_Crossline_2"
drawType
  1. face camera
  2. flat along xy-plane
  3. flat along xy-plane, elevated 1 unit to avoid z-fighting with floor
  4. face camera
  5. face along surface normal
  6. face camera (billboard)
  7. not drawn?
  8. face camera
visibilityType
  1. normal?
  2. fade out as you get closer (full opacity is pretty far away)
  3. normal?
  4. hidden?
  5. normal?
animType
  1. play, then destroy when complete
  2. play, then hold last frame until lifeTime runs out
  3. loop
  4. don't draw
  5. loop, don't necessarily start at first frame?
  6. play, then hold a random frame?
  7. don't draw?
animSpeed how long to display each frame of sprite animation, in 1900 seconds (a value of 60 will display each frame for 1 lifeTime unit)
animFriction

how much to slow animation as velocity decreases?

not sure of the details; reasonable values seem to be around 0 to forwardSpeed?

negative values do not appear to cause animation to speed up as velocity decreases

Rotation

rotationOffset * radians, clockwise
rotationSpeed * radians per 115 seconds, clockwise
rotationPivotX

offset from the particles's physical location to render the sprite

uses the same coordinate system as other vectors, except relative to the camera's orientation, rather than the particles's orientation

sprite rotation orbits the particles's physical location (this is how rockets and shockwave weapon projectiles spiral)

spawned particless will center on the sprite, but hit detection, splash damage, and sounds will still be based on the particles's physical location

rotationPivotY

Color

whiteColor (c)

alters light and dark colors of the sprite, respectively

displayColor = blackColor + spriteColor * (whiteColor - blackColor)

blackColor (c)
hueRandom

color randomization

values range from 0 to 255

saturationRandom
brightnessRandom
fadeInTime fade in during first fadeInTime 115 seconds of lifeTime
fadeOutTime fade out during last fadeOutTime 115 seconds of lifeTime

Collision

onCollideActor
  1. ignore; pass right through
  2. hit and disappear
  3. hit and reflect
    • onImpact event is ignored (unless bBullet is set)
    • if bBullet is set, bounce only up to twice, and only if angle is wide, based on mass
  4. hit and bounce
    • this is supposed to be reflection with speed reduction, but seems identical to reflect?
  5. hit and push against, dealing continuous damage
onCollideWall
onCollideFloor

Events

onImpact

happens when hitting an actor, wall, or floor (based on the onCollide properties above)

contains a sub-block for each surface type it can hit:

  1. grass/wood surfaces
  2. water (rarely used*)
  3. metallic enemies
  4. hard surfaces
  5. red-blooded enemies
  6. yellow-blooded enemies
  7. undead enemies (unused?)
  8. lava
  9. tar
  10. force field
onTick happens continuously, unless underwater
onExpire

happens when lifeTime runs out, unless underwater

onWaterImpact

happens when entering (or exiting?) water

onWaterTick happens continuously instead of onTick while underwater
onWaterExpire happens instead of onExpire when lifeTime runs out, if underwater

Each of these blocks can contain any combination of the following:

fx

spawn another effect

e.g., fx = "fx/blood.kfx"

sound

play a sound

e.g., sound = "sounds/shaders/bullet_impact_13.ksnd"

damageClass

deal damage, from defs/damageInfo.txt

e.g., damageClass = "S_Damage_Target_damage_10"

ignored by onWaterImpact