using System.Numerics; using AcDream.Core.Lighting; using DatReaderWriter.Types; using Xunit; namespace AcDream.Core.Tests.Lighting; public sealed class LightingHookSinkTests { [Fact] public void SetLightHook_FlipsOwnedLights() { var mgr = new LightManager(); var sink = new LightingHookSink(mgr); var light1 = new LightSource { Kind = LightKind.Point, OwnerId = 42, IsLit = true }; var light2 = new LightSource { Kind = LightKind.Point, OwnerId = 42, IsLit = true }; var other = new LightSource { Kind = LightKind.Point, OwnerId = 99, IsLit = true }; sink.RegisterOwnedLight(light1); sink.RegisterOwnedLight(light2); sink.RegisterOwnedLight(other); var hook = new SetLightHook { LightsOn = false }; sink.OnHook(entityId: 42, entityWorldPosition: Vector3.Zero, hook: hook); Assert.False(light1.IsLit); Assert.False(light2.IsLit); Assert.True(other.IsLit); // owner 99 untouched } [Fact] public void UnregisterOwner_RemovesAllOwnedLights() { var mgr = new LightManager(); var sink = new LightingHookSink(mgr); sink.RegisterOwnedLight(new LightSource { OwnerId = 7 }); sink.RegisterOwnedLight(new LightSource { OwnerId = 7 }); Assert.Equal(2, mgr.RegisteredCount); sink.UnregisterOwner(7); Assert.Equal(0, mgr.RegisteredCount); } [Fact] public void UnrelatedHook_Ignored() { var mgr = new LightManager(); var sink = new LightingHookSink(mgr); var light = new LightSource { OwnerId = 1, IsLit = true }; sink.RegisterOwnedLight(light); // Should not crash or change state for non-SetLight hooks. var noise = new SoundHook(); sink.OnHook(entityId: 1, entityWorldPosition: Vector3.Zero, hook: noise); Assert.True(light.IsLit); } } public sealed class LightInfoLoaderTests { [Fact] public void Load_EmptyLights_ReturnsEmpty() { var setup = new DatReaderWriter.DBObjs.Setup(); var result = LightInfoLoader.Load(setup, 1u, Vector3.Zero, Quaternion.Identity); Assert.Empty(result); } [Fact] public void Load_PointLight_ProducesCorrectSource() { var setup = new DatReaderWriter.DBObjs.Setup(); setup.Lights[0] = new LightInfo { ViewSpaceLocation = new Frame { Origin = new Vector3(1, 2, 3), Orientation = Quaternion.Identity, }, Color = new ColorARGB { Red = 255, Green = 200, Blue = 50, Alpha = 255 }, Intensity = 0.8f, Falloff = 8f, ConeAngle = 0f, // point }; var result = LightInfoLoader.Load(setup, ownerId: 77, entityPosition: new Vector3(100, 200, 300), entityRotation: Quaternion.Identity); Assert.Single(result); var light = result[0]; Assert.Equal(LightKind.Point, light.Kind); Assert.Equal(77u, light.OwnerId); Assert.Equal(8f, light.Range); Assert.Equal(0.8f, light.Intensity); Assert.Equal(new Vector3(101, 202, 303), light.WorldPosition); Assert.InRange(light.ColorLinear.X, 0.99f, 1.01f); } [Fact] public void Load_NonZeroConeAngle_ProducesSpot() { var setup = new DatReaderWriter.DBObjs.Setup(); setup.Lights[0] = new LightInfo { ViewSpaceLocation = new Frame { Origin = Vector3.Zero, Orientation = Quaternion.Identity }, Color = new ColorARGB { Red = 255, Green = 255, Blue = 255, Alpha = 255 }, Intensity = 1f, Falloff = 5f, ConeAngle = 0.5f, }; var result = LightInfoLoader.Load(setup, ownerId: 1, entityPosition: Vector3.Zero, entityRotation: Quaternion.Identity); Assert.Equal(LightKind.Spot, result[0].Kind); Assert.Equal(0.5f, result[0].ConeAngle); } }