mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Add property-based mount animation discovery and procedural lean
Mount Animation System: - Property-based jump animation discovery using sequence metadata - Chain linkage scoring (nextAnimation/aliasNext) for accurate detection - Correct loop detection: flags & 0x01 == 0 means looping - Avoids brake/stop animations via blendTime penalties - Works on any mount model without hardcoded animation IDs Mount Physics: - Physics-based jump height: vz = sqrt(2 * g * h) - Configurable MOUNT_JUMP_HEIGHT constant (1.0m default) - Procedural lean into turns for ground mounts - Smooth roll based on turn rate (±14° max, 6x/sec blend) Audio Improvements: - State-machine driven mount sounds (jump, land, rear-up) - Semantic sound methods (no animation ID dependencies) - Debug logging for missing sound files Bug Fixes: - Fixed mount animation sequencing (JumpStart → JumpLoop → JumpEnd) - Fixed animation loop flag interpretation (0x20 vs 0x21) - Rider bone attachment working correctly during all mount actions
This commit is contained in:
parent
3c783d1845
commit
c623fcef51
16 changed files with 1083 additions and 145 deletions
|
|
@ -118,6 +118,16 @@ bool GameHandler::isConnected() const {
|
|||
}
|
||||
|
||||
void GameHandler::update(float deltaTime) {
|
||||
// Timing profiling (log every 60 frames to reduce spam)
|
||||
static int profileCounter = 0;
|
||||
static float socketTime = 0.0f;
|
||||
static float taxiTime = 0.0f;
|
||||
static float distanceCheckTime = 0.0f;
|
||||
static float entityUpdateTime = 0.0f;
|
||||
static float totalTime = 0.0f;
|
||||
|
||||
auto updateStart = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Fire deferred char-create callback (outside ImGui render)
|
||||
if (pendingCharCreateResult_) {
|
||||
pendingCharCreateResult_ = false;
|
||||
|
|
@ -131,9 +141,12 @@ void GameHandler::update(float deltaTime) {
|
|||
}
|
||||
|
||||
// Update socket (processes incoming data and triggers callbacks)
|
||||
auto socketStart = std::chrono::high_resolution_clock::now();
|
||||
if (socket) {
|
||||
socket->update();
|
||||
}
|
||||
auto socketEnd = std::chrono::high_resolution_clock::now();
|
||||
socketTime += std::chrono::duration<float, std::milli>(socketEnd - socketStart).count();
|
||||
|
||||
// Validate target still exists
|
||||
if (targetGuid != 0 && !entityManager.hasEntity(targetGuid)) {
|
||||
|
|
@ -187,6 +200,9 @@ void GameHandler::update(float deltaTime) {
|
|||
taxiLandingCooldown_ -= deltaTime;
|
||||
}
|
||||
|
||||
// Taxi logic timing
|
||||
auto taxiStart = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Detect taxi flight landing: UNIT_FLAG_TAXI_FLIGHT (0x00000100) cleared
|
||||
if (onTaxiFlight_) {
|
||||
updateClientTaxi(deltaTime);
|
||||
|
|
@ -286,6 +302,12 @@ void GameHandler::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
auto taxiEnd = std::chrono::high_resolution_clock::now();
|
||||
taxiTime += std::chrono::duration<float, std::milli>(taxiEnd - taxiStart).count();
|
||||
|
||||
// Distance check timing
|
||||
auto distanceStart = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Leave combat if auto-attack target is too far away (leash range)
|
||||
if (autoAttacking && autoAttackTarget != 0) {
|
||||
auto targetEntity = entityManager.getEntity(autoAttackTarget);
|
||||
|
|
@ -350,10 +372,51 @@ void GameHandler::update(float deltaTime) {
|
|||
}
|
||||
}
|
||||
|
||||
auto distanceEnd = std::chrono::high_resolution_clock::now();
|
||||
distanceCheckTime += std::chrono::duration<float, std::milli>(distanceEnd - distanceStart).count();
|
||||
|
||||
// Entity update timing
|
||||
auto entityStart = std::chrono::high_resolution_clock::now();
|
||||
|
||||
// Update entity movement interpolation (keeps targeting in sync with visuals)
|
||||
// Only update entities within reasonable distance for performance
|
||||
const float updateRadiusSq = 150.0f * 150.0f; // 150 unit radius
|
||||
auto playerEntity = entityManager.getEntity(playerGuid);
|
||||
glm::vec3 playerPos = playerEntity ? glm::vec3(playerEntity->getX(), playerEntity->getY(), playerEntity->getZ()) : glm::vec3(0.0f);
|
||||
|
||||
for (auto& [guid, entity] : entityManager.getEntities()) {
|
||||
entity->updateMovement(deltaTime);
|
||||
// Always update player
|
||||
if (guid == playerGuid) {
|
||||
entity->updateMovement(deltaTime);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Distance cull other entities
|
||||
glm::vec3 entityPos(entity->getX(), entity->getY(), entity->getZ());
|
||||
float distSq = glm::dot(entityPos - playerPos, entityPos - playerPos);
|
||||
if (distSq < updateRadiusSq) {
|
||||
entity->updateMovement(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
auto entityEnd = std::chrono::high_resolution_clock::now();
|
||||
entityUpdateTime += std::chrono::duration<float, std::milli>(entityEnd - entityStart).count();
|
||||
}
|
||||
|
||||
auto updateEnd = std::chrono::high_resolution_clock::now();
|
||||
totalTime += std::chrono::duration<float, std::milli>(updateEnd - updateStart).count();
|
||||
|
||||
// Log profiling every 60 frames
|
||||
if (++profileCounter >= 60) {
|
||||
LOG_INFO("UPDATE PROFILE (60 frames): socket=", socketTime / 60.0f, "ms taxi=", taxiTime / 60.0f,
|
||||
"ms distance=", distanceCheckTime / 60.0f, "ms entity=", entityUpdateTime / 60.0f,
|
||||
"ms TOTAL=", totalTime / 60.0f, "ms");
|
||||
profileCounter = 0;
|
||||
socketTime = 0.0f;
|
||||
taxiTime = 0.0f;
|
||||
distanceCheckTime = 0.0f;
|
||||
entityUpdateTime = 0.0f;
|
||||
totalTime = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -418,6 +481,11 @@ void GameHandler::handlePacket(network::Packet& packet) {
|
|||
}
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_LOGIN_SETTIMESPEED:
|
||||
// Can be received during login or at any time after
|
||||
handleLoginSetTimeSpeed(packet);
|
||||
break;
|
||||
|
||||
case Opcode::SMSG_ACCOUNT_DATA_TIMES:
|
||||
// Can be received at any time after authentication
|
||||
handleAccountDataTimes(packet);
|
||||
|
|
@ -1472,6 +1540,28 @@ void GameHandler::selectCharacter(uint64_t characterGuid) {
|
|||
LOG_INFO("CMSG_PLAYER_LOGIN sent, entering world...");
|
||||
}
|
||||
|
||||
void GameHandler::handleLoginSetTimeSpeed(network::Packet& packet) {
|
||||
// SMSG_LOGIN_SETTIMESPEED (0x042)
|
||||
// Structure: uint32 gameTime, float timeScale
|
||||
// gameTime: Game time in seconds since epoch
|
||||
// timeScale: Time speed multiplier (typically 0.0166 for 1 day = 1 hour)
|
||||
|
||||
if (packet.getSize() < 8) {
|
||||
LOG_WARNING("SMSG_LOGIN_SETTIMESPEED: packet too small (", packet.getSize(), " bytes)");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t gameTimePacked = packet.readUInt32();
|
||||
float timeScale = packet.readFloat();
|
||||
|
||||
// Store for celestial/sky system use
|
||||
gameTime_ = static_cast<float>(gameTimePacked);
|
||||
timeSpeed_ = timeScale;
|
||||
|
||||
LOG_INFO("Server time: gameTime=", gameTime_, "s, timeSpeed=", timeSpeed_);
|
||||
LOG_INFO(" (1 game day = ", (1.0f / timeSpeed_) / 60.0f, " real minutes)");
|
||||
}
|
||||
|
||||
void GameHandler::handleLoginVerifyWorld(network::Packet& packet) {
|
||||
LOG_INFO("Handling SMSG_LOGIN_VERIFY_WORLD");
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue