mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-05-08 10:03:51 +00:00
Split all mega-files by single-responsibility concern and partially extracting AudioCoordinator and OverlaySystem from the Renderer facade. No behavioral changes. Splits: - game_handler.cpp (5,247 LOC) → core + callbacks + packets (3 files) - world_packets.cpp (4,453 LOC) → economy/entity/social/world (4 files) - game_screen.cpp (5,786 LOC) → core + frames + hud + minimap (4 files) - m2_renderer.cpp (3,343 LOC) → core + instance + particles + render (4 files) - chat_panel.cpp (3,140 LOC) → core + commands + utils (3 files) - entity_spawner.cpp (2,750 LOC) → core + player + processing (3 files) Extractions: - AudioCoordinator: include/audio/ + src/audio/ (owned by Renderer) - OverlaySystem: include/rendering/ + src/rendering/overlay_system.* CMakeLists.txt: registered all 17 new translation units. Related handler/callback files: minor include fixups post-split.
276 lines
14 KiB
C++
276 lines
14 KiB
C++
#include "core/transport_callback_handler.hpp"
|
|
#include "core/entity_spawner.hpp"
|
|
#include "core/world_loader.hpp"
|
|
#include "core/coordinates.hpp"
|
|
#include "core/logger.hpp"
|
|
#include "rendering/renderer.hpp"
|
|
#include "rendering/animation_controller.hpp"
|
|
#include "rendering/character_renderer.hpp"
|
|
#include "rendering/camera_controller.hpp"
|
|
#include "rendering/terrain_manager.hpp"
|
|
#include "rendering/m2_renderer.hpp"
|
|
#include "game/game_handler.hpp"
|
|
#include "game/transport_manager.hpp"
|
|
|
|
#include <set>
|
|
|
|
namespace wowee { namespace core {
|
|
|
|
TransportCallbackHandler::TransportCallbackHandler(
|
|
EntitySpawner& entitySpawner,
|
|
rendering::Renderer& renderer,
|
|
game::GameHandler& gameHandler,
|
|
WorldLoader* worldLoader)
|
|
: entitySpawner_(entitySpawner)
|
|
, renderer_(renderer)
|
|
, gameHandler_(gameHandler)
|
|
, worldLoader_(worldLoader)
|
|
{
|
|
}
|
|
|
|
void TransportCallbackHandler::setupCallbacks() {
|
|
// Mount callback (online mode) - defer heavy model load to next frame
|
|
gameHandler_.setMountCallback([this](uint32_t mountDisplayId) {
|
|
if (mountDisplayId == 0) {
|
|
// Dismount is instant (no loading needed)
|
|
if (renderer_.getCharacterRenderer() && entitySpawner_.getMountInstanceId() != 0) {
|
|
renderer_.getCharacterRenderer()->removeInstance(entitySpawner_.getMountInstanceId());
|
|
entitySpawner_.clearMountState();
|
|
}
|
|
entitySpawner_.setMountDisplayId(0);
|
|
if (auto* ac = renderer_.getAnimationController()) ac->clearMount();
|
|
LOG_INFO("Dismounted");
|
|
return;
|
|
}
|
|
// Queue the mount for processing in the next update() frame
|
|
entitySpawner_.setMountDisplayId(mountDisplayId);
|
|
});
|
|
|
|
// Taxi precache callback - preload terrain tiles along flight path
|
|
gameHandler_.setTaxiPrecacheCallback([this](const std::vector<glm::vec3>& path) {
|
|
if (!renderer_.getTerrainManager()) return;
|
|
|
|
std::set<std::pair<int, int>> uniqueTiles;
|
|
|
|
// Sample waypoints along path and gather tiles.
|
|
// Denser sampling + neighbor coverage reduces in-flight stream spikes.
|
|
const size_t stride = 2;
|
|
for (size_t i = 0; i < path.size(); i += stride) {
|
|
const auto& waypoint = path[i];
|
|
glm::vec3 renderPos = core::coords::canonicalToRender(waypoint);
|
|
int tileX = static_cast<int>(32 - (renderPos.x / 533.33333f));
|
|
int tileY = static_cast<int>(32 - (renderPos.y / 533.33333f));
|
|
|
|
if (tileX >= 0 && tileX <= 63 && tileY >= 0 && tileY <= 63) {
|
|
for (int dx = -1; dx <= 1; ++dx) {
|
|
for (int dy = -1; dy <= 1; ++dy) {
|
|
int nx = tileX + dx;
|
|
int ny = tileY + dy;
|
|
if (nx >= 0 && nx <= 63 && ny >= 0 && ny <= 63) {
|
|
uniqueTiles.insert({nx, ny});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Ensure final destination tile is included.
|
|
if (!path.empty()) {
|
|
glm::vec3 renderPos = core::coords::canonicalToRender(path.back());
|
|
int tileX = static_cast<int>(32 - (renderPos.x / 533.33333f));
|
|
int tileY = static_cast<int>(32 - (renderPos.y / 533.33333f));
|
|
if (tileX >= 0 && tileX <= 63 && tileY >= 0 && tileY <= 63) {
|
|
for (int dx = -1; dx <= 1; ++dx) {
|
|
for (int dy = -1; dy <= 1; ++dy) {
|
|
int nx = tileX + dx;
|
|
int ny = tileY + dy;
|
|
if (nx >= 0 && nx <= 63 && ny >= 0 && ny <= 63) {
|
|
uniqueTiles.insert({nx, ny});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<std::pair<int, int>> tilesToLoad(uniqueTiles.begin(), uniqueTiles.end());
|
|
if (tilesToLoad.size() > 512) {
|
|
tilesToLoad.resize(512);
|
|
}
|
|
LOG_INFO("Precaching ", tilesToLoad.size(), " tiles for taxi route");
|
|
renderer_.getTerrainManager()->precacheTiles(tilesToLoad);
|
|
});
|
|
|
|
// Taxi orientation callback - update mount rotation during flight
|
|
gameHandler_.setTaxiOrientationCallback([this](float yaw, float pitch, float roll) {
|
|
if (renderer_.getCameraController()) {
|
|
// Taxi callback now provides render-space yaw directly.
|
|
float yawDegrees = glm::degrees(yaw);
|
|
renderer_.getCameraController()->setFacingYaw(yawDegrees);
|
|
renderer_.setCharacterYaw(yawDegrees);
|
|
// Set mount pitch and roll for realistic flight animation
|
|
if (auto* ac = renderer_.getAnimationController()) ac->setMountPitchRoll(pitch, roll);
|
|
}
|
|
});
|
|
|
|
// Taxi flight start callback - keep non-blocking to avoid hitching at takeoff.
|
|
gameHandler_.setTaxiFlightStartCallback([this]() {
|
|
if (renderer_.getTerrainManager() && renderer_.getM2Renderer()) {
|
|
LOG_INFO("Taxi flight start: incremental terrain/M2 streaming active");
|
|
uint32_t m2Count = renderer_.getM2Renderer()->getModelCount();
|
|
uint32_t instCount = renderer_.getM2Renderer()->getInstanceCount();
|
|
LOG_INFO("Current M2 VRAM state: ", m2Count, " models (", instCount, " instances)");
|
|
}
|
|
});
|
|
|
|
// Transport spawn callback (online mode) - register transports with TransportManager
|
|
gameHandler_.setTransportSpawnCallback([this](uint64_t guid, uint32_t entry, uint32_t displayId, float x, float y, float z, float orientation) {
|
|
// Get the GameObject instance now so late queue processing can rely on stable IDs.
|
|
auto& goInstances2 = entitySpawner_.getGameObjectInstances();
|
|
auto it = goInstances2.find(guid);
|
|
if (it == goInstances2.end()) {
|
|
LOG_WARNING("Transport spawn callback: GameObject instance not found for GUID 0x", std::hex, guid, std::dec);
|
|
return;
|
|
}
|
|
|
|
auto pendingIt = entitySpawner_.hasTransportRegistrationPending(guid);
|
|
if (pendingIt) {
|
|
entitySpawner_.updateTransportRegistration(guid, displayId, x, y, z, orientation);
|
|
} else {
|
|
entitySpawner_.queueTransportRegistration(guid, entry, displayId, x, y, z, orientation);
|
|
}
|
|
});
|
|
|
|
// Transport move callback (online mode) - update transport gameobject positions
|
|
gameHandler_.setTransportMoveCallback([this](uint64_t guid, float x, float y, float z, float orientation) {
|
|
LOG_DEBUG("Transport move callback: GUID=0x", std::hex, guid, std::dec,
|
|
" pos=(", x, ", ", y, ", ", z, ") orientation=", orientation);
|
|
|
|
auto* transportManager = gameHandler_.getTransportManager();
|
|
if (!transportManager) {
|
|
LOG_WARNING("Transport move callback: TransportManager is null!");
|
|
return;
|
|
}
|
|
|
|
if (entitySpawner_.hasTransportRegistrationPending(guid)) {
|
|
entitySpawner_.setTransportPendingMove(guid, x, y, z, orientation);
|
|
LOG_DEBUG("Queued transport move for pending registration GUID=0x", std::hex, guid, std::dec);
|
|
return;
|
|
}
|
|
|
|
// Check if transport exists - if not, treat this as a late spawn (reconnection/server restart)
|
|
if (!transportManager->getTransport(guid)) {
|
|
LOG_DEBUG("Received position update for unregistered transport 0x", std::hex, guid, std::dec,
|
|
" - auto-spawning from position update");
|
|
|
|
// Get transport info from entity manager
|
|
auto entity = gameHandler_.getEntityManager().getEntity(guid);
|
|
if (entity && entity->getType() == game::ObjectType::GAMEOBJECT) {
|
|
auto go = std::static_pointer_cast<game::GameObject>(entity);
|
|
uint32_t entry = go->getEntry();
|
|
uint32_t displayId = go->getDisplayId();
|
|
|
|
// Find the WMO instance for this transport (should exist from earlier GameObject spawn)
|
|
auto& goInstances3 = entitySpawner_.getGameObjectInstances();
|
|
auto it = goInstances3.find(guid);
|
|
if (it != goInstances3.end()) {
|
|
uint32_t wmoInstanceId = it->second.instanceId;
|
|
|
|
// TransportAnimation.dbc is indexed by GameObject entry
|
|
uint32_t pathId = entry;
|
|
const bool preferServerData = gameHandler_.hasServerTransportUpdate(guid);
|
|
|
|
// Coordinates are already canonical (converted in game_handler.cpp)
|
|
glm::vec3 canonicalSpawnPos(x, y, z);
|
|
|
|
// Check if we have a real usable path, otherwise remap/infer/fall back to stationary.
|
|
const bool shipOrZeppelinDisplay =
|
|
(displayId == 3015 || displayId == 3031 || displayId == 7546 ||
|
|
displayId == 7446 || displayId == 1587 || displayId == 2454 ||
|
|
displayId == 807 || displayId == 808);
|
|
bool hasUsablePath = transportManager->hasPathForEntry(entry);
|
|
if (shipOrZeppelinDisplay) {
|
|
hasUsablePath = transportManager->hasUsableMovingPathForEntry(entry, 25.0f);
|
|
}
|
|
|
|
if (preferServerData) {
|
|
// Strict server-authoritative mode: no inferred/remapped fallback routes.
|
|
if (!hasUsablePath) {
|
|
std::vector<glm::vec3> path = { canonicalSpawnPos };
|
|
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
|
|
LOG_INFO("Auto-spawned transport in strict server-first mode (stationary fallback): entry=", entry,
|
|
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
|
} else {
|
|
LOG_INFO("Auto-spawned transport in server-first mode with entry DBC path: entry=", entry,
|
|
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
|
}
|
|
} else if (!hasUsablePath) {
|
|
bool allowZOnly = (displayId == 455 || displayId == 462);
|
|
uint32_t inferredPath = transportManager->inferDbcPathForSpawn(
|
|
canonicalSpawnPos, 1200.0f, allowZOnly);
|
|
if (inferredPath != 0) {
|
|
pathId = inferredPath;
|
|
LOG_INFO("Auto-spawned transport with inferred path: entry=", entry,
|
|
" inferredPath=", pathId, " displayId=", displayId,
|
|
" wmoInstance=", wmoInstanceId);
|
|
} else {
|
|
uint32_t remappedPath = transportManager->pickFallbackMovingPath(entry, displayId);
|
|
if (remappedPath != 0) {
|
|
pathId = remappedPath;
|
|
LOG_INFO("Auto-spawned transport with remapped fallback path: entry=", entry,
|
|
" remappedPath=", pathId, " displayId=", displayId,
|
|
" wmoInstance=", wmoInstanceId);
|
|
} else {
|
|
std::vector<glm::vec3> path = { canonicalSpawnPos };
|
|
transportManager->loadPathFromNodes(pathId, path, false, 0.0f);
|
|
LOG_INFO("Auto-spawned transport with stationary path: entry=", entry,
|
|
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
|
}
|
|
}
|
|
} else {
|
|
LOG_INFO("Auto-spawned transport with real path: entry=", entry,
|
|
" displayId=", displayId, " wmoInstance=", wmoInstanceId);
|
|
}
|
|
|
|
transportManager->registerTransport(guid, wmoInstanceId, pathId, canonicalSpawnPos, entry);
|
|
// Keep type in sync with the spawned instance; needed for M2 lift boarding/motion.
|
|
if (!it->second.isWmo) {
|
|
if (auto* tr = transportManager->getTransport(guid)) {
|
|
tr->isM2 = true;
|
|
}
|
|
}
|
|
} else {
|
|
entitySpawner_.setTransportPendingMove(guid, x, y, z, orientation);
|
|
LOG_DEBUG("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
|
" - WMO instance not found yet (queued move for replay)");
|
|
return;
|
|
}
|
|
} else {
|
|
entitySpawner_.setTransportPendingMove(guid, x, y, z, orientation);
|
|
LOG_DEBUG("Cannot auto-spawn transport 0x", std::hex, guid, std::dec,
|
|
" - entity not found in EntityManager (queued move for replay)");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Update TransportManager's internal state (position, rotation, transform matrices)
|
|
// This also updates the WMO renderer automatically
|
|
// Coordinates are already canonical (converted in game_handler.cpp when entity was created)
|
|
glm::vec3 canonicalPos(x, y, z);
|
|
transportManager->updateServerTransport(guid, canonicalPos, orientation);
|
|
|
|
// Move player with transport if riding it
|
|
if (gameHandler_.isOnTransport() && gameHandler_.getPlayerTransportGuid() == guid) {
|
|
auto* cc = renderer_.getCameraController();
|
|
if (cc) {
|
|
glm::vec3* ft = cc->getFollowTargetMutable();
|
|
if (ft) {
|
|
// Get player world position from TransportManager (handles transform properly)
|
|
glm::vec3 offset = gameHandler_.getPlayerTransportOffset();
|
|
glm::vec3 worldPos = transportManager->getPlayerWorldPosition(guid, offset);
|
|
*ft = worldPos;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
}} // namespace wowee::core
|