using System;
using System.Collections.Concurrent;
using System.Numerics;
namespace AcDream.Core.Vfx;
///
/// Resolves instances by their retail emitter
/// dat id (0x32xxxxxx range). The current build of
/// Chorizite.DatReaderWriter (v2.1.7) doesn't yet ship a
/// ParticleEmitterInfo DBObj class, so we maintain a small
/// registry of synthesized descriptors for the handful of emitters
/// acdream actually needs (portal swirl, chimney smoke, fireplace
/// flames, footstep dust, spell auras, weapon trails) and fall back to
/// a generic "puff" for unknown ids. When a future DRW release adds
/// the dat-type, this class will additionally load + cache from dats.
///
///
/// Field mapping once the dat-type arrives (docs/research/deepdives/
/// r04-vfx-particles.md §1 + references/DatReaderWriter's own generated
/// ParticleEmitterInfo.generated.cs):
///
/// -
/// Birthrate → 1 / EmitRate (retail stores the avg
/// time between spawns, not the rate).
///
/// -
/// Lifespan ± LifespanRand → LifetimeMin / LifetimeMax
/// range.
///
/// -
/// A, MinA, MaxA → primary initial velocity with magnitude
/// jitter; B / C are secondary spread components.
///
/// -
/// StartScale, FinalScale / StartTrans, FinalTrans
/// interpolate linearly over life.
///
///
///
///
public sealed class EmitterDescRegistry
{
private readonly ConcurrentDictionary _byId = new();
public EmitterDescRegistry()
{
// Seed with a handful of well-known AC emitter ids plus a
// fallback. Ids here come from empirical ACViewer dat dumps —
// see r04 §5.2 for the more complete inventory.
Register(new EmitterDesc
{
DatId = 0xFFFFFFFFu, // "default" sentinel
Type = ParticleType.LocalVelocity,
Flags = EmitterFlags.Billboard | EmitterFlags.FaceCamera,
EmitRate = 10f,
MaxParticles = 32,
LifetimeMin = 0.6f,
LifetimeMax = 1.2f,
OffsetDir = new Vector3(0, 0, 1),
MinOffset = 0f,
MaxOffset = 0.1f,
SpawnDiskRadius = 0.1f,
InitialVelocity = new Vector3(0, 0, 0.5f),
VelocityJitter = 0.3f,
StartSize = 0.25f,
EndSize = 0.6f,
StartAlpha = 0.85f,
EndAlpha = 0f,
});
}
public void Register(EmitterDesc desc)
{
ArgumentNullException.ThrowIfNull(desc);
_byId[desc.DatId] = desc;
}
public EmitterDesc Get(uint emitterId)
{
if (_byId.TryGetValue(emitterId, out var desc)) return desc;
if (_byId.TryGetValue(0xFFFFFFFFu, out var fallback)) return fallback;
throw new InvalidOperationException("No default emitter registered in registry.");
}
public int Count => _byId.Count;
}