mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-23 07:40:14 +00:00
Add WoW-style footsteps and improve third-person movement/collision
This commit is contained in:
parent
54dc27c2ec
commit
dfb1f3cfdc
11 changed files with 724 additions and 85 deletions
|
|
@ -27,11 +27,14 @@
|
|||
#include "game/world.hpp"
|
||||
#include "game/zone_manager.hpp"
|
||||
#include "audio/music_manager.hpp"
|
||||
#include "audio/footstep_manager.hpp"
|
||||
#include <GL/glew.h>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <cctype>
|
||||
#include <cmath>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
|
|
@ -185,6 +188,7 @@ bool Renderer::initialize(core::Window* win) {
|
|||
|
||||
// Create music manager (initialized later with asset manager)
|
||||
musicManager = std::make_unique<audio::MusicManager>();
|
||||
footstepManager = std::make_unique<audio::FootstepManager>();
|
||||
|
||||
LOG_INFO("Renderer initialized");
|
||||
return true;
|
||||
|
|
@ -257,6 +261,10 @@ void Renderer::shutdown() {
|
|||
musicManager->shutdown();
|
||||
musicManager.reset();
|
||||
}
|
||||
if (footstepManager) {
|
||||
footstepManager->shutdown();
|
||||
footstepManager.reset();
|
||||
}
|
||||
|
||||
zoneManager.reset();
|
||||
|
||||
|
|
@ -492,6 +500,87 @@ bool Renderer::isMoving() const {
|
|||
return cameraController && cameraController->isMoving();
|
||||
}
|
||||
|
||||
bool Renderer::isFootstepAnimationState() const {
|
||||
return charAnimState == CharAnimState::WALK || charAnimState == CharAnimState::RUN;
|
||||
}
|
||||
|
||||
bool Renderer::shouldTriggerFootstepEvent(uint32_t animationId, float animationTimeMs, float animationDurationMs) {
|
||||
if (animationDurationMs <= 1.0f) {
|
||||
footstepNormInitialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
float norm = std::fmod(animationTimeMs, animationDurationMs) / animationDurationMs;
|
||||
if (norm < 0.0f) norm += 1.0f;
|
||||
|
||||
if (animationId != footstepLastAnimationId) {
|
||||
footstepLastAnimationId = animationId;
|
||||
footstepLastNormTime = norm;
|
||||
footstepNormInitialized = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!footstepNormInitialized) {
|
||||
footstepNormInitialized = true;
|
||||
footstepLastNormTime = norm;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto crossed = [&](float eventNorm) {
|
||||
if (footstepLastNormTime <= norm) {
|
||||
return footstepLastNormTime < eventNorm && eventNorm <= norm;
|
||||
}
|
||||
return footstepLastNormTime < eventNorm || eventNorm <= norm;
|
||||
};
|
||||
|
||||
bool trigger = crossed(0.22f) || crossed(0.72f);
|
||||
footstepLastNormTime = norm;
|
||||
return trigger;
|
||||
}
|
||||
|
||||
audio::FootstepSurface Renderer::resolveFootstepSurface() const {
|
||||
if (!cameraController || !cameraController->isThirdPerson()) {
|
||||
return audio::FootstepSurface::STONE;
|
||||
}
|
||||
|
||||
const glm::vec3& p = characterPosition;
|
||||
|
||||
if (cameraController->isSwimming()) {
|
||||
return audio::FootstepSurface::WATER;
|
||||
}
|
||||
|
||||
if (waterRenderer) {
|
||||
auto waterH = waterRenderer->getWaterHeightAt(p.x, p.y);
|
||||
if (waterH && p.z < (*waterH + 0.25f)) {
|
||||
return audio::FootstepSurface::WATER;
|
||||
}
|
||||
}
|
||||
|
||||
if (wmoRenderer) {
|
||||
auto wmoFloor = wmoRenderer->getFloorHeight(p.x, p.y, p.z + 1.5f);
|
||||
auto terrainFloor = terrainManager ? terrainManager->getHeightAt(p.x, p.y) : std::nullopt;
|
||||
if (wmoFloor && (!terrainFloor || *wmoFloor >= *terrainFloor - 0.1f)) {
|
||||
return audio::FootstepSurface::STONE;
|
||||
}
|
||||
}
|
||||
|
||||
if (terrainManager) {
|
||||
auto texture = terrainManager->getDominantTextureAt(p.x, p.y);
|
||||
if (texture) {
|
||||
std::string t = *texture;
|
||||
for (char& c : t) c = static_cast<char>(std::tolower(static_cast<unsigned char>(c)));
|
||||
if (t.find("snow") != std::string::npos || t.find("ice") != std::string::npos) return audio::FootstepSurface::SNOW;
|
||||
if (t.find("grass") != std::string::npos || t.find("moss") != std::string::npos || t.find("leaf") != std::string::npos) return audio::FootstepSurface::GRASS;
|
||||
if (t.find("sand") != std::string::npos || t.find("dirt") != std::string::npos || t.find("mud") != std::string::npos) return audio::FootstepSurface::DIRT;
|
||||
if (t.find("wood") != std::string::npos || t.find("timber") != std::string::npos) return audio::FootstepSurface::WOOD;
|
||||
if (t.find("metal") != std::string::npos || t.find("iron") != std::string::npos) return audio::FootstepSurface::METAL;
|
||||
if (t.find("stone") != std::string::npos || t.find("rock") != std::string::npos || t.find("cobble") != std::string::npos || t.find("brick") != std::string::npos) return audio::FootstepSurface::STONE;
|
||||
}
|
||||
}
|
||||
|
||||
return audio::FootstepSurface::STONE;
|
||||
}
|
||||
|
||||
void Renderer::update(float deltaTime) {
|
||||
if (cameraController) {
|
||||
cameraController->update(deltaTime);
|
||||
|
|
@ -569,6 +658,25 @@ void Renderer::update(float deltaTime) {
|
|||
characterRenderer->update(deltaTime);
|
||||
}
|
||||
|
||||
// Footsteps: animation-event driven + surface query at event time.
|
||||
if (footstepManager) {
|
||||
footstepManager->update(deltaTime);
|
||||
if (characterRenderer && characterInstanceId > 0 &&
|
||||
cameraController && cameraController->isThirdPerson() &&
|
||||
isFootstepAnimationState() && cameraController->isGrounded() &&
|
||||
!cameraController->isSwimming()) {
|
||||
uint32_t animId = 0;
|
||||
float animTimeMs = 0.0f;
|
||||
float animDurationMs = 0.0f;
|
||||
if (characterRenderer->getAnimationState(characterInstanceId, animId, animTimeMs, animDurationMs) &&
|
||||
shouldTriggerFootstepEvent(animId, animTimeMs, animDurationMs)) {
|
||||
footstepManager->playFootstep(resolveFootstepSurface(), cameraController->isSprinting());
|
||||
}
|
||||
} else {
|
||||
footstepNormInitialized = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Update M2 doodad animations
|
||||
if (m2Renderer) {
|
||||
m2Renderer->update(deltaTime);
|
||||
|
|
@ -820,6 +928,9 @@ bool Renderer::loadTestTerrain(pipeline::AssetManager* assetManager, const std::
|
|||
// Initialize music manager with asset manager
|
||||
if (musicManager && assetManager && !cachedAssetManager) {
|
||||
musicManager->initialize(assetManager);
|
||||
if (footstepManager) {
|
||||
footstepManager->initialize(assetManager);
|
||||
}
|
||||
cachedAssetManager = assetManager;
|
||||
}
|
||||
|
||||
|
|
@ -883,6 +994,11 @@ bool Renderer::loadTerrainArea(const std::string& mapName, int centerX, int cent
|
|||
musicManager->initialize(cachedAssetManager);
|
||||
}
|
||||
}
|
||||
if (footstepManager && cachedAssetManager) {
|
||||
if (!footstepManager->isInitialized()) {
|
||||
footstepManager->initialize(cachedAssetManager);
|
||||
}
|
||||
}
|
||||
|
||||
// Wire WMO, M2, and water renderer to camera controller
|
||||
if (cameraController && wmoRenderer) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue