feat: add eSceneType enum, filter scene graph to general scenes, zone parsing improvements

- Add eSceneType enum (General, Audio) replacing raw uint32_t in SceneRef
- Filter BuildSceneGraph to only include General scenes
- Skip transitions referencing non-general scenes in adjacency graph
- Rename SceneRef unknown fields to scenePosition/sceneRadius
- Zone parsing and Level improvements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-06-21 11:38:47 -05:00
parent 1aeede3cd1
commit 7dd918d894
7 changed files with 386 additions and 112 deletions

View File

@@ -85,11 +85,29 @@ void Zone::LoadZoneIntoMemory() {
LoadScene(file);
}
//Read generic zone info:
BinaryIO::ReadString<uint8_t>(file, m_ZonePath, BinaryIO::ReadType::String);
// Zone boundary lines
uint8_t numBoundaries = 0;
BinaryIO::BinaryRead(file, numBoundaries);
m_Boundaries.reserve(numBoundaries);
for (uint8_t i = 0; i < numBoundaries; ++i) {
ZoneBoundary boundary;
BinaryIO::BinaryRead(file, boundary.normal);
BinaryIO::BinaryRead(file, boundary.point);
uint32_t packed;
BinaryIO::BinaryRead(file, packed);
boundary.destMapID = static_cast<uint16_t>(packed & 0xFFFF);
boundary.destInstanceID = static_cast<uint16_t>((packed >> 16) & 0xFFFF);
BinaryIO::BinaryRead(file, boundary.destSceneID);
BinaryIO::BinaryRead(file, boundary.spawnLocation);
m_Boundaries.push_back(boundary);
}
// Zone info strings
BinaryIO::ReadString<uint8_t>(file, m_ZoneRawPath, BinaryIO::ReadType::String);
BinaryIO::ReadString<uint8_t>(file, m_ZoneName, BinaryIO::ReadType::String);
BinaryIO::ReadString<uint8_t>(file, m_ZoneDesc, BinaryIO::ReadType::String);
if (m_FileFormatVersion > Zone::FileFormatVersion::PrePreAlpha) {
BinaryIO::ReadString<uint8_t>(file, m_ZoneName, BinaryIO::ReadType::String);
BinaryIO::ReadString<uint8_t>(file, m_ZoneDesc, BinaryIO::ReadType::String);
}
auto zoneFolderPath = m_ZoneFilePath.substr(0, m_ZoneFilePath.rfind('/') + 1);
if (!Game::assetManager->HasFile(zoneFolderPath + m_ZoneRawPath)) {
@@ -271,14 +289,15 @@ void Zone::LoadScene(std::istream& file) {
}
if (m_FileFormatVersion >= Zone::FileFormatVersion::LatePreAlpha) {
BinaryIO::BinaryRead(file, scene.sceneType);
lwoSceneID.SetLayerID(scene.sceneType);
lwoSceneID.SetLayerID(static_cast<uint32_t>(scene.sceneType));
BinaryIO::ReadString<uint8_t>(file, scene.name, BinaryIO::ReadType::String);
}
if (m_FileFormatVersion == Zone::FileFormatVersion::LatePreAlpha) {
BinaryIO::BinaryRead(file, scene.unknown1);
BinaryIO::BinaryRead(file, scene.unknown2);
BinaryIO::BinaryRead(file, scene.scenePosition);
BinaryIO::BinaryRead(file, scene.sceneRadius);
}
if (m_FileFormatVersion >= Zone::FileFormatVersion::LatePreAlpha) {
@@ -385,11 +404,39 @@ void Zone::LoadSceneTransition(std::istream& file) {
SceneTransitionInfo Zone::LoadSceneTransitionInfo(std::istream& file) {
SceneTransitionInfo info;
BinaryIO::BinaryRead(file, info.sceneID);
uint32_t sceneID, layerID;
BinaryIO::BinaryRead(file, sceneID);
BinaryIO::BinaryRead(file, layerID);
info.sceneID = LWOSCENEID(static_cast<int32_t>(sceneID), layerID);
BinaryIO::BinaryRead(file, info.position);
return info;
}
static void ReadLdfConfig(std::istream& file, PathWaypoint& waypoint, PathType pathType) {
uint32_t count;
BinaryIO::BinaryRead(file, count);
for (uint32_t i = 0; i < count; ++i) {
std::string parameter;
BinaryIO::ReadString<uint8_t>(file, parameter, BinaryIO::ReadType::WideString);
std::string value;
BinaryIO::ReadString<uint8_t>(file, value, BinaryIO::ReadType::WideString);
if (pathType == PathType::Movement || pathType == PathType::Rail) {
parameter.erase(std::remove_if(parameter.begin(), parameter.end(), ::isspace), parameter.end());
auto waypointCommand = WaypointCommandType::StringToWaypointCommandType(parameter);
if (waypointCommand == eWaypointCommandType::DELAY) value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
if (waypointCommand != eWaypointCommandType::INVALID) {
auto& command = waypoint.commands.emplace_back();
command.command = waypointCommand;
command.data = value;
} else LOG("Tried to load invalid waypoint command '%s'", parameter.c_str());
} else {
waypoint.config.ParseInsert(parameter + "=" + value);
}
}
}
void Zone::LoadPath(std::istream& file) {
Path path = Path();
@@ -397,6 +444,34 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::ReadString<uint8_t>(file, path.pathName, BinaryIO::ReadType::WideString);
if (path.pathVersion <= 2) {
// Legacy format: type_name string present, no behavior field,
// unified waypoint format (pos + rot + lock + speed + wait + config).
std::string typeName;
BinaryIO::ReadString<uint8_t>(file, typeName, BinaryIO::ReadType::WideString);
BinaryIO::BinaryRead(file, path.pathType);
BinaryIO::BinaryRead(file, path.flags);
BinaryIO::BinaryRead(file, path.waypointCount);
path.pathWaypoints.reserve(path.waypointCount);
for (uint32_t i = 0; i < path.waypointCount; ++i) {
PathWaypoint waypoint = PathWaypoint();
BinaryIO::BinaryRead(file, waypoint.position);
BinaryIO::BinaryRead(file, waypoint.rotation.w);
BinaryIO::BinaryRead(file, waypoint.rotation.x);
BinaryIO::BinaryRead(file, waypoint.rotation.y);
BinaryIO::BinaryRead(file, waypoint.rotation.z);
BinaryIO::BinaryRead(file, waypoint.movingPlatform.lockPlayer);
BinaryIO::BinaryRead(file, waypoint.speed);
BinaryIO::BinaryRead(file, waypoint.movingPlatform.wait);
ReadLdfConfig(file, waypoint, path.pathType);
path.pathWaypoints.push_back(waypoint);
}
m_Paths.push_back(path);
return;
}
BinaryIO::BinaryRead(file, path.pathType);
BinaryIO::BinaryRead(file, path.flags);
BinaryIO::BinaryRead(file, path.pathBehavior);
@@ -435,7 +510,6 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::ReadString<uint8_t>(file, path.camera.nextPath, BinaryIO::ReadType::WideString);
if (path.pathVersion >= 14) {
BinaryIO::BinaryRead(file, path.camera.rotatePlayer);
}
} else if (path.pathType == PathType::Spawner) {
BinaryIO::BinaryRead(file, path.spawner.spawnedLOT);
@@ -443,7 +517,9 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::BinaryRead(file, path.spawner.maxToSpawn);
BinaryIO::BinaryRead(file, path.spawner.amountMaintained);
BinaryIO::BinaryRead(file, path.spawner.spawnerObjID);
BinaryIO::BinaryRead(file, path.spawner.spawnerNetActive);
if (path.pathVersion >= 9) {
BinaryIO::BinaryRead(file, path.spawner.spawnerNetActive);
}
}
// Read waypoints
@@ -457,7 +533,6 @@ void Zone::LoadPath(std::istream& file) {
BinaryIO::BinaryRead(file, waypoint.position.y);
BinaryIO::BinaryRead(file, waypoint.position.z);
if (path.pathType == PathType::Spawner || path.pathType == PathType::MovingPlatform || path.pathType == PathType::Race || path.pathType == PathType::Camera || path.pathType == PathType::Rail) {
BinaryIO::BinaryRead(file, waypoint.rotation.w);
BinaryIO::BinaryRead(file, waypoint.rotation.x);
@@ -491,37 +566,11 @@ void Zone::LoadPath(std::istream& file) {
// object LDF configs
if (path.pathType == PathType::Movement || path.pathType == PathType::Spawner || path.pathType == PathType::Rail) {
uint32_t count;
BinaryIO::BinaryRead(file, count);
for (uint32_t i = 0; i < count; ++i) {
std::string parameter;
BinaryIO::ReadString<uint8_t>(file, parameter, BinaryIO::ReadType::WideString);
std::string value;
BinaryIO::ReadString<uint8_t>(file, value, BinaryIO::ReadType::WideString);
if (path.pathType == PathType::Movement || path.pathType == PathType::Rail) {
// cause NetDevil puts spaces in things that don't need spaces
parameter.erase(std::remove_if(parameter.begin(), parameter.end(), ::isspace), parameter.end());
auto waypointCommand = WaypointCommandType::StringToWaypointCommandType(parameter);
if (waypointCommand == eWaypointCommandType::DELAY) value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end());
if (waypointCommand != eWaypointCommandType::INVALID) {
auto& command = waypoint.commands.emplace_back();
command.command = waypointCommand;
command.data = value;
} else LOG("Tried to load invalid waypoint command '%s'", parameter.c_str());
} else {
waypoint.config.ParseInsert(parameter + "=" + value);
}
}
ReadLdfConfig(file, waypoint, path.pathType);
}
// We verify the waypoint heights against the navmesh because in many movement paths,
// the waypoint is located near 0 height,
if (path.pathType == PathType::Movement) {
if (dpWorld::IsLoaded()) {
// 2000 should be large enough for every world.
waypoint.position.y = dpWorld::GetNavMesh()->GetHeightAtPoint(waypoint.position, 2000.0f);
}
}