Player animation script (mp/playeranim.script)¶
What the file controls¶
The player animation script is a condition table. It answers:
which animation a player’s legs or torso plays for a state and move type;
which animation plays when an event occurs, such as firing or landing;
which animation plays when the player changes state;
which canned animation set is available to script code.
It does not define animation names. Every animation name in the file must
exist in the loaded multiplayer animation tree. The player animation table
is rebuilt from that tree, and a name that is absent from the tree is a load
error.
The file is not GSC. It has its own parser, its own section keywords, and its own condition and command grammar. It is also not the AI animation layer. The AI layer is a set of GSC scripts described in AI animation scripts (animscripts/*.gsc).
File and load order¶
The engine loads exactly one path:
mp/playeranim.script
The path is fixed in the stock build. The file is read once and cached. The parser then runs twice:
Before animation trees are finalized, the parser registers the animation names used by the file as runtime animation names. For each new name it records a reference to the
multiplayertree throughScr_FindAnim.After the static player animation table is rebuilt from the
multiplayertree, the parser runs again. This pass stamps animation-group state flags, event flags, andblendtimevalues onto the static entries, and fills each command slot duration from the entry duration.
The two passes explain why a name can be accepted in the first pass and still
fail if the multiplayer tree does not define it. Add the node to
animtrees/multiplayer.atr first, then reference it here.
The parser does not accept a path argument, a second file, or an include.
There is no #include and no section inheritance.
Sections¶
A file is a sequence of sections. A section keyword is recognized only at brace depth zero. The section keywords are, in parser table order:
defines
animations
canned_animations
statechanges
events
A section keyword at a deeper brace depth is an error. A new section resets the current animation group and the current event.
The parser starts in the defines section. A set token before the first
section keyword is parsed as a definition. Any other token that is not a
section keyword is skipped. A non-set token in defines is also
skipped. This tolerance lets stray text in the stock file pass without
becoming syntax. Comments are removed by the tokenizer.
DEFINES¶
DEFINES builds named bit masks for bitmask condition types. The grammar is:
set <condition-type> <alias> = <bit-expression>
Only three condition types are bitmask types and therefore valid after
set:
WEAPONS;WEAPONCLASS;MOVETYPE.
A set for any other condition type is an error:
BG_AnimParseAnimScript: can not make a define of type '%s'
Stock examples:
set weapons stickgrenades = RGD-33russianfrag_mp AND Stielhandgranate_mp
set weaponclass autofire = mg AND smg
set movetype moving = walk AND walkcr AND walkcrbk AND walkbk AND run AND runbk AND runcr AND runcrbk AND walkprone AND walkpronebk
The bit expression language is:
expression = [ "MINUS" | "NOT" ] clause
{ ( "AND" | "MINUS" | "NOT" ) clause } ;
clause = value | "all" | "none" ;
value = <alias or stock value name>
The operators are unusual:
ANDcombines the accumulated masks with a bitwise OR. It is not an intersection.MINUSremoves the next clause’s mask from the result.NOTis an alias forMINUS.A value name is looked up first in the aliases already defined for the same condition type, then in the stock value table.
allsets both 32-bit words to all ones.nonesets bit 0. It does not produce an empty mask. The parser appliesnoneas soon as it reads the token, so a followingANDreportsBG_ParseConditionBits: unexpected 'AND'. Placenonelast when it shares an expression with other values.A comma is not an operator inside a
setexpression. It ends the expression. The section parser then handles the next token normally: asetstarts a new definition, and anything else is skipped.
Do not put two values next to each other without an operator. The parser joins the words into one alias name.
A bitmask condition matches when any bit in the script mask intersects the client’s condition bits. The alias is therefore a set of alternatives, not a requirement that every bit be present.
Each condition type accepts at most 16 aliases. The alias name is local to its condition type.
Condition types¶
The stock parser recognizes eleven condition types. Names are case-insensitive.
Type |
Mode |
Values |
Meaning |
|---|---|---|---|
|
Bitmask |
The dynamic weapon table. Stock files usually define aliases. |
Weapon identity. |
|
Bitmask |
|
Weapon class. |
|
Equal |
|
Mounted position. |
|
Equal |
|
Vehicle type. |
|
Equal |
|
Vehicle motion. |
|
Bitmask |
The move-type table listed below. |
Movement family. |
|
Equal |
No value table. |
Bare keyword. The parser supplies value 1. |
|
Equal |
No value table. |
Bare keyword. The parser supplies value 1. |
|
Equal |
No value table. |
Bare keyword. The parser supplies value 1. |
|
Equal |
|
Weapon position. |
|
Equal |
|
Strafe state. |
The three bare keywords take no value:
crouching
underhand
firing
Writing crouching 1 is wrong. The parser will read 1 as the next
condition type and fail.
States and move types¶
ANIMATIONS and CANNED_ANIMATIONS use the same state and move-type
tables. State names are case-insensitive.
State |
Index |
Meaning |
|---|---|---|
|
0 |
No immediate threat. |
|
1 |
Suspicious or searching. |
|
2 |
Enemy known, not yet in combat. |
|
3 |
Active combat. |
Move types are the inner blocks under a state. The stock table is:
Index |
Name |
Index |
Name |
|---|---|---|---|
0 |
|
9 |
|
1 |
|
10 |
|
2 |
|
11 |
|
3 |
|
12 |
|
4 |
|
13 |
|
5 |
|
14 |
|
6 |
|
15 |
|
7 |
|
16 |
|
8 |
|
17 |
|
The table has no straferight or strafeleft entry. Strafe state is a
condition type, STRAFING, not a move type.
ANIMATIONS¶
ANIMATIONS is the main state table. Its grammar is:
STATE <state>
{
<move-type>
{
<conditions>
{
<commands>
}
}
}
Empty state blocks are valid:
STATE RELAXED
{
}
Empty move-type blocks are also valid. A state can contain several move-type blocks, and a move type can contain several condition blocks.
Stock structure:
STATE RELAXED
{
}
STATE ALERT
{
}
STATE COMBAT
{
idle
{
default
{
both stand_idle
}
}
}
The first condition block that matches wins. Put specific conditions before
general ones, and end the list with default.
Conditions¶
When a condition block has conditions, it lists them all on one line. The condition parser stops at the end of the line, so a condition line ends before the opening brace. Conditions are ANDed: every condition must match for the block to be selected.
weaponclass lmg, movetype prone
{
both c_mg_dp28_prone_fire
}
A bitmask condition value uses the same expression syntax as DEFINES. The
expression ends at a comma or at the end of the line. Write the comma directly
after the preceding value, as in weaponclass lmg, movetype prone. An
equal-mode value has one trailing comma removed. A condition with no value
table, such as crouching, underhand, or firing, reads no value
token. A comma after one is read as the next condition type, which is not a
condition name. Separate bare conditions with whitespace.
default means an unconditional block:
default
{
both stand_idle
}
Use default alone. The parser returns immediately when it sees
default, but it does not clear conditions that were already parsed earlier
in the same list.
An empty condition list is an error:
BG_ParseConditions: no conditions found
An equal-mode condition with no value is an error:
BG_ParseConditions: expected condition value, found end of line
A script may have at most 11 conditions:
BG_ParseConditions: exceeded maximum conditions (11)
CANNED_ANIMATIONS¶
CANNED_ANIMATIONS uses the same grammar as ANIMATIONS and stores its
tables separately in table->canned.
The stock multiplayer file contains no CANNED_ANIMATIONS section. The
recovered source has the parser and the table layout, but no recovered stock
call site reads the canned table. Treat the section as reserved parser
support, not as a documented stock content path.
If you add one, keep its animation names in the multiplayer tree, as with
the main table.
STATECHANGES¶
STATECHANGES describes animations for a transition between two states. The
grammar is:
statechange <to-state> <from-state>
{
<conditions>
{
<commands>
}
}
The parser reads the destination state first and the source state second. It stores the entry at:
statechanges[toState][fromState]
The runtime call is:
BG_AnimScriptStateChange(player, fromState, toState);
That function reads the same [to][from] cell. Keep the declaration order
and the runtime argument order distinct when reading or writing a
STATECHANGES block.
The stock multiplayer file contains no STATECHANGES section. The parser
and the lookup path exist, but no stock example establishes a content
convention.
A state change with no matching entry returns -1. A dead player also
returns -1.
EVENTS¶
EVENTS describes one-shot reactions. The grammar is:
<event>
{
<conditions>
{
<commands>
}
}
The stock event names are:
Index |
Event |
Index |
Event |
|---|---|---|---|
0 |
|
8 |
|
1 |
|
9 |
|
2 |
|
10 |
|
3 |
|
11 |
|
4 |
|
12 |
|
5 |
|
13 |
|
6 |
|
14 |
|
7 |
|
15 |
|
Events select the first matching script, then randomly select one of that script’s commands. The random choice differs from state animation selection, which is deterministic per client.
DEATH is the only event that still plays when the player is dead. Every
other event returns -1 once pmType is PM_TYPE_DEAD or later.
Commands¶
A condition block contains up to eight command lines. Each command line names up to two body-part slots:
<slot> <animation> [duration N] [turretanim] [blendtime N]
[<slot> <animation> ...]
[sound <alias>]
The slots are:
Slot |
Meaning |
|---|---|
|
Lower-body animation. |
|
Upper-body animation. |
|
One animation on both body parts. It ends the slot sequence. |
A command line can carry at most two slots. The common stock form is one legs slot and one torso slot:
legs c_mp_jeep_passenger_idle torso pt_stand_ads_pistol
both is a single slot and terminates the sequence:
both c_mg_dp28_prone_fire
Slot parameters:
Parameter |
Effect |
|---|---|
|
Overrides the slot duration with |
|
Marks the animation as a turret animation. Valid only on |
|
Sets the shared static animation entry’s blend time to |
|
Plays a sound alias with the command. A raw |
If duration is omitted, the slot uses the static animation entry’s
duration.
The stock file has duration values from 5 to 550. A small value is intentional in some jump and takeoff entries; it is not a missing zero.
Examples from the stock multiplayer file:
torso pt_prone_shoot_auto duration 150
weaponclass lmg, movetype prone
{
both c_mg_dp28_prone_fire
}
both pb_runjump_takeoff duration 5 blendtime 100
legs c_mp_jeep_passenger_idle torso pt_stand_ads_pistol
mounted vehDriver, vehicle flak88
{
both standMG42_aim turretanim
}
Command selection and playback¶
State animation selection walks the state table from the requested state down
to RELAXED. A non-empty list that yields no matching script does not stop
the walk; the next lower state is tried. This gives COMBAT a fallback path
through ALERT and QUERY to RELAXED.
Within a list, the first script whose conditions all match is selected. For state animations, the command is:
commands[clientNum % commandCount]
This is deterministic per client. For events and state changes, the command is selected randomly:
commands[randrange(0, commandCount)]
The random range includes zero and excludes commandCount.
The selected command then runs through BG_AnimScriptAnimation:
Play slot 0, if present.
Play slot 1, if present.
Play the command’s sound alias, if present.
Return the duration if a legs or both animation started, otherwise
-1.
BG_ExecuteCommand converts that result to a boolean. It also refuses to
play a state animation once the player is dead.
State animations run with setTimer false. Events and state changes run
with setTimer true.
Recovered quirks¶
The recovered body contains three behaviors that content authors should know.
The second slot’s duration is computed from
command.duration[0], notcommand.duration[1]. If a command gives the legs and torso slots different durations, the torso slot uses the legs value.The second slot’s legs-started test checks
command.bodyPart[0], notcommand.bodyPart[1]. The second playback result can therefore overwrite the first result in the return path.The bitmask parser clears the low word of its accumulator per condition but does not clear the high word per condition. A later bitmask condition in the same list can inherit high bits from an earlier one.
These are stock behaviors, not recommendations. Avoid relying on them in new content.
Stock multiplayer examples¶
The stock file uses the sections in this order:
Section |
Approximate stock line |
|---|---|
|
18 |
|
98 |
|
100 |
|
105 |
|
110 |
|
1019 |
STATE RELAXED and STATE ALERT are empty in the stock file. The state
fallback path still reaches COMBAT content from those states.
A weapon-class and stance combination:
weaponclass pistol, movetype crouching, weapon_position ads
{
torso pt_crouch_shoot_ads_pistol
}
A mounted vehicle pose:
mounted vehDriver, vehicle flak88
{
both standMG42_aim turretanim
}
A jump command with both an explicit duration and a shared blend time:
both pb_runjump_takeoff duration 5 blendtime 100
Stale comment block¶
A large comment block above ANIMATIONS describes an older condition and
command vocabulary. The recovered parser does not implement that vocabulary.
Do not copy the comment block as authority.
Comment block says |
Actual stock parser |
|---|---|
Condition |
There is no |
Condition |
No such condition type. The recovered table has |
Condition |
No such condition type. |
Condition |
No such condition type. |
Body part |
The body-part table has only |
Move types |
The move-type table has no such entries. Use the |
|
The parser requires a sound alias and rejects a |
The comment block is useful as a historical description of the file’s intent. It is not a grammar.
Limits and diagnostics¶
Limit |
Value |
|---|---|
File size |
At most 99,998 bytes. A file of 99,999 bytes or more is rejected. |
Scripts per list |
|
Global scripts |
2,048. |
Conditions per script |
|
Commands per condition block |
8. A ninth command reports
|
Aliases per condition type |
|
Runtime animation names |
512. A longer registration reports
|
Animation name length |
Less than 64 bytes. |
Important diagnostics:
Message |
Cause |
|---|---|
|
The file is missing, empty, or at least 99,999 bytes. |
|
A section keyword appears inside a block, or a block is malformed. |
|
A |
|
A |
|
A |
|
|
|
A condition block is empty. |
|
An equal-mode condition has no value. |
|
A body-part slot has no animation name. |
|
A command parameter is not recognized. |
|
|
|
|
|
The |
Implementation basis¶
Source |
Role |
|---|---|
|
File load, section dispatch, |
|
Condition list parsing and condition-count limit. |
|
Bitmask clause parsing, aliases, |
|
Body-part slots, duration, turretanim, blendtime, sound. |
|
Stock condition, state, move-type, event, and body-part tables. |
|
Condition evaluation and first-match selection. |
|
State fallback, command selection, playback, events, and state changes. |
|
Runtime animation-name registration and limits. |
|
Static player table rebuild from the |
|
Script, command, list, and table layout. |
|
Stock examples, section order, and the stale comment block. |