mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-25 08:30:13 +00:00
Persist single-player settings and add defaults
This commit is contained in:
parent
46939808ad
commit
697c4b8218
6 changed files with 205 additions and 2 deletions
|
|
@ -173,6 +173,19 @@ public:
|
|||
|
||||
// Single-player: mark character list ready for selection UI
|
||||
void setSinglePlayerCharListReady();
|
||||
struct SinglePlayerSettings {
|
||||
bool fullscreen = false;
|
||||
bool vsync = true;
|
||||
bool shadows = true;
|
||||
int resWidth = 1920;
|
||||
int resHeight = 1080;
|
||||
int musicVolume = 30;
|
||||
int sfxVolume = 100;
|
||||
float mouseSensitivity = 0.2f;
|
||||
bool invertMouse = false;
|
||||
};
|
||||
bool getSinglePlayerSettings(SinglePlayerSettings& out) const;
|
||||
void setSinglePlayerSettings(const SinglePlayerSettings& settings);
|
||||
|
||||
// Inventory
|
||||
Inventory& getInventory() { return inventory; }
|
||||
|
|
@ -600,6 +613,7 @@ private:
|
|||
SP_DIRTY_XP = 1 << 7,
|
||||
SP_DIRTY_POSITION = 1 << 8,
|
||||
SP_DIRTY_STATS = 1 << 9,
|
||||
SP_DIRTY_SETTINGS = 1 << 10,
|
||||
SP_DIRTY_ALL = 0xFFFFFFFFu
|
||||
};
|
||||
void markSinglePlayerDirty(uint32_t flags, bool highPriority);
|
||||
|
|
@ -616,6 +630,8 @@ private:
|
|||
float spLastDirtyOrientation_ = 0.0f;
|
||||
std::unordered_map<uint64_t, bool> spHasState_;
|
||||
std::unordered_map<uint64_t, float> spSavedOrientation_;
|
||||
SinglePlayerSettings spSettings_{};
|
||||
bool spSettingsLoaded_ = false;
|
||||
};
|
||||
|
||||
} // namespace game
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ public:
|
|||
|
||||
void setMovementSpeed(float speed) { movementSpeed = speed; }
|
||||
void setMouseSensitivity(float sensitivity) { mouseSensitivity = sensitivity; }
|
||||
float getMouseSensitivity() const { return mouseSensitivity; }
|
||||
void setInvertMouse(bool invert) { invertMouse = invert; }
|
||||
bool isInvertMouse() const { return invertMouse; }
|
||||
void setEnabled(bool enabled) { this->enabled = enabled; }
|
||||
void setTerrainManager(TerrainManager* tm) { terrainManager = tm; }
|
||||
void setWMORenderer(WMORenderer* wmo) { wmoRenderer = wmo; }
|
||||
|
|
@ -90,6 +93,7 @@ private:
|
|||
|
||||
// Mouse settings
|
||||
float mouseSensitivity = 0.2f;
|
||||
bool invertMouse = false;
|
||||
bool mouseButtonDown = false;
|
||||
bool leftMouseDown = false;
|
||||
bool rightMouseDown = false;
|
||||
|
|
|
|||
|
|
@ -1151,6 +1151,32 @@ void Application::startSinglePlayer() {
|
|||
// Load weapon models for equipped items (after inventory is populated)
|
||||
loadEquippedWeapons();
|
||||
|
||||
if (gameHandler && renderer && window) {
|
||||
game::GameHandler::SinglePlayerSettings settings;
|
||||
if (gameHandler->getSinglePlayerSettings(settings)) {
|
||||
window->setVsync(settings.vsync);
|
||||
window->setFullscreen(settings.fullscreen);
|
||||
if (settings.resWidth > 0 && settings.resHeight > 0) {
|
||||
window->applyResolution(settings.resWidth, settings.resHeight);
|
||||
}
|
||||
renderer->setShadowsEnabled(settings.shadows);
|
||||
if (auto* music = renderer->getMusicManager()) {
|
||||
music->setVolume(settings.musicVolume);
|
||||
}
|
||||
float sfxScale = static_cast<float>(settings.sfxVolume) / 100.0f;
|
||||
if (auto* footstep = renderer->getFootstepManager()) {
|
||||
footstep->setVolumeScale(sfxScale);
|
||||
}
|
||||
if (auto* activity = renderer->getActivitySoundManager()) {
|
||||
activity->setVolumeScale(sfxScale);
|
||||
}
|
||||
if (auto* cameraController = renderer->getCameraController()) {
|
||||
cameraController->setMouseSensitivity(settings.mouseSensitivity);
|
||||
cameraController->setInvertMouse(settings.invertMouse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Loading screen: load terrain and wait for streaming before spawning ---
|
||||
const SpawnPreset* spawnPreset = selectSpawnPreset(std::getenv("WOW_SPAWN"));
|
||||
// Canonical WoW coords: +X=North, +Y=West, +Z=Up
|
||||
|
|
|
|||
|
|
@ -193,6 +193,18 @@ struct SinglePlayerSqlite {
|
|||
" status INTEGER,"
|
||||
" progress INTEGER,"
|
||||
" PRIMARY KEY (guid, quest)"
|
||||
");"
|
||||
"CREATE TABLE IF NOT EXISTS character_settings ("
|
||||
" guid INTEGER PRIMARY KEY,"
|
||||
" fullscreen INTEGER,"
|
||||
" vsync INTEGER,"
|
||||
" shadows INTEGER,"
|
||||
" res_w INTEGER,"
|
||||
" res_h INTEGER,"
|
||||
" music_volume INTEGER,"
|
||||
" sfx_volume INTEGER,"
|
||||
" mouse_sensitivity REAL,"
|
||||
" invert_mouse INTEGER"
|
||||
");";
|
||||
return exec(kSchema);
|
||||
}
|
||||
|
|
@ -1354,6 +1366,19 @@ void GameHandler::setSinglePlayerCharListReady() {
|
|||
setState(WorldState::CHAR_LIST_RECEIVED);
|
||||
}
|
||||
|
||||
bool GameHandler::getSinglePlayerSettings(SinglePlayerSettings& out) const {
|
||||
if (!singlePlayerMode_ || !spSettingsLoaded_) return false;
|
||||
out = spSettings_;
|
||||
return true;
|
||||
}
|
||||
|
||||
void GameHandler::setSinglePlayerSettings(const SinglePlayerSettings& settings) {
|
||||
if (!singlePlayerMode_) return;
|
||||
spSettings_ = settings;
|
||||
spSettingsLoaded_ = true;
|
||||
markSinglePlayerDirty(SP_DIRTY_SETTINGS, true);
|
||||
}
|
||||
|
||||
bool GameHandler::getSinglePlayerCreateInfo(Race race, Class cls, SinglePlayerCreateInfo& out) const {
|
||||
auto& db = getSinglePlayerCreateDb();
|
||||
uint16_t key = static_cast<uint16_t>((static_cast<uint32_t>(race) << 8) |
|
||||
|
|
@ -1433,6 +1458,8 @@ bool GameHandler::loadSinglePlayerCharacterState(uint64_t guid) {
|
|||
auto& sp = getSinglePlayerSqlite();
|
||||
if (!sp.db) return false;
|
||||
|
||||
spSettingsLoaded_ = false;
|
||||
|
||||
const char* sqlChar =
|
||||
"SELECT level, zone, map, position_x, position_y, position_z, orientation, money, xp, health, max_health, has_state "
|
||||
"FROM characters WHERE guid=?;";
|
||||
|
|
@ -1608,6 +1635,26 @@ bool GameHandler::loadSinglePlayerCharacterState(uint64_t guid) {
|
|||
spLastDirtyZ_ = movementInfo.z;
|
||||
spLastDirtyOrientation_ = movementInfo.orientation;
|
||||
|
||||
const char* sqlSettings =
|
||||
"SELECT fullscreen, vsync, shadows, res_w, res_h, music_volume, sfx_volume, mouse_sensitivity, invert_mouse "
|
||||
"FROM character_settings WHERE guid=?;";
|
||||
if (sqlite3_prepare_v2(sp.db, sqlSettings, -1, &stmt, nullptr) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(stmt, 1, static_cast<sqlite3_int64>(guid));
|
||||
if (sqlite3_step(stmt) == SQLITE_ROW) {
|
||||
spSettings_.fullscreen = sqlite3_column_int(stmt, 0) != 0;
|
||||
spSettings_.vsync = sqlite3_column_int(stmt, 1) != 0;
|
||||
spSettings_.shadows = sqlite3_column_int(stmt, 2) != 0;
|
||||
spSettings_.resWidth = sqlite3_column_int(stmt, 3);
|
||||
spSettings_.resHeight = sqlite3_column_int(stmt, 4);
|
||||
spSettings_.musicVolume = sqlite3_column_int(stmt, 5);
|
||||
spSettings_.sfxVolume = sqlite3_column_int(stmt, 6);
|
||||
spSettings_.mouseSensitivity = static_cast<float>(sqlite3_column_double(stmt, 7));
|
||||
spSettings_.invertMouse = sqlite3_column_int(stmt, 8) != 0;
|
||||
spSettingsLoaded_ = true;
|
||||
}
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -1760,6 +1807,32 @@ void GameHandler::saveSinglePlayerCharacterState(bool force) {
|
|||
spHasState_[activeCharacterGuid_] = true;
|
||||
spSavedOrientation_[activeCharacterGuid_] = movementInfo.orientation;
|
||||
|
||||
if (spSettingsLoaded_ && (force || (spDirtyFlags_ & SP_DIRTY_SETTINGS))) {
|
||||
const char* upsertSettings =
|
||||
"INSERT INTO character_settings "
|
||||
"(guid, fullscreen, vsync, shadows, res_w, res_h, music_volume, sfx_volume, mouse_sensitivity, invert_mouse) "
|
||||
"VALUES (?,?,?,?,?,?,?,?,?,?) "
|
||||
"ON CONFLICT(guid) DO UPDATE SET "
|
||||
"fullscreen=excluded.fullscreen, vsync=excluded.vsync, shadows=excluded.shadows, "
|
||||
"res_w=excluded.res_w, res_h=excluded.res_h, music_volume=excluded.music_volume, "
|
||||
"sfx_volume=excluded.sfx_volume, mouse_sensitivity=excluded.mouse_sensitivity, "
|
||||
"invert_mouse=excluded.invert_mouse;";
|
||||
if (sqlite3_prepare_v2(sp.db, upsertSettings, -1, &stmt, nullptr) == SQLITE_OK) {
|
||||
sqlite3_bind_int64(stmt, 1, static_cast<sqlite3_int64>(activeCharacterGuid_));
|
||||
sqlite3_bind_int(stmt, 2, spSettings_.fullscreen ? 1 : 0);
|
||||
sqlite3_bind_int(stmt, 3, spSettings_.vsync ? 1 : 0);
|
||||
sqlite3_bind_int(stmt, 4, spSettings_.shadows ? 1 : 0);
|
||||
sqlite3_bind_int(stmt, 5, spSettings_.resWidth);
|
||||
sqlite3_bind_int(stmt, 6, spSettings_.resHeight);
|
||||
sqlite3_bind_int(stmt, 7, spSettings_.musicVolume);
|
||||
sqlite3_bind_int(stmt, 8, spSettings_.sfxVolume);
|
||||
sqlite3_bind_double(stmt, 9, spSettings_.mouseSensitivity);
|
||||
sqlite3_bind_int(stmt, 10, spSettings_.invertMouse ? 1 : 0);
|
||||
sqlite3_step(stmt);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
sqlite3_stmt* del = nullptr;
|
||||
const char* delInv = "DELETE FROM character_inventory WHERE guid=?;";
|
||||
if (sqlite3_prepare_v2(sp.db, delInv, -1, &del, nullptr) == SQLITE_OK) {
|
||||
|
|
|
|||
|
|
@ -952,7 +952,8 @@ void CameraController::processMouseMotion(const SDL_MouseMotionEvent& event) {
|
|||
|
||||
// Directly update stored yaw/pitch (no lossy forward-vector derivation)
|
||||
yaw -= event.xrel * mouseSensitivity;
|
||||
pitch += event.yrel * mouseSensitivity;
|
||||
float invert = invertMouse ? -1.0f : 1.0f;
|
||||
pitch += event.yrel * mouseSensitivity * invert;
|
||||
|
||||
// WoW-style pitch limits: can look almost straight down, limited upward
|
||||
pitch = glm::clamp(pitch, MIN_PITCH, MAX_PITCH);
|
||||
|
|
|
|||
|
|
@ -1802,6 +1802,23 @@ void GameScreen::renderSettingsWindow() {
|
|||
{3840, 2160},
|
||||
};
|
||||
static const 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 kDefaultMusicVolume = 30;
|
||||
constexpr int kDefaultSfxVolume = 100;
|
||||
constexpr float kDefaultMouseSensitivity = 0.2f;
|
||||
constexpr bool kDefaultInvertMouse = false;
|
||||
|
||||
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();
|
||||
|
|
@ -1822,6 +1839,10 @@ void GameScreen::renderSettingsWindow() {
|
|||
if (pendingSfxVolume < 0) pendingSfxVolume = 0;
|
||||
if (pendingSfxVolume > 100) pendingSfxVolume = 100;
|
||||
}
|
||||
if (auto* cameraController = renderer->getCameraController()) {
|
||||
pendingMouseSensitivity = cameraController->getMouseSensitivity();
|
||||
pendingInvertMouse = cameraController->isInvertMouse();
|
||||
}
|
||||
}
|
||||
pendingResIndex = 0;
|
||||
int curW = window->getWidth();
|
||||
|
|
@ -1832,13 +1853,34 @@ void GameScreen::renderSettingsWindow() {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (auto* gameHandler = core::Application::getInstance().getGameHandler()) {
|
||||
if (gameHandler->isSinglePlayerMode()) {
|
||||
game::GameHandler::SinglePlayerSettings spSettings;
|
||||
if (gameHandler->getSinglePlayerSettings(spSettings)) {
|
||||
pendingFullscreen = spSettings.fullscreen;
|
||||
pendingVsync = spSettings.vsync;
|
||||
pendingShadows = spSettings.shadows;
|
||||
pendingMusicVolume = spSettings.musicVolume;
|
||||
pendingSfxVolume = spSettings.sfxVolume;
|
||||
pendingMouseSensitivity = spSettings.mouseSensitivity;
|
||||
pendingInvertMouse = spSettings.invertMouse;
|
||||
for (int i = 0; i < kResCount; i++) {
|
||||
if (kResolutions[i][0] == spSettings.resWidth &&
|
||||
kResolutions[i][1] == spSettings.resHeight) {
|
||||
pendingResIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
settingsInit = true;
|
||||
}
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
float screenW = io.DisplaySize.x;
|
||||
float screenH = io.DisplaySize.y;
|
||||
ImVec2 size(380.0f, 320.0f);
|
||||
ImVec2 size(400.0f, 420.0f);
|
||||
ImVec2 pos((screenW - size.x) * 0.5f, (screenH - size.y) * 0.5f);
|
||||
|
||||
ImGui::SetNextWindowPos(pos, ImGuiCond_Always);
|
||||
|
|
@ -1863,6 +1905,12 @@ void GameScreen::renderSettingsWindow() {
|
|||
resItems[i] = resBuf[i];
|
||||
}
|
||||
ImGui::Combo(resLabel, &pendingResIndex, resItems, kResCount);
|
||||
if (ImGui::Button("Restore Video Defaults", ImVec2(-1, 0))) {
|
||||
pendingFullscreen = kDefaultFullscreen;
|
||||
pendingVsync = kDefaultVsync;
|
||||
pendingShadows = kDefaultShadows;
|
||||
pendingResIndex = defaultResIndex;
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
|
@ -1871,6 +1919,22 @@ void GameScreen::renderSettingsWindow() {
|
|||
ImGui::Text("Audio");
|
||||
ImGui::SliderInt("Music Volume", &pendingMusicVolume, 0, 100, "%d");
|
||||
ImGui::SliderInt("SFX Volume", &pendingSfxVolume, 0, 100, "%d");
|
||||
if (ImGui::Button("Restore Audio Defaults", ImVec2(-1, 0))) {
|
||||
pendingMusicVolume = kDefaultMusicVolume;
|
||||
pendingSfxVolume = kDefaultSfxVolume;
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
ImGui::Spacing();
|
||||
|
||||
ImGui::Text("Controls");
|
||||
ImGui::SliderFloat("Mouse Sensitivity", &pendingMouseSensitivity, 0.05f, 1.0f, "%.2f");
|
||||
ImGui::Checkbox("Invert Mouse", &pendingInvertMouse);
|
||||
if (ImGui::Button("Restore Control Defaults", ImVec2(-1, 0))) {
|
||||
pendingMouseSensitivity = kDefaultMouseSensitivity;
|
||||
pendingInvertMouse = kDefaultInvertMouse;
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
ImGui::Separator();
|
||||
|
|
@ -1891,6 +1955,25 @@ void GameScreen::renderSettingsWindow() {
|
|||
if (auto* activity = renderer->getActivitySoundManager()) {
|
||||
activity->setVolumeScale(sfxScale);
|
||||
}
|
||||
if (auto* cameraController = renderer->getCameraController()) {
|
||||
cameraController->setMouseSensitivity(pendingMouseSensitivity);
|
||||
cameraController->setInvertMouse(pendingInvertMouse);
|
||||
}
|
||||
}
|
||||
if (auto* gameHandler = core::Application::getInstance().getGameHandler()) {
|
||||
if (gameHandler->isSinglePlayerMode()) {
|
||||
game::GameHandler::SinglePlayerSettings spSettings;
|
||||
spSettings.fullscreen = pendingFullscreen;
|
||||
spSettings.vsync = pendingVsync;
|
||||
spSettings.shadows = pendingShadows;
|
||||
spSettings.resWidth = kResolutions[pendingResIndex][0];
|
||||
spSettings.resHeight = kResolutions[pendingResIndex][1];
|
||||
spSettings.musicVolume = pendingMusicVolume;
|
||||
spSettings.sfxVolume = pendingSfxVolume;
|
||||
spSettings.mouseSensitivity = pendingMouseSensitivity;
|
||||
spSettings.invertMouse = pendingInvertMouse;
|
||||
gameHandler->setSinglePlayerSettings(spSettings);
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::Spacing();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue