namespace AcDream.Core.World;
///
/// Allocates stable, collision-free ids for procedurally generated scenery.
///
///
/// The top nibble is fixed at 0x8: tests
/// (id & 0xF0000000u) == 0x80000000u, the full nibble, not bit 31
/// alone. Bit 31 alone is NOT a reliable procedural-scenery test — it is
/// also set by LandblockStaticEntityIdAllocator's 0xC...
/// namespace (top nibble 1100) and by the synthetic render ids
/// 0xDA11_D0xx (paperdoll) and 0xFFFF_FF01 (portal tunnel).
/// LandblockPhysicsPublisher.cs's isOutdoorMesh predicate
/// intentionally tests bit 31 alone — it wants "any of these broader
/// synthetic/outdoor namespaces", not specifically procedural scenery — but
/// that is the one deliberate exception; every OTHER consumer, including
/// foliage-wind classification, must use . The
/// remaining 28 bits are X(8), Y(8), and a 12-bit per-landblock counter:
/// 0x8XXYYIII.
///
///
///
/// The old 8-bit counter aborted generation after 256 drawable spawns. That
/// exception rejected the entire streamed landblock before render publication,
/// exposing the grey clear color as portal space ended. A 12-bit counter gives
/// 4096 entries while retaining full landblock coordinates. Overflow fails
/// before it can alias the next landblock.
///
///
public static class ProceduralSceneryIdAllocator
{
public const uint MaxCounter = 0xFFFu;
public static uint Base(uint landblockX, uint landblockY)
=> 0x80000000u
| ((landblockX & 0xFFu) << 20)
| ((landblockY & 0xFFu) << 12);
public static uint Allocate(uint landblockX, uint landblockY, ref uint counter)
{
if (counter > MaxCounter)
throw new InvalidDataException(
$"Landblock ({landblockX & 0xFFu:X2},{landblockY & 0xFFu:X2}) exceeds the 4096-entry procedural scenery id namespace.");
return Base(landblockX, landblockY) + counter++;
}
///
/// The full top-nibble test — 0x8..., not bit 31 alone. Bit 31
/// alone also matches LandblockStaticEntityIdAllocator's
/// 0xC... ids (top nibble 1100 also has bit 31 set) and
/// the synthetic render ids 0xDA11_D0xx / 0xFFFF_FF01.
///
public static bool IsInNamespace(uint entityId) =>
(entityId & 0xF0000000u) == 0x80000000u;
}