fix(gameplay): tighten TB elevator bounds and reset stale combat visuals
Some checks are pending
Build / Build (arm64) (push) Waiting to run
Build / Build (x86-64) (push) Waiting to run
Build / Build (macOS arm64) (push) Waiting to run
Build / Build (windows-arm64) (push) Waiting to run
Build / Build (windows-x86-64) (push) Waiting to run
Security / CodeQL (C/C++) (push) Waiting to run
Security / Semgrep (push) Waiting to run
Security / Sanitizer Build (ASan/UBSan) (push) Waiting to run

This commit is contained in:
Kelsi 2026-03-14 09:19:16 -07:00
parent 448560a0d2
commit 075b4c1772
3 changed files with 32 additions and 18 deletions

View file

@ -161,6 +161,7 @@ public:
// Targeting support // Targeting support
void setTargetPosition(const glm::vec3* pos); void setTargetPosition(const glm::vec3* pos);
void setInCombat(bool combat) { inCombat_ = combat; } void setInCombat(bool combat) { inCombat_ = combat; }
void resetCombatVisualState();
bool isMoving() const; bool isMoving() const;
void triggerMeleeSwing(); void triggerMeleeSwing();
void setEquippedWeaponType(uint32_t inventoryType) { equippedWeaponInvType_ = inventoryType; meleeAnimId = 0; } void setEquippedWeaponType(uint32_t inventoryType) { equippedWeaponInvType_ = inventoryType; meleeAnimId = 0; }

View file

@ -847,6 +847,7 @@ void Application::logoutToLogin() {
world.reset(); world.reset();
if (renderer) { if (renderer) {
renderer->resetCombatVisualState();
// Remove old player model so it doesn't persist into next session // Remove old player model so it doesn't persist into next session
if (auto* charRenderer = renderer->getCharacterRenderer()) { if (auto* charRenderer = renderer->getCharacterRenderer()) {
charRenderer->removeInstance(1); charRenderer->removeInstance(1);
@ -1114,6 +1115,15 @@ void Application::update(float deltaTime) {
gameHandler->isTaxiMountActive() || gameHandler->isTaxiMountActive() ||
gameHandler->isTaxiActivationPending()); gameHandler->isTaxiActivationPending());
bool onTransportNow = gameHandler && gameHandler->isOnTransport(); bool onTransportNow = gameHandler && gameHandler->isOnTransport();
// Clear stale client-side transport state when the tracked transport no longer exists.
if (onTransportNow && gameHandler->getTransportManager()) {
auto* currentTracked = gameHandler->getTransportManager()->getTransport(
gameHandler->getPlayerTransportGuid());
if (!currentTracked) {
gameHandler->clearPlayerTransport();
onTransportNow = false;
}
}
// M2 transports (trams) use position-delta approach: player keeps normal // M2 transports (trams) use position-delta approach: player keeps normal
// movement and the transport's frame-to-frame delta is applied on top. // movement and the transport's frame-to-frame delta is applied on top.
// Only WMO transports (ships) use full external-driven mode. // Only WMO transports (ships) use full external-driven mode.
@ -1349,24 +1359,12 @@ void Application::update(float deltaTime) {
} else { } else {
glm::vec3 renderPos = renderer->getCharacterPosition(); glm::vec3 renderPos = renderer->getCharacterPosition();
// M2 transport riding: apply transport's frame-to-frame position delta // M2 transport riding: resolve in canonical space and lock once per frame.
// so the player moves with the tram while retaining normal movement input. // This avoids visible jitter from mixed render/canonical delta application.
if (isM2Transport && gameHandler->getTransportManager()) { if (isM2Transport && gameHandler->getTransportManager()) {
auto* tr = gameHandler->getTransportManager()->getTransport( auto* tr = gameHandler->getTransportManager()->getTransport(
gameHandler->getPlayerTransportGuid()); gameHandler->getPlayerTransportGuid());
if (tr) { if (tr) {
static glm::vec3 lastTransportCanonical(0);
static uint64_t lastTransportGuid = 0;
if (lastTransportGuid == gameHandler->getPlayerTransportGuid()) {
glm::vec3 deltaCanonical = tr->position - lastTransportCanonical;
glm::vec3 deltaRender = core::coords::canonicalToRender(deltaCanonical)
- core::coords::canonicalToRender(glm::vec3(0));
renderPos += deltaRender;
renderer->getCharacterPosition() = renderPos;
}
lastTransportCanonical = tr->position;
lastTransportGuid = gameHandler->getPlayerTransportGuid();
// Keep passenger locked to elevator vertical motion while grounded. // Keep passenger locked to elevator vertical motion while grounded.
// Without this, floor clamping can hold world-Z static unless the // Without this, floor clamping can hold world-Z static unless the
// player is jumping, which makes lifts appear to not move vertically. // player is jumping, which makes lifts appear to not move vertically.
@ -1428,8 +1426,8 @@ void Application::update(float deltaTime) {
glm::vec3 playerCanonical = core::coords::renderToCanonical(renderPos); glm::vec3 playerCanonical = core::coords::renderToCanonical(renderPos);
constexpr float kM2BoardHorizDistSq = 12.0f * 12.0f; constexpr float kM2BoardHorizDistSq = 12.0f * 12.0f;
constexpr float kM2BoardVertDist = 15.0f; constexpr float kM2BoardVertDist = 15.0f;
constexpr float kTbLiftBoardHorizDistSq = 95.0f * 95.0f; constexpr float kTbLiftBoardHorizDistSq = 22.0f * 22.0f;
constexpr float kTbLiftBoardVertDist = 80.0f; constexpr float kTbLiftBoardVertDist = 14.0f;
uint64_t bestGuid = 0; uint64_t bestGuid = 0;
float bestScore = 1e30f; float bestScore = 1e30f;
@ -1474,11 +1472,16 @@ void Application::update(float deltaTime) {
const bool isThunderBluffLift = const bool isThunderBluffLift =
(tr->entry >= 20649u && tr->entry <= 20657u); (tr->entry >= 20649u && tr->entry <= 20657u);
constexpr float kM2DisembarkHorizDistSq = 15.0f * 15.0f; constexpr float kM2DisembarkHorizDistSq = 15.0f * 15.0f;
constexpr float kTbLiftDisembarkHorizDistSq = 120.0f * 120.0f; constexpr float kTbLiftDisembarkHorizDistSq = 28.0f * 28.0f;
constexpr float kM2DisembarkVertDist = 18.0f;
constexpr float kTbLiftDisembarkVertDist = 16.0f;
const float disembarkHorizDistSq = isThunderBluffLift const float disembarkHorizDistSq = isThunderBluffLift
? kTbLiftDisembarkHorizDistSq ? kTbLiftDisembarkHorizDistSq
: kM2DisembarkHorizDistSq; : kM2DisembarkHorizDistSq;
if (horizDistSq > disembarkHorizDistSq) { const float disembarkVertDist = isThunderBluffLift
? kTbLiftDisembarkVertDist
: kM2DisembarkVertDist;
if (horizDistSq > disembarkHorizDistSq || std::abs(diff.z) > disembarkVertDist) {
gameHandler->clearPlayerTransport(); gameHandler->clearPlayerTransport();
LOG_DEBUG("M2 transport disembark"); LOG_DEBUG("M2 transport disembark");
} }
@ -1911,6 +1914,9 @@ void Application::setupUICallbacks() {
gameHandler->setWorldEntryCallback([this](uint32_t mapId, float x, float y, float z, bool isInitialEntry) { gameHandler->setWorldEntryCallback([this](uint32_t mapId, float x, float y, float z, bool isInitialEntry) {
LOG_INFO("Online world entry: mapId=", mapId, " pos=(", x, ", ", y, ", ", z, ")" LOG_INFO("Online world entry: mapId=", mapId, " pos=(", x, ", ", y, ", ", z, ")"
" initial=", isInitialEntry); " initial=", isInitialEntry);
if (renderer) {
renderer->resetCombatVisualState();
}
// Reconnect to the same map: terrain stays loaded but all online entities are stale. // Reconnect to the same map: terrain stays loaded but all online entities are stale.
// Despawn them properly so the server's fresh CREATE_OBJECTs will re-populate the world. // Despawn them properly so the server's fresh CREATE_OBJECTs will re-populate the world.

View file

@ -2718,6 +2718,13 @@ void Renderer::setTargetPosition(const glm::vec3* pos) {
targetPosition = pos; targetPosition = pos;
} }
void Renderer::resetCombatVisualState() {
inCombat_ = false;
targetPosition = nullptr;
meleeSwingTimer = 0.0f;
meleeSwingCooldown = 0.0f;
}
bool Renderer::isMoving() const { bool Renderer::isMoving() const {
return cameraController && cameraController->isMoving(); return cameraController && cameraController->isMoving();
} }