mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-06-25 16:14:20 +00:00
feat: raw terrain parsing for scene data
Replace old dNavigation/dTerrain raw parser with new Raw module in dZoneManager. Parse heightmaps, color maps, and scene maps from .raw files to determine which scene a position belongs to. Build scene adjacency graph from terrain data and scene transitions. Adds NiColor type, SceneColor lookup table, eSceneType enum, terrain mesh generation with OBJ export, and debug slash commands for scene visualization. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -817,6 +817,42 @@ void SlashCommandHandler::Startup() {
|
||||
};
|
||||
RegisterCommand(ExecuteCommand);
|
||||
|
||||
Command GetSceneCommand{
|
||||
.help = "Get the current scene ID and name at your position",
|
||||
.info = "Displays the scene ID and name at the player's current position. Scenes do not care about height.",
|
||||
.aliases = { "getscene", "scene" },
|
||||
.handle = DEVGMCommands::GetScene,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
};
|
||||
RegisterCommand(GetSceneCommand);
|
||||
|
||||
Command GetAdjacentScenesCommand{
|
||||
.help = "Get all scenes adjacent to your current scene",
|
||||
.info = "Displays all scenes that are directly connected to the player's current scene via scene transitions.",
|
||||
.aliases = { "getadjacentscenes", "adjacentscenes" },
|
||||
.handle = DEVGMCommands::GetAdjacentScenes,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
};
|
||||
RegisterCommand(GetAdjacentScenesCommand);
|
||||
|
||||
Command SpawnScenePointsCommand{
|
||||
.help = "Spawn bricks at points across your current scene",
|
||||
.info = "Spawns bricks at sampled points across the player's current scene using terrain scene map data.",
|
||||
.aliases = { "spawnscenepoints" },
|
||||
.handle = DEVGMCommands::SpawnScenePoints,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
};
|
||||
RegisterCommand(SpawnScenePointsCommand);
|
||||
|
||||
Command SpawnAllScenePointsCommand{
|
||||
.help = "Spawn bricks at ALL vertices in ALL scenes (high density, many entities)",
|
||||
.info = "Spawns bricks at every vertex in the terrain mesh for all scenes in the zone. WARNING: Creates a massive number of entities for maximum accuracy visualization.",
|
||||
.aliases = { "spawnallscenepoints", "spawnallscenes" },
|
||||
.handle = DEVGMCommands::SpawnAllScenePoints,
|
||||
.requiredLevel = eGameMasterLevel::DEVELOPER
|
||||
};
|
||||
RegisterCommand(SpawnAllScenePointsCommand);
|
||||
|
||||
// Register Greater Than Zero Commands
|
||||
|
||||
Command KickCommand{
|
||||
|
||||
@@ -1890,4 +1890,273 @@ namespace DEVGMCommands {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetScene(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
const auto position = entity->GetPosition();
|
||||
|
||||
// Get the scene ID from the zone manager
|
||||
const auto sceneID = Game::zoneManager->GetSceneIDFromPosition(position);
|
||||
|
||||
if (sceneID == LWOSCENEID_INVALID) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"No scene found at current position.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the scene reference from the zone to get the name
|
||||
const auto* zone = Game::zoneManager->GetZone();
|
||||
if (!zone) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"No zone loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the feedback message
|
||||
std::ostringstream feedback;
|
||||
feedback << "Scene ID: " << sceneID.GetSceneID();
|
||||
feedback << " (Layer: " << sceneID.GetLayerID() << ")";
|
||||
|
||||
// Get the scene name
|
||||
const auto* sceneRef = zone->GetScene(sceneID);
|
||||
if (sceneRef && !sceneRef->name.empty()) {
|
||||
feedback << " - Name: " << sceneRef->name;
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
}
|
||||
|
||||
void GetAdjacentScenes(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
const auto position = entity->GetPosition();
|
||||
|
||||
// Get the scene ID from the zone manager
|
||||
const auto sceneID = Game::zoneManager->GetSceneIDFromPosition(position);
|
||||
|
||||
if (sceneID == LWOSCENEID_INVALID) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"No scene found at current position.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the zone reference
|
||||
const auto* zone = Game::zoneManager->GetZone();
|
||||
if (!zone) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"No zone loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get current scene info
|
||||
const auto* currentScene = zone->GetScene(sceneID);
|
||||
std::string currentSceneName = currentScene && !currentScene->name.empty() ? currentScene->name : "Unknown";
|
||||
|
||||
// Get adjacent scenes
|
||||
const auto adjacentSceneIDs = Game::zoneManager->GetAdjacentScenes(sceneID);
|
||||
|
||||
if (adjacentSceneIDs.empty()) {
|
||||
std::ostringstream feedback;
|
||||
feedback << "Current Scene: " << sceneID.GetSceneID() << " (" << currentSceneName << ")";
|
||||
feedback << " - No adjacent scenes found.";
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the feedback message with current scene
|
||||
std::ostringstream feedback;
|
||||
feedback << "Current Scene: " << sceneID.GetSceneID() << " (" << currentSceneName << ")";
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
|
||||
// List all adjacent scenes
|
||||
feedback.str("");
|
||||
feedback << "Adjacent Scenes (" << adjacentSceneIDs.size() << "):";
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
|
||||
for (const auto& adjSceneID : adjacentSceneIDs) {
|
||||
feedback.str("");
|
||||
feedback << " - Scene ID: " << adjSceneID.GetSceneID();
|
||||
feedback << " (Layer: " << adjSceneID.GetLayerID() << ")";
|
||||
|
||||
// Get the scene name if available
|
||||
const auto* sceneRef = zone->GetScene(adjSceneID);
|
||||
if (sceneRef && !sceneRef->name.empty()) {
|
||||
feedback << " - " << sceneRef->name;
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
}
|
||||
}
|
||||
|
||||
void SpawnScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
// Hardcoded to use LOT 33
|
||||
const uint32_t lot = 33;
|
||||
|
||||
// Get player's current position and scene
|
||||
const auto position = entity->GetPosition();
|
||||
const auto currentSceneID = Game::zoneManager->GetSceneIDFromPosition(position);
|
||||
if (currentSceneID == LWOSCENEID_INVALID) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"No scene found at current position.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the zone
|
||||
const auto* zone = Game::zoneManager->GetZone();
|
||||
if (!zone) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"No zone loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the Raw terrain data
|
||||
const auto& raw = zone->GetZoneRaw();
|
||||
if (raw.chunks.empty()) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Zone does not have valid terrain data.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn at all sceneMap points in the current scene
|
||||
uint32_t spawnedCount = 0;
|
||||
|
||||
for (const auto& chunk : raw.chunks) {
|
||||
if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty()
|
||||
|| chunk.width <= 1 || chunk.height <= 1 || chunk.scaleFactor <= 0.0f) continue;
|
||||
|
||||
for (uint32_t i = 0; i < chunk.width; ++i) {
|
||||
for (uint32_t j = 0; j < chunk.height; ++j) {
|
||||
const uint32_t heightIndex = chunk.width * i + j;
|
||||
if (heightIndex >= chunk.heightMap.size()) continue;
|
||||
|
||||
const float y = chunk.heightMap[heightIndex];
|
||||
|
||||
const float sceneMapI = (static_cast<float>(i) / static_cast<float>(chunk.width - 1)) * static_cast<float>(chunk.colorMapResolution - 1);
|
||||
const float sceneMapJ = (static_cast<float>(j) / static_cast<float>(chunk.height - 1)) * static_cast<float>(chunk.colorMapResolution - 1);
|
||||
|
||||
const uint32_t sceneI = std::min(static_cast<uint32_t>(sceneMapI), chunk.colorMapResolution - 1);
|
||||
const uint32_t sceneJ = std::min(static_cast<uint32_t>(sceneMapJ), chunk.colorMapResolution - 1);
|
||||
const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ;
|
||||
|
||||
uint8_t sceneID = 0;
|
||||
if (sceneIndex < chunk.sceneMap.size()) {
|
||||
sceneID = chunk.sceneMap[sceneIndex];
|
||||
}
|
||||
|
||||
if (sceneID == currentSceneID.GetSceneID()) {
|
||||
const float worldX = (static_cast<float>(i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor;
|
||||
const float worldY = y;
|
||||
const float worldZ = (static_cast<float>(j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor;
|
||||
|
||||
NiPoint3 spawnPos(worldX, worldY, worldZ);
|
||||
EntityInfo info;
|
||||
info.lot = lot + currentSceneID.GetSceneID();
|
||||
info.pos = spawnPos;
|
||||
info.rot = QuatUtils::IDENTITY;
|
||||
info.spawner = nullptr;
|
||||
info.spawnerID = entity->GetObjectID();
|
||||
info.spawnerNodeID = 0;
|
||||
info.settings.Insert(u"SpawnedFromSlashCommand", true);
|
||||
|
||||
Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr);
|
||||
if (newEntity != nullptr) {
|
||||
Game::entityManager->ConstructEntity(newEntity);
|
||||
spawnedCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (spawnedCount == 0) {
|
||||
std::ostringstream feedback;
|
||||
feedback << "No spawn points found in current scene (ID: " << currentSceneID.GetSceneID() << ").";
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
return;
|
||||
}
|
||||
|
||||
// Send feedback
|
||||
const auto* sceneRef = zone->GetScene(currentSceneID);
|
||||
const std::string sceneName = sceneRef ? sceneRef->name : "Unknown";
|
||||
std::ostringstream feedback;
|
||||
feedback << "Spawned " << spawnedCount << " points (LOT " << lot + currentSceneID.GetSceneID() << ") in scene "
|
||||
<< currentSceneID.GetSceneID() << " (" << sceneName << ").";
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
}
|
||||
|
||||
void SpawnAllScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
|
||||
// Hardcoded to use LOT 33
|
||||
const uint32_t lot = 33;
|
||||
|
||||
// Get the zone
|
||||
const auto* zone = Game::zoneManager->GetZone();
|
||||
if (!zone) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"No zone loaded.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the Raw terrain data
|
||||
const auto& raw = zone->GetZoneRaw();
|
||||
if (raw.chunks.empty()) {
|
||||
ChatPackets::SendSystemMessage(sysAddr, u"Zone does not have valid terrain data.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Spawn at all sceneMap points across all scenes
|
||||
uint32_t spawnedCount = 0;
|
||||
std::map<uint8_t, uint32_t> sceneSpawnCounts; // Track spawns per scene
|
||||
|
||||
for (const auto& chunk : raw.chunks) {
|
||||
if (chunk.sceneMap.empty() || chunk.colorMapResolution == 0 || chunk.heightMap.empty()
|
||||
|| chunk.width <= 1 || chunk.height <= 1 || chunk.scaleFactor <= 0.0f) continue;
|
||||
|
||||
for (uint32_t i = 0; i < chunk.width; ++i) {
|
||||
for (uint32_t j = 0; j < chunk.height; ++j) {
|
||||
const uint32_t heightIndex = chunk.width * i + j;
|
||||
if (heightIndex >= chunk.heightMap.size()) continue;
|
||||
|
||||
const float y = chunk.heightMap[heightIndex];
|
||||
|
||||
const float sceneMapI = (static_cast<float>(i) / static_cast<float>(chunk.width - 1)) * static_cast<float>(chunk.colorMapResolution - 1);
|
||||
const float sceneMapJ = (static_cast<float>(j) / static_cast<float>(chunk.height - 1)) * static_cast<float>(chunk.colorMapResolution - 1);
|
||||
|
||||
const uint32_t sceneI = std::min(static_cast<uint32_t>(sceneMapI), chunk.colorMapResolution - 1);
|
||||
const uint32_t sceneJ = std::min(static_cast<uint32_t>(sceneMapJ), chunk.colorMapResolution - 1);
|
||||
const uint32_t sceneIndex = sceneI * chunk.colorMapResolution + sceneJ;
|
||||
|
||||
uint8_t sceneID = 0;
|
||||
if (sceneIndex < chunk.sceneMap.size()) {
|
||||
sceneID = chunk.sceneMap[sceneIndex];
|
||||
}
|
||||
|
||||
if (sceneID == 0) continue;
|
||||
|
||||
const float worldX = (static_cast<float>(i) + (chunk.offsetX / chunk.scaleFactor)) * chunk.scaleFactor;
|
||||
const float worldY = y;
|
||||
const float worldZ = (static_cast<float>(j) + (chunk.offsetZ / chunk.scaleFactor)) * chunk.scaleFactor;
|
||||
|
||||
NiPoint3 spawnPos(worldX, worldY, worldZ);
|
||||
EntityInfo info;
|
||||
info.lot = lot + sceneID;
|
||||
info.pos = spawnPos;
|
||||
info.rot = QuatUtils::IDENTITY;
|
||||
info.spawner = nullptr;
|
||||
info.spawnerID = entity->GetObjectID();
|
||||
info.spawnerNodeID = 0;
|
||||
info.settings.Insert(u"SpawnedFromSlashCommand", true);
|
||||
|
||||
Entity* newEntity = Game::entityManager->CreateEntity(info, nullptr);
|
||||
if (newEntity != nullptr) {
|
||||
Game::entityManager->ConstructEntity(newEntity);
|
||||
spawnedCount++;
|
||||
sceneSpawnCounts[sceneID]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send detailed feedback
|
||||
std::ostringstream feedback;
|
||||
feedback << "Spawned " << spawnedCount << " total points (base LOT " << lot << ") across "
|
||||
<< sceneSpawnCounts.size() << " scenes:\n";
|
||||
|
||||
for (const auto& [sceneID, count] : sceneSpawnCounts) {
|
||||
const auto* sceneRef = zone->GetScene(LWOSCENEID(sceneID));
|
||||
const std::string sceneName = sceneRef ? sceneRef->name : "Unknown";
|
||||
feedback << " Scene " << static_cast<int>(sceneID) << ", LOT: " << (lot + sceneID) << " (" << sceneName << "): " << count << " points\n";
|
||||
}
|
||||
|
||||
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -77,6 +77,10 @@ namespace DEVGMCommands {
|
||||
void Barfight(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Despawn(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void Execute(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void GetScene(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void GetAdjacentScenes(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void SpawnScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
void SpawnAllScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args);
|
||||
}
|
||||
|
||||
#endif //!DEVGMCOMMANDS_H
|
||||
|
||||
Reference in New Issue
Block a user