feat(overhaul): port S5 particle cell visibility
Publish the exact completed walk landscape set as the typed retained particle-view product. Apply retail CLandCell membership versus constant-true non-null CEnvCell eligibility while preserving the x87 distance and AP-116 behavior. Delete the null-root terrain visibility reconstruction and the dead drawable-cell point-light feedback chain. Retire AP-117, correct AP-85 and AD-21, and keep directional shadows, building degrade, AP-232, probes, RHI, shaders, and DAT outside this chunk. Automated return: Release 0 warnings/0 errors; Core VFX 111/111; App particle/frame/renderer/terrain 146/146; warmed production allocation 1/1 at 0 B. First official hermetic artifact s5-c1-hermetic-20260904 is preserved at 16759/16760 with only the stale 162-row assertion; after the bounded 161 correction, exact pin 1/1 and fresh s5-c1-hermetic-corrected-20260904 16760/16760. InstalledDat 385 pass, 10 documented failures, 1 documented skip, no new identity. Mutation 1: making EnvCell eligibility set-dependent first failed ParticleSystemTests.cs:553 Assert.True, expected true actual false. Mutation 2: making outdoor eligibility constant true first failed ParticleSystemTests.cs:559 Assert.False, expected false actual true. Mutation 3: feeding the diagnostic union first failed WorldSceneRendererTests.cs:279 HashSet equality, expected [16842755], actual [16843008, 16842755]. Mutation 4: restoring CollectVisibleCells first failed TerrainParticleCellVisibilityTests.cs:37 and named TerrainModernRenderer.cs. Mutation 5: restoring ObserveDrawableCells first failed TerrainParticleCellVisibilityTests.cs:37 and named WorldRenderFrameBuilder.cs. Mutation 6: changing the inclusive boundary from <= to < first failed ParticleSystemTests.cs:519 Assert.True, expected true actual false. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
efb0756190
commit
94a6b5ef39
18 changed files with 352 additions and 269 deletions
|
|
@ -1,58 +1,42 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Rendering;
|
||||
using AcDream.App.Tests.Rendering.Walk;
|
||||
|
||||
namespace AcDream.App.Tests.Rendering;
|
||||
|
||||
/// <summary>
|
||||
/// S3 landing hygiene (H2): <see cref="TerrainModernRenderer.CollectVisibleCells"/>
|
||||
/// is frustum-only now — its clip-plane-list and NDC-AABB scissor
|
||||
/// parameters and the CPU/GPU clip-region equivalence check they fed had
|
||||
/// exactly one production caller (<see cref="TerrainModernRenderer.Draw"/>),
|
||||
/// and that caller never actually passed either (grep). The doorway-clip/scissor
|
||||
/// rejection cases this file used to pin (<c>RejectsCellsOutsideDoorwayClipPlanes</c>,
|
||||
/// <c>RejectsCellsOutsideDoorwayScissorAabb</c>, <c>UnionsCellsFromEveryLandscapeSlice</c>)
|
||||
/// pinned a mechanism nothing in production ever fed — deleted with the
|
||||
/// parameters. What remains still pins the one thing this method's callers
|
||||
/// actually rely on: it publishes the visible terrain cell set per landblock,
|
||||
/// respecting an explicit frustum when one is given.
|
||||
/// Campaign OVERHAUL S5-c1: the null-root terrain path remains a safety draw,
|
||||
/// but it may not reconstruct or publish a competing cell-visibility answer.
|
||||
/// The same architecture pin protects the deleted point-light feedback seam.
|
||||
/// </summary>
|
||||
public sealed class TerrainParticleCellVisibilityTests
|
||||
{
|
||||
[Fact]
|
||||
public void CollectVisibleCells_PublishesLandscapeCellsWithoutEntitySurvivors()
|
||||
public void ProductionRetainsNoReconstructedVisibilityOrLightFeedbackSymbols()
|
||||
{
|
||||
var cells = new HashSet<uint>();
|
||||
string sourceRoot = Path.Combine(WalkOracleTraceRepoRoot.Find(), "src");
|
||||
string[] production = Directory.GetFiles(
|
||||
sourceRoot,
|
||||
"*.cs",
|
||||
SearchOption.AllDirectories);
|
||||
string[] forbidden =
|
||||
[
|
||||
"CollectVisibleCells",
|
||||
"TerrainVisibleCellIds",
|
||||
"ObserveDrawableCells",
|
||||
"ClearDrawableCells",
|
||||
];
|
||||
|
||||
TerrainModernRenderer.CollectVisibleCells(
|
||||
cells,
|
||||
0xA9B4FFFFu,
|
||||
Vector3.Zero,
|
||||
zMin: 0f,
|
||||
zMax: 20f,
|
||||
frustum: null);
|
||||
|
||||
Assert.Equal(64, cells.Count);
|
||||
Assert.Contains(0xA9B40001u, cells);
|
||||
Assert.Contains(0xA9B40040u, cells);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CollectVisibleCells_ForceDrawnParentStillUsesCellFrustum()
|
||||
{
|
||||
var cells = new HashSet<uint>();
|
||||
FrustumPlanes frustum = FrustumPlanes.FromViewProjection(Matrix4x4.Identity);
|
||||
|
||||
// The parent landblock may have entered TerrainModernRenderer's draw
|
||||
// list through neverCullLandblockId. The cell collector deliberately
|
||||
// receives no such bypass and still rejects its off-frustum cells.
|
||||
TerrainModernRenderer.CollectVisibleCells(
|
||||
cells,
|
||||
0xA9B4FFFFu,
|
||||
new Vector3(100f, 100f, 100f),
|
||||
zMin: 100f,
|
||||
zMax: 120f,
|
||||
frustum);
|
||||
|
||||
Assert.Empty(cells);
|
||||
foreach (string symbol in forbidden)
|
||||
{
|
||||
string[] owners = production
|
||||
.Where(path => File.ReadAllText(path).Contains(
|
||||
symbol,
|
||||
StringComparison.Ordinal))
|
||||
.Select(path => Path.GetRelativePath(sourceRoot, path))
|
||||
.OrderBy(path => path, StringComparer.Ordinal)
|
||||
.ToArray();
|
||||
Assert.True(
|
||||
owners.Length == 0,
|
||||
$"Production symbol {symbol} remains in: {string.Join(", ", owners)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public sealed class ParticleVisibilityControllerTests
|
|||
|
||||
controller.BeginFrame(Vector3.Zero);
|
||||
controller.UseWorldView();
|
||||
controller.MarkVisibleCells(new HashSet<uint> { 0x01010001u });
|
||||
controller.MarkVisibleLandscapeCells(new HashSet<uint> { 0x01010001u });
|
||||
controller.CompleteFrame();
|
||||
controller.Apply(particles, 1f);
|
||||
|
||||
|
|
@ -84,15 +84,125 @@ public sealed class ParticleVisibilityControllerTests
|
|||
|
||||
controller.BeginFrame(Vector3.Zero);
|
||||
controller.UseWorldView();
|
||||
controller.MarkVisibleCells([0x01010001u]);
|
||||
controller.MarkVisibleLandscapeCells([0x01010001u]);
|
||||
controller.CompleteFrame();
|
||||
|
||||
controller.BeginFrame(new Vector3(1000f, 1000f, 0f));
|
||||
controller.UseWorldView();
|
||||
controller.MarkVisibleCells([0x02020001u]);
|
||||
controller.MarkVisibleLandscapeCells([0x02020001u]);
|
||||
controller.AbortFrame();
|
||||
controller.Apply(particles, 1f);
|
||||
|
||||
Assert.True(Assert.Single(particles.EnumerateEmitters()).ViewEligible);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CompleteAbortAndResetPublishOnlyCompleteLandscapeTransactions()
|
||||
{
|
||||
var particles = new ParticleSystem(new EmitterDescRegistry(), new Random(1));
|
||||
int first = particles.SpawnEmitter(
|
||||
new EmitterDesc
|
||||
{
|
||||
DatId = 0x32000004u,
|
||||
Type = ParticleType.Still,
|
||||
MaxDegradeDistance = 100f,
|
||||
MaxParticles = 1,
|
||||
},
|
||||
Vector3.Zero,
|
||||
attachedObjectId: 43u);
|
||||
int second = particles.SpawnEmitter(
|
||||
new EmitterDesc
|
||||
{
|
||||
DatId = 0x32000005u,
|
||||
Type = ParticleType.Still,
|
||||
MaxDegradeDistance = 100f,
|
||||
MaxParticles = 1,
|
||||
},
|
||||
Vector3.Zero,
|
||||
attachedObjectId: 44u);
|
||||
particles.UpdateEmitterOwnerCell(first, 0x0101_0001u);
|
||||
particles.UpdateEmitterOwnerCell(second, 0x0101_0002u);
|
||||
var controller = new ParticleVisibilityController();
|
||||
|
||||
controller.BeginFrame(Vector3.Zero);
|
||||
controller.UseWorldView();
|
||||
controller.MarkVisibleLandscapeCells([0x0101_0001u]);
|
||||
controller.CompleteFrame();
|
||||
|
||||
controller.BeginFrame(new Vector3(1000f, 0f, 0f));
|
||||
controller.UseWorldView();
|
||||
controller.MarkVisibleLandscapeCells([0x0101_0002u]);
|
||||
controller.AbortFrame();
|
||||
controller.Apply(particles, 1f);
|
||||
Assert.True(particles.EnumerateEmitters().Single(e => e.Handle == first).ViewEligible);
|
||||
Assert.False(particles.EnumerateEmitters().Single(e => e.Handle == second).ViewEligible);
|
||||
|
||||
controller.BeginFrame(Vector3.Zero);
|
||||
controller.UseWorldView();
|
||||
controller.MarkVisibleLandscapeCells([0x0101_0002u]);
|
||||
controller.CompleteFrame();
|
||||
controller.Apply(particles, 1f);
|
||||
Assert.False(particles.EnumerateEmitters().Single(e => e.Handle == first).ViewEligible);
|
||||
Assert.True(particles.EnumerateEmitters().Single(e => e.Handle == second).ViewEligible);
|
||||
|
||||
controller.Reset();
|
||||
controller.Apply(particles, 1f);
|
||||
Assert.All(particles.EnumerateEmitters(), emitter => Assert.False(emitter.ViewEligible));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(0u)]
|
||||
[InlineData(0x0101_0100u)]
|
||||
public void MarkVisibleLandscapeCells_RejectsNonLandscapeIds(uint cellId)
|
||||
{
|
||||
var controller = new ParticleVisibilityController();
|
||||
controller.BeginFrame(Vector3.Zero);
|
||||
controller.UseWorldView();
|
||||
|
||||
ArgumentException error = Assert.Throws<ArgumentException>(
|
||||
() => controller.MarkVisibleLandscapeCells([cellId]));
|
||||
|
||||
Assert.Contains($"0x{cellId:X8}", error.Message, StringComparison.Ordinal);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProductionApply_WarmedParticleViewPathDoesNotAllocate()
|
||||
{
|
||||
var particles = new ParticleSystem(new EmitterDescRegistry(), new Random(1));
|
||||
int outdoor = particles.SpawnEmitter(
|
||||
new EmitterDesc
|
||||
{
|
||||
DatId = 0x32000006u,
|
||||
Type = ParticleType.Still,
|
||||
MaxDegradeDistance = 100f,
|
||||
MaxParticles = 1,
|
||||
},
|
||||
Vector3.Zero,
|
||||
attachedObjectId: 45u);
|
||||
int environment = particles.SpawnEmitter(
|
||||
new EmitterDesc
|
||||
{
|
||||
DatId = 0x32000007u,
|
||||
Type = ParticleType.Still,
|
||||
MaxDegradeDistance = 100f,
|
||||
MaxParticles = 1,
|
||||
},
|
||||
Vector3.Zero,
|
||||
attachedObjectId: 46u);
|
||||
particles.UpdateEmitterOwnerCell(outdoor, 0x0101_0001u);
|
||||
particles.UpdateEmitterOwnerCell(environment, 0x0101_0100u);
|
||||
var controller = new ParticleVisibilityController();
|
||||
controller.BeginFrame(Vector3.Zero);
|
||||
controller.UseWorldView();
|
||||
controller.MarkVisibleLandscapeCells([0x0101_0001u]);
|
||||
controller.CompleteFrame();
|
||||
|
||||
long allocated = ZeroAllocationProbe.MeasureWarmed(
|
||||
() => controller.Apply(particles, 1f),
|
||||
batchSize: 256,
|
||||
warmupBatches: 2,
|
||||
samples: 4);
|
||||
|
||||
Assert.True(allocated == 0, $"Particle view allocated {allocated} bytes.");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -316,7 +316,7 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
Assert.DoesNotContain(
|
||||
"one stable far-to-near stream keyed", architecture, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
Assert.Contains("162 active rows", register, StringComparison.Ordinal);
|
||||
Assert.Contains("161 active rows", register, StringComparison.Ordinal);
|
||||
int apSectionStart = register.IndexOf(
|
||||
"## 3. Documented approximation (AP)", StringComparison.Ordinal);
|
||||
int apSectionEnd = register.IndexOf(
|
||||
|
|
@ -338,7 +338,7 @@ public sealed class WalkStaticStreamPopulatorTests
|
|||
Assert.Single(File.ReadLines(registerPath), static line =>
|
||||
line.StartsWith("| AP-243 |", StringComparison.Ordinal));
|
||||
Assert.Equal(
|
||||
162,
|
||||
161,
|
||||
File.ReadLines(registerPath).Count(static line =>
|
||||
line.StartsWith("| AP-", StringComparison.Ordinal)));
|
||||
Assert.Contains("greater than 50 m", register, StringComparison.Ordinal);
|
||||
|
|
|
|||
|
|
@ -52,9 +52,6 @@ public sealed class WorldRenderFrameBuilderTests
|
|||
in foundation,
|
||||
waitingForLogin: true,
|
||||
activeDayGroup: null);
|
||||
var drawableCells = new HashSet<uint> { 0x01010001u };
|
||||
builder.ObserveDrawableCells(drawableCells);
|
||||
builder.ClearDrawableCells();
|
||||
|
||||
Assert.Equal(
|
||||
[
|
||||
|
|
@ -74,8 +71,6 @@ public sealed class WorldRenderFrameBuilderTests
|
|||
Assert.Null(result.ClipRoot);
|
||||
Assert.True(visibility.WaitingForLogin);
|
||||
Assert.Equal(foundation, environment.Foundation);
|
||||
Assert.Same(drawableCells, environment.DrawableCells);
|
||||
Assert.Equal(1, environment.ClearCount);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -254,7 +249,7 @@ public sealed class WorldRenderFrameBuilderTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void Runtime_environment_uses_resident_lights_independent_of_drawable_cells()
|
||||
public void Runtime_environment_uses_all_resident_lights()
|
||||
{
|
||||
const uint visibleCell = 0x01010100u;
|
||||
const uint hiddenCell = 0x01010101u;
|
||||
|
|
@ -280,10 +275,6 @@ public sealed class WorldRenderFrameBuilderTests
|
|||
lightingUbo: null,
|
||||
new WorldRenderRangeState(4, 12),
|
||||
skyPes: null);
|
||||
var borrowed = new HashSet<uint> { visibleCell };
|
||||
environment.ObserveDrawableCells(borrowed);
|
||||
borrowed.Clear();
|
||||
borrowed.Add(hiddenCell);
|
||||
WorldCameraFrame camera = CameraFrame(new FlyCamera());
|
||||
WorldRootFrame roots = default;
|
||||
RenderFrameFoundation foundation = default;
|
||||
|
|
@ -293,7 +284,6 @@ public sealed class WorldRenderFrameBuilderTests
|
|||
Assert.Contains(visibleLight, lighting.PointSnapshot);
|
||||
Assert.Contains(hiddenLight, lighting.PointSnapshot);
|
||||
|
||||
environment.ClearDrawableCells();
|
||||
environment.Prepare(in camera, in roots, in foundation, activeDayGroup: null);
|
||||
|
||||
Assert.Contains(visibleLight, lighting.PointSnapshot);
|
||||
|
|
@ -544,10 +534,6 @@ public sealed class WorldRenderFrameBuilderTests
|
|||
{
|
||||
public RenderFrameFoundation Foundation { get; private set; }
|
||||
|
||||
public IReadOnlySet<uint>? DrawableCells { get; private set; }
|
||||
|
||||
public int ClearCount { get; private set; }
|
||||
|
||||
public void Prepare(
|
||||
in WorldCameraFrame camera,
|
||||
in WorldRootFrame roots,
|
||||
|
|
@ -558,10 +544,6 @@ public sealed class WorldRenderFrameBuilderTests
|
|||
Foundation = foundation;
|
||||
}
|
||||
|
||||
public void ObserveDrawableCells(IReadOnlySet<uint> drawableCells) =>
|
||||
DrawableCells = drawableCells;
|
||||
|
||||
public void ClearDrawableCells() => ClearCount++;
|
||||
}
|
||||
|
||||
private sealed class RecordingAnimated(
|
||||
|
|
|
|||
|
|
@ -194,9 +194,7 @@ public sealed class WorldSceneRendererTests
|
|||
"flat:clip",
|
||||
"flat:sky",
|
||||
"flat:terrain",
|
||||
"visibility:mark",
|
||||
"flat:entities",
|
||||
"frame:clear-cells",
|
||||
"passes:disable-clip",
|
||||
"particles:global",
|
||||
"flat:weather",
|
||||
|
|
@ -220,7 +218,7 @@ public sealed class WorldSceneRendererTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void PViewWorld_PublishesTheWalkVisibleCellsOnce()
|
||||
public void PViewWorld_PublishesTheWalkLandscapeCellsOnce()
|
||||
{
|
||||
var root = new LoadedCell
|
||||
{
|
||||
|
|
@ -240,7 +238,6 @@ public sealed class WorldSceneRendererTests
|
|||
"passes:begin",
|
||||
"pview:draw",
|
||||
"visibility:mark",
|
||||
"frame:observe-cells",
|
||||
"diagnostics:pview",
|
||||
"passes:disable-clip",
|
||||
"particles:none",
|
||||
|
|
@ -265,7 +262,7 @@ public sealed class WorldSceneRendererTests
|
|||
}
|
||||
|
||||
[Fact]
|
||||
public void PViewWorld_PublishesLookInCellsToParticleAndLightVisibility()
|
||||
public void PViewWorld_PublishesOnlyLandscapeCellsToParticleVisibility()
|
||||
{
|
||||
var root = new LoadedCell
|
||||
{
|
||||
|
|
@ -276,15 +273,11 @@ public sealed class WorldSceneRendererTests
|
|||
|
||||
rig.Renderer.Render(default);
|
||||
|
||||
// Retail gates effects on cell->IsInView() (ShouldDrawParticles
|
||||
// @0x0050FE60), and a cell entered through a building portal
|
||||
// (DrawBuilding @0x0059F2A0 -> PView::ConstructView @0x005A57B0) is
|
||||
// drawn by the same traversal as a flooded cell. The particle gate and
|
||||
// light-candidate scope must therefore receive the walk's one visited
|
||||
// set, including look-ins.
|
||||
Assert.Contains(0x01010003u, rig.Visibility.MarkedCells);
|
||||
Assert.NotNull(rig.Frames.ObservedCells);
|
||||
Assert.Contains(0x01010003u, rig.Frames.ObservedCells!);
|
||||
// The borrowed result deliberately has three distinct products:
|
||||
// DrawableCells contains the EnvCell, VisibleCells is their diagnostic
|
||||
// union, and VisibleLandscapeCells contains only this outdoor id.
|
||||
Assert.Equal([0x0101_0003u], rig.Visibility.MarkedCells);
|
||||
Assert.DoesNotContain(0x0101_0100u, rig.Visibility.MarkedCells);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -925,15 +918,6 @@ public sealed class WorldSceneRendererTests
|
|||
return frame;
|
||||
}
|
||||
|
||||
public IReadOnlySet<uint>? ObservedCells { get; private set; }
|
||||
|
||||
public void ObserveDrawableCells(IReadOnlySet<uint> drawableCells)
|
||||
{
|
||||
calls.Add("frame:observe-cells");
|
||||
ObservedCells = new HashSet<uint>(drawableCells);
|
||||
}
|
||||
|
||||
public void ClearDrawableCells() => calls.Add("frame:clear-cells");
|
||||
}
|
||||
|
||||
private sealed class EntitySource : IWorldSceneEntitySource
|
||||
|
|
@ -995,7 +979,7 @@ public sealed class WorldSceneRendererTests
|
|||
{
|
||||
public HashSet<uint> MarkedCells { get; } = [];
|
||||
|
||||
public void MarkVisibleCells(HashSet<uint> cellIds)
|
||||
public void MarkVisibleLandscapeCells(HashSet<uint> cellIds)
|
||||
{
|
||||
calls.Add("visibility:mark");
|
||||
MarkedCells.UnionWith(cellIds);
|
||||
|
|
@ -1015,13 +999,14 @@ public sealed class WorldSceneRendererTests
|
|||
public PViewRenderer(List<string> calls)
|
||||
{
|
||||
_calls = calls;
|
||||
// Distinct flood-only vs in-view sets: 0x01010003 is a look-in
|
||||
// cell that is drawn but never part of the main flood.
|
||||
// Distinct typed products: EnvCell shell preparation, diagnostic
|
||||
// union, and the outdoor landscape IsInView answer.
|
||||
_interiorResult = new RetailPViewFrameResult().Reset(
|
||||
ClipFrameAssembler.BeginWalkFrame(
|
||||
ClipFrame.NoClip(), outdoorRoot: false),
|
||||
[0x01010001u],
|
||||
[0x01010001u, 0x01010003u],
|
||||
[0x0101_0100u],
|
||||
[0x0101_0100u, 0x0101_0003u],
|
||||
[0x0101_0003u],
|
||||
default,
|
||||
default,
|
||||
diagnosticPartition: null);
|
||||
|
|
@ -1030,6 +1015,7 @@ public sealed class WorldSceneRendererTests
|
|||
ClipFrame.NoClip(), outdoorRoot: true),
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
default,
|
||||
default,
|
||||
diagnosticPartition: null);
|
||||
|
|
@ -1062,8 +1048,6 @@ public sealed class WorldSceneRendererTests
|
|||
|
||||
public bool ThrowOnAbort { get; set; }
|
||||
|
||||
public HashSet<uint>? TerrainVisibleCellIds { get; } = [0x01010001u];
|
||||
|
||||
public RenderFrameFoundation? SkyFoundation { get; private set; }
|
||||
|
||||
public RenderFrameFoundation? WeatherFoundation { get; private set; }
|
||||
|
|
|
|||
|
|
@ -529,6 +529,62 @@ public sealed class ParticleSystemTests
|
|||
Assert.False(Assert.Single(sys.EnumerateEmitters()).ViewEligible);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyRetailView_UsesLandscapeMembershipButEnvCellConstantVirtual()
|
||||
{
|
||||
var sys = MakeSystem();
|
||||
var desc = new EmitterDesc
|
||||
{
|
||||
DatId = 0x3200007Bu,
|
||||
Type = ParticleType.Still,
|
||||
MaxDegradeDistance = 10f,
|
||||
MaxParticles = 1,
|
||||
};
|
||||
int outdoor = sys.SpawnEmitter(desc, Vector3.Zero, attachedObjectId: 21u);
|
||||
int environment = sys.SpawnEmitter(desc, Vector3.Zero, attachedObjectId: 22u);
|
||||
int cellLess = sys.SpawnEmitter(desc, Vector3.Zero, attachedObjectId: 23u);
|
||||
sys.UpdateEmitterOwnerCell(outdoor, 0x0101_0001u);
|
||||
sys.UpdateEmitterOwnerCell(environment, 0x0101_0100u);
|
||||
|
||||
sys.ApplyRetailView(Vector3.Zero, new HashSet<uint> { 0x0101_0001u }, true);
|
||||
|
||||
ParticleEmitter[] first = sys.EnumerateEmitters().ToArray();
|
||||
Assert.True(first.Single(emitter => emitter.Handle == outdoor).ViewEligible);
|
||||
Assert.True(first.Single(emitter => emitter.Handle == environment).ViewEligible);
|
||||
Assert.False(first.Single(emitter => emitter.Handle == cellLess).ViewEligible);
|
||||
|
||||
sys.ApplyRetailView(Vector3.Zero, new HashSet<uint>(), true);
|
||||
|
||||
ParticleEmitter[] second = sys.EnumerateEmitters().ToArray();
|
||||
Assert.False(second.Single(emitter => emitter.Handle == outdoor).ViewEligible);
|
||||
Assert.True(second.Single(emitter => emitter.Handle == environment).ViewEligible);
|
||||
Assert.False(second.Single(emitter => emitter.Handle == cellLess).ViewEligible);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyRetailView_NoCompletedViewRejectsBothCellFamilies()
|
||||
{
|
||||
var sys = MakeSystem();
|
||||
var desc = new EmitterDesc
|
||||
{
|
||||
DatId = 0x3200007Du,
|
||||
Type = ParticleType.Still,
|
||||
MaxDegradeDistance = 10f,
|
||||
MaxParticles = 1,
|
||||
};
|
||||
int outdoor = sys.SpawnEmitter(desc, Vector3.Zero, attachedObjectId: 31u);
|
||||
int environment = sys.SpawnEmitter(desc, Vector3.Zero, attachedObjectId: 32u);
|
||||
sys.UpdateEmitterOwnerCell(outdoor, 0x0101_0001u);
|
||||
sys.UpdateEmitterOwnerCell(environment, 0x0101_0100u);
|
||||
|
||||
sys.ApplyRetailView(
|
||||
Vector3.Zero,
|
||||
new HashSet<uint> { 0x0101_0001u },
|
||||
hasCompletedView: false);
|
||||
|
||||
Assert.All(sys.EnumerateEmitters(), emitter => Assert.False(emitter.ViewEligible));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ApplyRetailView_UnorderedOwnerDistanceMatchesRetailX87Comparison()
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue