← Back to home

Fixing a 22-Year-Old Bug in Jedi Academy

A bug in the animation event system that prevented NPC variant sounds from playing

Background

Star Wars Jedi Knight: Jedi Academy released in September 2003. The game includes an animation event system that triggers sounds at specific animation frames - things like stormtrooper armor sounds on death, or protocol droid footsteps.

For certain NPC variants, these animation sounds weren't playing. The audio files existed in the game data, and the configuration files were set up correctly, but the sounds would be skipped at runtime for most NPC variants.

The Bug

The animation event system loads sound definitions from config files like models/players/stormtrooper/animevents.cfg. When loaded, these events are tagged with a modelOnly identifier set to the model name (e.g., "stormtrooper").

At runtime, when deciding whether to play a sound, the code compared this tag against the NPC's NPC_type value. Here's the problem:

NPC variants share the same model but have different type names.
A "stcommander" uses the stormtrooper model, but its NPC_type is "stcommander" - not "stormtrooper". The comparison fails, and the sound is skipped.

// OLD (buggy) code
hstring myModel = g_entities[entNum].NPC_type;
// ^ Returns "stcommander" for stormtrooper commanders
if (animEvents[i].modelOnly == myModel.handle())
// ^ "stormtrooper" != "stcommander" - FAILS!
// NEW (fixed) code
hstring myModel = level.knownAnimFileSets[animFileIndex].filename;
// ^ Returns "stormtrooper" (the actual model being used)
if (animEvents[i].modelOnly == myModel.handle())
// ^ "stormtrooper" == "stormtrooper" - WORKS!

Affected NPCs

The bug affected 15 NPC variants across 4 model types: stormtrooper commanders and officers, imperial protocol droids, dark jedi cultist variants, and reborn duelists.

ModelAffected VariantsMissing Sounds
stormtrooperstcommander, stofficer, stofficeralt, rockettrooper, rockettrooper_wDeath sounds (armor clatter)
protocolprotocol_impWalking sounds, death sounds
cultist7 variants (destroyer, drain, grip, lightning, saber, etc.)Victory gesture
reborn_newreborn_dual, reborn_staffVictory gesture

Interactive Demo

Click the buttons below to see (and hear) the difference. The OLD method shows the buggy behavior - variants are silent. The NEW method shows the fix - all NPCs play their sounds correctly.

PLAYS = Sound triggers
SILENT = Sound skipped (bug)
playerModel: stormtrooper
Imperial Stormtroopers
Death sounds (armor clatter)

OLD Method (NPC_type)

Compares modelOnly="stormtrooper" against NPC_type

NEW Method (animFileSet.filename)

Compares modelOnly="stormtrooper" against filename="stormtrooper"

playerModel: protocol
Protocol Droids (C-3PO style)
Walk sounds & death sounds

OLD Method (NPC_type)

Compares modelOnly="protocol" against NPC_type

NEW Method (animFileSet.filename)

Compares modelOnly="protocol" against filename="protocol"

playerModel: cultist
Dark Jedi Cultists
Victory gesture sound

OLD Method (NPC_type)

Compares modelOnly="cultist" against NPC_type

NEW Method (animFileSet.filename)

Compares modelOnly="cultist" against filename="cultist"

playerModel: reborn_new
New Reborn Models
Victory gesture sound

OLD Method (NPC_type)

Compares modelOnly="reborn_new" against NPC_type

NEW Method (animFileSet.filename)

Compares modelOnly="reborn_new" against filename="reborn_new"

Click a button to test sound playback

The Fix

The fix changes the comparison to use the animation file set's filename instead of the NPC's type name. Since the filename is always the base model name (e.g., "stormtrooper"), it matches correctly regardless of which variant NPC is using that model.

Timeline

September 2003

Jedi Knight: Jedi Academy releases with the bug present in the original code

2013

Raven Software releases the source code. The OpenJK project begins maintaining an open-source version

November 2025

Bug discovered and fix submitted to OpenJK