feat: allow for teleporting to player or relative position (#1683)

* allow for teleporting to player or relative position

* Update Commands.md

* Update Commands.md

* Update SlashCommandHandler.cpp
This commit is contained in:
David Markowitz 2024-12-17 11:39:28 -08:00 committed by GitHub
parent e1c20192f7
commit ba364800fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 48 additions and 21 deletions

View File

@ -287,8 +287,8 @@ void SlashCommandHandler::Startup() {
RegisterCommand(SpawnPhysicsVertsCommand); RegisterCommand(SpawnPhysicsVertsCommand);
Command TeleportCommand{ Command TeleportCommand{
.help = "Teleports you", .help = "Teleports you to a position or a player to another player.",
.info = "Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z)", .info = "Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z). Any of the coordinates can use the syntax of an exact position (10.0), or a relative position (~+10.0). A ~ means use the current value of that axis as the base value. Addition or subtraction is supported (~+10) (~-10). If source player and target player are players that exist in the world, then the source player will be teleported to target player.",
.aliases = { "teleport", "tele", "tp" }, .aliases = { "teleport", "tele", "tp" },
.handle = DEVGMCommands::Teleport, .handle = DEVGMCommands::Teleport,
.requiredLevel = eGameMasterLevel::JUNIOR_DEVELOPER .requiredLevel = eGameMasterLevel::JUNIOR_DEVELOPER

View File

@ -555,25 +555,45 @@ namespace DEVGMCommands {
} }
} }
std::optional<float> ParseRelativeAxis(const float sourcePos, const std::string& toParse) {
if (toParse.empty()) return std::nullopt;
// relative offset from current position
if (toParse[0] == '~') {
if (toParse.size() == 1) return sourcePos;
if (toParse.size() < 3 || !(toParse[1] != '+' || toParse[1] != '-')) return std::nullopt;
const auto offset = GeneralUtils::TryParse<float>(toParse.substr(2));
if (!offset.has_value()) return std::nullopt;
bool isNegative = toParse[1] == '-';
return isNegative ? sourcePos - offset.value() : sourcePos + offset.value();
}
return GeneralUtils::TryParse<float>(toParse);
}
void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args) { void Teleport(Entity* entity, const SystemAddress& sysAddr, const std::string args) {
const auto splitArgs = GeneralUtils::SplitString(args, ' '); const auto splitArgs = GeneralUtils::SplitString(args, ' ');
const auto& sourcePos = entity->GetPosition();
NiPoint3 pos{}; NiPoint3 pos{};
auto* sourceEntity = entity;
if (splitArgs.size() == 3) { if (splitArgs.size() == 3) {
const auto x = ParseRelativeAxis(sourcePos.x, splitArgs[0]);
const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0));
if (!x) { if (!x) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); ChatPackets::SendSystemMessage(sysAddr, u"Invalid x.");
return; return;
} }
const auto y = GeneralUtils::TryParse<float>(splitArgs.at(1)); const auto y = ParseRelativeAxis(sourcePos.y, splitArgs[1]);
if (!y) { if (!y) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid y."); ChatPackets::SendSystemMessage(sysAddr, u"Invalid y.");
return; return;
} }
const auto z = GeneralUtils::TryParse<float>(splitArgs.at(2)); const auto z = ParseRelativeAxis(sourcePos.z, splitArgs[2]);
if (!z) { if (!z) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z."); ChatPackets::SendSystemMessage(sysAddr, u"Invalid z.");
return; return;
@ -584,32 +604,39 @@ namespace DEVGMCommands {
pos.SetZ(z.value()); pos.SetZ(z.value());
LOG("Teleporting objectID: %llu to %f, %f, %f", entity->GetObjectID(), pos.x, pos.y, pos.z); LOG("Teleporting objectID: %llu to %f, %f, %f", entity->GetObjectID(), pos.x, pos.y, pos.z);
GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr);
} else if (splitArgs.size() == 2) { } else if (splitArgs.size() == 2) {
const auto x = ParseRelativeAxis(sourcePos.x, splitArgs[0]);
auto* sourcePlayer = PlayerManager::GetPlayer(splitArgs[0]);
if (!x && !sourcePlayer) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x or source player not found.");
return;
}
if (sourcePlayer) sourceEntity = sourcePlayer;
const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0)); const auto z = ParseRelativeAxis(sourcePos.z, splitArgs[1]);
if (!x) { const auto* const targetPlayer = PlayerManager::GetPlayer(splitArgs[1]);
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x."); if (!z && !targetPlayer) {
return; ChatPackets::SendSystemMessage(sysAddr, u"Invalid z or target player not found.");
}
const auto z = GeneralUtils::TryParse<float>(splitArgs.at(1));
if (!z) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z.");
return; return;
} }
if (x && z) {
pos.SetX(x.value()); pos.SetX(x.value());
pos.SetY(0.0f); pos.SetY(0.0f);
pos.SetZ(z.value()); pos.SetZ(z.value());
} else if (sourcePlayer && targetPlayer) {
pos = targetPlayer->GetPosition();
} else {
ChatPackets::SendSystemMessage(sysAddr, u"Unable to teleport.");
return;
}
LOG("Teleporting objectID: %llu to X: %f, Z: %f", entity->GetObjectID(), pos.x, pos.z); LOG("Teleporting objectID: %llu to X: %f, Z: %f", entity->GetObjectID(), pos.x, pos.z);
GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr);
} else { } else {
ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /teleport <x> (<y>) <z> - if no Y given, will teleport to the height of the terrain (or any physics object)."); ChatPackets::SendSystemMessage(sysAddr, u"Correct usage: /teleport <x> (<y>) <z> - if no Y given, will teleport to the height of the terrain (or any physics object).");
} }
GameMessages::SendTeleport(sourceEntity->GetObjectID(), pos, sourceEntity->GetRotation(), sourceEntity->GetSystemAddress());
auto* possessorComponent = entity->GetComponent<PossessorComponent>(); auto* possessorComponent = sourceEntity->GetComponent<PossessorComponent>();
if (possessorComponent) { if (possessorComponent) {
auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable()); auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable());

View File

@ -59,7 +59,7 @@ These commands are primarily for development and testing. The usage of many of t
|testmap|`/testmap <zone> (force) (clone-id)`|Transfers you to the given zone by id and clone id. Add "force" to skip checking if the zone is accessible (this can softlock your character, though, if you e.g. try to teleport to Frostburgh).|1| |testmap|`/testmap <zone> (force) (clone-id)`|Transfers you to the given zone by id and clone id. Add "force" to skip checking if the zone is accessible (this can softlock your character, though, if you e.g. try to teleport to Frostburgh).|1|
|reportproxphys|`/reportproxphys`|Prints to console the position and radius of proximity sensors.|6| |reportproxphys|`/reportproxphys`|Prints to console the position and radius of proximity sensors.|6|
|spawnphysicsverts|`/spawnphysicsverts`|Spawns a 1x1 brick at all vertices of phantom physics objects.|6| |spawnphysicsverts|`/spawnphysicsverts`|Spawns a 1x1 brick at all vertices of phantom physics objects.|6|
|teleport|`/teleport <x> (y) <z>` or <br> `/tele <x> (y) <z>`|Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z). Alias: `/tele`.|6| |teleport|`/teleport <x/source player> (y) <z/target player>` or <br> `/tele <x/source player> (y) <z/target player>`|Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z). Any of the coordinates can use the syntax of an exact position (10.0), or a relative position (~+10.0). A ~ means use the current value of that axis as the base value. Addition or subtraction is supported (~+10) (~-10). If source player and target player are players that exist in the world, then the source player will be teleported to target player. Alias: `/tele`.|6|
|activatespawner|`/activatespawner <spawner name>`|Activates spawner by name.|8| |activatespawner|`/activatespawner <spawner name>`|Activates spawner by name.|8|
|addmission|`/addmission <mission id>`|Accepts the mission, adding it to your journal.|8| |addmission|`/addmission <mission id>`|Accepts the mission, adding it to your journal.|8|
|boost|`/boost (time)`|Adds a passive boost action if you are in a vehicle. If time is given it will end after that amount of time|8| |boost|`/boost (time)`|Adds a passive boost action if you are in a vehicle. If time is given it will end after that amount of time|8|