refactor(zone): LWOSCENEID lives with the zone code, its layer is an eSceneType

LWOSCENEID and LWOSCENEID_INVALID move from dCommonVars.h to
dZoneManager/LWOSCENEID.h; only the zone code uses them. The layer half
is the scene's layer, now the new eSceneType (dCommon/dEnums) instead of
a uint32_t: General (0), Audio (1, the *_audio.lvl scenes named
"Audio") and FX (2, Nexus Tower's *_fxs.lvl scenes named "FXs"), the
three layers the zone files of every client on disk use. ZoneScene's
sceneType is an eSceneType too, so a scene's LWOSCENEID is built from it
directly, and the dashboard compares against eSceneType::General instead
of 0. The zone checksum still hashes the layer's number.

Check in game: every world loads (the zone checksum the client checks is
unchanged), Nexus Tower included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-09-29 04:49:42 -05:00
parent 0143ed834c
commit d5563ad5da
10 changed files with 58 additions and 33 deletions

View File

@@ -10,6 +10,7 @@
#include "NiPoint3.h"
#include "NiQuaternion.h"
#include "LDFFormat.h"
#include "eSceneType.h"
#include "eWaypointCommandType.h"
/**
@@ -26,7 +27,7 @@ struct WaypointCommand {
struct ZoneScene {
std::string filename;
uint32_t id{};
uint32_t sceneType{}; //0 = general, 1 = audio?
eSceneType sceneType{};
std::string name;
NiPoint3 unknown1;
float unknown2{};

View File

@@ -54,32 +54,6 @@ constexpr float PI = 3.14159f;
//============ STRUCTS ==============
struct LWOSCENEID {
public:
constexpr LWOSCENEID() noexcept { m_sceneID = -1; m_layerID = 0; }
constexpr LWOSCENEID(int32_t sceneID) noexcept { m_sceneID = sceneID; m_layerID = 0; }
constexpr LWOSCENEID(int32_t sceneID, uint32_t layerID) noexcept { m_sceneID = sceneID; m_layerID = layerID; }
constexpr LWOSCENEID& operator=(const LWOSCENEID& rhs) noexcept { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; }
constexpr LWOSCENEID& operator=(const int32_t rhs) noexcept { m_sceneID = rhs; m_layerID = 0; return *this; }
constexpr bool operator<(const LWOSCENEID& rhs) const noexcept { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); }
constexpr bool operator<(const int32_t rhs) const noexcept { return m_sceneID < rhs; }
constexpr bool operator==(const LWOSCENEID& rhs) const noexcept { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); }
constexpr bool operator==(const int32_t rhs) const noexcept { return m_sceneID == rhs; }
constexpr int32_t GetSceneID() const noexcept { return m_sceneID; }
constexpr uint32_t GetLayerID() const noexcept { return m_layerID; }
constexpr void SetSceneID(const int32_t sceneID) noexcept { m_sceneID = sceneID; }
constexpr void SetLayerID(const uint32_t layerID) noexcept { m_layerID = layerID; }
private:
int32_t m_sceneID;
uint32_t m_layerID;
};
struct LWOZONEID {
public:
constexpr const LWOMAPID& GetMapID() const noexcept { return m_MapID; }
@@ -99,7 +73,6 @@ private:
LWOCLONEID m_CloneID = LWOCLONEID_INVALID; //To differentiate between "your property" and "my property". Always 0 for non-prop worlds.
};
constexpr LWOSCENEID LWOSCENEID_INVALID = -1;
struct FriendData {
public:

View File

@@ -0,0 +1,13 @@
#ifndef ESCENETYPE_H
#define ESCENETYPE_H
#include <cstdint>
// A zone scene's layer (the second half of the client's SceneAndLayer); zones split into a scene per layer
enum class eSceneType : uint32_t {
General = 0, // the scene's objects ("Global Scene" and the named scenes)
Audio = 1, // *_audio.lvl scenes, named "Audio"
FX = 2, // *_fxs.lvl scenes, named "FXs" (Nexus Tower)
};
#endif //!ESCENETYPE_H

View File

@@ -701,7 +701,7 @@ std::optional<std::string> ZoneTerrainLayersJson(uint32_t zoneId) {
const auto luz = ClientAssets::ReadResFile("maps/" + zone->luzPath);
std::string error;
if (const auto header = luz ? ZonePaths::ReadHeader(*luz, error) : std::nullopt) {
for (const auto& scene : header->scenes) if (scene.sceneType == 0) names.try_emplace(scene.id, scene.name);
for (const auto& scene : header->scenes) if (scene.sceneType == eSceneType::General) names.try_emplace(scene.id, scene.name);
}
std::map<uint8_t, uint64_t> cells;
nlohmann::json chunks = nlohmann::json::array();

View File

@@ -373,7 +373,7 @@ namespace {
if (!lvl) continue;
const auto objectsBefore = assetOf.size();
const auto lighting = WorldScene::ReadLighting(*lvl);
if (lighting && scene.sceneType == 0) lightingOf.try_emplace(scene.id, LightingJson(*lighting));
if (lighting && scene.sceneType == eSceneType::General) lightingOf.try_emplace(scene.id, LightingJson(*lighting));
if (sky < 0) {
const auto skydome = JoinPath("", WorldScene::ReadSkydome(*lvl));
if (skydome.ends_with(".nif") && Files().paths.contains(skydome)) sky = static_cast<int64_t>(scenery.IndexOf(skydome));

View File

@@ -138,7 +138,7 @@ namespace {
size_t index = 0;
for (const auto& scene : zoneFile->scenes) {
// Audio scenes share their general scene's id; the general one names it
if (scene.sceneType == 0 || !scenes.contains(std::to_string(scene.id))) scenes[std::to_string(scene.id)] = scene.name;
if (scene.sceneType == eSceneType::General || !scenes.contains(std::to_string(scene.id))) scenes[std::to_string(scene.id)] = scene.name;
const auto lvl = ClientAssets::ReadResFile("maps/" + folder + scene.filename);
if (!lvl) continue;
for (const auto& object : WorldScene::ReadObjects(*lvl)) {

37
dZoneManager/LWOSCENEID.h Normal file
View File

@@ -0,0 +1,37 @@
#ifndef LWOSCENEID_H
#define LWOSCENEID_H
#include <cstdint>
#include "eSceneType.h"
// A scene of a zone: its scene ID and layer
struct LWOSCENEID {
public:
constexpr LWOSCENEID() noexcept { m_sceneID = -1; m_layerID = eSceneType::General; }
constexpr LWOSCENEID(int32_t sceneID) noexcept { m_sceneID = sceneID; m_layerID = eSceneType::General; }
constexpr LWOSCENEID(int32_t sceneID, eSceneType layerID) noexcept { m_sceneID = sceneID; m_layerID = layerID; }
constexpr LWOSCENEID& operator=(const LWOSCENEID& rhs) noexcept { m_sceneID = rhs.m_sceneID; m_layerID = rhs.m_layerID; return *this; }
constexpr LWOSCENEID& operator=(const int32_t rhs) noexcept { m_sceneID = rhs; m_layerID = eSceneType::General; return *this; }
constexpr bool operator<(const LWOSCENEID& rhs) const noexcept { return (m_sceneID < rhs.m_sceneID || (m_sceneID == rhs.m_sceneID && m_layerID < rhs.m_layerID)); }
constexpr bool operator<(const int32_t rhs) const noexcept { return m_sceneID < rhs; }
constexpr bool operator==(const LWOSCENEID& rhs) const noexcept { return (m_sceneID == rhs.m_sceneID && m_layerID == rhs.m_layerID); }
constexpr bool operator==(const int32_t rhs) const noexcept { return m_sceneID == rhs; }
constexpr int32_t GetSceneID() const noexcept { return m_sceneID; }
constexpr eSceneType GetLayerID() const noexcept { return m_layerID; }
constexpr void SetSceneID(const int32_t sceneID) noexcept { m_sceneID = sceneID; }
constexpr void SetLayerID(const eSceneType layerID) noexcept { m_layerID = layerID; }
private:
int32_t m_sceneID;
eSceneType m_layerID;
};
constexpr LWOSCENEID LWOSCENEID_INVALID = -1;
#endif //!LWOSCENEID_H

View File

@@ -181,7 +181,7 @@ uint32_t Zone::CalculateChecksum() const {
sum2 += sum1 += (sceneID >> 16);
sum2 += sum1 += (sceneID & 0xffff);
uint32_t layerID = scene.GetLayerID();
uint32_t layerID = GeneralUtils::ToUnderlying(scene.GetLayerID());
sum2 += sum1 += (layerID >> 16);
sum2 += sum1 += (layerID & 0xffff);

View File

@@ -2,6 +2,7 @@
#include "dZMCommon.h"
#include "LDFFormat.h"
#include "LWOSCENEID.h"
#include "ZoneFile.h"
#include "ZoneScenes.h"
#include "tinyxml2.h"

View File

@@ -65,7 +65,7 @@ TEST(ZoneFileTests, ReadsScenesAndPaths) {
ASSERT_EQ(zone.scenes.size(), 1u);
EXPECT_EQ(zone.scenes[0].filename, "scene.lvl");
EXPECT_EQ(zone.scenes[0].id, 7u);
EXPECT_EQ(zone.scenes[0].sceneType, 2u);
EXPECT_EQ(zone.scenes[0].sceneType, eSceneType::FX);
EXPECT_EQ(zone.zoneRawPath, "zone.raw");
ASSERT_EQ(zone.sceneTransitions.size(), 1u);
EXPECT_EQ(zone.sceneTransitions[0].points.size(), 2u);