mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-22 23:30:14 +00:00
Fix movement, mounts, and terrain seams
This commit is contained in:
parent
c5a4d04bf5
commit
9f19d9fa1a
8 changed files with 93 additions and 27 deletions
|
|
@ -906,6 +906,7 @@ private:
|
|||
NpcSwingCallback npcSwingCallback_;
|
||||
MountCallback mountCallback_;
|
||||
uint32_t currentMountDisplayId_ = 0;
|
||||
float preMountRunSpeed_ = 0.0f;
|
||||
float serverRunSpeed_ = 7.0f;
|
||||
bool playerDead_ = false;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -449,6 +449,7 @@ struct UpdateBlock {
|
|||
// Movement data (for MOVEMENT updates)
|
||||
bool hasMovement = false;
|
||||
float x = 0.0f, y = 0.0f, z = 0.0f, orientation = 0.0f;
|
||||
float runSpeed = 0.0f;
|
||||
|
||||
// Field data (for VALUES and CREATE updates)
|
||||
std::map<uint16_t, uint32_t> fields;
|
||||
|
|
|
|||
|
|
@ -1305,6 +1305,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
glm::vec3 pos = core::coords::serverToCanonical(glm::vec3(block.x, block.y, block.z));
|
||||
entity->setPosition(pos.x, pos.y, pos.z, block.orientation);
|
||||
LOG_DEBUG(" Position: (", pos.x, ", ", pos.y, ", ", pos.z, ")");
|
||||
if (block.guid == playerGuid && block.runSpeed > 0.1f && block.runSpeed < 100.0f) {
|
||||
serverRunSpeed_ = block.runSpeed;
|
||||
}
|
||||
}
|
||||
|
||||
// Set fields
|
||||
|
|
@ -1358,6 +1361,16 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
if (block.guid == playerGuid) {
|
||||
uint32_t old = currentMountDisplayId_;
|
||||
currentMountDisplayId_ = val;
|
||||
if (old == 0 && val != 0) {
|
||||
preMountRunSpeed_ = serverRunSpeed_;
|
||||
} else if (old != 0 && val == 0) {
|
||||
if (preMountRunSpeed_ > 0.1f && preMountRunSpeed_ < 100.0f) {
|
||||
serverRunSpeed_ = preMountRunSpeed_;
|
||||
} else {
|
||||
serverRunSpeed_ = 7.0f;
|
||||
}
|
||||
preMountRunSpeed_ = 0.0f;
|
||||
}
|
||||
if (val != old && mountCallback_) mountCallback_(val);
|
||||
}
|
||||
unit->setMountDisplayId(val);
|
||||
|
|
@ -1490,6 +1503,16 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
if (block.guid == playerGuid) {
|
||||
uint32_t old = currentMountDisplayId_;
|
||||
currentMountDisplayId_ = val;
|
||||
if (old == 0 && val != 0) {
|
||||
preMountRunSpeed_ = serverRunSpeed_;
|
||||
} else if (old != 0 && val == 0) {
|
||||
if (preMountRunSpeed_ > 0.1f && preMountRunSpeed_ < 100.0f) {
|
||||
serverRunSpeed_ = preMountRunSpeed_;
|
||||
} else {
|
||||
serverRunSpeed_ = 7.0f;
|
||||
}
|
||||
preMountRunSpeed_ = 0.0f;
|
||||
}
|
||||
if (val != old && mountCallback_) mountCallback_(val);
|
||||
}
|
||||
unit->setMountDisplayId(val);
|
||||
|
|
@ -1501,6 +1524,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) {
|
|||
}
|
||||
// Update XP / inventory slot / skill fields for player entity
|
||||
if (block.guid == playerGuid) {
|
||||
if (block.hasMovement && block.runSpeed > 0.1f && block.runSpeed < 100.0f) {
|
||||
serverRunSpeed_ = block.runSpeed;
|
||||
}
|
||||
for (const auto& [key, val] : block.fields) {
|
||||
lastPlayerFields_[key] = val;
|
||||
}
|
||||
|
|
@ -3569,6 +3595,9 @@ void GameHandler::interactWithGameObject(uint64_t guid) {
|
|||
if (state != WorldState::IN_WORLD || !socket) return;
|
||||
auto packet = GameObjectUsePacket::build(guid);
|
||||
socket->send(packet);
|
||||
// Many lootable chests require a loot request after use.
|
||||
auto loot = LootPacket::build(guid);
|
||||
socket->send(loot);
|
||||
}
|
||||
|
||||
void GameHandler::selectGossipOption(uint32_t optionId) {
|
||||
|
|
|
|||
|
|
@ -703,7 +703,7 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
|
|||
|
||||
// Speeds (7 speed values)
|
||||
/*float walkSpeed =*/ packet.readFloat();
|
||||
/*float runSpeed =*/ packet.readFloat();
|
||||
float runSpeed = packet.readFloat();
|
||||
/*float runBackSpeed =*/ packet.readFloat();
|
||||
/*float swimSpeed =*/ packet.readFloat();
|
||||
/*float swimBackSpeed =*/ packet.readFloat();
|
||||
|
|
@ -712,6 +712,8 @@ bool UpdateObjectParser::parseMovementBlock(network::Packet& packet, UpdateBlock
|
|||
/*float turnRate =*/ packet.readFloat();
|
||||
/*float pitchRate =*/ packet.readFloat();
|
||||
|
||||
block.runSpeed = runSpeed;
|
||||
|
||||
// Spline data
|
||||
if (moveFlags & 0x08000000) { // MOVEMENTFLAG_SPLINE_ENABLED
|
||||
uint32_t splineFlags = packet.readUInt32();
|
||||
|
|
|
|||
|
|
@ -218,9 +218,12 @@ std::vector<TerrainVertex> TerrainMeshGenerator::generateVertices(const MapChunk
|
|||
vertex.texCoord[0] = offsetX / 8.0f;
|
||||
vertex.texCoord[1] = offsetY / 8.0f;
|
||||
|
||||
// Layer UV for alpha map sampling (0-1 range per chunk)
|
||||
vertex.layerUV[0] = offsetX / 8.0f;
|
||||
vertex.layerUV[1] = offsetY / 8.0f;
|
||||
// Layer UV for alpha map sampling (0-1 range per chunk).
|
||||
// Sample at texel centers of the 64x64 alpha map to avoid edge seams.
|
||||
constexpr float alphaTexels = 64.0f;
|
||||
constexpr float alphaStep = (alphaTexels - 1.0f) / 8.0f; // 63 texels across 8 quads
|
||||
vertex.layerUV[0] = (offsetX * alphaStep + 0.5f) / alphaTexels;
|
||||
vertex.layerUV[1] = (offsetY * alphaStep + 0.5f) / alphaTexels;
|
||||
|
||||
vertices.push_back(vertex);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1259,26 +1259,7 @@ bool CameraController::isMoving() const {
|
|||
if (!enabled || !camera) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ImGui::GetIO().WantCaptureKeyboard) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& input = core::Input::getInstance();
|
||||
bool keyW = input.isKeyPressed(SDL_SCANCODE_W);
|
||||
bool keyS = input.isKeyPressed(SDL_SCANCODE_S);
|
||||
bool keyA = input.isKeyPressed(SDL_SCANCODE_A);
|
||||
bool keyD = input.isKeyPressed(SDL_SCANCODE_D);
|
||||
bool keyQ = input.isKeyPressed(SDL_SCANCODE_Q);
|
||||
bool keyE = input.isKeyPressed(SDL_SCANCODE_E);
|
||||
|
||||
// In third-person without RMB, A/D are turn keys (not movement).
|
||||
if (thirdPerson && !rightMouseDown) {
|
||||
return keyW || keyS || keyQ || keyE || autoRunning;
|
||||
}
|
||||
|
||||
bool mouseAutorun = leftMouseDown && rightMouseDown;
|
||||
return keyW || keyS || keyA || keyD || keyQ || keyE || mouseAutorun || autoRunning;
|
||||
return moveForwardActive || moveBackwardActive || strafeLeftActive || strafeRightActive || autoRunning;
|
||||
}
|
||||
|
||||
bool CameraController::isSprinting() const {
|
||||
|
|
|
|||
|
|
@ -647,6 +647,7 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
tightMax = glm::max(tightMax, v.position);
|
||||
}
|
||||
bool foliageOrTreeLike = false;
|
||||
bool chestName = false;
|
||||
{
|
||||
std::string lowerName = model.name;
|
||||
std::transform(lowerName.begin(), lowerName.end(), lowerName.begin(),
|
||||
|
|
@ -684,9 +685,16 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
(lowerName.find("box") != std::string::npos) ||
|
||||
(lowerName.find("chest") != std::string::npos) ||
|
||||
(lowerName.find("barrel") != std::string::npos);
|
||||
chestName = (lowerName.find("chest") != std::string::npos);
|
||||
bool foliageName =
|
||||
(lowerName.find("bush") != std::string::npos) ||
|
||||
(lowerName.find("grass") != std::string::npos) ||
|
||||
(lowerName.find("drygrass") != std::string::npos) ||
|
||||
(lowerName.find("dry_grass") != std::string::npos) ||
|
||||
(lowerName.find("dry-grass") != std::string::npos) ||
|
||||
(lowerName.find("deadgrass") != std::string::npos) ||
|
||||
(lowerName.find("dead_grass") != std::string::npos) ||
|
||||
(lowerName.find("dead-grass") != std::string::npos) ||
|
||||
((lowerName.find("plant") != std::string::npos) && !isPlanter) ||
|
||||
(lowerName.find("flower") != std::string::npos) ||
|
||||
(lowerName.find("shrub") != std::string::npos) ||
|
||||
|
|
@ -694,6 +702,10 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
(lowerName.find("vine") != std::string::npos) ||
|
||||
(lowerName.find("lily") != std::string::npos) ||
|
||||
(lowerName.find("weed") != std::string::npos) ||
|
||||
(lowerName.find("pumpkin") != std::string::npos) ||
|
||||
(lowerName.find("firefly") != std::string::npos) ||
|
||||
(lowerName.find("fireflies") != std::string::npos) ||
|
||||
(lowerName.find("fireflys") != std::string::npos) ||
|
||||
(lowerName.find("mushroom") != std::string::npos) ||
|
||||
(lowerName.find("fungus") != std::string::npos) ||
|
||||
(lowerName.find("toadstool") != std::string::npos);
|
||||
|
|
@ -763,7 +775,7 @@ bool M2Renderer::loadModel(const pipeline::M2Model& model, uint32_t modelId) {
|
|||
break;
|
||||
}
|
||||
}
|
||||
gpuModel.disableAnimation = foliageOrTreeLike;
|
||||
gpuModel.disableAnimation = foliageOrTreeLike || chestName;
|
||||
|
||||
// Flag smoke models for UV scroll animation (particle emitters not implemented)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -662,7 +662,27 @@ void Renderer::updateCharacterAnimation() {
|
|||
characterRenderer->setInstanceRotation(mountInstanceId_, glm::vec3(0.0f, 0.0f, yawRad));
|
||||
|
||||
// Drive mount model animation: idle when still, run when moving
|
||||
uint32_t mountAnimId = moving ? ANIM_RUN : ANIM_STAND;
|
||||
auto pickMountAnim = [&](std::initializer_list<uint32_t> candidates, uint32_t fallback) -> uint32_t {
|
||||
for (uint32_t id : candidates) {
|
||||
if (characterRenderer->hasAnimation(mountInstanceId_, id)) {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
};
|
||||
|
||||
uint32_t mountAnimId = ANIM_STAND;
|
||||
if (moving) {
|
||||
if (anyStrafeLeft) {
|
||||
mountAnimId = pickMountAnim({ANIM_STRAFE_RUN_LEFT, ANIM_STRAFE_WALK_LEFT, ANIM_RUN}, ANIM_RUN);
|
||||
} else if (anyStrafeRight) {
|
||||
mountAnimId = pickMountAnim({ANIM_STRAFE_RUN_RIGHT, ANIM_STRAFE_WALK_RIGHT, ANIM_RUN}, ANIM_RUN);
|
||||
} else if (movingBackward) {
|
||||
mountAnimId = pickMountAnim({ANIM_BACKPEDAL}, ANIM_RUN);
|
||||
} else {
|
||||
mountAnimId = ANIM_RUN;
|
||||
}
|
||||
}
|
||||
uint32_t curMountAnim = 0;
|
||||
float curMountTime = 0, curMountDur = 0;
|
||||
bool haveMountState = characterRenderer->getAnimationState(mountInstanceId_, curMountAnim, curMountTime, curMountDur);
|
||||
|
|
@ -827,7 +847,24 @@ void Renderer::updateCharacterAnimation() {
|
|||
break;
|
||||
|
||||
case CharAnimState::MOUNT:
|
||||
break; // Handled by early return above
|
||||
// If we got here, the mount state was cleared externally but the
|
||||
// animation state hasn't been reset yet. Fall back to normal logic.
|
||||
if (swim) {
|
||||
newState = moving ? CharAnimState::SWIM : CharAnimState::SWIM_IDLE;
|
||||
} else if (sitting && grounded) {
|
||||
newState = CharAnimState::SIT_DOWN;
|
||||
} else if (!grounded && jumping) {
|
||||
newState = CharAnimState::JUMP_START;
|
||||
} else if (!grounded) {
|
||||
newState = CharAnimState::JUMP_MID;
|
||||
} else if (moving && sprinting) {
|
||||
newState = CharAnimState::RUN;
|
||||
} else if (moving) {
|
||||
newState = CharAnimState::WALK;
|
||||
} else {
|
||||
newState = CharAnimState::IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (forceMelee) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue