Third-person player characters now animate every action with readable
intent (all networked, cel style preserved):
- Look-around: upper body follows the owner's camera pitch — distributed
over spine/neck/head so aiming up/down reads on the whole silhouette
(driven by the already-synced HeadPivot rotation on remotes)
- Reload: the baked PistolReload clip plays as a one-shot whenever the
equipped weapon starts reloading (local edge-detect poll -> new synced
action counter replays it on every peer)
- Grenade throw: new Throw clip baked from the library's overhead swing
(Sword_Attack retarget); triggers on throw, synced the same way
- Shot recoil: visible kick on the shooter's model (shoulders snap back,
forearms rise, fast decay) driven by the existing fire-effects RPC —
remote players' shots now look like shots
- Slide: trailing arm braces against the ground for balance on top of the
feet-first pose
- Armed idle confirmed in capture: weapon held at ready, not arms-down
Plumbing: synced_action/synced_action_seq added to all three player
spawners' replication configs; ACTIONS table on the model maps names to
clips + lock times.
Verified: 18 clips resolve (incl Throw), smoke 0 failures, movement
11/11, acoustics PASSED; third-person run + armed idle captures clean.
Co-Authored-By: Claude Fable 5 <[email protected]>
- SkinnedPlayerModel: generic play_oneshot() (generalizes the Land lock),
Hit and Dance in the clip table (Dance loops), restartable clips
- Hit flinch: victims visibly react on every peer's screen via the existing
damage broadcast — no new RPC
- Dance emote on B: toggles while grounded and idle, breaks on any movement;
synced to remotes via synced_is_dancing (all three spawner configs)
- Anime radial speed-lines overlay (canvas shader): fades in past ~1.2x walk
speed, spikes on dash, widescreen-corrected clear center
- New input action 'emote' (B) in project.godot
11/11 FSM tests, spawn smoke 0 failures.
Co-Authored-By: Claude Fable 5 <[email protected]>
- toon.gdshader: banded diffuse with cool-tinted shadows (3 tones), stepped
specular, rim light, and an optional world-triplanar albedo path so level
geometry keeps the 0.5 m grid with no UVs
- toon_outline.gdshader: inverted-hull ink outline, applied via
material_overlay so it works on skinned (deforming) meshes
- LevelMaterials: tinted() now returns toon grid materials; toonify()/
apply_toon_recursive() convert imported character/prop materials in place
- SkinnedPlayerModel: characters get toon shading + outline on load
- LevelEnvironment: shared stylized environment for all maps — saturated
anime sky, linear tonemap (filmic/ACES crush cel bands), bloom for
emissives, saturation+contrast grade, light depth haze; test level and
dust2 now use it and the procedural arena (which had NO environment)
gets it plus runtime toonification of its baked geometry
Co-Authored-By: Claude Fable 5 <[email protected]>
- Quake-style air-strafe accelerate (real speed gain from strafing; external
speed from dash/rockets never clamped)
- Wall run: keeps entry momentum (incl. upward carry), accelerates along the
wall instead of hard-setting velocity, gravity fades in over the run
- Slide: slope physics (gravity projected along floor accelerates downhill),
flat-ground decel instead of multiplicative friction, entry boost, direct
slide->wallrun and slide->dash transitions
- Dash: cooldown 10s -> 2s, per-player (was a static shared across instances),
momentum fully kept on exit, FOV punch event
- Grapple: taut pendulum with active rope control (W reels in, S pays out),
release keeps swing energy
- Wall climb: carries upward momentum in, jump-off kick, shared vault helper
- Crouch capsule: single owner in the machine, smoothly lerped, ceiling check;
camera eye height follows via crouch factor (no more head snapping)
- Landing: machine emits 'land' events scaled by impact; camera dip + pitched
down thud; footsteps get pitch variation
- Camera rig: event-driven (land dip, dash FOV kick, vault pitch impulse),
slide roll tilt
- Model: Jump vs Fall split by vertical velocity, Land one-shot on heavy
landings, wall-run body lean (synced to remotes via synced_wall_side)
Co-Authored-By: Claude Fable 5 <[email protected]>
Layer a procedural SkeletonModifier3D (ShooterPoseModifier) on top of the base
clip so the character reads like a movement-shooter avatar:
- Directional lean: banks into the movement direction (right/left/back) and
blends smoothly for diagonals, driven by velocity relative to facing.
- Slide: leans the torso back and pitches the head up to look forward, instead
of the base clip's forward-torso/legs-out "spine break".
- Airborne: plays a composed Jump pose (weapon ready) rather than a flailing
fall.
- Weapon hold: the base clip already keeps the arms down (weapon at the hip);
on ADS both arms lift and swing in toward centre-front to aim. ADS is read
from the active weapon and synced (synced_is_ads) so remote players raise
their weapons too.
The controller feeds movement direction + ADS to SkinnedPlayerModel.set_locomotion()
each frame (works for local and remote via synced velocity/rotation). All
rotations are authored in skeleton space (fwd=+Z, up=+Y, right=-X) and converted
per-bone; tuning constants are at the top of ShooterPoseModifier.
Verified by rendering the poses (idle/strafe/back/slide/ads) in a real Godot
viewport. Smoke test 30/30.
Co-Authored-By: Claude Fable 5 <[email protected]>
Character pipeline (tools/):
- sketchfab_import.py: search/download via Sketchfab Download API with
license/attribution tracking (SKETCHFAB_API_TOKEN)
- autorig.py: headless Blender auto-rig — fits a Mixamo-named skeleton
to unrigged humanoids, binds automatic weights
- merge_animations.py: merges the shared Mixamo-skeleton animation
library onto any rigged character, strips root motion, canonical names
- pipeline.py: one command chains download -> rig -> animate -> register
In-game skin system:
- SkinManager autoload reads skins.json (auto-written by the pipeline)
- SkinnedPlayerModel rewritten: canonical clips with fallback chains,
blend times, speed-scaled locomotion, weapon bone attachment
- First-person: full animated body for the owner, head hidden via
SkeletonModifier3D; third-person: full model for other players
- Skin selector in main menu; skin id synced in multiplayer
- Fixed GLBLoader crash (GLTFDocument.get_animation_count doesn't exist)
Multiplayer sync overhaul:
- Movement is now client-authoritative: the owning peer simulates
locally (no input round-trip), server keeps health/kills/death
- Remote players interpolate synced_position/velocity with
extrapolation and snap-on-teleport
- Knockback/impulses routed to the simulating peer
Audio:
- AudioManager autoload: SFX/Weapons/Footsteps/UI/Music buses, pooled
3D players, variation + pitch randomization, auto-registration from
assets/sounds
Docs: 3D_ASSET_PIPELINE.md rewritten end-to-end, new ASSET_SOURCES.md
(non-procedural animation/map/sound sources) and SOUND_DESIGN.md.
Verified with debug/spawn_smoke_test.gd (headless: 24/24 checks pass).
Co-Authored-By: Claude Fable 5 <[email protected]>