mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 17:43:52 +00:00
Add bindpoint support and WMO snap fix
This commit is contained in:
parent
132a6ea3c9
commit
189f4a0a58
7 changed files with 94 additions and 0 deletions
|
|
@ -373,6 +373,8 @@ public:
|
||||||
void unstuck();
|
void unstuck();
|
||||||
void setUnstuckGyCallback(UnstuckCallback cb) { unstuckGyCallback_ = std::move(cb); }
|
void setUnstuckGyCallback(UnstuckCallback cb) { unstuckGyCallback_ = std::move(cb); }
|
||||||
void unstuckGy();
|
void unstuckGy();
|
||||||
|
using BindPointCallback = std::function<void(uint32_t mapId, float x, float y, float z)>;
|
||||||
|
void setBindPointCallback(BindPointCallback cb) { bindPointCallback_ = std::move(cb); }
|
||||||
|
|
||||||
// Creature spawn callback (online mode - triggered when creature enters view)
|
// Creature spawn callback (online mode - triggered when creature enters view)
|
||||||
// Parameters: guid, displayId, x, y, z (canonical), orientation
|
// Parameters: guid, displayId, x, y, z (canonical), orientation
|
||||||
|
|
@ -837,6 +839,7 @@ private:
|
||||||
WorldEntryCallback worldEntryCallback_;
|
WorldEntryCallback worldEntryCallback_;
|
||||||
UnstuckCallback unstuckCallback_;
|
UnstuckCallback unstuckCallback_;
|
||||||
UnstuckCallback unstuckGyCallback_;
|
UnstuckCallback unstuckGyCallback_;
|
||||||
|
BindPointCallback bindPointCallback_;
|
||||||
CreatureSpawnCallback creatureSpawnCallback_;
|
CreatureSpawnCallback creatureSpawnCallback_;
|
||||||
CreatureDespawnCallback creatureDespawnCallback_;
|
CreatureDespawnCallback creatureDespawnCallback_;
|
||||||
CreatureMoveCallback creatureMoveCallback_;
|
CreatureMoveCallback creatureMoveCallback_;
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,7 @@ enum class Opcode : uint16_t {
|
||||||
CMSG_GAMEOBJECT_QUERY = 0x05E,
|
CMSG_GAMEOBJECT_QUERY = 0x05E,
|
||||||
SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x05F,
|
SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x05F,
|
||||||
CMSG_SET_ACTIVE_MOVER = 0x26A,
|
CMSG_SET_ACTIVE_MOVER = 0x26A,
|
||||||
|
CMSG_BINDER_ACTIVATE = 0x1B2,
|
||||||
|
|
||||||
// ---- XP ----
|
// ---- XP ----
|
||||||
SMSG_LOG_XPGAIN = 0x1D0,
|
SMSG_LOG_XPGAIN = 0x1D0,
|
||||||
|
|
@ -272,6 +273,7 @@ enum class Opcode : uint16_t {
|
||||||
// ---- Battleground ----
|
// ---- Battleground ----
|
||||||
SMSG_BATTLEFIELD_PORT_DENIED = 0x014B,
|
SMSG_BATTLEFIELD_PORT_DENIED = 0x014B,
|
||||||
SMSG_REMOVED_FROM_PVP_QUEUE = 0x0170,
|
SMSG_REMOVED_FROM_PVP_QUEUE = 0x0170,
|
||||||
|
SMSG_BINDPOINTUPDATE = 0x01B3,
|
||||||
CMSG_BATTLEFIELD_LIST = 0x023C,
|
CMSG_BATTLEFIELD_LIST = 0x023C,
|
||||||
SMSG_BATTLEFIELD_LIST = 0x023D,
|
SMSG_BATTLEFIELD_LIST = 0x023D,
|
||||||
CMSG_BATTLEFIELD_JOIN = 0x023E,
|
CMSG_BATTLEFIELD_JOIN = 0x023E,
|
||||||
|
|
|
||||||
|
|
@ -1558,6 +1558,30 @@ public:
|
||||||
static bool parse(network::Packet& packet, GossipMessageData& data);
|
static bool parse(network::Packet& packet, GossipMessageData& data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Bind Point (Hearthstone)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
struct BindPointUpdateData {
|
||||||
|
float x = 0.0f;
|
||||||
|
float y = 0.0f;
|
||||||
|
float z = 0.0f;
|
||||||
|
uint32_t mapId = 0;
|
||||||
|
uint32_t zoneId = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** CMSG_BINDER_ACTIVATE packet builder */
|
||||||
|
class BinderActivatePacket {
|
||||||
|
public:
|
||||||
|
static network::Packet build(uint64_t npcGuid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** SMSG_BINDPOINTUPDATE parser */
|
||||||
|
class BindPointUpdateParser {
|
||||||
|
public:
|
||||||
|
static bool parse(network::Packet& packet, BindPointUpdateData& data);
|
||||||
|
};
|
||||||
|
|
||||||
/** CMSG_QUESTGIVER_QUERY_QUEST packet builder */
|
/** CMSG_QUESTGIVER_QUERY_QUEST packet builder */
|
||||||
class QuestgiverQueryQuestPacket {
|
class QuestgiverQueryQuestPacket {
|
||||||
public:
|
public:
|
||||||
|
|
|
||||||
|
|
@ -645,6 +645,15 @@ void Application::setupUICallbacks() {
|
||||||
cc->reset();
|
cc->reset();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Bind point update (innkeeper)
|
||||||
|
gameHandler->setBindPointCallback([this](uint32_t mapId, float x, float y, float z) {
|
||||||
|
if (!renderer || !renderer->getCameraController()) return;
|
||||||
|
glm::vec3 canonical(x, y, z);
|
||||||
|
glm::vec3 renderPos = core::coords::canonicalToRender(canonical);
|
||||||
|
renderer->getCameraController()->setDefaultSpawn(renderPos, 0.0f, 15.0f);
|
||||||
|
LOG_INFO("Bindpoint set: mapId=", mapId, " pos=(", x, ", ", y, ", ", z, ")");
|
||||||
|
});
|
||||||
|
|
||||||
// Faction hostility map is built in buildFactionHostilityMap() when character enters world
|
// Faction hostility map is built in buildFactionHostilityMap() when character enters world
|
||||||
|
|
||||||
// Creature spawn callback (online mode) - spawn creature models
|
// Creature spawn callback (online mode) - spawn creature models
|
||||||
|
|
|
||||||
|
|
@ -577,6 +577,22 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
||||||
case Opcode::SMSG_GOSSIP_MESSAGE:
|
case Opcode::SMSG_GOSSIP_MESSAGE:
|
||||||
handleGossipMessage(packet);
|
handleGossipMessage(packet);
|
||||||
break;
|
break;
|
||||||
|
case Opcode::SMSG_BINDPOINTUPDATE: {
|
||||||
|
BindPointUpdateData data;
|
||||||
|
if (BindPointUpdateParser::parse(packet, data)) {
|
||||||
|
LOG_INFO("Bindpoint updated: mapId=", data.mapId,
|
||||||
|
" pos=(", data.x, ", ", data.y, ", ", data.z, ")");
|
||||||
|
if (bindPointCallback_) {
|
||||||
|
glm::vec3 canonical = core::coords::serverToCanonical(
|
||||||
|
glm::vec3(data.x, data.y, data.z));
|
||||||
|
bindPointCallback_(data.mapId, canonical.x, canonical.y, canonical.z);
|
||||||
|
}
|
||||||
|
addSystemChatMessage("Your home has been set.");
|
||||||
|
} else {
|
||||||
|
LOG_WARNING("Failed to parse SMSG_BINDPOINTUPDATE");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Opcode::SMSG_GOSSIP_COMPLETE:
|
case Opcode::SMSG_GOSSIP_COMPLETE:
|
||||||
handleGossipComplete(packet);
|
handleGossipComplete(packet);
|
||||||
break;
|
break;
|
||||||
|
|
@ -4194,6 +4210,21 @@ void GameHandler::selectGossipOption(uint32_t optionId) {
|
||||||
if (state != WorldState::IN_WORLD || !socket || !gossipWindowOpen) return;
|
if (state != WorldState::IN_WORLD || !socket || !gossipWindowOpen) return;
|
||||||
auto packet = GossipSelectOptionPacket::build(currentGossip.npcGuid, currentGossip.menuId, optionId);
|
auto packet = GossipSelectOptionPacket::build(currentGossip.npcGuid, currentGossip.menuId, optionId);
|
||||||
socket->send(packet);
|
socket->send(packet);
|
||||||
|
|
||||||
|
// If this is an innkeeper "make this inn your home" option, send binder activate.
|
||||||
|
for (const auto& opt : currentGossip.options) {
|
||||||
|
if (opt.id != optionId) continue;
|
||||||
|
std::string text = opt.text;
|
||||||
|
std::transform(text.begin(), text.end(), text.begin(),
|
||||||
|
[](unsigned char c){ return static_cast<char>(std::tolower(c)); });
|
||||||
|
if (text.find("make this inn your home") != std::string::npos ||
|
||||||
|
text.find("set your home") != std::string::npos) {
|
||||||
|
auto bindPkt = BinderActivatePacket::build(currentGossip.npcGuid);
|
||||||
|
socket->send(bindPkt);
|
||||||
|
LOG_INFO("Sent CMSG_BINDER_ACTIVATE for npc=0x", std::hex, currentGossip.npcGuid, std::dec);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameHandler::selectGossipQuest(uint32_t questId) {
|
void GameHandler::selectGossipQuest(uint32_t questId) {
|
||||||
|
|
|
||||||
|
|
@ -2450,6 +2450,26 @@ bool GossipMessageParser::parse(network::Packet& packet, GossipMessageData& data
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// Bind Point (Hearthstone)
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
network::Packet BinderActivatePacket::build(uint64_t npcGuid) {
|
||||||
|
network::Packet pkt(static_cast<uint16_t>(Opcode::CMSG_BINDER_ACTIVATE));
|
||||||
|
pkt.writeUInt64(npcGuid);
|
||||||
|
return pkt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BindPointUpdateParser::parse(network::Packet& packet, BindPointUpdateData& data) {
|
||||||
|
if (packet.getSize() < 20) return false;
|
||||||
|
data.x = packet.readFloat();
|
||||||
|
data.y = packet.readFloat();
|
||||||
|
data.z = packet.readFloat();
|
||||||
|
data.mapId = packet.readUInt32();
|
||||||
|
data.zoneId = packet.readUInt32();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool QuestRequestItemsParser::parse(network::Packet& packet, QuestRequestItemsData& data) {
|
bool QuestRequestItemsParser::parse(network::Packet& packet, QuestRequestItemsData& data) {
|
||||||
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
if (packet.getSize() - packet.getReadPos() < 20) return false;
|
||||||
data.npcGuid = packet.readUInt64();
|
data.npcGuid = packet.readUInt64();
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,11 @@ std::optional<float> selectReachableFloor(const std::optional<float>& terrainH,
|
||||||
if (terrainH && *terrainH <= refZ + maxStepUp) reachTerrain = terrainH;
|
if (terrainH && *terrainH <= refZ + maxStepUp) reachTerrain = terrainH;
|
||||||
if (wmoH && *wmoH <= refZ + maxStepUp) reachWmo = wmoH;
|
if (wmoH && *wmoH <= refZ + maxStepUp) reachWmo = wmoH;
|
||||||
|
|
||||||
|
// Avoid snapping up to higher WMO floors when entering buildings.
|
||||||
|
if (reachTerrain && reachWmo && *reachWmo > refZ + 2.0f) {
|
||||||
|
return reachTerrain;
|
||||||
|
}
|
||||||
|
|
||||||
if (reachTerrain && reachWmo) {
|
if (reachTerrain && reachWmo) {
|
||||||
// Both available: prefer the one closest to the player's feet.
|
// Both available: prefer the one closest to the player's feet.
|
||||||
// This prevents tunnels/caves from snapping the player up to the
|
// This prevents tunnels/caves from snapping the player up to the
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue