Add mount rider bob and hoofbeat sounds, improve world map

- Rider character bobs with mount's run animation (sinusoidal, 0.12u amplitude)
- Mount hoofbeat footstep sounds triggered at 4 points per animation cycle
- M key opens map directly to player's current zone instead of continent
- Mouse wheel scroll zooms map in/out between world, continent, and zone views
- Fog of war darkens unexplored zones on continent view, clears on visit
This commit is contained in:
Kelsi 2026-02-07 18:38:36 -08:00
parent 0874f4f239
commit 35fff9307d
4 changed files with 174 additions and 11 deletions

View file

@ -347,6 +347,38 @@ int WorldMap::findBestContinentForPlayer(const glm::vec3& playerRenderPos) const
return bestIdx;
}
int WorldMap::findZoneForPlayer(const glm::vec3& playerRenderPos) const {
float wowX = playerRenderPos.y; // north/south
float wowY = playerRenderPos.x; // west/east
int bestIdx = -1;
float bestArea = std::numeric_limits<float>::max();
for (int i = 0; i < static_cast<int>(zones.size()); i++) {
const auto& z = zones[i];
if (z.areaID == 0) continue; // skip continent-level entries
float minX = std::min(z.locLeft, z.locRight);
float maxX = std::max(z.locLeft, z.locRight);
float minY = std::min(z.locTop, z.locBottom);
float maxY = std::max(z.locTop, z.locBottom);
float spanX = maxX - minX;
float spanY = maxY - minY;
if (spanX < 0.001f || spanY < 0.001f) continue;
bool contains = (wowX >= minX && wowX <= maxX && wowY >= minY && wowY <= maxY);
if (contains) {
float area = spanX * spanY;
if (area < bestArea) {
bestArea = area;
bestIdx = i;
}
}
}
return bestIdx;
}
bool WorldMap::zoneBelongsToContinent(int zoneIdx, int contIdx) const {
if (zoneIdx < 0 || zoneIdx >= static_cast<int>(zones.size())) return false;
if (contIdx < 0 || contIdx >= static_cast<int>(zones.size())) return false;
@ -691,6 +723,52 @@ glm::vec2 WorldMap::renderPosToMapUV(const glm::vec3& renderPos, int zoneIdx) co
return glm::vec2(u, v);
}
// --------------------------------------------------------
// Exploration tracking
// --------------------------------------------------------
void WorldMap::updateExploration(const glm::vec3& playerRenderPos) {
int zoneIdx = findZoneForPlayer(playerRenderPos);
if (zoneIdx >= 0) {
exploredZones.insert(zoneIdx);
}
}
void WorldMap::zoomIn(const glm::vec3& playerRenderPos) {
if (viewLevel == ViewLevel::WORLD) {
// World → Continent
if (continentIdx >= 0) {
loadZoneTextures(continentIdx);
compositeZone(continentIdx);
currentIdx = continentIdx;
viewLevel = ViewLevel::CONTINENT;
}
} else if (viewLevel == ViewLevel::CONTINENT) {
// Continent → Zone (use player's current zone)
int zoneIdx = findZoneForPlayer(playerRenderPos);
if (zoneIdx >= 0 && zoneBelongsToContinent(zoneIdx, continentIdx)) {
loadZoneTextures(zoneIdx);
compositeZone(zoneIdx);
currentIdx = zoneIdx;
viewLevel = ViewLevel::ZONE;
}
}
}
void WorldMap::zoomOut() {
if (viewLevel == ViewLevel::ZONE) {
// Zone → Continent
if (continentIdx >= 0) {
compositeZone(continentIdx);
currentIdx = continentIdx;
viewLevel = ViewLevel::CONTINENT;
}
} else if (viewLevel == ViewLevel::CONTINENT) {
// Continent → World
enterWorldView();
}
}
// --------------------------------------------------------
// Main render
// --------------------------------------------------------
@ -700,6 +778,11 @@ void WorldMap::render(const glm::vec3& playerRenderPos, int screenWidth, int scr
auto& input = core::Input::getInstance();
// Track exploration even when map is closed
if (!zones.empty()) {
updateExploration(playerRenderPos);
}
// When map is open, always allow M/Escape to close (bypass ImGui keyboard capture)
if (open) {
if (input.isKeyJustPressed(SDL_SCANCODE_M) ||
@ -707,6 +790,14 @@ void WorldMap::render(const glm::vec3& playerRenderPos, int screenWidth, int scr
open = false;
return;
}
// Mouse wheel: scroll up = zoom in, scroll down = zoom out
auto& io = ImGui::GetIO();
if (io.MouseWheel > 0.0f) {
zoomIn(playerRenderPos);
} else if (io.MouseWheel < 0.0f) {
zoomOut();
}
} else {
auto& io = ImGui::GetIO();
if (!io.WantCaptureKeyboard && input.isKeyJustPressed(SDL_SCANCODE_M)) {
@ -721,8 +812,15 @@ void WorldMap::render(const glm::vec3& playerRenderPos, int screenWidth, int scr
compositedIdx = -1;
}
// Ensure continent textures are loaded and composited
if (continentIdx >= 0) {
// Open directly to the player's current zone
int playerZone = findZoneForPlayer(playerRenderPos);
if (playerZone >= 0 && continentIdx >= 0 &&
zoneBelongsToContinent(playerZone, continentIdx)) {
loadZoneTextures(playerZone);
compositeZone(playerZone);
currentIdx = playerZone;
viewLevel = ViewLevel::ZONE;
} else if (continentIdx >= 0) {
loadZoneTextures(continentIdx);
compositeZone(continentIdx);
currentIdx = continentIdx;
@ -942,17 +1040,25 @@ void WorldMap::renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWi
float sx1 = imgMin.x + zuMax * displayW;
float sy1 = imgMin.y + zvMax * displayH;
bool explored = exploredZones.count(zi) > 0;
// Check hover
bool hovered = (mousePos.x >= sx0 && mousePos.x <= sx1 &&
mousePos.y >= sy0 && mousePos.y <= sy1);
// Fog of war: darken unexplored zones
if (!explored) {
drawList->AddRectFilled(ImVec2(sx0, sy0), ImVec2(sx1, sy1),
IM_COL32(0, 0, 0, 160));
}
if (hovered) {
hoveredZone = zi;
drawList->AddRectFilled(ImVec2(sx0, sy0), ImVec2(sx1, sy1),
IM_COL32(255, 255, 200, 40));
drawList->AddRect(ImVec2(sx0, sy0), ImVec2(sx1, sy1),
IM_COL32(255, 215, 0, 180), 0.0f, 0, 2.0f);
} else {
} else if (explored) {
drawList->AddRect(ImVec2(sx0, sy0), ImVec2(sx1, sy1),
IM_COL32(255, 255, 255, 30), 0.0f, 0, 1.0f);
}
@ -1022,11 +1128,11 @@ void WorldMap::renderImGuiOverlay(const glm::vec3& playerRenderPos, int screenWi
// Help text
const char* helpText;
if (viewLevel == ViewLevel::ZONE) {
helpText = "Right-click to zoom out | M or Escape to close";
helpText = "Scroll out or right-click to zoom out | M or Escape to close";
} else if (viewLevel == ViewLevel::WORLD) {
helpText = "Select a continent | M or Escape to close";
helpText = "Select a continent | Scroll in to zoom | M or Escape to close";
} else {
helpText = "Click a zone to zoom in | Right-click for World | M or Escape to close";
helpText = "Click zone or scroll in to zoom | Scroll out / right-click for World | M or Escape to close";
}
ImVec2 textSize = ImGui::CalcTextSize(helpText);
float textY = mapY + displayH + 8.0f;