using DatReaderWriter;
using DatReaderWriter.DBObjs;
namespace AcDream.Core.World;
public static class LandblockLoader
{
private const uint GfxObjMask = 0x01000000u;
private const uint SetupMask = 0x02000000u;
private const uint TypeMask = 0xFF000000u;
///
/// Load a single landblock (heightmap + static objects) from the dats.
///
/// Null if the landblock is missing from the cell dat.
public static LoadedLandblock? Load(DatCollection dats, uint landblockId)
{
var block = dats.Get(landblockId);
if (block is null)
return null;
var info = dats.Get((landblockId & 0xFFFF0000u) | 0xFFFEu);
var entities = info is null
? Array.Empty()
: BuildEntitiesFromInfo(info);
return new LoadedLandblock(landblockId, block, entities);
}
///
/// Pure mapping from a parsed LandBlockInfo to a list of WorldEntity.
/// Each Stab and BuildingInfo becomes one entity. Unsupported id types
/// (neither GfxObj 0x01xxxxxx nor Setup 0x02xxxxxx) are silently skipped.
/// MeshRefs is left empty at this stage — Task 5 populates it.
///
public static IReadOnlyList BuildEntitiesFromInfo(LandBlockInfo info)
{
var result = new List(info.Objects.Count + info.Buildings.Count);
uint nextId = 1;
foreach (var stab in info.Objects)
{
if (!IsSupported(stab.Id))
continue;
result.Add(new WorldEntity
{
Id = nextId++,
SourceGfxObjOrSetupId = stab.Id,
Position = stab.Frame.Origin,
Rotation = stab.Frame.Orientation,
MeshRefs = Array.Empty(),
});
}
foreach (var building in info.Buildings)
{
if (!IsSupported(building.ModelId))
continue;
result.Add(new WorldEntity
{
Id = nextId++,
SourceGfxObjOrSetupId = building.ModelId,
Position = building.Frame.Origin,
Rotation = building.Frame.Orientation,
MeshRefs = Array.Empty(),
});
}
return result;
}
private static bool IsSupported(uint id)
{
var type = id & TypeMask;
return type == GfxObjMask || type == SetupMask;
}
}