using System.Numerics; using AcDream.App.Rendering; using DatReaderWriter.Types; namespace AcDream.App.Tests.Rendering; /// /// The SetOmega hook is what moves AC's ambient flyers; ignoring it left the /// birds and butterflies animating in place. /// public sealed class StaticAnimatingOmegaHookTests { private static SetOmegaHook Omega(float x, float y, float z) => new() { Axis = new Vector3(x, y, z) }; [Fact] public void NoHooksLeavesOmegaUntouched() { Assert.False( RetailStaticAnimatingObjectScheduler.TryResolveOmega([], out _)); } [Fact] public void AHookBatchWithoutSetOmegaLeavesOmegaUntouched() { // A batch that carries other hooks must not zero a previously set // omega: retail's set_omega is only ever called BY the hook. Assert.False( RetailStaticAnimatingObjectScheduler.TryResolveOmega( [new SoundTableHook()], out _)); } [Fact] public void SetOmegaIsTakenVerbatim() { Assert.True( RetailStaticAnimatingObjectScheduler.TryResolveOmega( [Omega(0f, 0f, -0.027f)], out Vector3 omega)); // CPhysicsObj::set_omega @ 0x0050F6D0 assigns the axis outright — no // scaling, and animate_static_object does not multiply it by elapsed // time either. Assert.Equal(new Vector3(0f, 0f, -0.027f), omega); } [Fact] public void TheLastSetOmegaInABatchWins() { // Retail executes a hook batch in order and each set_omega overwrites // the vector outright rather than accumulating. Assert.True( RetailStaticAnimatingObjectScheduler.TryResolveOmega( [Omega(0f, 0f, -0.02f), Omega(0f, 0f, 0.05f)], out Vector3 omega)); Assert.Equal(new Vector3(0f, 0f, 0.05f), omega); } }