feat: scene point commands take a step and use brick LOTs from the BrickIDTable

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Aaron Kimbrell
2026-10-16 11:13:19 -05:00
parent 200958dd79
commit fc70efa503
3 changed files with 38 additions and 20 deletions

View File

@@ -837,7 +837,7 @@ void SlashCommandHandler::Startup() {
Command SpawnScenePointsCommand{ Command SpawnScenePointsCommand{
.help = "Spawn bricks at points across your current scene", .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.", .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" }, .aliases = { "spawnscenepoints" },
.handle = DEVGMCommands::SpawnScenePoints, .handle = DEVGMCommands::SpawnScenePoints,
.requiredLevel = eGameMasterLevel::DEVELOPER .requiredLevel = eGameMasterLevel::DEVELOPER
@@ -845,8 +845,8 @@ void SlashCommandHandler::Startup() {
RegisterCommand(SpawnScenePointsCommand); RegisterCommand(SpawnScenePointsCommand);
Command SpawnAllScenePointsCommand{ Command SpawnAllScenePointsCommand{
.help = "Spawn bricks at ALL vertices in ALL scenes (high density, many entities)", .help = "Spawn bricks across all scenes in the zone",
.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.", .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" }, .aliases = { "spawnallscenepoints", "spawnallscenes" },
.handle = DEVGMCommands::SpawnAllScenePoints, .handle = DEVGMCommands::SpawnAllScenePoints,
.requiredLevel = eGameMasterLevel::DEVELOPER .requiredLevel = eGameMasterLevel::DEVELOPER

View File

@@ -1,5 +1,6 @@
#include "DEVGMCommands.h" #include "DEVGMCommands.h"
#include <algorithm>
#include <ranges> #include <ranges>
// Classes // Classes
@@ -24,6 +25,8 @@
// Database // Database
#include "Database.h" #include "Database.h"
#include "CDClientManager.h"
#include "CDBrickIDTableTable.h"
#include "CDObjectsTable.h" #include "CDObjectsTable.h"
#include "CDRewardCodesTable.h" #include "CDRewardCodesTable.h"
#include "CDLootMatrixTable.h" #include "CDLootMatrixTable.h"
@@ -1891,6 +1894,19 @@ 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) { void GetScene(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto position = entity->GetPosition(); const auto position = entity->GetPosition();
@@ -1982,8 +1998,9 @@ namespace DEVGMCommands {
} }
void SpawnScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args) { void SpawnScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
// Hardcoded to use LOT 33 // Spawn one brick every <step> heightmap points (default 8)
const uint32_t lot = 33; const auto parsedStep = GeneralUtils::TryParse<uint32_t>(args);
const uint32_t step = parsedStep && *parsedStep > 0 ? *parsedStep : 8;
// Get player's current position and scene // Get player's current position and scene
const auto position = entity->GetPosition(); const auto position = entity->GetPosition();
@@ -2012,15 +2029,13 @@ namespace DEVGMCommands {
for (const auto& chunk : raw.chunks) { for (const auto& chunk : raw.chunks) {
if (!chunk.IsValidForSceneLookup()) continue; if (!chunk.IsValidForSceneLookup()) continue;
for (uint32_t i = 0; i < chunk.width; ++i) { for (uint32_t i = 0; i < chunk.width; i += step) {
for (uint32_t j = 0; j < chunk.height; ++j) { for (uint32_t j = 0; j < chunk.height; j += step) {
if (i * chunk.width + j >= chunk.heightMap.size()) continue;
const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j); const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j);
if (sceneID != currentSceneID.GetSceneID()) continue; if (sceneID != currentSceneID.GetSceneID()) continue;
EntityInfo info; EntityInfo info;
info.lot = lot + sceneID; info.lot = GetScenePointLot(sceneID);
info.pos = chunk.GridToWorldPos(i, j); info.pos = chunk.GridToWorldPos(i, j);
info.rot = QuatUtils::IDENTITY; info.rot = QuatUtils::IDENTITY;
info.spawner = nullptr; info.spawner = nullptr;
@@ -2048,14 +2063,15 @@ namespace DEVGMCommands {
const auto* sceneRef = zone->GetScene(currentSceneID); const auto* sceneRef = zone->GetScene(currentSceneID);
const std::string sceneName = sceneRef ? sceneRef->name : "Unknown"; const std::string sceneName = sceneRef ? sceneRef->name : "Unknown";
std::ostringstream feedback; std::ostringstream feedback;
feedback << "Spawned " << spawnedCount << " points (LOT " << lot + currentSceneID.GetSceneID() << ") in scene " feedback << "Spawned " << spawnedCount << " points (LOT " << GetScenePointLot(currentSceneID.GetSceneID()) << ") in scene "
<< currentSceneID.GetSceneID() << " (" << sceneName << ")."; << currentSceneID.GetSceneID() << " (" << sceneName << ").";
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str())); ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));
} }
void SpawnAllScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args) { void SpawnAllScenePoints(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
// Hardcoded to use LOT 33 // Spawn one brick every <step> heightmap points (default 8)
const uint32_t lot = 33; const auto parsedStep = GeneralUtils::TryParse<uint32_t>(args);
const uint32_t step = parsedStep && *parsedStep > 0 ? *parsedStep : 8;
// Get the zone // Get the zone
const auto* zone = Game::zoneManager->GetZone(); const auto* zone = Game::zoneManager->GetZone();
@@ -2077,15 +2093,13 @@ namespace DEVGMCommands {
for (const auto& chunk : raw.chunks) { for (const auto& chunk : raw.chunks) {
if (!chunk.IsValidForSceneLookup()) continue; if (!chunk.IsValidForSceneLookup()) continue;
for (uint32_t i = 0; i < chunk.width; ++i) { for (uint32_t i = 0; i < chunk.width; i += step) {
for (uint32_t j = 0; j < chunk.height; ++j) { for (uint32_t j = 0; j < chunk.height; j += step) {
if (i * chunk.width + j >= chunk.heightMap.size()) continue;
const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j); const uint8_t sceneID = chunk.GetSceneIDAtGrid(i, j);
if (sceneID == 0) continue; if (sceneID == 0) continue;
EntityInfo info; EntityInfo info;
info.lot = lot + sceneID; info.lot = GetScenePointLot(sceneID);
info.pos = chunk.GridToWorldPos(i, j); info.pos = chunk.GridToWorldPos(i, j);
info.rot = QuatUtils::IDENTITY; info.rot = QuatUtils::IDENTITY;
info.spawner = nullptr; info.spawner = nullptr;
@@ -2105,13 +2119,13 @@ namespace DEVGMCommands {
// Send detailed feedback // Send detailed feedback
std::ostringstream feedback; std::ostringstream feedback;
feedback << "Spawned " << spawnedCount << " total points (base LOT " << lot << ") across " feedback << "Spawned " << spawnedCount << " total points across "
<< sceneSpawnCounts.size() << " scenes:\n"; << sceneSpawnCounts.size() << " scenes:\n";
for (const auto& [sceneID, count] : sceneSpawnCounts) { for (const auto& [sceneID, count] : sceneSpawnCounts) {
const auto* sceneRef = zone->GetScene(LWOSCENEID(sceneID)); const auto* sceneRef = zone->GetScene(LWOSCENEID(sceneID));
const std::string sceneName = sceneRef ? sceneRef->name : "Unknown"; const std::string sceneName = sceneRef ? sceneRef->name : "Unknown";
feedback << " Scene " << static_cast<int>(sceneID) << ", LOT: " << (lot + sceneID) << " (" << sceneName << "): " << count << " points\n"; feedback << " Scene " << static_cast<int>(sceneID) << ", LOT: " << GetScenePointLot(sceneID) << " (" << sceneName << "): " << count << " points\n";
} }
ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str())); ChatPackets::SendSystemMessage(sysAddr, GeneralUtils::ASCIIToUTF16(feedback.str()));

View File

@@ -143,6 +143,10 @@ These commands are primarily for development and testing. The usage of many of t
|freecam|`/freecam`|Toggles freecam mode|8| |freecam|`/freecam`|Toggles freecam mode|8|
|freemoney|`/freemoney <coins>`|Give yourself coins. Aliases: `/givemoney`, `/money`, `/givecoins`, `/coins`.|8| |freemoney|`/freemoney <coins>`|Give yourself coins. Aliases: `/givemoney`, `/money`, `/givecoins`, `/coins`.|8|
|getnavmeshheight|`/getnavmeshheight`|Display the navmesh height at your current position|8| |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| |giveuscore|`/giveuscore <uscore>`|Gives uscore|8|
|gmadditem|`/gmadditem <id> (count)`|Adds the given item to your inventory by id. Aliases: `/give`.|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| |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|