Transport hell

This commit is contained in:
Kelsi 2026-02-11 00:54:38 -08:00
parent 2e923311d0
commit f3f3b62880
12 changed files with 912 additions and 126 deletions

View file

@ -87,7 +87,7 @@ private:
void despawnOnlineCreature(uint64_t guid);
void buildCreatureDisplayLookups();
std::string getModelPathForDisplayId(uint32_t displayId) const;
void spawnOnlineGameObject(uint64_t guid, uint32_t displayId, float x, float y, float z, float orientation);
void spawnOnlineGameObject(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation);
void despawnOnlineGameObject(uint64_t guid);
void buildGameObjectDisplayLookups();
std::string getGameObjectModelPathForDisplayId(uint32_t displayId) const;
@ -197,6 +197,7 @@ private:
struct PendingGameObjectSpawn {
uint64_t guid;
uint32_t entry;
uint32_t displayId;
float x, y, z, orientation;
};

View file

@ -462,8 +462,8 @@ public:
void setCreatureDespawnCallback(CreatureDespawnCallback cb) { creatureDespawnCallback_ = std::move(cb); }
// GameObject spawn callback (online mode - triggered when gameobject enters view)
// Parameters: guid, displayId, x, y, z (canonical), orientation
using GameObjectSpawnCallback = std::function<void(uint64_t guid, uint32_t displayId, float x, float y, float z, float orientation)>;
// Parameters: guid, entry, displayId, x, y, z (canonical), orientation
using GameObjectSpawnCallback = std::function<void(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation)>;
void setGameObjectSpawnCallback(GameObjectSpawnCallback cb) { gameObjectSpawnCallback_ = std::move(cb); }
// GameObject despawn callback (online mode - triggered when gameobject leaves view)
@ -483,10 +483,25 @@ public:
using TransportMoveCallback = std::function<void(uint64_t guid, float x, float y, float z, float orientation)>;
void setTransportMoveCallback(TransportMoveCallback cb) { transportMoveCallback_ = std::move(cb); }
// Transport spawn callback (online mode - triggered when transport GameObject is first detected)
// Parameters: guid, entry, displayId, x, y, z (canonical), orientation
using TransportSpawnCallback = std::function<void(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation)>;
void setTransportSpawnCallback(TransportSpawnCallback cb) { transportSpawnCallback_ = std::move(cb); }
// Notify that a transport has been spawned (called after WMO instance creation)
void notifyTransportSpawned(uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation) {
if (transportSpawnCallback_) {
transportSpawnCallback_(guid, entry, displayId, x, y, z, orientation);
}
}
// Transport state for player-on-transport
bool isOnTransport() const { return playerTransportGuid_ != 0; }
uint64_t getPlayerTransportGuid() const { return playerTransportGuid_; }
glm::vec3 getPlayerTransportOffset() const { return playerTransportOffset_; }
// Check if a GUID is a known transport
bool isTransportGuid(uint64_t guid) const { return transportGuids_.count(guid) > 0; }
glm::vec3 getComposedWorldPosition(); // Compose transport transform * local offset
TransportManager* getTransportManager() { return transportManager_.get(); }
void setPlayerOnTransport(uint64_t transportGuid, const glm::vec3& localOffset) {
@ -792,6 +807,7 @@ private:
// ---- Creature movement handler ----
void handleMonsterMove(network::Packet& packet);
void handleMonsterMoveTransport(network::Packet& packet);
// ---- Phase 5 handlers ----
void handleLootResponse(network::Packet& packet);
@ -979,6 +995,7 @@ private:
CreatureDespawnCallback creatureDespawnCallback_;
CreatureMoveCallback creatureMoveCallback_;
TransportMoveCallback transportMoveCallback_;
TransportSpawnCallback transportSpawnCallback_;
GameObjectSpawnCallback gameObjectSpawnCallback_;
GameObjectDespawnCallback gameObjectDespawnCallback_;

View file

@ -50,6 +50,7 @@ enum class Opcode : uint16_t {
// ---- Entity/Object updates ----
SMSG_UPDATE_OBJECT = 0x0A9,
SMSG_COMPRESSED_UPDATE_OBJECT = 0x1F6,
SMSG_MONSTER_MOVE_TRANSPORT = 0x2AE,
SMSG_DESTROY_OBJECT = 0x0AA,
// ---- Chat ----

View file

@ -3,6 +3,7 @@
#include <cstdint>
#include <vector>
#include <unordered_map>
#include <string>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -10,22 +11,29 @@ namespace wowee::rendering {
class WMORenderer;
}
namespace wowee::pipeline {
class AssetManager;
}
namespace wowee::game {
struct TimedPoint {
uint32_t tMs; // Time in milliseconds from DBC
glm::vec3 pos; // Position at this time
};
struct TransportPath {
uint32_t pathId;
std::vector<glm::vec3> waypoints; // Position keyframes
std::vector<glm::quat> rotations; // Optional rotation keyframes
bool looping;
float speed; // units/sec (default 18.0f like taxi)
std::vector<TimedPoint> points; // Time-indexed waypoints (includes duplicate first point at end for wrap)
bool looping; // Set to false after adding explicit wrap point
uint32_t durationMs; // Total loop duration in ms (includes wrap segment if added)
};
struct ActiveTransport {
uint64_t guid; // Entity GUID
uint32_t wmoInstanceId; // WMO renderer instance ID
uint32_t pathId; // Current path
size_t currentSegment; // Current waypoint index
float segmentProgress; // Distance along segment
glm::vec3 basePosition; // Spawn position (base offset for path)
glm::vec3 position; // Current world position
glm::quat rotation; // Current world rotation
glm::mat4 transform; // Cached world transform
@ -39,6 +47,17 @@ struct ActiveTransport {
glm::vec3 deckMin;
glm::vec3 deckMax;
bool hasDeckBounds;
// Time-based animation (deterministic, no drift)
uint32_t localClockMs; // Local path time in milliseconds
bool hasServerClock; // Whether we've synced with server time
int32_t serverClockOffsetMs; // Offset: serverClock - localNow
bool useClientAnimation; // Use client-side path animation
float serverYaw; // Server-authoritative yaw (radians)
bool hasServerYaw; // Whether we've received server yaw
float lastServerUpdate; // Time of last server movement update
int serverUpdateCount; // Number of server updates received
};
class TransportManager {
@ -49,7 +68,7 @@ public:
void setWMORenderer(rendering::WMORenderer* renderer) { wmoRenderer_ = renderer; }
void update(float deltaTime);
void registerTransport(uint64_t guid, uint32_t wmoInstanceId, uint32_t pathId);
void registerTransport(uint64_t guid, uint32_t wmoInstanceId, uint32_t pathId, const glm::vec3& spawnWorldPos);
void unregisterTransport(uint64_t guid);
ActiveTransport* getTransport(uint64_t guid);
@ -59,15 +78,30 @@ public:
void loadPathFromNodes(uint32_t pathId, const std::vector<glm::vec3>& waypoints, bool looping = true, float speed = 18.0f);
void setDeckBounds(uint64_t guid, const glm::vec3& min, const glm::vec3& max);
// Load transport paths from TransportAnimation.dbc
bool loadTransportAnimationDBC(pipeline::AssetManager* assetMgr);
// Check if a path exists for a given GameObject entry
bool hasPathForEntry(uint32_t entry) const;
// Update server-controlled transport position/rotation directly (bypasses path movement)
void updateServerTransport(uint64_t guid, const glm::vec3& position, float orientation);
// Enable/disable client-side animation for transports without server updates
void setClientSideAnimation(bool enabled) { clientSideAnimation_ = enabled; }
bool isClientSideAnimation() const { return clientSideAnimation_; }
private:
void updateTransportMovement(ActiveTransport& transport, float deltaTime);
glm::vec3 interpolatePath(const TransportPath& path, size_t segmentIdx, float t);
glm::quat calculateOrientation(const TransportPath& path, size_t segmentIdx, float t);
glm::vec3 evalTimedCatmullRom(const TransportPath& path, uint32_t pathTimeMs);
glm::quat orientationFromTangent(const TransportPath& path, uint32_t pathTimeMs);
void updateTransformMatrices(ActiveTransport& transport);
std::unordered_map<uint64_t, ActiveTransport> transports_;
std::unordered_map<uint32_t, TransportPath> paths_;
std::unordered_map<uint32_t, TransportPath> paths_; // Indexed by transportEntry (pathId from TransportAnimation.dbc)
rendering::WMORenderer* wmoRenderer_ = nullptr;
bool clientSideAnimation_ = true; // Enable client animation - smooth movement, synced with server updates
float elapsedTime_ = 0.0f; // Total elapsed time (seconds)
};
} // namespace wowee::game

View file

@ -252,6 +252,13 @@ public:
*/
void setInstancePosition(uint32_t instanceId, const glm::vec3& position);
/**
* Update the full transform of an existing instance (e.g., for WMO doodads following parent WMO)
* @param instanceId Instance ID returned by createInstance()
* @param transform New world transform matrix
*/
void setInstanceTransform(uint32_t instanceId, const glm::mat4& transform);
/**
* Remove a specific instance by ID
* @param instanceId Instance ID returned by createInstance()

View file

@ -406,6 +406,13 @@ private:
glm::vec3 worldBoundsMax;
std::vector<std::pair<glm::vec3, glm::vec3>> worldGroupBounds;
// Doodad tracking: M2 instances that are children of this WMO
struct DoodadInfo {
uint32_t m2InstanceId; // ID of the M2 instance
glm::mat4 localTransform; // Local transform relative to WMO origin
};
std::vector<DoodadInfo> doodads;
void updateModelMatrix();
};