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
3 changed files with 48 additions and 21 deletions

View File

@@ -287,8 +287,8 @@ void SlashCommandHandler::Startup() {
RegisterCommand(SpawnPhysicsVertsCommand);
Command TeleportCommand{
.help = "Teleports you",
.info = "Teleports you. If no Y is given, you are teleported to the height of the terrain or physics object at (x, z)",
.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). 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" },
.handle = DEVGMCommands::Teleport,
.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) {
const auto splitArgs = GeneralUtils::SplitString(args, ' ');
const auto& sourcePos = entity->GetPosition();
NiPoint3 pos{};
auto* sourceEntity = entity;
if (splitArgs.size() == 3) {
const auto x = GeneralUtils::TryParse<float>(splitArgs.at(0));
const auto x = ParseRelativeAxis(sourcePos.x, splitArgs[0]);
if (!x) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x.");
return;
}
const auto y = GeneralUtils::TryParse<float>(splitArgs.at(1));
const auto y = ParseRelativeAxis(sourcePos.y, splitArgs[1]);
if (!y) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid y.");
return;
}
const auto z = GeneralUtils::TryParse<float>(splitArgs.at(2));
const auto z = ParseRelativeAxis(sourcePos.z, splitArgs[2]);
if (!z) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z.");
return;
@@ -584,32 +604,39 @@ namespace DEVGMCommands {
pos.SetZ(z.value());
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) {
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));
if (!x) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x.");
const auto z = ParseRelativeAxis(sourcePos.z, splitArgs[1]);
const auto* const targetPlayer = PlayerManager::GetPlayer(splitArgs[1]);
if (!z && !targetPlayer) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z or target player not found.");
return;
}
const auto z = GeneralUtils::TryParse<float>(splitArgs.at(1));
if (!z) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z.");
if (x && z) {
pos.SetX(x.value());
pos.SetY(0.0f);
pos.SetZ(z.value());
} else if (sourcePlayer && targetPlayer) {
pos = targetPlayer->GetPosition();
} else {
ChatPackets::SendSystemMessage(sysAddr, u"Unable to teleport.");
return;
}
pos.SetX(x.value());
pos.SetY(0.0f);
pos.SetZ(z.value());
LOG("Teleporting objectID: %llu to X: %f, Z: %f", entity->GetObjectID(), pos.x, pos.z);
GameMessages::SendTeleport(entity->GetObjectID(), pos, NiQuaternion(), sysAddr);
} 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).");
}
GameMessages::SendTeleport(sourceEntity->GetObjectID(), pos, sourceEntity->GetRotation(), sourceEntity->GetSystemAddress());
auto* possessorComponent = entity->GetComponent<PossessorComponent>();
auto* possessorComponent = sourceEntity->GetComponent<PossessorComponent>();
if (possessorComponent) {
auto* possassableEntity = Game::entityManager->GetEntity(possessorComponent->GetPossessable());