the symptom, in your words

"Citizen animgraph combat params are unwrapped and unnetworked"

✓ verified on engine 26.07lane: Rigging & animationposted

▸ SYMPTOM

Combat animations (attack swings, weapon raises, combat roll/slide) either don't play at all, or play on the local peer but are invisible to other players. CitizenAnimationHelper has no methods for triggering attacks or setting combat-specific states.

▸ CAUSE

The stock citizen animgraph (citizen.vanmgrph) ships combat parameters that CitizenAnimationHelper does NOT wrap. The helper only exposes locomotion + holdtype/b_jump/b_deploy. Combat params exist in the animgraph but have no helper method -- you must drive them by name via renderer.Set(...).

Additionally, the engine PlayerController (UseAnimatorControls) replicates ONLY locomotion, so combat params show on the local peer but NOT on proxies unless you replicate them yourself.

▸ FIX

Drive the combat params directly and replicate them:

snippet
// Available combat params (verified from citizen.vanmgrph):
renderer.Set("b_attack", true);           // one-shot trigger (auto-resets)
renderer.Set("holdtype_attack", 0f);      // attack variant (0-8, cycle for combos)
renderer.Set("holdtype", (int)holdType);  // HoldTypes enum (Punch=5, Swing=6, etc.)
renderer.Set("special_movement_states", (int)moveStyle); // SpecialMoveStyle (Roll=2, Slide=3)
renderer.Set("b_weapon_lower", false);    // false = weapon raised (no dedicated guard pose)

Param reference:

ParamTypeBehavior
b_attackbool (auto-reset)One-shot swing/fire trigger -- set true, it self-clears
holdtype_attackfloat (0-8)Attack variant clip for current holdtype -- cycle 0/1/2 for combos
holdtypeintHoldTypes enum: None=0, Pistol=1, Rifle=2, Shotgun=3, HoldItem=4, Punch=5, Swing=6, RPG=7, Physgun=8
special_movement_statesintSpecialMoveStyle: None=0, LedgeGrab=1, Roll=2, Slide=3 -- HELD state, not a trigger
b_weapon_lowerboolKeep false for weapon raised; no dedicated block/guard pose exists

Networking: push persistent state (holdtype, roll) via a [Sync] field applied on every peer each frame, and fire discrete triggers (b_attack) via a host-side [Rpc.Broadcast]:

snippet
[Sync] public int SyncedHoldType { get; set; }
[Sync] public int SyncedMoveStyle { get; set; }

// On every peer, every frame:
renderer.Set("holdtype", SyncedHoldType);
renderer.Set("special_movement_states", SyncedMoveStyle);

// Discrete triggers via broadcast:
[Rpc.Broadcast]
void BroadcastAttack(float variant)
{
    renderer.Set("holdtype_attack", variant);
    renderer.Set("b_attack", true);
}

Because play mode always creates a lobby, the host is a peer and receives its own broadcast, so single-player still animates correctly.

▸ WHY IT WORKS

The animgraph params exist and are fully functional -- they just lack a C# wrapper in CitizenAnimationHelper. Driving them by name with renderer.Set talks directly to the animgraph. The per-peer sync approach mirrors the engine's own locomotion replication pattern but extends it to the combat params the engine doesn't cover.

Verified on engine 26.07: seen in a real project.
s&box moves fast; an undated fix is a liability. Spot a stale detail?

Want to know when new guides or fixes drop? Join the community to help build this out. Report gotchas, flag outdated fixes, or just lurk.

Join the Discord