chore: remove the scene point spawn commands and send /getadjacentscenes as one message

The spawn commands were only for testing. /getscene and /getadjacentscenes stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-10-26 20:33:41 -05:00
parent 56e6d010b3
commit 09dd74843c
5 changed files with 12 additions and 224 deletions

View File

@@ -835,23 +835,6 @@ void SlashCommandHandler::Startup() {
};
RegisterCommand(GetAdjacentScenesCommand);
Command SpawnScenePointsCommand{
.help = "Spawn bricks at points across your current scene",
.info = "Spawns bricks across the player's current scene using terrain scene map data, one every <step> heightmap points (default 8). Usage: /spawnscenepoints (step)",
.aliases = { "spawnscenepoints" },
.handle = DEVGMCommands::SpawnScenePoints,
.requiredLevel = eGameMasterLevel::DEVELOPER
};
RegisterCommand(SpawnScenePointsCommand);
Command SpawnAllScenePointsCommand{
.help = "Spawn bricks across all scenes in the zone",
.info = "Spawns bricks across every scene in the zone, one every <step> heightmap points (default 8; 1 = every point, a massive number of entities). Usage: /spawnallscenepoints (step)",
.aliases = { "spawnallscenepoints", "spawnallscenes" },
.handle = DEVGMCommands::SpawnAllScenePoints,
.requiredLevel = eGameMasterLevel::DEVELOPER
};
RegisterCommand(SpawnAllScenePointsCommand);
// Register Greater Than Zero Commands

View File

@@ -1,6 +1,5 @@
#include "DEVGMCommands.h"
#include <algorithm>
#include <ranges>
// Classes
@@ -25,8 +24,6 @@
// Database
#include "Database.h"
#include "CDClientManager.h"
#include "CDBrickIDTableTable.h"
#include "CDObjectsTable.h"
#include "CDRewardCodesTable.h"
#include "CDLootMatrixTable.h"
@@ -1894,19 +1891,6 @@ namespace DEVGMCommands {
}
}
// A different LEGO brick per scene so the scenes can be told apart, from the BrickIDTable (every brick's LOT)
static LOT GetScenePointLot(uint8_t sceneID) {
static const std::vector<LOT> brickLots = [] {
std::vector<LOT> lots;
for (const auto& brick : CDClientManager::GetTable<CDBrickIDTableTable>()->Query([](const CDBrickIDTable&) { return true; })) {
lots.push_back(brick.NDObjectID);
}
std::ranges::sort(lots);
return lots;
}();
return brickLots.empty() ? LOT_NULL : brickLots[sceneID % brickLots.size()];
}
void GetScene(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto position = entity->GetPosition();
@@ -1964,171 +1948,23 @@ namespace DEVGMCommands {
// 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()));
}
}
if (adjacentSceneIDs.empty()) {
feedback << " - No adjacent scenes found.";
} else {
feedback << "\nAdjacent Scenes (" << adjacentSceneIDs.size() << "):";
for (const auto& adjSceneID : adjacentSceneIDs) {
feedback << "\n - Scene ID: " << adjSceneID.GetSceneID() << " (Layer: " << adjSceneID.GetLayerID() << ")";
void SpawnScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
// Spawn one brick every <step> heightmap points (default 8)
const auto parsedStep = GeneralUtils::TryParse<uint32_t>(args);
const uint32_t step = parsedStep && *parsedStep > 0 ? *parsedStep : 8;
// 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;
}
uint32_t spawnedCount = 0;
for (const auto& chunk : raw.chunks) {
if (!chunk.IsValidForSceneLookup()) continue;
for (uint32_t i = 0; i < chunk.width; i += step) {
for (uint32_t j = 0; j < chunk.height; j += step) {
const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j);
if (sceneID != currentSceneID.GetSceneID()) continue;
EntityInfo info;
info.lot = GetScenePointLot(sceneID);
info.pos = chunk.GridToWorldPos(i, j);
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++;
}
// Get the scene name if available
const auto* sceneRef = zone->GetScene(adjSceneID);
if (sceneRef && !sceneRef->name.empty()) {
feedback << " - " << sceneRef->name;
}
}
}
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 " << GetScenePointLot(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) {
// Spawn one brick every <step> heightmap points (default 8)
const auto parsedStep = GeneralUtils::TryParse<uint32_t>(args);
const uint32_t step = parsedStep && *parsedStep > 0 ? *parsedStep : 8;
// 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;
}
uint32_t spawnedCount = 0;
std::map<uint8_t, uint32_t> sceneSpawnCounts;
for (const auto& chunk : raw.chunks) {
if (!chunk.IsValidForSceneLookup()) continue;
for (uint32_t i = 0; i < chunk.width; i += step) {
for (uint32_t j = 0; j < chunk.height; j += step) {
const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j);
if (sceneID == 0) continue;
EntityInfo info;
info.lot = GetScenePointLot(sceneID);
info.pos = chunk.GridToWorldPos(i, j);
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 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: " << GetScenePointLot(sceneID) << " (" << sceneName << "): " << count << " points\n";
}
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
}
};

View File

@@ -79,8 +79,6 @@ namespace DEVGMCommands {
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

View File

@@ -145,8 +145,6 @@ These commands are primarily for development and testing. The usage of many of t
|getnavmeshheight|`/getnavmeshheight`|Display the navmesh height at your current position|8|
|getscene|`/getscene`|Display the scene ID and name at your current position (alias `/scene`)|8|
|getadjacentscenes|`/getadjacentscenes`|List the scenes connected to your current scene by scene transitions (alias `/adjacentscenes`)|8|
|spawnscenepoints|`/spawnscenepoints (step)`|Spawns a brick every `step` terrain points (default 8) across your current scene|8|
|spawnallscenepoints|`/spawnallscenepoints (step)`|Spawns a brick every `step` terrain points (default 8) across every scene, one brick type per scene (alias `/spawnallscenes`)|8|
|giveuscore|`/giveuscore <uscore>`|Gives uscore|8|
|gmadditem|`/gmadditem <id> (count)`|Adds the given item to your inventory by id. Aliases: `/give`.|8|
|inspect|`/inspect <component or ldf variable or player name> (-m <waypoint> | -a <animation> | -s | -p | -f (faction) | -t)`|Finds the closest entity with the given component or LNV variable (ignoring players and racing cars), printing its ID, distance from the player, and whether it is sleeping, as well as the IDs of all components the entity has. Use `localCharacter` or `zoneControl` to inspect your current character or the zone control object.|8|

View File

@@ -5,8 +5,6 @@
#include <fstream>
#include "AssetManager.h"
#include "CDBrickIDTableTable.h"
#include "CDComponentsRegistryTable.h"
#include "CDZoneTableTable.h"
#include "DEVGMCommands.h"
#include "Entity.h"
@@ -230,41 +228,16 @@ TEST_F(ZoneSceneTest, UnreadableRawStillLoadsTheZone) {
EXPECT_EQ(Game::zoneManager->GetSceneIDFromPosition(NiPoint3(0.5f, 0.0f, 0.5f)), LWOSCENEID_INVALID);
}
TEST_F(ZoneSceneTest, ScenePointCommandsSpawnBricks) {
TEST_F(ZoneSceneTest, SceneCommandsReport) {
WriteZone();
WriteFile("test.raw", MakeRaw());
LoadZone();
auto& bricks = CDClientManager::GetEntriesMutable<CDBrickIDTableTable>();
bricks = { { 40, 3005 }, { 33, 3001 }, { 35, 3003 } };
// the bricks have no components, so creating them needs no client database
for (const LOT lot : { 33, 35, 40 }) CDClientManager::GetEntriesMutable<CDComponentsRegistryTable>()[lot] = 0;
EntityInfo playerInfo = info;
playerInfo.pos = NiPoint3(0.5f, 0.0f, 0.5f); // scene 1
auto player = std::make_unique<Entity>(1, playerInfo);
// Grid point (i, j) is at (2i, 2j); its scene cell is (min(i, 1), min(j, 1)), so of the 9 points scene 1 has 5,
// scene 2 has 2 ((0, 1), (0, 2)) and scene 3 has 2 ((1, 0), (2, 0))
DEVGMCommands::SpawnScenePoints(player.get(), UNASSIGNED_SYSTEM_ADDRESS, "1");
// bricks sorted by LOT: 33, 35, 40; scene 1 -> 35, scene 2 -> 40, scene 3 -> 33
const auto sceneOne = Game::entityManager->GetEntitiesByLOT(35);
EXPECT_EQ(sceneOne.size(), 5);
for (const auto* brick : sceneOne) EXPECT_EQ(Game::zoneManager->GetSceneIDFromPosition(brick->GetPosition()), LWOSCENEID(1, 0));
EXPECT_TRUE(Game::entityManager->GetEntitiesByLOT(40).empty());
// the default step of 8 only samples point (0, 0)
DEVGMCommands::SpawnAllScenePoints(player.get(), UNASSIGNED_SYSTEM_ADDRESS, "");
EXPECT_EQ(Game::entityManager->GetEntitiesByLOT(35).size(), 5 + 1);
EXPECT_TRUE(Game::entityManager->GetEntitiesByLOT(40).empty());
DEVGMCommands::SpawnAllScenePoints(player.get(), UNASSIGNED_SYSTEM_ADDRESS, "1");
EXPECT_EQ(Game::entityManager->GetEntitiesByLOT(35).size(), 6 + 5);
EXPECT_EQ(Game::entityManager->GetEntitiesByLOT(40).size(), 2);
EXPECT_EQ(Game::entityManager->GetEntitiesByLOT(33).size(), 2);
// the scene commands only report; they must not fail with or without scene data
// the scene commands only report; they must not fail
DEVGMCommands::GetScene(player.get(), UNASSIGNED_SYSTEM_ADDRESS, "");
DEVGMCommands::GetAdjacentScenes(player.get(), UNASSIGNED_SYSTEM_ADDRESS, "");
bricks.clear();
}