diff --git a/src/teleport-to-node.cpp b/src/teleport-to-node.cpp index 5d4892b..d22d40c 100644 --- a/src/teleport-to-node.cpp +++ b/src/teleport-to-node.cpp @@ -1,5 +1,8 @@ /* - * Teleport to nearest mining node (.telenode) — AzerothCore ChatCommandTable style + * Teleport to nearest mining node (.telenode) + * - AzerothCore ChatCommandTable style (matches si_commandscript) + * - Skips depleted/in-use nodes using loot/go state + * - Avoids "sticky" re-teleport by skipping the last-node GUID and a min distance */ #include "ScriptMgr.h" @@ -8,48 +11,64 @@ #include "ChatCommand.h" #include "Player.h" #include "GameObject.h" -#include "GameTime.h" +#include "ObjectGuid.h" #include +#include #include using namespace Acore::ChatCommands; -namespace -{ - // === CONFIG === - static constexpr float TELEPORT_DISTANCE = 200.0f; // yards - static constexpr float Z_BUMP = 1.5f; // anti-clip offset +// === CONFIG === +static constexpr float TELEPORT_DISTANCE = 200.0f; // yards (search radius) +static constexpr float Z_BUMP = 1.5f; // anti-clip offset on Z +static constexpr float MIN_RETELEPORT_DIST = 8.0f; // don't pick nodes closer than this to the player - // Mining node entries (trim as needed) - static const std::vector kVeinEntries = { - 324,1610,1667,1731,1732,1733,1734,2054,2055,3763,3764,19903, - 73940,73941,103711,103713,105569,123848,150080,150082,175404, - 176643,177388,179144,179224,180215,181109,181248,181249,181557, - 185557,191133,1735,2040,2047,2653,73939,123309,123310,150079, - 150081,165658,176645,181108,181555,181556,181569,181570,185877, - 189978,189979,189980,189981,195036, - }; +// Mining node entries (trim to the entries you actually want) +static const std::vector kVeinEntries = { + 324, 1610, 1667, 1731, 1732, 1733, 1734, 2054, 2055, 3763, 3764, 19903, + 73940, 73941, 103711, 103713, 105569, 123848, 150080, 150082, 175404, + 176643, 177388, 179144, 179224, 180215, 181109, 181248, 181249, 181557, + 185557, 191133, 1735, 2040, 2047, 2653, 73939, 123309, 123310, 150079, + 150081, 165658, 176645, 181108, 181555, 181556, 181569, 181570, 185877, + 189978, 189979, 189980, 189981, 195036, +}; - static GameObject* FindNearestReadyVein(Player* player, float maxRange, float minDistanceFromPlayer) +// Remember the last teleported-to node for each player (prevents “sticky” coords) +static std::unordered_map s_lastNodeByPlayer; + +static GameObject* FindNearestReadyVein(Player* player, float maxRange, float minDistanceFromPlayer) { GameObject* nearest = nullptr; float nearestDist = std::numeric_limits::infinity(); + // Skip the last node we teleported to (if any) + ObjectGuid lastNodeGuid; + if (auto it = s_lastNodeByPlayer.find(player->GetGUID()); it != s_lastNodeByPlayer.end()) + lastNodeGuid = it->second; + for (uint32 entry : kVeinEntries) { + // Fast query: nearest object of this entry (we still validate it) if (GameObject* go = player->FindNearestGameObject(entry, maxRange)) { + // Don’t pick the node we just used + if (go->GetGUID() == lastNodeGuid) + continue; + + // Ignore nodes not in the default/usable state or not loot-ready + // (common depleted states are GO_ACTIVATED/GO_JUST_DEACTIVATED) + if (go->GetLootState() != GO_READY) + continue; + + if (go->GetGoState() != GO_STATE_READY) + continue; + + // Don’t pick a node that’s basically under the player float d = player->GetDistance(go); if (d < minDistanceFromPlayer) continue; - if (go->IsInUse()) - continue; - - if (go->GetLootState() != GO_READY) - continue; - if (d <= maxRange && d < nearestDist) { nearest = go; @@ -60,7 +79,7 @@ namespace return nearest; } - static bool DoTeleNode(Player* player) +static bool DoTeleNode(Player* player) { if (!player) return false; @@ -68,14 +87,14 @@ namespace if (player->IsInCombat()) { ChatHandler(player->GetSession()).PSendSysMessage("You can't use this while in combat."); - return true; + return true; // handled } - // Skip anything within ~8 yards so we don't pick the node we just mined - constexpr float kMinReTeleportDist = 8.0f; - - if (GameObject* node = FindNearestReadyVein(player, TELEPORT_DISTANCE, kMinReTeleportDist)) + if (GameObject* node = FindNearestReadyVein(player, TELEPORT_DISTANCE, MIN_RETELEPORT_DIST)) { + // Remember this node so the next call won’t bounce back here if it hasn’t despawned yet + s_lastNodeByPlayer[player->GetGUID()] = node->GetGUID(); + player->TeleportTo( node->GetMapId(), node->GetPositionX(), @@ -88,6 +107,7 @@ namespace { ChatHandler(player->GetSession()).PSendSysMessage("No mining nodes found within range."); } + return true; } @@ -96,7 +116,7 @@ class telenode_commandscript : public CommandScript public: telenode_commandscript() : CommandScript("telenode_commandscript") { } - // Match your si_commandscript style: ChatCommandTable and { name, handler, SEC, Console } + // Matches your si_commandscript style: ChatCommandTable with { name, handler, SEC, Console } ChatCommandTable GetCommands() const override { static ChatCommandTable commandTable = @@ -107,7 +127,6 @@ public: } private: - // No-arg handler (Acore parser will pass only ChatHandler*) static bool HandleTeleNode(ChatHandler* handler) { if (!handler) @@ -125,7 +144,7 @@ private: } }; -// Export symbol called by your loader.cpp +// Export symbol; call this from your module loader (e.g., Addmod_teleport_to_nodeScripts) void AddSC_telenode_commandscript() { new telenode_commandscript();