diff --git a/tests/AcDream.Core.Tests/Rendering/Issue93TownNetworkFountainRoomLightInspectionTests.cs b/tests/AcDream.Core.Tests/Rendering/Issue93TownNetworkFountainRoomLightInspectionTests.cs index 4dc7f889..aa7e1159 100644 --- a/tests/AcDream.Core.Tests/Rendering/Issue93TownNetworkFountainRoomLightInspectionTests.cs +++ b/tests/AcDream.Core.Tests/Rendering/Issue93TownNetworkFountainRoomLightInspectionTests.cs @@ -134,4 +134,53 @@ public class Issue93TownNetworkFountainRoomLightInspectionTests _out.WriteLine($" meshRefs survivor count = {survivors} (markerSkipped={markerSkipped} gfxNull={gfxNull}) " + $"=> GameWindow.cs:7324 would {(survivors == 0 ? "DROP" : "KEEP")} this entity"); } + + /// + /// Follow-up (same session, 2026-07-09): user confirmed lighting improved but + /// reported missing candle flames + fountain water particles. Hypothesis tested: + /// same mesh-empty hydration-gate class as the light fix, just for + /// Setup.DefaultScript instead of Setup.Lights (GpuWorldState.cs:221 fires + /// ambient particle scripts from DefaultScript.DataId per hydrated entity — the + /// SAME lb.Entities list EntityHydrationRules.ShouldKeepEntity gates). + /// REFUTED — do not re-chase this specific mechanism for #79/#93's particle + /// residual. The fountain (0x02000AA3) has a SURVIVING mesh part (1/1) AND a + /// real DefaultScript (0x33000B21, almost certainly the water spray) — never + /// dropped by the gate, mesh or no. The "16 ring objects" guess (0x02001967) was + /// WRONG: real mesh, DefaultScript==0 — not candles, no script at all. Filed as + /// its own issue (missing ambient particles) — the remaining suspects are + /// downstream of hydration entirely: does EntityScriptActivator/ + /// PhysicsScriptRunner actually fire DefaultScript=0x33000B21 for dat-hydrated + /// (ServerGuid==0) entities at runtime, does that script resolve to a real + /// PhysicsScriptTable entry, and does the resulting emitter reach the particle + /// renderer. NOT investigated further this session — separate root cause, + /// separate fix. + /// + [Theory] + [InlineData(0x02000AA3u)] // the fountain itself (cell 0x00070144 center) + [InlineData(0x02001967u)] // the 16 ring objects around the fountain (candles?) + [InlineData(0x020018C5u)] // the other cell-center static + public void FountainAndCandleSetup_DefaultScriptAndMeshSurvivorCount_Dump(uint setupId) + { + var datDir = ResolveDatDir(); + if (datDir is null) { _out.WriteLine("SKIP: no dat dir"); return; } + using var dats = new DatCollection(datDir, DatAccessType.Read); + + var setup = dats.Get(setupId); + Assert.NotNull(setup); + + _out.WriteLine($"=== Setup 0x{setupId:X8}: Parts={setup!.Parts.Count} DefaultScript.DataId=0x{setup.DefaultScript.DataId:X8} Lights={setup.Lights.Count} ==="); + + var flat = AcDream.Core.Meshing.SetupMesh.Flatten(setup); + int survivors = 0; + foreach (var mr in flat) + { + if (AcDream.Core.Meshing.GfxObjDegradeResolver.IsRuntimeHiddenMarker(dats, mr.GfxObjId)) continue; + if (dats.Get(mr.GfxObjId) is null) continue; + survivors++; + } + bool wouldBeKeptByCurrentFix = AcDream.Core.Meshing.EntityHydrationRules.ShouldKeepEntity(survivors, setup.Lights.Count); + _out.WriteLine($" flattened={flat.Count} meshSurvivors={survivors} " + + $"hasDefaultScript={setup.DefaultScript.DataId != 0} " + + $"=> current ShouldKeepEntity(mesh,lights) = {wouldBeKeptByCurrentFix}"); + } }