mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-04-03 20:03:50 +00:00
- Add `DialogManager` + `SettingsPanel` UI modules - Refactor `GameScreen` to delegate dialogs and settings to new domains - Update CMakeLists.txt to include new sources - Include header/source files: - dialog_manager.hpp - settings_panel.hpp - dialog_manager.cpp - settings_panel.cpp - game_screen.cpp - Keep `GameScreen` surface behavior while decoupling feature responsibilities
1258 lines
49 KiB
C++
1258 lines
49 KiB
C++
// ============================================================
|
|
// SettingsPanel — extracted from GameScreen
|
|
// Owns all settings UI rendering, settings state, and
|
|
// graphics preset logic.
|
|
// ============================================================
|
|
#include "ui/settings_panel.hpp"
|
|
#include "ui/inventory_screen.hpp"
|
|
#include "ui/chat_panel.hpp"
|
|
#include "ui/keybinding_manager.hpp"
|
|
#include "core/application.hpp"
|
|
#include "core/logger.hpp"
|
|
#include "rendering/renderer.hpp"
|
|
#include "rendering/camera.hpp"
|
|
#include "rendering/camera_controller.hpp"
|
|
#include "rendering/minimap.hpp"
|
|
#include "rendering/terrain_manager.hpp"
|
|
#include "rendering/wmo_renderer.hpp"
|
|
#include "rendering/character_renderer.hpp"
|
|
#include "game/zone_manager.hpp"
|
|
#include "audio/audio_engine.hpp"
|
|
#include "audio/music_manager.hpp"
|
|
#include "audio/ambient_sound_manager.hpp"
|
|
#include "audio/ui_sound_manager.hpp"
|
|
#include "audio/combat_sound_manager.hpp"
|
|
#include "audio/spell_sound_manager.hpp"
|
|
#include "audio/movement_sound_manager.hpp"
|
|
#include "audio/footstep_manager.hpp"
|
|
#include "audio/npc_voice_manager.hpp"
|
|
#include "audio/mount_sound_manager.hpp"
|
|
#include "audio/activity_sound_manager.hpp"
|
|
#include <imgui.h>
|
|
#include <imgui_internal.h>
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <string>
|
|
|
|
namespace wowee { namespace ui {
|
|
|
|
void SettingsPanel::renderSettingsInterfaceTab(std::function<void()> saveCallback) {
|
|
ImGui::Spacing();
|
|
ImGui::BeginChild("InterfaceSettings", ImVec2(0, 360), true);
|
|
|
|
ImGui::SeparatorText("Action Bars");
|
|
ImGui::Spacing();
|
|
ImGui::SetNextItemWidth(200.0f);
|
|
if (ImGui::SliderFloat("Action Bar Scale", &pendingActionBarScale, 0.5f, 1.5f, "%.2fx")) {
|
|
saveCallback();
|
|
}
|
|
ImGui::Spacing();
|
|
|
|
if (ImGui::Checkbox("Show Second Action Bar", &pendingShowActionBar2)) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(Shift+1 through Shift+=)");
|
|
|
|
if (pendingShowActionBar2) {
|
|
ImGui::Spacing();
|
|
ImGui::TextUnformatted("Second Bar Position Offset");
|
|
ImGui::SetNextItemWidth(160.0f);
|
|
if (ImGui::SliderFloat("Horizontal##bar2x", &pendingActionBar2OffsetX, -600.0f, 600.0f, "%.0f px")) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SetNextItemWidth(160.0f);
|
|
if (ImGui::SliderFloat("Vertical##bar2y", &pendingActionBar2OffsetY, -400.0f, 400.0f, "%.0f px")) {
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Button("Reset Position##bar2")) {
|
|
pendingActionBar2OffsetX = 0.0f;
|
|
pendingActionBar2OffsetY = 0.0f;
|
|
saveCallback();
|
|
}
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
if (ImGui::Checkbox("Show Right Side Bar", &pendingShowRightBar)) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(Slots 25-36)");
|
|
if (pendingShowRightBar) {
|
|
ImGui::SetNextItemWidth(160.0f);
|
|
if (ImGui::SliderFloat("Vertical Offset##rbar", &pendingRightBarOffsetY, -400.0f, 400.0f, "%.0f px")) {
|
|
saveCallback();
|
|
}
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
if (ImGui::Checkbox("Show Left Side Bar", &pendingShowLeftBar)) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(Slots 37-48)");
|
|
if (pendingShowLeftBar) {
|
|
ImGui::SetNextItemWidth(160.0f);
|
|
if (ImGui::SliderFloat("Vertical Offset##lbar", &pendingLeftBarOffsetY, -400.0f, 400.0f, "%.0f px")) {
|
|
saveCallback();
|
|
}
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::SeparatorText("Nameplates");
|
|
ImGui::Spacing();
|
|
ImGui::SetNextItemWidth(200.0f);
|
|
if (ImGui::SliderFloat("Nameplate Scale", &nameplateScale_, 0.5f, 2.0f, "%.2fx")) {
|
|
saveCallback();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::SeparatorText("Network");
|
|
ImGui::Spacing();
|
|
if (ImGui::Checkbox("Show Latency Meter", &pendingShowLatencyMeter)) {
|
|
showLatencyMeter_ = pendingShowLatencyMeter;
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(ms indicator near minimap)");
|
|
|
|
if (ImGui::Checkbox("Show DPS/HPS Meter", &showDPSMeter_)) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(damage/healing per second above action bar)");
|
|
|
|
if (ImGui::Checkbox("Show Cooldown Tracker", &showCooldownTracker_)) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(active spell cooldowns near action bar)");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::SeparatorText("Screen Effects");
|
|
ImGui::Spacing();
|
|
if (ImGui::Checkbox("Damage Flash", &damageFlashEnabled_)) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(red vignette on taking damage)");
|
|
|
|
if (ImGui::Checkbox("Low Health Vignette", &lowHealthVignetteEnabled_)) {
|
|
saveCallback();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::TextDisabled("(pulsing red edges below 20%% HP)");
|
|
|
|
ImGui::EndChild();
|
|
}
|
|
|
|
void SettingsPanel::renderSettingsGameplayTab(InventoryScreen& inventoryScreen,
|
|
std::function<void()> saveCallback) {
|
|
auto* renderer = core::Application::getInstance().getRenderer();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("Controls");
|
|
ImGui::Separator();
|
|
if (ImGui::SliderFloat("Mouse Sensitivity", &pendingMouseSensitivity, 0.05f, 1.0f, "%.2f")) {
|
|
if (renderer) {
|
|
if (auto* cameraController = renderer->getCameraController()) {
|
|
cameraController->setMouseSensitivity(pendingMouseSensitivity);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Invert Mouse", &pendingInvertMouse)) {
|
|
if (renderer) {
|
|
if (auto* cameraController = renderer->getCameraController()) {
|
|
cameraController->setInvertMouse(pendingInvertMouse);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Extended Camera Zoom", &pendingExtendedZoom)) {
|
|
if (renderer) {
|
|
if (auto* cameraController = renderer->getCameraController()) {
|
|
cameraController->setExtendedZoom(pendingExtendedZoom);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::SliderFloat("Camera Stiffness", &pendingCameraStiffness, 5.0f, 100.0f, "%.0f")) {
|
|
if (renderer) {
|
|
if (auto* cameraController = renderer->getCameraController()) {
|
|
cameraController->setCameraSmoothSpeed(pendingCameraStiffness);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
ImGui::SetItemTooltip("Higher = tighter camera with less sway. Default: 30");
|
|
if (ImGui::SliderFloat("Camera Pivot Height", &pendingPivotHeight, 0.0f, 3.0f, "%.1f")) {
|
|
if (renderer) {
|
|
if (auto* cameraController = renderer->getCameraController()) {
|
|
cameraController->setPivotHeight(pendingPivotHeight);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
ImGui::SetItemTooltip("Height of camera orbit point above feet. Lower = less detached feel. Default: 1.8");
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("Allow the camera to zoom out further than normal");
|
|
|
|
if (ImGui::SliderFloat("Field of View", &pendingFov, 45.0f, 110.0f, "%.0f°")) {
|
|
if (renderer) {
|
|
if (auto* camera = renderer->getCamera()) {
|
|
camera->setFov(pendingFov);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("Camera field of view in degrees (default: 70)");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("Interface");
|
|
ImGui::Separator();
|
|
if (ImGui::SliderInt("UI Opacity", &pendingUiOpacity, 20, 100, "%d%%")) {
|
|
uiOpacity_ = static_cast<float>(pendingUiOpacity) / 100.0f;
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Rotate Minimap", &pendingMinimapRotate)) {
|
|
// Force north-up minimap.
|
|
minimapRotate_ = false;
|
|
pendingMinimapRotate = false;
|
|
if (renderer) {
|
|
if (auto* minimap = renderer->getMinimap()) {
|
|
minimap->setRotateWithCamera(false);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Square Minimap", &pendingMinimapSquare)) {
|
|
minimapSquare_ = pendingMinimapSquare;
|
|
if (renderer) {
|
|
if (auto* minimap = renderer->getMinimap()) {
|
|
minimap->setSquareShape(minimapSquare_);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Show Nearby NPC Dots", &pendingMinimapNpcDots)) {
|
|
minimapNpcDots_ = pendingMinimapNpcDots;
|
|
saveCallback();
|
|
}
|
|
// Zoom controls
|
|
ImGui::Text("Minimap Zoom:");
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(" - ")) {
|
|
if (renderer) {
|
|
if (auto* minimap = renderer->getMinimap()) {
|
|
minimap->zoomOut();
|
|
saveCallback();
|
|
}
|
|
}
|
|
}
|
|
ImGui::SameLine();
|
|
if (ImGui::Button(" + ")) {
|
|
if (renderer) {
|
|
if (auto* minimap = renderer->getMinimap()) {
|
|
minimap->zoomIn();
|
|
saveCallback();
|
|
}
|
|
}
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Loot");
|
|
ImGui::Separator();
|
|
if (ImGui::Checkbox("Auto Loot", &pendingAutoLoot)) {
|
|
saveCallback(); // per-frame sync applies pendingAutoLoot to gameHandler
|
|
}
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("Automatically pick up all items when looting");
|
|
if (ImGui::Checkbox("Auto Sell Greys", &pendingAutoSellGrey)) {
|
|
saveCallback();
|
|
}
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("Automatically sell all grey (poor quality) items when opening a vendor");
|
|
if (ImGui::Checkbox("Auto Repair", &pendingAutoRepair)) {
|
|
saveCallback();
|
|
}
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("Automatically repair all damaged equipment when opening an armorer vendor");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Bags");
|
|
ImGui::Separator();
|
|
if (ImGui::Checkbox("Separate Bag Windows", &pendingSeparateBags)) {
|
|
inventoryScreen.setSeparateBags(pendingSeparateBags);
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Show Key Ring", &pendingShowKeyring)) {
|
|
inventoryScreen.setShowKeyring(pendingShowKeyring);
|
|
saveCallback();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
if (ImGui::Button("Restore Gameplay Defaults", ImVec2(-1, 0))) {
|
|
pendingMouseSensitivity = 0.2f;
|
|
pendingInvertMouse = false;
|
|
pendingExtendedZoom = false;
|
|
pendingUiOpacity = 65;
|
|
pendingMinimapRotate = false;
|
|
pendingMinimapSquare = false;
|
|
pendingMinimapNpcDots = false;
|
|
pendingSeparateBags = true;
|
|
inventoryScreen.setSeparateBags(true);
|
|
pendingShowKeyring = true;
|
|
inventoryScreen.setShowKeyring(true);
|
|
uiOpacity_ = 0.65f;
|
|
minimapRotate_ = false;
|
|
minimapSquare_ = false;
|
|
minimapNpcDots_ = false;
|
|
if (renderer) {
|
|
if (auto* cameraController = renderer->getCameraController()) {
|
|
cameraController->setMouseSensitivity(pendingMouseSensitivity);
|
|
cameraController->setInvertMouse(pendingInvertMouse);
|
|
cameraController->setExtendedZoom(pendingExtendedZoom);
|
|
}
|
|
if (auto* minimap = renderer->getMinimap()) {
|
|
minimap->setRotateWithCamera(minimapRotate_);
|
|
minimap->setSquareShape(minimapSquare_);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
|
|
}
|
|
|
|
void SettingsPanel::renderSettingsControlsTab(std::function<void()> saveCallback) {
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("Keybindings");
|
|
ImGui::Separator();
|
|
|
|
auto& km = ui::KeybindingManager::getInstance();
|
|
int numActions = km.getActionCount();
|
|
|
|
for (int i = 0; i < numActions; ++i) {
|
|
auto action = static_cast<ui::KeybindingManager::Action>(i);
|
|
const char* actionName = km.getActionName(action);
|
|
ImGuiKey currentKey = km.getKeyForAction(action);
|
|
|
|
// Display current binding
|
|
ImGui::Text("%s:", actionName);
|
|
ImGui::SameLine(200);
|
|
|
|
// Get human-readable key name (basic implementation)
|
|
const char* keyName = "Unknown";
|
|
if (currentKey >= ImGuiKey_A && currentKey <= ImGuiKey_Z) {
|
|
static char keyBuf[16];
|
|
snprintf(keyBuf, sizeof(keyBuf), "%c", 'A' + (currentKey - ImGuiKey_A));
|
|
keyName = keyBuf;
|
|
} else if (currentKey >= ImGuiKey_0 && currentKey <= ImGuiKey_9) {
|
|
static char keyBuf[16];
|
|
snprintf(keyBuf, sizeof(keyBuf), "%c", '0' + (currentKey - ImGuiKey_0));
|
|
keyName = keyBuf;
|
|
} else if (currentKey == ImGuiKey_Escape) {
|
|
keyName = "Escape";
|
|
} else if (currentKey == ImGuiKey_Enter) {
|
|
keyName = "Enter";
|
|
} else if (currentKey == ImGuiKey_Tab) {
|
|
keyName = "Tab";
|
|
} else if (currentKey == ImGuiKey_Space) {
|
|
keyName = "Space";
|
|
} else if (currentKey >= ImGuiKey_F1 && currentKey <= ImGuiKey_F12) {
|
|
static char keyBuf[16];
|
|
snprintf(keyBuf, sizeof(keyBuf), "F%d", 1 + (currentKey - ImGuiKey_F1));
|
|
keyName = keyBuf;
|
|
}
|
|
|
|
ImGui::Text("[%s]", keyName);
|
|
|
|
// Rebind button
|
|
ImGui::SameLine(350);
|
|
if (ImGui::Button(awaitingKeyPress_ && pendingRebindAction_ == i ? "Waiting..." : "Rebind", ImVec2(100, 0))) {
|
|
pendingRebindAction_ = i;
|
|
awaitingKeyPress_ = true;
|
|
}
|
|
}
|
|
|
|
// Handle key press during rebinding
|
|
if (awaitingKeyPress_ && pendingRebindAction_ >= 0) {
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Text("Press any key to bind to this action (Esc to cancel)...");
|
|
|
|
// Check for any key press
|
|
bool foundKey = false;
|
|
ImGuiKey newKey = ImGuiKey_None;
|
|
for (int k = ImGuiKey_NamedKey_BEGIN; k < ImGuiKey_NamedKey_END; ++k) {
|
|
if (ImGui::IsKeyPressed(static_cast<ImGuiKey>(k), false)) {
|
|
if (k == ImGuiKey_Escape) {
|
|
// Cancel rebinding
|
|
awaitingKeyPress_ = false;
|
|
pendingRebindAction_ = -1;
|
|
foundKey = true;
|
|
break;
|
|
}
|
|
newKey = static_cast<ImGuiKey>(k);
|
|
foundKey = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (foundKey && newKey != ImGuiKey_None) {
|
|
auto action = static_cast<ui::KeybindingManager::Action>(pendingRebindAction_);
|
|
km.setKeyForAction(action, newKey);
|
|
awaitingKeyPress_ = false;
|
|
pendingRebindAction_ = -1;
|
|
saveCallback();
|
|
}
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
if (ImGui::Button("Reset to Defaults", ImVec2(-1, 0))) {
|
|
km.resetToDefaults();
|
|
awaitingKeyPress_ = false;
|
|
pendingRebindAction_ = -1;
|
|
saveCallback();
|
|
}
|
|
|
|
}
|
|
|
|
void SettingsPanel::renderSettingsAudioTab(std::function<void()> saveCallback) {
|
|
auto* renderer = core::Application::getInstance().getRenderer();
|
|
ImGui::Spacing();
|
|
ImGui::BeginChild("AudioSettings", ImVec2(0, 360), true);
|
|
|
|
// Helper lambda to apply audio settings
|
|
auto applyAudioSettings = [&]() {
|
|
applyAudioVolumes(renderer);
|
|
saveCallback();
|
|
};
|
|
|
|
ImGui::Text("Master Volume");
|
|
if (ImGui::SliderInt("##MasterVolume", &pendingMasterVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::Checkbox("Enable WoWee Music", &pendingUseOriginalSoundtrack)) {
|
|
if (renderer) {
|
|
if (auto* zm = renderer->getZoneManager()) {
|
|
zm->setUseOriginalSoundtrack(pendingUseOriginalSoundtrack);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::IsItemHovered())
|
|
ImGui::SetTooltip("Include WoWee music tracks in zone music rotation");
|
|
ImGui::Separator();
|
|
|
|
ImGui::Text("Music");
|
|
if (ImGui::SliderInt("##MusicVolume", &pendingMusicVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Ambient Sounds");
|
|
if (ImGui::SliderInt("##AmbientVolume", &pendingAmbientVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
ImGui::TextWrapped("Weather, zones, cities, emitters");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("UI Sounds");
|
|
if (ImGui::SliderInt("##UiVolume", &pendingUiVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
ImGui::TextWrapped("Buttons, loot, quest complete");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Combat Sounds");
|
|
if (ImGui::SliderInt("##CombatVolume", &pendingCombatVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
ImGui::TextWrapped("Weapon swings, impacts, grunts");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Spell Sounds");
|
|
if (ImGui::SliderInt("##SpellVolume", &pendingSpellVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
ImGui::TextWrapped("Magic casting and impacts");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Movement Sounds");
|
|
if (ImGui::SliderInt("##MovementVolume", &pendingMovementVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
ImGui::TextWrapped("Water splashes, jump/land");
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Footsteps");
|
|
if (ImGui::SliderInt("##FootstepVolume", &pendingFootstepVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("NPC Voices");
|
|
if (ImGui::SliderInt("##NpcVoiceVolume", &pendingNpcVoiceVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Mount Sounds");
|
|
if (ImGui::SliderInt("##MountVolume", &pendingMountVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Text("Activity Sounds");
|
|
if (ImGui::SliderInt("##ActivityVolume", &pendingActivityVolume, 0, 100, "%d%%")) {
|
|
applyAudioSettings();
|
|
}
|
|
ImGui::TextWrapped("Swimming, eating, drinking");
|
|
|
|
ImGui::EndChild();
|
|
|
|
if (ImGui::Button("Restore Audio Defaults", ImVec2(-1, 0))) {
|
|
pendingMasterVolume = 100;
|
|
pendingMusicVolume = 30; // default music volume
|
|
pendingAmbientVolume = 100;
|
|
pendingUiVolume = 100;
|
|
pendingCombatVolume = 100;
|
|
pendingSpellVolume = 100;
|
|
pendingMovementVolume = 100;
|
|
pendingFootstepVolume = 100;
|
|
pendingNpcVoiceVolume = 100;
|
|
pendingMountVolume = 100;
|
|
pendingActivityVolume = 100;
|
|
applyAudioSettings();
|
|
}
|
|
|
|
}
|
|
|
|
void SettingsPanel::renderSettingsAboutTab() {
|
|
ImGui::Spacing();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::TextWrapped("WoWee - World of Warcraft Client Emulator");
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("Developer");
|
|
ImGui::Indent();
|
|
ImGui::Text("Kelsi Davis");
|
|
ImGui::Unindent();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("GitHub");
|
|
ImGui::Indent();
|
|
ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "https://github.com/Kelsidavis/WoWee");
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
|
ImGui::SetTooltip("Click to copy");
|
|
}
|
|
if (ImGui::IsItemClicked()) {
|
|
ImGui::SetClipboardText("https://github.com/Kelsidavis/WoWee");
|
|
}
|
|
ImGui::Unindent();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::Text("Contact");
|
|
ImGui::Indent();
|
|
ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "github.com/Kelsidavis");
|
|
if (ImGui::IsItemHovered()) {
|
|
ImGui::SetMouseCursor(ImGuiMouseCursor_Hand);
|
|
ImGui::SetTooltip("Click to copy");
|
|
}
|
|
if (ImGui::IsItemClicked()) {
|
|
ImGui::SetClipboardText("https://github.com/Kelsidavis");
|
|
}
|
|
ImGui::Unindent();
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::TextWrapped("A multi-expansion WoW client supporting Classic, TBC, and WotLK (3.3.5a).");
|
|
ImGui::Spacing();
|
|
ImGui::TextDisabled("Built with Vulkan, SDL2, and ImGui");
|
|
|
|
}
|
|
|
|
void SettingsPanel::renderSettingsWindow(InventoryScreen& inventoryScreen, ChatPanel& chatPanel,
|
|
std::function<void()> saveCallback) {
|
|
if (!showSettingsWindow) return;
|
|
|
|
auto* window = core::Application::getInstance().getWindow();
|
|
auto* renderer = core::Application::getInstance().getRenderer();
|
|
if (!window) return;
|
|
|
|
static constexpr int kResolutions[][2] = {
|
|
{1280, 720},
|
|
{1600, 900},
|
|
{1920, 1080},
|
|
{2560, 1440},
|
|
{3840, 2160},
|
|
};
|
|
static constexpr int kResCount = sizeof(kResolutions) / sizeof(kResolutions[0]);
|
|
constexpr int kDefaultResW = 1920;
|
|
constexpr int kDefaultResH = 1080;
|
|
constexpr bool kDefaultFullscreen = false;
|
|
constexpr bool kDefaultVsync = true;
|
|
constexpr bool kDefaultShadows = true;
|
|
constexpr int kDefaultGroundClutterDensity = 100;
|
|
|
|
int defaultResIndex = 0;
|
|
for (int i = 0; i < kResCount; i++) {
|
|
if (kResolutions[i][0] == kDefaultResW && kResolutions[i][1] == kDefaultResH) {
|
|
defaultResIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!settingsInit) {
|
|
pendingFullscreen = window->isFullscreen();
|
|
pendingVsync = window->isVsyncEnabled();
|
|
if (renderer) {
|
|
renderer->setShadowsEnabled(pendingShadows);
|
|
renderer->setShadowDistance(pendingShadowDistance);
|
|
// Read non-volume settings from actual state (volumes come from saved settings)
|
|
if (auto* cameraController = renderer->getCameraController()) {
|
|
pendingMouseSensitivity = cameraController->getMouseSensitivity();
|
|
pendingInvertMouse = cameraController->isInvertMouse();
|
|
cameraController->setExtendedZoom(pendingExtendedZoom);
|
|
}
|
|
}
|
|
pendingResIndex = 0;
|
|
int curW = window->getWidth();
|
|
int curH = window->getHeight();
|
|
for (int i = 0; i < kResCount; i++) {
|
|
if (kResolutions[i][0] == curW && kResolutions[i][1] == curH) {
|
|
pendingResIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
pendingUiOpacity = static_cast<int>(std::lround(uiOpacity_ * 100.0f));
|
|
pendingMinimapRotate = minimapRotate_;
|
|
pendingMinimapSquare = minimapSquare_;
|
|
pendingMinimapNpcDots = minimapNpcDots_;
|
|
pendingShowLatencyMeter = showLatencyMeter_;
|
|
if (renderer) {
|
|
if (auto* minimap = renderer->getMinimap()) {
|
|
minimap->setRotateWithCamera(minimapRotate_);
|
|
minimap->setSquareShape(minimapSquare_);
|
|
}
|
|
if (auto* zm = renderer->getZoneManager()) {
|
|
pendingUseOriginalSoundtrack = zm->getUseOriginalSoundtrack();
|
|
}
|
|
}
|
|
settingsInit = true;
|
|
}
|
|
|
|
ImGuiIO& io = ImGui::GetIO();
|
|
float screenW = io.DisplaySize.x;
|
|
float screenH = io.DisplaySize.y;
|
|
ImVec2 size(520.0f, std::min(screenH * 0.9f, 720.0f));
|
|
ImVec2 pos((screenW - size.x) * 0.5f, (screenH - size.y) * 0.5f);
|
|
|
|
ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
|
|
ImGui::SetNextWindowSize(size, ImGuiCond_Always);
|
|
ImGuiWindowFlags flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
|
|
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar;
|
|
|
|
if (ImGui::Begin("##SettingsWindow", nullptr, flags)) {
|
|
ImGui::Text("Settings");
|
|
ImGui::Separator();
|
|
|
|
if (ImGui::BeginTabBar("SettingsTabs", ImGuiTabBarFlags_None)) {
|
|
// ============================================================
|
|
// VIDEO TAB
|
|
// ============================================================
|
|
if (ImGui::BeginTabItem("Video")) {
|
|
ImGui::Spacing();
|
|
|
|
// Graphics Quality Presets
|
|
{
|
|
const char* presetLabels[] = { "Custom", "Low", "Medium", "High", "Ultra" };
|
|
int presetIdx = static_cast<int>(pendingGraphicsPreset);
|
|
if (ImGui::Combo("Quality Preset", &presetIdx, presetLabels, 5)) {
|
|
pendingGraphicsPreset = static_cast<GraphicsPreset>(presetIdx);
|
|
if (pendingGraphicsPreset != GraphicsPreset::CUSTOM) {
|
|
applyGraphicsPreset(pendingGraphicsPreset);
|
|
saveCallback();
|
|
}
|
|
}
|
|
ImGui::TextDisabled("Adjust these for custom settings");
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
if (ImGui::Checkbox("Fullscreen", &pendingFullscreen)) {
|
|
window->setFullscreen(pendingFullscreen);
|
|
updateGraphicsPresetFromCurrentSettings();
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("VSync", &pendingVsync)) {
|
|
window->setVsync(pendingVsync);
|
|
updateGraphicsPresetFromCurrentSettings();
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Shadows", &pendingShadows)) {
|
|
if (renderer) renderer->setShadowsEnabled(pendingShadows);
|
|
updateGraphicsPresetFromCurrentSettings();
|
|
saveCallback();
|
|
}
|
|
if (pendingShadows) {
|
|
ImGui::SameLine();
|
|
ImGui::SetNextItemWidth(150.0f);
|
|
if (ImGui::SliderFloat("Distance##shadow", &pendingShadowDistance, 40.0f, 500.0f, "%.0f")) {
|
|
if (renderer) renderer->setShadowDistance(pendingShadowDistance);
|
|
updateGraphicsPresetFromCurrentSettings();
|
|
saveCallback();
|
|
}
|
|
}
|
|
{
|
|
if (ImGui::Checkbox("Water Refraction", &pendingWaterRefraction)) {
|
|
if (renderer) renderer->setWaterRefractionEnabled(pendingWaterRefraction);
|
|
updateGraphicsPresetFromCurrentSettings();
|
|
saveCallback();
|
|
}
|
|
}
|
|
{
|
|
const char* aaLabels[] = { "Off", "2x MSAA", "4x MSAA", "8x MSAA" };
|
|
bool fsr2Active = renderer && renderer->isFSR2Enabled();
|
|
if (fsr2Active) {
|
|
ImGui::BeginDisabled();
|
|
int disabled = 0;
|
|
ImGui::Combo("Anti-Aliasing (FSR3)", &disabled, "Off (FSR3 active)\0", 1);
|
|
ImGui::EndDisabled();
|
|
} else if (ImGui::Combo("Anti-Aliasing", &pendingAntiAliasing, aaLabels, 4)) {
|
|
static const VkSampleCountFlagBits aaSamples[] = {
|
|
VK_SAMPLE_COUNT_1_BIT, VK_SAMPLE_COUNT_2_BIT,
|
|
VK_SAMPLE_COUNT_4_BIT, VK_SAMPLE_COUNT_8_BIT
|
|
};
|
|
if (renderer) renderer->setMsaaSamples(aaSamples[pendingAntiAliasing]);
|
|
updateGraphicsPresetFromCurrentSettings();
|
|
saveCallback();
|
|
}
|
|
// FXAA — post-process, combinable with MSAA or FSR3
|
|
{
|
|
if (ImGui::Checkbox("FXAA (post-process)", &pendingFXAA)) {
|
|
if (renderer) renderer->setFXAAEnabled(pendingFXAA);
|
|
updateGraphicsPresetFromCurrentSettings();
|
|
saveCallback();
|
|
}
|
|
if (ImGui::IsItemHovered()) {
|
|
if (fsr2Active)
|
|
ImGui::SetTooltip("FXAA applies spatial anti-aliasing after FSR3 upscaling.\nFSR3 + FXAA is the recommended ultra-quality combination.");
|
|
else
|
|
ImGui::SetTooltip("FXAA smooths jagged edges as a post-process pass.\nCan be combined with MSAA for extra quality.");
|
|
}
|
|
}
|
|
}
|
|
// FSR Upscaling
|
|
{
|
|
// FSR mode selection: Off, FSR 1.0 (Spatial), FSR 3.x (Temporal)
|
|
const char* fsrModeLabels[] = { "Off", "FSR 1.0 (Spatial)", "FSR 3.x (Temporal)" };
|
|
int fsrMode = pendingUpscalingMode;
|
|
if (ImGui::Combo("Upscaling", &fsrMode, fsrModeLabels, 3)) {
|
|
pendingUpscalingMode = fsrMode;
|
|
pendingFSR = (fsrMode == 1);
|
|
if (renderer) {
|
|
renderer->setFSREnabled(fsrMode == 1);
|
|
renderer->setFSR2Enabled(fsrMode == 2);
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (fsrMode > 0) {
|
|
if (fsrMode == 2 && renderer) {
|
|
ImGui::TextDisabled("FSR3 backend: %s",
|
|
renderer->isAmdFsr2SdkAvailable() ? "AMD FidelityFX SDK" : "Internal fallback");
|
|
if (renderer->isAmdFsr3FramegenSdkAvailable()) {
|
|
if (ImGui::Checkbox("AMD FSR3 Frame Generation (Experimental)", &pendingAMDFramegen)) {
|
|
renderer->setAmdFsr3FramegenEnabled(pendingAMDFramegen);
|
|
saveCallback();
|
|
}
|
|
const char* runtimeStatus = "Unavailable";
|
|
if (renderer->isAmdFsr3FramegenRuntimeActive()) {
|
|
runtimeStatus = "Active";
|
|
} else if (renderer->isAmdFsr3FramegenRuntimeReady()) {
|
|
runtimeStatus = "Ready";
|
|
} else {
|
|
runtimeStatus = "Unavailable";
|
|
}
|
|
ImGui::TextDisabled("Runtime: %s (%s)",
|
|
runtimeStatus, renderer->getAmdFsr3FramegenRuntimePath());
|
|
if (!renderer->isAmdFsr3FramegenRuntimeReady()) {
|
|
const std::string& runtimeErr = renderer->getAmdFsr3FramegenRuntimeError();
|
|
if (!runtimeErr.empty()) {
|
|
ImGui::TextDisabled("Reason: %s", runtimeErr.c_str());
|
|
}
|
|
}
|
|
} else {
|
|
ImGui::BeginDisabled();
|
|
bool disabledFg = false;
|
|
ImGui::Checkbox("AMD FSR3 Frame Generation (Experimental)", &disabledFg);
|
|
ImGui::EndDisabled();
|
|
ImGui::TextDisabled("Requires FidelityFX-SDK framegen headers.");
|
|
}
|
|
}
|
|
const char* fsrQualityLabels[] = { "Native (100%)", "Ultra Quality (77%)", "Quality (67%)", "Balanced (59%)" };
|
|
static constexpr float fsrScaleFactors[] = { 0.77f, 0.67f, 0.59f, 1.00f };
|
|
static constexpr int displayToInternal[] = { 3, 0, 1, 2 };
|
|
pendingFSRQuality = std::clamp(pendingFSRQuality, 0, 3);
|
|
int fsrQualityDisplay = 0;
|
|
for (int i = 0; i < 4; ++i) {
|
|
if (displayToInternal[i] == pendingFSRQuality) {
|
|
fsrQualityDisplay = i;
|
|
break;
|
|
}
|
|
}
|
|
if (ImGui::Combo("FSR Quality", &fsrQualityDisplay, fsrQualityLabels, 4)) {
|
|
pendingFSRQuality = displayToInternal[fsrQualityDisplay];
|
|
if (renderer) renderer->setFSRQuality(fsrScaleFactors[pendingFSRQuality]);
|
|
saveCallback();
|
|
}
|
|
if (ImGui::SliderFloat("FSR Sharpness", &pendingFSRSharpness, 0.0f, 2.0f, "%.1f")) {
|
|
if (renderer) renderer->setFSRSharpness(pendingFSRSharpness);
|
|
saveCallback();
|
|
}
|
|
if (fsrMode == 2) {
|
|
ImGui::SeparatorText("FSR3 Tuning");
|
|
if (ImGui::SliderFloat("Jitter Sign", &pendingFSR2JitterSign, -2.0f, 2.0f, "%.2f")) {
|
|
if (renderer) {
|
|
renderer->setFSR2DebugTuning(
|
|
pendingFSR2JitterSign,
|
|
pendingFSR2MotionVecScaleX,
|
|
pendingFSR2MotionVecScaleY);
|
|
}
|
|
saveCallback();
|
|
}
|
|
ImGui::TextDisabled("Tip: 0.38 is the current recommended default.");
|
|
}
|
|
}
|
|
}
|
|
if (ImGui::SliderInt("Ground Clutter Density", &pendingGroundClutterDensity, 0, 150, "%d%%")) {
|
|
if (renderer) {
|
|
if (auto* tm = renderer->getTerrainManager()) {
|
|
tm->setGroundClutterDensityScale(static_cast<float>(pendingGroundClutterDensity) / 100.0f);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (ImGui::Checkbox("Normal Mapping", &pendingNormalMapping)) {
|
|
if (renderer) {
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setNormalMappingEnabled(pendingNormalMapping);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setNormalMappingEnabled(pendingNormalMapping);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (pendingNormalMapping) {
|
|
if (ImGui::SliderFloat("Normal Map Strength", &pendingNormalMapStrength, 0.0f, 2.0f, "%.1f")) {
|
|
if (renderer) {
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setNormalMapStrength(pendingNormalMapStrength);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setNormalMapStrength(pendingNormalMapStrength);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
}
|
|
if (ImGui::Checkbox("Parallax Mapping", &pendingPOM)) {
|
|
if (renderer) {
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setPOMEnabled(pendingPOM);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setPOMEnabled(pendingPOM);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
if (pendingPOM) {
|
|
const char* pomLabels[] = { "Low", "Medium", "High" };
|
|
if (ImGui::Combo("Parallax Quality", &pendingPOMQuality, pomLabels, 3)) {
|
|
if (renderer) {
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setPOMQuality(pendingPOMQuality);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setPOMQuality(pendingPOMQuality);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
}
|
|
|
|
const char* resLabel = "Resolution";
|
|
const char* resItems[kResCount];
|
|
char resBuf[kResCount][16];
|
|
for (int i = 0; i < kResCount; i++) {
|
|
snprintf(resBuf[i], sizeof(resBuf[i]), "%dx%d", kResolutions[i][0], kResolutions[i][1]);
|
|
resItems[i] = resBuf[i];
|
|
}
|
|
if (ImGui::Combo(resLabel, &pendingResIndex, resItems, kResCount)) {
|
|
window->applyResolution(kResolutions[pendingResIndex][0], kResolutions[pendingResIndex][1]);
|
|
saveCallback();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
ImGui::SetNextItemWidth(200.0f);
|
|
if (ImGui::SliderInt("Brightness", &pendingBrightness, 0, 100, "%d%%")) {
|
|
if (renderer) renderer->setBrightness(static_cast<float>(pendingBrightness) / 50.0f);
|
|
saveCallback();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
if (ImGui::Button("Restore Video Defaults", ImVec2(-1, 0))) {
|
|
pendingFullscreen = kDefaultFullscreen;
|
|
pendingVsync = kDefaultVsync;
|
|
pendingShadows = kDefaultShadows;
|
|
pendingShadowDistance = 300.0f;
|
|
pendingGroundClutterDensity = kDefaultGroundClutterDensity;
|
|
pendingAntiAliasing = 0;
|
|
pendingNormalMapping = true;
|
|
pendingNormalMapStrength = 0.8f;
|
|
pendingPOM = true;
|
|
pendingPOMQuality = 1;
|
|
pendingResIndex = defaultResIndex;
|
|
pendingBrightness = 50;
|
|
window->setFullscreen(pendingFullscreen);
|
|
window->setVsync(pendingVsync);
|
|
window->applyResolution(kResolutions[pendingResIndex][0], kResolutions[pendingResIndex][1]);
|
|
if (renderer) renderer->setBrightness(1.0f);
|
|
pendingWaterRefraction = false;
|
|
if (renderer) {
|
|
renderer->setShadowsEnabled(pendingShadows);
|
|
renderer->setShadowDistance(pendingShadowDistance);
|
|
}
|
|
if (renderer) renderer->setWaterRefractionEnabled(pendingWaterRefraction);
|
|
if (renderer) renderer->setMsaaSamples(VK_SAMPLE_COUNT_1_BIT);
|
|
if (renderer) {
|
|
if (auto* tm = renderer->getTerrainManager()) {
|
|
tm->setGroundClutterDensityScale(static_cast<float>(pendingGroundClutterDensity) / 100.0f);
|
|
}
|
|
}
|
|
if (renderer) {
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setNormalMappingEnabled(pendingNormalMapping);
|
|
wr->setNormalMapStrength(pendingNormalMapStrength);
|
|
wr->setPOMEnabled(pendingPOM);
|
|
wr->setPOMQuality(pendingPOMQuality);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setNormalMappingEnabled(pendingNormalMapping);
|
|
cr->setNormalMapStrength(pendingNormalMapStrength);
|
|
cr->setPOMEnabled(pendingPOM);
|
|
cr->setPOMQuality(pendingPOMQuality);
|
|
}
|
|
}
|
|
saveCallback();
|
|
}
|
|
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
// ============================================================
|
|
// INTERFACE TAB
|
|
// ============================================================
|
|
if (ImGui::BeginTabItem("Interface")) {
|
|
renderSettingsInterfaceTab(saveCallback);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
// ============================================================
|
|
// AUDIO TAB
|
|
// ============================================================
|
|
if (ImGui::BeginTabItem("Audio")) {
|
|
renderSettingsAudioTab(saveCallback);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
// ============================================================
|
|
// GAMEPLAY TAB
|
|
// ============================================================
|
|
if (ImGui::BeginTabItem("Gameplay")) {
|
|
renderSettingsGameplayTab(inventoryScreen, saveCallback);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
// ============================================================
|
|
// CONTROLS TAB
|
|
// ============================================================
|
|
if (ImGui::BeginTabItem("Controls")) {
|
|
renderSettingsControlsTab(saveCallback);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
// ============================================================
|
|
// CHAT TAB
|
|
// ============================================================
|
|
if (ImGui::BeginTabItem("Chat")) {
|
|
chatPanel.renderSettingsTab(saveCallback);
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
// ============================================================
|
|
// ABOUT TAB
|
|
// ============================================================
|
|
if (ImGui::BeginTabItem("About")) {
|
|
renderSettingsAboutTab();
|
|
ImGui::EndTabItem();
|
|
}
|
|
|
|
ImGui::EndTabBar();
|
|
}
|
|
|
|
ImGui::Spacing();
|
|
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f, 10.0f));
|
|
if (ImGui::Button("Back to Game", ImVec2(-1, 0))) {
|
|
showSettingsWindow = false;
|
|
}
|
|
ImGui::PopStyleVar();
|
|
}
|
|
ImGui::End();
|
|
}
|
|
|
|
void SettingsPanel::applyGraphicsPreset(GraphicsPreset preset) {
|
|
auto* renderer = core::Application::getInstance().getRenderer();
|
|
|
|
// Define preset values based on quality level
|
|
switch (preset) {
|
|
case GraphicsPreset::LOW: {
|
|
pendingShadows = false;
|
|
pendingShadowDistance = 100.0f;
|
|
pendingAntiAliasing = 0; // Off
|
|
pendingNormalMapping = false;
|
|
pendingPOM = false;
|
|
pendingGroundClutterDensity = 25;
|
|
if (renderer) {
|
|
renderer->setShadowsEnabled(false);
|
|
renderer->setMsaaSamples(VK_SAMPLE_COUNT_1_BIT);
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setNormalMappingEnabled(false);
|
|
wr->setPOMEnabled(false);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setNormalMappingEnabled(false);
|
|
cr->setPOMEnabled(false);
|
|
}
|
|
if (auto* tm = renderer->getTerrainManager()) {
|
|
tm->setGroundClutterDensityScale(0.25f);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case GraphicsPreset::MEDIUM: {
|
|
pendingShadows = true;
|
|
pendingShadowDistance = 200.0f;
|
|
pendingAntiAliasing = 1; // 2x MSAA
|
|
pendingNormalMapping = true;
|
|
pendingNormalMapStrength = 0.6f;
|
|
pendingPOM = true;
|
|
pendingPOMQuality = 0; // Low
|
|
pendingGroundClutterDensity = 60;
|
|
if (renderer) {
|
|
renderer->setShadowsEnabled(true);
|
|
renderer->setShadowDistance(200.0f);
|
|
renderer->setMsaaSamples(VK_SAMPLE_COUNT_2_BIT);
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setNormalMappingEnabled(true);
|
|
wr->setNormalMapStrength(0.6f);
|
|
wr->setPOMEnabled(true);
|
|
wr->setPOMQuality(0);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setNormalMappingEnabled(true);
|
|
cr->setNormalMapStrength(0.6f);
|
|
cr->setPOMEnabled(true);
|
|
cr->setPOMQuality(0);
|
|
}
|
|
if (auto* tm = renderer->getTerrainManager()) {
|
|
tm->setGroundClutterDensityScale(0.60f);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case GraphicsPreset::HIGH: {
|
|
pendingShadows = true;
|
|
pendingShadowDistance = 350.0f;
|
|
pendingAntiAliasing = 2; // 4x MSAA
|
|
pendingNormalMapping = true;
|
|
pendingNormalMapStrength = 0.8f;
|
|
pendingPOM = true;
|
|
pendingPOMQuality = 1; // Medium
|
|
pendingGroundClutterDensity = 100;
|
|
if (renderer) {
|
|
renderer->setShadowsEnabled(true);
|
|
renderer->setShadowDistance(350.0f);
|
|
renderer->setMsaaSamples(VK_SAMPLE_COUNT_4_BIT);
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setNormalMappingEnabled(true);
|
|
wr->setNormalMapStrength(0.8f);
|
|
wr->setPOMEnabled(true);
|
|
wr->setPOMQuality(1);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setNormalMappingEnabled(true);
|
|
cr->setNormalMapStrength(0.8f);
|
|
cr->setPOMEnabled(true);
|
|
cr->setPOMQuality(1);
|
|
}
|
|
if (auto* tm = renderer->getTerrainManager()) {
|
|
tm->setGroundClutterDensityScale(1.0f);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case GraphicsPreset::ULTRA: {
|
|
pendingShadows = true;
|
|
pendingShadowDistance = 500.0f;
|
|
pendingAntiAliasing = 3; // 8x MSAA
|
|
pendingFXAA = true; // FXAA on top of MSAA for maximum smoothness
|
|
pendingNormalMapping = true;
|
|
pendingNormalMapStrength = 1.2f;
|
|
pendingPOM = true;
|
|
pendingPOMQuality = 2; // High
|
|
pendingGroundClutterDensity = 150;
|
|
if (renderer) {
|
|
renderer->setShadowsEnabled(true);
|
|
renderer->setShadowDistance(500.0f);
|
|
renderer->setMsaaSamples(VK_SAMPLE_COUNT_8_BIT);
|
|
renderer->setFXAAEnabled(true);
|
|
if (auto* wr = renderer->getWMORenderer()) {
|
|
wr->setNormalMappingEnabled(true);
|
|
wr->setNormalMapStrength(1.2f);
|
|
wr->setPOMEnabled(true);
|
|
wr->setPOMQuality(2);
|
|
}
|
|
if (auto* cr = renderer->getCharacterRenderer()) {
|
|
cr->setNormalMappingEnabled(true);
|
|
cr->setNormalMapStrength(1.2f);
|
|
cr->setPOMEnabled(true);
|
|
cr->setPOMQuality(2);
|
|
}
|
|
if (auto* tm = renderer->getTerrainManager()) {
|
|
tm->setGroundClutterDensityScale(1.5f);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
currentGraphicsPreset = preset;
|
|
pendingGraphicsPreset = preset;
|
|
}
|
|
|
|
void SettingsPanel::updateGraphicsPresetFromCurrentSettings() {
|
|
// Check if current settings match any preset, otherwise mark as CUSTOM
|
|
// This is a simplified check; could be enhanced with more detailed matching
|
|
|
|
auto matchesPreset = [this](GraphicsPreset preset) -> bool {
|
|
switch (preset) {
|
|
case GraphicsPreset::LOW:
|
|
return !pendingShadows && pendingAntiAliasing == 0 && !pendingNormalMapping && !pendingPOM &&
|
|
pendingGroundClutterDensity <= 30;
|
|
case GraphicsPreset::MEDIUM:
|
|
return pendingShadows && pendingShadowDistance >= 180 && pendingShadowDistance <= 220 &&
|
|
pendingAntiAliasing == 1 && pendingNormalMapping && pendingPOM &&
|
|
pendingGroundClutterDensity >= 50 && pendingGroundClutterDensity <= 70;
|
|
case GraphicsPreset::HIGH:
|
|
return pendingShadows && pendingShadowDistance >= 330 && pendingShadowDistance <= 370 &&
|
|
pendingAntiAliasing == 2 && pendingNormalMapping && pendingPOM &&
|
|
pendingGroundClutterDensity >= 90 && pendingGroundClutterDensity <= 110;
|
|
case GraphicsPreset::ULTRA:
|
|
return pendingShadows && pendingShadowDistance >= 480 && pendingAntiAliasing == 3 &&
|
|
pendingFXAA && pendingNormalMapping && pendingPOM && pendingGroundClutterDensity >= 140;
|
|
default:
|
|
return false;
|
|
}
|
|
};
|
|
|
|
// Try to match a preset, otherwise mark as custom
|
|
if (matchesPreset(GraphicsPreset::LOW)) {
|
|
pendingGraphicsPreset = GraphicsPreset::LOW;
|
|
} else if (matchesPreset(GraphicsPreset::MEDIUM)) {
|
|
pendingGraphicsPreset = GraphicsPreset::MEDIUM;
|
|
} else if (matchesPreset(GraphicsPreset::HIGH)) {
|
|
pendingGraphicsPreset = GraphicsPreset::HIGH;
|
|
} else if (matchesPreset(GraphicsPreset::ULTRA)) {
|
|
pendingGraphicsPreset = GraphicsPreset::ULTRA;
|
|
} else {
|
|
pendingGraphicsPreset = GraphicsPreset::CUSTOM;
|
|
}
|
|
}
|
|
|
|
std::string SettingsPanel::getSettingsPath() {
|
|
std::string dir;
|
|
#ifdef _WIN32
|
|
const char* appdata = std::getenv("APPDATA");
|
|
dir = appdata ? std::string(appdata) + "\\wowee" : ".";
|
|
#else
|
|
const char* home = std::getenv("HOME");
|
|
dir = home ? std::string(home) + "/.wowee" : ".";
|
|
#endif
|
|
return dir + "/settings.cfg";
|
|
}
|
|
|
|
void SettingsPanel::applyAudioVolumes(rendering::Renderer* renderer) {
|
|
if (!renderer) return;
|
|
float masterScale = soundMuted_ ? 0.0f : static_cast<float>(pendingMasterVolume) / 100.0f;
|
|
audio::AudioEngine::instance().setMasterVolume(masterScale);
|
|
if (auto* music = renderer->getMusicManager())
|
|
music->setVolume(pendingMusicVolume);
|
|
if (auto* ambient = renderer->getAmbientSoundManager())
|
|
ambient->setVolumeScale(pendingAmbientVolume / 100.0f);
|
|
if (auto* ui = renderer->getUiSoundManager())
|
|
ui->setVolumeScale(pendingUiVolume / 100.0f);
|
|
if (auto* combat = renderer->getCombatSoundManager())
|
|
combat->setVolumeScale(pendingCombatVolume / 100.0f);
|
|
if (auto* spell = renderer->getSpellSoundManager())
|
|
spell->setVolumeScale(pendingSpellVolume / 100.0f);
|
|
if (auto* movement = renderer->getMovementSoundManager())
|
|
movement->setVolumeScale(pendingMovementVolume / 100.0f);
|
|
if (auto* footstep = renderer->getFootstepManager())
|
|
footstep->setVolumeScale(pendingFootstepVolume / 100.0f);
|
|
if (auto* npcVoice = renderer->getNpcVoiceManager())
|
|
npcVoice->setVolumeScale(pendingNpcVoiceVolume / 100.0f);
|
|
if (auto* mount = renderer->getMountSoundManager())
|
|
mount->setVolumeScale(pendingMountVolume / 100.0f);
|
|
if (auto* activity = renderer->getActivitySoundManager())
|
|
activity->setVolumeScale(pendingActivityVolume / 100.0f);
|
|
}
|
|
|
|
|
|
} // namespace ui
|
|
} // namespace wowee
|