acdream/tests/AcDream.Core.Tests/Lighting/LightingHookSinkTests.cs
Erik 1e70a5a484 fix(G.3 A7): torch range = Falloff x 1.5 (retail rangeAdjust) — wider pools (#133)
Retail PrimD3DRender::config_hardware_light (0x0059ad30) sets the hardware light
Range = Falloff * rangeAdjust (1.5, global 0x00820cc4). We used Range = Falloff, so
torches reached only 2/3 of retail -> tight 'candle/spotlight' bubbles in dungeons.
Match retail's reach. Ambient 0.20 confirmed retail-faithful (the 0.30 was CreatureMode,
not world cells). Lighting suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 20:58:03 +02:00

119 lines
4 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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(12f, light.Range); // Falloff 8 × retail rangeAdjust 1.5 (config_hardware_light)
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);
}
}