fix: complete retail parity stability pass
All checks were successful
CI / linux-portable (push) Successful in 3m41s
CI / windows-gate (push) Successful in 6m49s
CI / release (push) Successful in 3m22s

This commit is contained in:
Erik 2026-08-28 20:01:39 +02:00
parent d3df4cb20a
commit f7aa8e0eb7
131 changed files with 7765 additions and 1190 deletions

View file

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Numerics;
using AcDream.App.Rendering;
using AcDream.App.Rendering.Vfx;
using AcDream.Core.Physics;
using AcDream.Core.Vfx;
using AcDream.Core.World;
using DatReaderWriter.Types;
@ -22,20 +23,59 @@ public sealed class SkyPesFrameControllerTests
{
private const uint AuroraSetup = 0x02000714u;
private const uint AuroraScript = 0x330007DBu;
private const uint LightningEmitter = 0x320002C2u;
private sealed class Harness
{
private sealed class RecordingHookSink : IAnimationHookSink
{
public List<(uint EntityId, Vector3 Position, AnimationHook Hook)> Calls { get; } = [];
public void OnHook(
uint entityId,
Vector3 entityWorldPosition,
AnimationHook hook) =>
Calls.Add((entityId, entityWorldPosition, hook));
}
public readonly List<uint> ResolvedScriptIds = [];
public readonly List<string> Diagnostics = [];
public readonly List<(uint EntityId, Vector3 Position, AnimationHook Hook)> HookCalls;
public readonly PhysicsScriptRunner Runner;
public readonly SkyPesFrameController Controller;
public readonly ParticleSystem Particles;
public Harness()
public Harness(AnimationHook? hook = null, double hookTime = 0.0)
{
var registry = new EmitterDescRegistry();
var system = new ParticleSystem(registry, new Random(42));
registry.Register(new EmitterDesc
{
DatId = LightningEmitter,
Type = ParticleType.Still,
Flags = EmitterFlags.Billboard,
EmitterKind = ParticleEmitterKind.BirthratePerSec,
MaxParticles = 2,
InitialParticles = 1,
LifetimeMin = 0.01f,
LifetimeMax = 0.01f,
Lifespan = 0.01f,
StartSize = 1f,
EndSize = 1f,
StartAlpha = 1f,
EndAlpha = 1f,
Birthrate = 1000f,
});
Particles = new ParticleSystem(registry, new Random(42));
var poses = new EntityEffectPoseRegistry();
var sink = new ParticleHookSink(system, poses);
var sink = new ParticleHookSink(Particles, poses)
{
DiagnosticSink = Diagnostics.Add,
};
var recording = new RecordingHookSink();
HookCalls = recording.Calls;
var router = new AnimationHookRouter();
router.Register(sink);
router.Register(recording);
Runner = new PhysicsScriptRunner(
id =>
{
@ -43,12 +83,12 @@ public sealed class SkyPesFrameControllerTests
var script = new DatPhysicsScript();
script.ScriptData.Add(new PhysicsScriptData
{
StartTime = 0.0,
Hook = new SoundHook(),
StartTime = hookTime,
Hook = hook ?? new SoundHook(),
});
return script;
},
sink);
router);
Controller = new SkyPesFrameController(
Runner,
sink,
@ -169,4 +209,52 @@ public sealed class SkyPesFrameControllerTests
h.Controller.Update(0.1f, null, Vector3.Zero);
Assert.Equal(0, h.Runner.ActiveScriptCount);
}
[Fact]
public void LightningPartZeroUsesTheLiveCarrierPoseAndStopsOnDayGroupFlip()
{
var create = new CreateParticleHook
{
EmitterInfoId = LightningEmitter,
EmitterId = 1u,
PartIndex = 0u,
Offset = new Frame(),
};
var h = new Harness(create);
h.Controller.Update(0.1f, Group(Carrier()), new Vector3(4f, 5f, 6f));
h.Runner.Tick(0.0);
Assert.Equal(1, h.Particles.ActiveEmitterCount);
Assert.Empty(h.Diagnostics);
h.Controller.Update(0.1f, null, Vector3.Zero);
h.Runner.Tick(1.0);
Assert.Equal(0, h.Runner.ActiveScriptCount);
Assert.Empty(h.Diagnostics);
}
[Fact]
public void PersistentWeatherCarrier_RefreshesSoundAnchorToCurrentCamera()
{
var sound = new SoundTweakedHook
{
SoundId = 0x0A00038Bu,
Volume = 0.1f,
Priority = 1f,
};
var h = new Harness(sound, hookTime: 1.0);
var initialCamera = new Vector3(10f, 20f, 30f);
var currentCamera = new Vector3(410f, 520f, 630f);
h.Controller.Update(0.3f, Group(Carrier()), initialCamera);
h.Controller.Update(0.4f, Group(Carrier()), currentCamera);
h.Runner.Tick(1.0);
var call = Assert.Single(h.HookCalls);
Assert.Same(sound, call.Hook);
Assert.Equal(currentCamera, call.Position);
Assert.Single(h.ResolvedScriptIds);
}
}