Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 5 commits into from
Dec 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions dGame/dUtilities/SlashCommandHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
63 changes: 45 additions & 18 deletions dGame/dUtilities/SlashCommands/DEVGMCommands.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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 = GeneralUtils::TryParse<float>(splitArgs.at(0));
if (!x) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid x.");
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 z = GeneralUtils::TryParse<float>(splitArgs.at(1));
if (!z) {
ChatPackets::SendSystemMessage(sysAddr, u"Invalid z.");
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;
}

pos.SetX(x.value());
pos.SetY(0.0f);
pos.SetZ(z.value());

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;
}
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());

Expand Down
2 changes: 1 addition & 1 deletion docs/Commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -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|
|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|
|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|
EmosewaMC marked this conversation as resolved.
Show resolved Hide resolved
|activatespawner|`/activatespawner <spawner name>`|Activates spawner by name.|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|
Expand Down
Loading