Add spellbook, fix WMO floor clipping, and polish UI/visuals

- Add spellbook screen (P key) with Spell.dbc name lookup and action bar assignment
- Default Attack and Hearthstone spells available in single player
- Fix WMO floor clipping (gryphon roost) by tightening ceiling rejection threshold
- Darken ocean water, increase wave motion and opacity
- Add M2 model distance fade-in to prevent pop-in
- Reposition chat window, add slash/enter key focus
- Remove debug key commands (keep only F1 perf HUD, N minimap)
- Performance: return chat history by const ref, use deque for O(1) pop_front
This commit is contained in:
Kelsi 2026-02-04 11:31:08 -08:00
parent c49bb58e47
commit 4bc5064515
17 changed files with 486 additions and 431 deletions

View file

@ -214,282 +214,10 @@ void Application::run() {
LOG_INFO("Performance HUD: ", enabled ? "ON" : "OFF");
}
}
// F2: Toggle wireframe
else if (event.key.keysym.scancode == SDL_SCANCODE_F2) {
static bool wireframe = false;
wireframe = !wireframe;
if (renderer) {
renderer->setWireframeMode(wireframe);
LOG_INFO("Wireframe mode: ", wireframe ? "ON" : "OFF");
}
}
// F3: Load test terrain (if in main menu/auth state)
else if (event.key.keysym.scancode == SDL_SCANCODE_F3) {
if (assetManager && assetManager->isInitialized()) {
LOG_INFO("Loading test terrain...");
// Load a test ADT tile (Elwynn Forest)
if (renderer->loadTestTerrain(assetManager.get(),
"World\\Maps\\Azeroth\\Azeroth_32_49.adt")) {
LOG_INFO("Test terrain loaded! Use WASD/QE to move, hold right mouse to look");
}
} else {
LOG_WARNING("Asset manager not initialized. Set WOW_DATA_PATH environment variable.");
}
}
// F4: Toggle frustum culling
else if (event.key.keysym.scancode == SDL_SCANCODE_F4) {
if (renderer && renderer->getTerrainRenderer()) {
static bool culling = true;
culling = !culling;
renderer->getTerrainRenderer()->setFrustumCulling(culling);
LOG_INFO("Frustum culling: ", culling ? "ON" : "OFF");
}
}
// F5: Show rendering statistics
else if (event.key.keysym.scancode == SDL_SCANCODE_F5) {
if (renderer && renderer->getTerrainRenderer()) {
auto* terrain = renderer->getTerrainRenderer();
LOG_INFO("=== Rendering Statistics ===");
LOG_INFO(" Total chunks: ", terrain->getChunkCount());
LOG_INFO(" Rendered: ", terrain->getRenderedChunkCount());
LOG_INFO(" Culled: ", terrain->getCulledChunkCount());
LOG_INFO(" Triangles: ", terrain->getTriangleCount());
if (terrain->getChunkCount() > 0) {
float visiblePercent = (terrain->getRenderedChunkCount() * 100.0f) / terrain->getChunkCount();
LOG_INFO(" Visible: ", static_cast<int>(visiblePercent), "%");
}
// Show terrain manager stats
if (renderer->getTerrainManager()) {
auto* manager = renderer->getTerrainManager();
LOG_INFO(" Loaded tiles: ", manager->getLoadedTileCount());
auto currentTile = manager->getCurrentTile();
LOG_INFO(" Current tile: [", currentTile.x, ",", currentTile.y, "]");
}
}
}
// F6: Load multi-tile terrain area
else if (event.key.keysym.scancode == SDL_SCANCODE_F6) {
if (assetManager && assetManager->isInitialized()) {
LOG_INFO("Loading 3x3 terrain area (Elwynn Forest)...");
// Load 3x3 grid of tiles (Elwynn Forest area)
if (renderer->loadTerrainArea("Azeroth", 32, 49, 1)) {
LOG_INFO("Terrain area loaded! Streaming enabled.");
LOG_INFO("Move around to see dynamic tile loading/unloading");
}
} else {
LOG_WARNING("Asset manager not initialized. Set WOW_DATA_PATH environment variable.");
}
}
// F7: Toggle terrain streaming
else if (event.key.keysym.scancode == SDL_SCANCODE_F7) {
if (renderer && renderer->getTerrainManager()) {
static bool streaming = true;
streaming = !streaming;
renderer->setTerrainStreaming(streaming);
}
}
// F8: Toggle water rendering
else if (event.key.keysym.scancode == SDL_SCANCODE_F8) {
if (renderer && renderer->getWaterRenderer()) {
static bool water = true;
water = !water;
renderer->getWaterRenderer()->setEnabled(water);
LOG_INFO("Water rendering: ", water ? "ON" : "OFF");
}
}
// F9: Toggle time progression
else if (event.key.keysym.scancode == SDL_SCANCODE_F9) {
if (renderer && renderer->getSkybox()) {
bool progression = !renderer->getSkybox()->isTimeProgressionEnabled();
renderer->getSkybox()->setTimeProgression(progression);
LOG_INFO("Time progression: ", progression ? "ON" : "OFF");
}
}
// Plus/Equals: Advance time
else if (event.key.keysym.scancode == SDL_SCANCODE_EQUALS ||
event.key.keysym.scancode == SDL_SCANCODE_KP_PLUS) {
if (renderer && renderer->getSkybox()) {
float time = renderer->getSkybox()->getTimeOfDay() + 1.0f;
renderer->getSkybox()->setTimeOfDay(time);
LOG_INFO("Time of day: ", static_cast<int>(time), ":00");
}
}
// Minus: Rewind time
else if (event.key.keysym.scancode == SDL_SCANCODE_MINUS ||
event.key.keysym.scancode == SDL_SCANCODE_KP_MINUS) {
if (renderer && renderer->getSkybox()) {
float time = renderer->getSkybox()->getTimeOfDay() - 1.0f;
renderer->getSkybox()->setTimeOfDay(time);
LOG_INFO("Time of day: ", static_cast<int>(time), ":00");
}
}
// F10: Toggle celestial rendering (sun/moon)
else if (event.key.keysym.scancode == SDL_SCANCODE_F10) {
if (renderer && renderer->getCelestial()) {
bool enabled = !renderer->getCelestial()->isEnabled();
renderer->getCelestial()->setEnabled(enabled);
LOG_INFO("Celestial rendering: ", enabled ? "ON" : "OFF");
}
}
// F11: Toggle star field
else if (event.key.keysym.scancode == SDL_SCANCODE_F11) {
if (renderer && renderer->getStarField()) {
bool enabled = !renderer->getStarField()->isEnabled();
renderer->getStarField()->setEnabled(enabled);
LOG_INFO("Star field: ", enabled ? "ON" : "OFF");
}
}
// F12: Toggle distance fog
else if (event.key.keysym.scancode == SDL_SCANCODE_F12) {
if (renderer && renderer->getTerrainRenderer()) {
bool enabled = !renderer->getTerrainRenderer()->isFogEnabled();
renderer->getTerrainRenderer()->setFogEnabled(enabled);
LOG_INFO("Distance fog: ", enabled ? "ON" : "OFF");
}
}
// C: Toggle clouds
else if (event.key.keysym.scancode == SDL_SCANCODE_C) {
if (renderer && renderer->getClouds()) {
bool enabled = !renderer->getClouds()->isEnabled();
renderer->getClouds()->setEnabled(enabled);
LOG_INFO("Clouds: ", enabled ? "ON" : "OFF");
}
}
// [ (Left bracket): Decrease cloud density
else if (event.key.keysym.scancode == SDL_SCANCODE_LEFTBRACKET) {
if (renderer && renderer->getClouds()) {
float density = renderer->getClouds()->getDensity() - 0.1f;
renderer->getClouds()->setDensity(density);
LOG_INFO("Cloud density: ", static_cast<int>(density * 100), "%");
}
}
// ] (Right bracket): Increase cloud density
else if (event.key.keysym.scancode == SDL_SCANCODE_RIGHTBRACKET) {
if (renderer && renderer->getClouds()) {
float density = renderer->getClouds()->getDensity() + 0.1f;
renderer->getClouds()->setDensity(density);
LOG_INFO("Cloud density: ", static_cast<int>(density * 100), "%");
}
}
// L: Toggle lens flare
else if (event.key.keysym.scancode == SDL_SCANCODE_L) {
if (renderer && renderer->getLensFlare()) {
bool enabled = !renderer->getLensFlare()->isEnabled();
renderer->getLensFlare()->setEnabled(enabled);
LOG_INFO("Lens flare: ", enabled ? "ON" : "OFF");
}
}
// , (Comma): Decrease lens flare intensity
else if (event.key.keysym.scancode == SDL_SCANCODE_COMMA) {
if (renderer && renderer->getLensFlare()) {
float intensity = renderer->getLensFlare()->getIntensity() - 0.1f;
renderer->getLensFlare()->setIntensity(intensity);
LOG_INFO("Lens flare intensity: ", static_cast<int>(intensity * 100), "%");
}
}
// . (Period): Increase lens flare intensity
else if (event.key.keysym.scancode == SDL_SCANCODE_PERIOD) {
if (renderer && renderer->getLensFlare()) {
float intensity = renderer->getLensFlare()->getIntensity() + 0.1f;
renderer->getLensFlare()->setIntensity(intensity);
LOG_INFO("Lens flare intensity: ", static_cast<int>(intensity * 100), "%");
}
}
// M: Toggle moon phase cycling
else if (event.key.keysym.scancode == SDL_SCANCODE_M) {
if (renderer && renderer->getCelestial()) {
bool cycling = !renderer->getCelestial()->isMoonPhaseCycling();
renderer->getCelestial()->setMoonPhaseCycling(cycling);
LOG_INFO("Moon phase cycling: ", cycling ? "ON" : "OFF");
}
}
// ; (Semicolon): Previous moon phase
else if (event.key.keysym.scancode == SDL_SCANCODE_SEMICOLON) {
if (renderer && renderer->getCelestial()) {
float phase = renderer->getCelestial()->getMoonPhase() - 0.05f;
if (phase < 0.0f) phase += 1.0f;
renderer->getCelestial()->setMoonPhase(phase);
// Log phase name
const char* phaseName = "Unknown";
if (phase < 0.0625f || phase >= 0.9375f) phaseName = "New Moon";
else if (phase < 0.1875f) phaseName = "Waxing Crescent";
else if (phase < 0.3125f) phaseName = "First Quarter";
else if (phase < 0.4375f) phaseName = "Waxing Gibbous";
else if (phase < 0.5625f) phaseName = "Full Moon";
else if (phase < 0.6875f) phaseName = "Waning Gibbous";
else if (phase < 0.8125f) phaseName = "Last Quarter";
else phaseName = "Waning Crescent";
LOG_INFO("Moon phase: ", phaseName, " (", static_cast<int>(phase * 100), "%)");
}
}
// ' (Apostrophe): Next moon phase
else if (event.key.keysym.scancode == SDL_SCANCODE_APOSTROPHE) {
if (renderer && renderer->getCelestial()) {
float phase = renderer->getCelestial()->getMoonPhase() + 0.05f;
if (phase >= 1.0f) phase -= 1.0f;
renderer->getCelestial()->setMoonPhase(phase);
// Log phase name
const char* phaseName = "Unknown";
if (phase < 0.0625f || phase >= 0.9375f) phaseName = "New Moon";
else if (phase < 0.1875f) phaseName = "Waxing Crescent";
else if (phase < 0.3125f) phaseName = "First Quarter";
else if (phase < 0.4375f) phaseName = "Waxing Gibbous";
else if (phase < 0.5625f) phaseName = "Full Moon";
else if (phase < 0.6875f) phaseName = "Waning Gibbous";
else if (phase < 0.8125f) phaseName = "Last Quarter";
else phaseName = "Waning Crescent";
LOG_INFO("Moon phase: ", phaseName, " (", static_cast<int>(phase * 100), "%)");
}
}
// X key reserved for sit (handled in camera_controller)
// < (Shift+,): Decrease weather intensity
else if (event.key.keysym.scancode == SDL_SCANCODE_COMMA &&
(event.key.keysym.mod & KMOD_SHIFT)) {
if (renderer && renderer->getWeather()) {
float intensity = renderer->getWeather()->getIntensity() - 0.1f;
renderer->getWeather()->setIntensity(intensity);
LOG_INFO("Weather intensity: ", static_cast<int>(intensity * 100), "%");
}
}
// > (Shift+.): Increase weather intensity
else if (event.key.keysym.scancode == SDL_SCANCODE_PERIOD &&
(event.key.keysym.mod & KMOD_SHIFT)) {
if (renderer && renderer->getWeather()) {
float intensity = renderer->getWeather()->getIntensity() + 0.1f;
renderer->getWeather()->setIntensity(intensity);
LOG_INFO("Weather intensity: ", static_cast<int>(intensity * 100), "%");
}
}
// K: Spawn player character at camera position
else if (event.key.keysym.scancode == SDL_SCANCODE_K) {
spawnPlayerCharacter();
}
// J: Remove all characters
else if (event.key.keysym.scancode == SDL_SCANCODE_J) {
if (renderer && renderer->getCharacterRenderer()) {
// Note: CharacterRenderer doesn't have removeAll(), so we'd need to track IDs
// For now, just log
LOG_INFO("Character removal not yet implemented");
}
}
// N: Toggle minimap
else if (event.key.keysym.scancode == SDL_SCANCODE_N) {
if (renderer && renderer->getMinimap()) {
renderer->getMinimap()->toggle();
LOG_INFO("Minimap ", renderer->getMinimap()->isEnabled() ? "enabled" : "disabled");
}
}
// P: Remove all WMO buildings (O key removed)
else if (event.key.keysym.scancode == SDL_SCANCODE_P) {
if (renderer && renderer->getWMORenderer()) {
renderer->getWMORenderer()->clearInstances();
LOG_INFO("Cleared all WMO instances");
}
}
}