Player animation script (``mp/playeranim.script``) ==================================================== .. meta:: :description: Source-derived reference for the stock Call of Duty: United Offensive multiplayer player animation script: sections, conditions, states, move types, commands, events, state changes, and runtime selection. :keywords: call of duty, united offensive, cod uo, playeranim.script, animation script, anim conditions, statechange, canned animations Status and authority -------------------- This page specifies ``mp/playeranim.script`` as implemented by the recovered stock Call of Duty: United Offensive game module. The authority is: * ``src/bg/bg_animation_script_parser.c`` for the file and section parser; * ``src/bg/bg_parse_conditions.c`` and ``src/bg/bg_parse_condition_bits.c`` for condition clauses and aliases; * ``src/bg/bg_parse_commands.c`` for animation commands; * ``src/bg/bg_animation_conditions.c`` for condition evaluation and script selection; * ``src/bg/bg_animation_execution.c`` for playback and event dispatch; * ``src/bg/bg_animation_script_data.c`` for the stock name tables; * ``src/bg/bg_animation_lookup.c`` for animation-name registration; * ``src/bg/bg_animation_finalize.c`` for the static player animation table. The page describes the stock multiplayer file. It does not describe a later Call of Duty animation-script dialect, a single-player file, or a custom compatibility extension. 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 :doc:`ai_animation_scripts`. File and load order ------------------- The engine loads exactly one path: .. code-block:: text 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 ``multiplayer`` tree through ``Scr_FindAnim``. #. After the static player animation table is rebuilt from the ``multiplayer`` tree, the parser runs again. This pass stamps animation-group state flags, event flags, and ``blendtime`` values 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: .. code-block:: text 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: .. code-block:: text set = 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: .. code-block:: text BG_AnimParseAnimScript: can not make a define of type '%s' Stock examples: .. code-block:: text 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: .. code-block:: text expression = [ "MINUS" | "NOT" ] clause { ( "AND" | "MINUS" | "NOT" ) clause } ; clause = value | "all" | "none" ; value = The operators are unusual: * ``AND`` combines the accumulated masks with a bitwise OR. It is not an intersection. * ``MINUS`` removes the next clause's mask from the result. * ``NOT`` is an alias for ``MINUS``. * A value name is looked up first in the aliases already defined for the same condition type, then in the stock value table. * ``all`` sets both 32-bit words to all ones. * ``none`` sets bit 0. It does not produce an empty mask. The parser applies ``none`` as soon as it reads the token, so a following ``AND`` reports ``BG_ParseConditionBits: unexpected 'AND'``. Place ``none`` last when it shares an expression with other values. * A comma is not an operator inside a ``set`` expression. It ends the expression. The section parser then handles the next token normally: a ``set`` starts 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. .. list-table:: :header-rows: 1 :widths: 20 12 44 24 * - Type - Mode - Values - Meaning * - ``WEAPONS`` - Bitmask - The dynamic weapon table. Stock files usually define aliases. - Weapon identity. * - ``WEAPONCLASS`` - Bitmask - ``RIFLE``, ``MG``, ``SMG``, ``LMG``, ``PISTOL``, ``GRENADE``, ``ROCKETLAUNCHER``, ``TURRET``, ``SPOTTER``, ``NON-PLAYER``, ``FLAMETHROWER``. - Weapon class. * - ``MOUNTED`` - Equal - ``MG42``, ``vehDriver``, ``vehGunner``, ``vehPassenger1`` through ``vehPassenger4``. - Mounted position. * - ``VEHICLE`` - Equal - ``TANK``, ``JEEP``, ``FLAK88``. - Vehicle type. * - ``VEHICLE_MOTION`` - Equal - ``IDLE``, ``FORWARD``, ``REVERSING``. - Vehicle motion. * - ``MOVETYPE`` - Bitmask - The move-type table listed below. - Movement family. * - ``UNDERHAND`` - Equal - No value table. - Bare keyword. The parser supplies value 1. * - ``CROUCHING`` - Equal - No value table. - Bare keyword. The parser supplies value 1. * - ``FIRING`` - Equal - No value table. - Bare keyword. The parser supplies value 1. * - ``WEAPON_POSITION`` - Equal - ``HIP``, ``ADS``. - Weapon position. * - ``STRAFING`` - Equal - ``NOT``, ``LEFT``, ``RIGHT``. - Strafe state. The three bare keywords take no value: .. code-block:: text 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. .. list-table:: :header-rows: 1 :widths: 16 10 74 * - State - Index - Meaning * - ``RELAXED`` - 0 - No immediate threat. * - ``QUERY`` - 1 - Suspicious or searching. * - ``ALERT`` - 2 - Enemy known, not yet in combat. * - ``COMBAT`` - 3 - Active combat. Move types are the inner blocks under a state. The stock table is: .. list-table:: :header-rows: 1 :widths: 10 34 10 34 * - Index - Name - Index - Name * - 0 - ``** UNUSED **`` - 9 - ``WALKPRONEBK`` * - 1 - ``IDLE`` - 10 - ``RUN`` * - 2 - ``IDLECR`` - 11 - ``RUNBK`` * - 3 - ``IDLEPRONE`` - 12 - ``RUNCR`` * - 4 - ``WALK`` - 13 - ``RUNCRBK`` * - 5 - ``WALKBK`` - 14 - ``TURNRIGHT`` * - 6 - ``WALKCR`` - 15 - ``TURNLEFT`` * - 7 - ``WALKCRBK`` - 16 - ``CLIMBUP`` * - 8 - ``WALKPRONE`` - 17 - ``CLIMBDOWN`` 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: .. code-block:: text STATE { { { } } } Empty state blocks are valid: .. code-block:: text 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: .. code-block:: text 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. .. code-block:: text 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: .. code-block:: text 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: .. code-block:: text BG_ParseConditions: no conditions found An equal-mode condition with no value is an error: .. code-block:: text BG_ParseConditions: expected condition value, found end of line A script may have at most 11 conditions: .. code-block:: text 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: .. code-block:: text statechange { { } } The parser reads the destination state first and the source state second. It stores the entry at: .. code-block:: text statechanges[toState][fromState] The runtime call is: .. code-block:: c 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: .. code-block:: text { { } } The stock event names are: .. list-table:: :header-rows: 1 :widths: 10 40 10 40 * - Index - Event - Index - Event * - 0 - ``PAIN`` - 8 - ``CLIMBMOUNT`` * - 1 - ``DEATH`` - 9 - ``CLIMBDISMOUNT`` * - 2 - ``FIREWEAPON`` - 10 - ``RELOAD`` * - 3 - ``JUMP`` - 11 - ``CROUCH_TO_PRONE`` * - 4 - ``JUMPBK`` - 12 - ``PRONE_TO_CROUCH`` * - 5 - ``LAND`` - 13 - ``MELEEATTACK`` * - 6 - ``DROPWEAPON`` - 14 - ``LMG_DEPLOY`` * - 7 - ``RAISEWEAPON`` - 15 - ``LMG_BREAKDOWN`` 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: .. code-block:: text [duration N] [turretanim] [blendtime N] [ ...] [sound ] The slots are: .. list-table:: :header-rows: 1 :widths: 18 82 * - Slot - Meaning * - ``legs`` - Lower-body animation. * - ``torso`` - Upper-body animation. * - ``both`` - 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: .. code-block:: text legs c_mp_jeep_passenger_idle torso pt_stand_ads_pistol ``both`` is a single slot and terminates the sequence: .. code-block:: text both c_mg_dp28_prone_fire Slot parameters: .. list-table:: :header-rows: 1 :widths: 20 80 * - Parameter - Effect * - ``duration N`` - Overrides the slot duration with ``N`` milliseconds. The runtime adds 50 ms to the value before starting the animation. * - ``turretanim`` - Marks the animation as a turret animation. Valid only on ``both``. * - ``blendtime N`` - Sets the shared static animation entry's blend time to ``N`` milliseconds. This is not a per-command value. It affects every use of that animation. * - ``sound `` - Plays a sound alias with the command. A raw ``.wav`` path is rejected: ``wav files not supported, only sound scripts``. 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: .. code-block:: text 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: .. code-block:: text commands[clientNum % commandCount] This is deterministic per client. For events and state changes, the command is selected randomly: .. code-block:: text 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]``, not ``command.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]``, not ``command.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: .. list-table:: :header-rows: 1 :widths: 34 66 * - Section - Approximate stock line * - ``DEFINES`` - 18 * - ``ANIMATIONS`` - 98 * - ``STATE RELAXED`` - 100 * - ``STATE ALERT`` - 105 * - ``STATE COMBAT`` - 110 * - ``EVENTS`` - 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: .. code-block:: text weaponclass pistol, movetype crouching, weapon_position ads { torso pt_crouch_shoot_ads_pistol } A mounted vehicle pose: .. code-block:: text mounted vehDriver, vehicle flak88 { both standMG42_aim turretanim } A jump command with both an explicit duration and a shared blend time: .. code-block:: text 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. .. list-table:: :header-rows: 1 :widths: 30 70 * - Comment block says - Actual stock parser * - Condition ``position`` - There is no ``position`` condition. Use ``WEAPON_POSITION`` with ``HIP`` or ``ADS``. * - Condition ``enemy_weapon`` - No such condition type. The recovered table has ``WEAPONS`` and ``WEAPONCLASS``. * - Condition ``underwater`` - No such condition type. * - Condition ``leaning`` - No such condition type. * - Body part ``turret`` - The body-part table has only ``LEGS``, ``TORSO``, and ``BOTH``. ``turretanim`` is a command parameter on ``both``. * - Move types ``straferight`` and ``strafeleft`` - The move-type table has no such entries. Use the ``STRAFING`` condition with ``LEFT`` or ``RIGHT``. * - ``sound `` - The parser requires a sound alias and rejects a ``.wav`` path. The comment block is useful as a historical description of the file's intent. It is not a grammar. Limits and diagnostics ---------------------- .. list-table:: :header-rows: 1 :widths: 34 66 * - Limit - Value * - File size - At most 99,998 bytes. A file of 99,999 bytes or more is rejected. * - Scripts per list - 128. * - Global scripts - 2,048. * - Conditions per script - 11. * - Commands per condition block - 8. A ninth command reports ``BG_ParseCommands: exceeded maximum number of animations (8)``. * - Aliases per condition type - 16. * - Runtime animation names - 512. A longer registration reports ``BG_AnimationIndexForString: exceeded maximum runtime animations (512)``. * - Animation name length - Less than 64 bytes. Important diagnostics: .. list-table:: :header-rows: 1 :widths: 40 60 * - Message - Cause * - ``Couldn't load player animation script %s`` - The file is missing, empty, or at least 99,999 bytes. * - ``BG_AnimParseAnimScript: unexpected '%s'`` - A section keyword appears inside a block, or a block is malformed. * - ``BG_AnimParseAnimScript: expected condition type string`` - A ``set`` line has no condition type. * - ``BG_AnimParseAnimScript: can not make a define of type '%s'`` - A ``set`` uses an equal-mode condition. * - ``BG_AnimParseAnimScript: expected '=', found '%s'`` - A ``set`` line is missing ``=``. * - ``BG_AnimParseAnimScript: expected state type`` - ``STATE`` has no state name. * - ``BG_ParseConditions: no conditions found`` - A condition block is empty. * - ``BG_ParseConditions: expected condition value, found end of line`` - An equal-mode condition has no value. * - ``BG_ParseCommands: expected animation`` - A body-part slot has no animation name. * - ``BG_ParseCommands: unknown parameter '%s'`` - A command parameter is not recognized. * - ``BG_ParseCommands: Turret animations can only be played on the 'both' body part`` - ``turretanim`` is on ``legs`` or ``torso``. * - ``wav files not supported, only sound scripts`` - ``sound`` names a raw wave file. * - ``Could not find animation tree '%s'`` - The ``multiplayer`` tree is missing when the player table is built. Authoring checklist ------------------- #. Add every animation name to ``animtrees/multiplayer.atr`` before using it here. #. Put specific condition blocks before general ones. #. End a condition list with a single ``default`` block. #. Write a comma directly after a bitmask value. Separate other conditions with whitespace. #. Use ``legs`` and ``torso`` for split-body poses, and ``both`` for a single full-body animation. #. Use ``duration`` only when the asset duration is wrong for the state. #. Use ``blendtime`` deliberately. It changes the shared animation entry, not one command. #. Use a sound alias, not a ``.wav`` path. #. Check the ``STATE`` and event names against the stock tables above. #. Rebuild the ``multiplayer`` tree after adding a node, then reload the map. Related pages ------------- Animation tree syntax, sync groups, and pruning are specified in :doc:`animtrees`. The AI animation behavior scripts are specified in :doc:`ai_animation_scripts`. Implementation basis -------------------- .. list-table:: :header-rows: 1 :widths: 56 44 * - Source - Role * - ``src/bg/bg_animation_script_parser.c`` - File load, section dispatch, ``DEFINES`` and ``ANIMATIONS`` structure. * - ``src/bg/bg_parse_conditions.c`` - Condition list parsing and condition-count limit. * - ``src/bg/bg_parse_condition_bits.c`` - Bitmask clause parsing, aliases, ``AND``, ``MINUS``, ``NOT``, ``all``, ``none``. * - ``src/bg/bg_parse_commands.c`` - Body-part slots, duration, turretanim, blendtime, sound. * - ``src/bg/bg_animation_script_data.c`` - Stock condition, state, move-type, event, and body-part tables. * - ``src/bg/bg_animation_conditions.c`` - Condition evaluation and first-match selection. * - ``src/bg/bg_animation_execution.c`` - State fallback, command selection, playback, events, and state changes. * - ``src/bg/bg_animation_lookup.c`` - Runtime animation-name registration and limits. * - ``src/bg/bg_animation_finalize.c`` - Static player table rebuild from the ``multiplayer`` tree. * - ``src/qcommon/bg_animation_types.h`` - Script, command, list, and table layout. * - ``mp/playeranim.script`` - Stock examples, section order, and the stale comment block.