mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-17 09:33:51 +00:00
Add taxi system, fix WMO interior lighting, ramp collision, and /unstuck
- Implement flight path system: SMSG_SHOWTAXINODES parser, CMSG_ACTIVATETAXIEXPRESS builder, BFS multi-hop pathfinding through TaxiNodes/TaxiPath DBC, taxi destination UI, movement blocking during flight - Fix WMO interiors too dark by boosting vertex color lighting multiplier - Dim M2 objects inside WMO interiors (rugs, furniture) via per-instance interior detection - Fix ramp/stair clipping by lowering wall collision normal threshold from 0.85 to 0.55 - Restore 5-sample cardinal footprint for ground detection to fix rug slipping - Fix /unstuck command to reset player Z to WMO/terrain floor height - Handle MSG_MOVE_TELEPORT_ACK and SMSG_TRANSFER_PENDING for hearthstone teleports - Fix spawning under Stormwind with online-mode camera controller reset
This commit is contained in:
parent
c5a1fe927b
commit
3c2a728ec4
15 changed files with 691 additions and 108 deletions
|
|
@ -364,6 +364,11 @@ public:
|
|||
using WorldEntryCallback = std::function<void(uint32_t mapId, float x, float y, float z)>;
|
||||
void setWorldEntryCallback(WorldEntryCallback cb) { worldEntryCallback_ = std::move(cb); }
|
||||
|
||||
// Unstuck callback (resets player Z to floor height)
|
||||
using UnstuckCallback = std::function<void()>;
|
||||
void setUnstuckCallback(UnstuckCallback cb) { unstuckCallback_ = std::move(cb); }
|
||||
void unstuck();
|
||||
|
||||
// Creature spawn callback (online mode - triggered when creature enters view)
|
||||
// Parameters: guid, displayId, x, y, z (canonical), orientation
|
||||
using CreatureSpawnCallback = std::function<void(uint64_t guid, uint32_t displayId, float x, float y, float z, float orientation)>;
|
||||
|
|
@ -450,6 +455,27 @@ public:
|
|||
}
|
||||
const std::unordered_map<uint64_t, QuestGiverStatus>& getNpcQuestStatuses() const { return npcQuestStatus_; }
|
||||
|
||||
// Taxi / Flight Paths
|
||||
bool isTaxiWindowOpen() const { return taxiWindowOpen_; }
|
||||
void closeTaxi();
|
||||
void activateTaxi(uint32_t destNodeId);
|
||||
bool isOnTaxiFlight() const { return onTaxiFlight_; }
|
||||
const ShowTaxiNodesData& getTaxiData() const { return currentTaxiData_; }
|
||||
uint32_t getTaxiCurrentNode() const { return currentTaxiData_.nearestNode; }
|
||||
|
||||
struct TaxiNode {
|
||||
uint32_t id = 0;
|
||||
uint32_t mapId = 0;
|
||||
float x = 0, y = 0, z = 0;
|
||||
std::string name;
|
||||
};
|
||||
struct TaxiPathEdge {
|
||||
uint32_t pathId = 0;
|
||||
uint32_t fromNode = 0, toNode = 0;
|
||||
uint32_t cost = 0;
|
||||
};
|
||||
const std::unordered_map<uint32_t, TaxiNode>& getTaxiNodes() const { return taxiNodes_; }
|
||||
|
||||
// Vendor
|
||||
void openVendor(uint64_t npcGuid);
|
||||
void closeVendor();
|
||||
|
|
@ -601,6 +627,14 @@ private:
|
|||
void handleListInventory(network::Packet& packet);
|
||||
void addMoneyCopper(uint32_t amount);
|
||||
|
||||
// ---- Teleport handler ----
|
||||
void handleTeleportAck(network::Packet& packet);
|
||||
|
||||
// ---- Taxi handlers ----
|
||||
void handleShowTaxiNodes(network::Packet& packet);
|
||||
void handleActivateTaxiReply(network::Packet& packet);
|
||||
void loadTaxiDbc();
|
||||
|
||||
// ---- Server info handlers ----
|
||||
void handleQueryTimeResponse(network::Packet& packet);
|
||||
void handlePlayedTime(network::Packet& packet);
|
||||
|
|
@ -686,8 +720,9 @@ private:
|
|||
float pingInterval = 30.0f; // Ping interval (30 seconds)
|
||||
uint32_t lastLatency = 0; // Last measured latency (milliseconds)
|
||||
|
||||
// Player GUID
|
||||
// Player GUID and map
|
||||
uint64_t playerGuid = 0;
|
||||
uint32_t currentMapId_ = 0;
|
||||
|
||||
// ---- Phase 1: Name caches ----
|
||||
std::unordered_map<uint64_t, std::string> playerNameCache;
|
||||
|
|
@ -742,6 +777,7 @@ private:
|
|||
|
||||
// ---- Phase 3: Spells ----
|
||||
WorldEntryCallback worldEntryCallback_;
|
||||
UnstuckCallback unstuckCallback_;
|
||||
CreatureSpawnCallback creatureSpawnCallback_;
|
||||
CreatureDespawnCallback creatureDespawnCallback_;
|
||||
CreatureMoveCallback creatureMoveCallback_;
|
||||
|
|
@ -800,6 +836,15 @@ private:
|
|||
return it != factionHostileMap_.end() ? it->second : true; // default hostile if unknown
|
||||
}
|
||||
|
||||
// Taxi / Flight Paths
|
||||
std::unordered_map<uint32_t, TaxiNode> taxiNodes_;
|
||||
std::vector<TaxiPathEdge> taxiPathEdges_;
|
||||
bool taxiDbcLoaded_ = false;
|
||||
bool taxiWindowOpen_ = false;
|
||||
ShowTaxiNodesData currentTaxiData_;
|
||||
uint64_t taxiNpcGuid_ = 0;
|
||||
bool onTaxiFlight_ = false;
|
||||
|
||||
// Vendor
|
||||
bool vendorWindowOpen = false;
|
||||
ListInventoryData currentVendorItems;
|
||||
|
|
|
|||
|
|
@ -236,6 +236,15 @@ enum class Opcode : uint16_t {
|
|||
// ---- Death/Respawn ----
|
||||
CMSG_REPOP_REQUEST = 0x015A,
|
||||
CMSG_SPIRIT_HEALER_ACTIVATE = 0x0176,
|
||||
|
||||
// ---- Teleport / Transfer ----
|
||||
MSG_MOVE_TELEPORT_ACK = 0x0C7,
|
||||
SMSG_TRANSFER_PENDING = 0x003F,
|
||||
|
||||
// ---- Taxi / Flight Paths ----
|
||||
SMSG_SHOWTAXINODES = 0x01A9,
|
||||
SMSG_ACTIVATETAXIREPLY = 0x01AE,
|
||||
CMSG_ACTIVATETAXIEXPRESS = 0x0312,
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
|||
|
|
@ -1652,6 +1652,48 @@ public:
|
|||
static bool parse(network::Packet& packet, ListInventoryData& data);
|
||||
};
|
||||
|
||||
// ============================================================
|
||||
// Taxi / Flight Paths
|
||||
// ============================================================
|
||||
|
||||
static constexpr uint32_t TLK_TAXI_MASK_SIZE = 12;
|
||||
|
||||
/** SMSG_SHOWTAXINODES data */
|
||||
struct ShowTaxiNodesData {
|
||||
uint32_t windowInfo = 0; // 1 = show window
|
||||
uint64_t npcGuid = 0;
|
||||
uint32_t nearestNode = 0; // Taxi node player is at
|
||||
uint32_t nodeMask[TLK_TAXI_MASK_SIZE] = {};
|
||||
bool isNodeKnown(uint32_t nodeId) const {
|
||||
uint32_t idx = nodeId / 32;
|
||||
uint32_t bit = nodeId % 32;
|
||||
return idx < TLK_TAXI_MASK_SIZE && (nodeMask[idx] & (1u << bit));
|
||||
}
|
||||
};
|
||||
|
||||
/** SMSG_SHOWTAXINODES parser */
|
||||
class ShowTaxiNodesParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, ShowTaxiNodesData& data);
|
||||
};
|
||||
|
||||
/** SMSG_ACTIVATETAXIREPLY data */
|
||||
struct ActivateTaxiReplyData {
|
||||
uint32_t result = 0; // 0 = OK
|
||||
};
|
||||
|
||||
/** SMSG_ACTIVATETAXIREPLY parser */
|
||||
class ActivateTaxiReplyParser {
|
||||
public:
|
||||
static bool parse(network::Packet& packet, ActivateTaxiReplyData& data);
|
||||
};
|
||||
|
||||
/** CMSG_ACTIVATETAXIEXPRESS packet builder */
|
||||
class ActivateTaxiExpressPacket {
|
||||
public:
|
||||
static network::Packet build(uint64_t npcGuid, const std::vector<uint32_t>& pathNodes);
|
||||
};
|
||||
|
||||
/** CMSG_REPOP_REQUEST packet builder */
|
||||
class RepopRequestPacket {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ public:
|
|||
}
|
||||
|
||||
void reset();
|
||||
void setOnlineMode(bool online) { onlineMode = online; }
|
||||
void startIntroPan(float durationSec = 2.8f, float orbitDegrees = 140.0f);
|
||||
bool isIntroActive() const { return introActive; }
|
||||
|
||||
|
|
@ -63,6 +64,7 @@ public:
|
|||
bool isSitting() const { return sitting; }
|
||||
bool isSwimming() const { return swimming; }
|
||||
const glm::vec3* getFollowTarget() const { return followTarget; }
|
||||
glm::vec3* getFollowTargetMutable() { return followTarget; }
|
||||
|
||||
// Movement callback for sending opcodes to server
|
||||
using MovementCallback = std::function<void(uint32_t opcode)>;
|
||||
|
|
@ -134,10 +136,6 @@ private:
|
|||
static constexpr float JUMP_BUFFER_TIME = 0.15f; // 150ms input buffer
|
||||
static constexpr float COYOTE_TIME = 0.10f; // 100ms grace after leaving ground
|
||||
|
||||
// Cached floor height between frames (skip expensive probes when barely moving)
|
||||
std::optional<float> cachedFloorHeight;
|
||||
glm::vec2 cachedFloorPos = glm::vec2(0.0f);
|
||||
|
||||
// Cached isInsideWMO result (throttled to avoid per-frame cost)
|
||||
bool cachedInsideWMO = false;
|
||||
int insideWMOCheckCounter = 0;
|
||||
|
|
@ -188,6 +186,9 @@ private:
|
|||
static constexpr float WOW_GRAVITY = -19.29f;
|
||||
static constexpr float WOW_JUMP_VELOCITY = 7.96f;
|
||||
|
||||
// Online mode: trust server position, don't prefer outdoors over WMO floors
|
||||
bool onlineMode = false;
|
||||
|
||||
// Default spawn position (Goldshire Inn)
|
||||
glm::vec3 defaultPosition = glm::vec3(-9464.0f, 62.0f, 200.0f);
|
||||
float defaultYaw = 0.0f;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ namespace rendering {
|
|||
|
||||
class Shader;
|
||||
class Camera;
|
||||
class WMORenderer;
|
||||
|
||||
/**
|
||||
* GPU representation of an M2 model
|
||||
|
|
@ -276,7 +277,10 @@ public:
|
|||
}
|
||||
void clearShadowMap() { shadowEnabled = false; }
|
||||
|
||||
void setWMORenderer(WMORenderer* wmo) { wmoRenderer = wmo; }
|
||||
|
||||
private:
|
||||
WMORenderer* wmoRenderer = nullptr;
|
||||
pipeline::AssetManager* assetManager = nullptr;
|
||||
std::unique_ptr<Shader> shader;
|
||||
|
||||
|
|
|
|||
|
|
@ -203,6 +203,12 @@ public:
|
|||
*/
|
||||
bool isInsideWMO(float glX, float glY, float glZ, uint32_t* outModelId = nullptr) const;
|
||||
|
||||
/**
|
||||
* Check if a position is inside an interior WMO group (flag 0x2000).
|
||||
* Used to dim M2 lighting for doodads placed indoors.
|
||||
*/
|
||||
bool isInsideInteriorWMO(float glX, float glY, float glZ) const;
|
||||
|
||||
/**
|
||||
* Raycast against WMO bounding boxes for camera collision
|
||||
* @param origin Ray origin (e.g., character head position)
|
||||
|
|
|
|||
|
|
@ -143,6 +143,7 @@ private:
|
|||
void renderQuestRequestItemsWindow(game::GameHandler& gameHandler);
|
||||
void renderQuestOfferRewardWindow(game::GameHandler& gameHandler);
|
||||
void renderVendorWindow(game::GameHandler& gameHandler);
|
||||
void renderTaxiWindow(game::GameHandler& gameHandler);
|
||||
void renderDeathScreen(game::GameHandler& gameHandler);
|
||||
void renderEscapeMenu();
|
||||
void renderSettingsWindow();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue