Adds IAnimationHookSink + AnimationHookRouter for fan-out of animation
hooks to downstream subsystems (audio, particles, combat, renderer
mutators). GameWindow.TickAnimations now drains ConsumePendingHooks
every tick and broadcasts each hook via the router with the entity's
world position pre-computed.
The router is a composite sink: register N sinks once at startup, each
sees every hook. Registration is idempotent, unregister works, and a
throwing sink no longer poisons dispatch (each OnHook call is wrapped in
try/catch so one bad subsystem can't halt the whole animation tick).
A NullAnimationHookSink is provided for headless tests / offline mode.
6 router tests verify: single/multi sink fan-out, idempotent register,
unregister, throwing-sink isolation, null-sink no-op.
Total: 376 Core tests + 109 Core.Net = 485 (up from 479).
This closes Phase E.1 plumbing; E.2 (audio) and E.3 (particles) will
each register a concrete sink that translates their hook types into
real-world effects.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>