From ed47e585e0aba0aa3c436ee555c19a0eeda606e3 Mon Sep 17 00:00:00 2001 From: JonBooth78 Date: Tue, 31 Oct 2023 20:22:16 +1300 Subject: [PATCH] Create a game settings tab in the settings view and add a difficulty slider to it Difficulty is stored 2 ways, once for the Engine as a config value that will apply to any new game you start. A game itself (so a savefile) will persist it's own difficulty and if it's not available on load, will use the engine configured value. Also, separate out settings persistance from closing the window, so now if you open the settings window, change the settings and then save the game they are persisted in the saved game state. --- data/lang/ui-core/en.json | 8 ++++++ data/meta/CoreObject/Game.meta.lua | 3 +- data/meta/Engine.lua | 11 ++++++- data/pigui/modules/settings-window.lua | 40 ++++++++++++++++++++------ src/Game.cpp | 26 +++++++++++++++++ src/Game.h | 10 +++++++ src/lua/LuaEngine.cpp | 40 ++++++++++++++++++++++++++ src/lua/LuaGame.cpp | 10 +++++++ 8 files changed, 137 insertions(+), 11 deletions(-) diff --git a/data/lang/ui-core/en.json b/data/lang/ui-core/en.json index 237131a84d4..57a4477bac9 100644 --- a/data/lang/ui-core/en.json +++ b/data/lang/ui-core/en.json @@ -355,6 +355,10 @@ "description": "tooltip description in settings menu", "message": "Lower gives better performance, at cost of visual aesthetics" }, + "DIFFICULTY": { + "description": "Label next to the slider that allows the player to set the difficulty of the game", + "message": "Difficulty" + }, "DISABLE_SCREENSHOT_INFO": { "description": "", "message": "Disable Screenshot Info" @@ -658,6 +662,10 @@ "GAME_TIME": { "description": "", "message": "Game time" + }, + "GAMEPLAY": { + "description": "The tooltip for the settings/options tab for changing gameplay related options", + "message": "Gameplay" }, "GENERAL_CREW": { "description": "", diff --git a/data/meta/CoreObject/Game.meta.lua b/data/meta/CoreObject/Game.meta.lua index 64bba7b51dd..817e79275a6 100644 --- a/data/meta/CoreObject/Game.meta.lua +++ b/data/meta/CoreObject/Game.meta.lua @@ -14,8 +14,7 @@ ---@field sectorView unknown #TODO: add type info for SectorView interface ---@field time number Game time in seconds since Jan. 1 3200 ---@field paused boolean - ----@class Game +---@field difficulty number The current game difficulty (between 0 and 1, easy to hard). See also Engine.SetDifficulty local Game = {} -- Ensure the CoreImport field is visible to static analysis diff --git a/data/meta/Engine.lua b/data/meta/Engine.lua index 5a93383e3de..250d7400ab2 100644 --- a/data/meta/Engine.lua +++ b/data/meta/Engine.lua @@ -18,6 +18,15 @@ ---@class Engine local Engine = {} --- TODO: add information about Engine methods +-- TODO: add information about all Engine methods + +--- Get the current difficulty - either that for the current loaded game or the default for any new game if there is no game loaded +--- see also Game.difficulty +---@return integer Difficulty from 0 to 100, easy to hard +function Engine:GetDifficulty() end + +--- Set the difficulty for the current game (if loaded) and the default for any new games created +---@param percent integer Difficulty from 1 to 100 (clamped) +function Engine:SetDifficulty( percent ) end return Engine diff --git a/data/pigui/modules/settings-window.lua b/data/pigui/modules/settings-window.lua index 6a799fff22c..758b7d17efd 100644 --- a/data/pigui/modules/settings-window.lua +++ b/data/pigui/modules/settings-window.lua @@ -37,6 +37,8 @@ local needBackgroundStarRefresh = false local starDensity = Engine.GetAmountStars() * 100 local starFieldStarSizeFactor = Engine.GetStarFieldStarSizeFactor() * 100 +local difficulty = Engine.GetDifficulty() + local function combo(label, selected, items, tooltip) local color = colors.buttonBlue local changed, ret = 0, nil @@ -170,7 +172,6 @@ local function showVideoOptions() local displaySpeedLines = Engine.GetDisplaySpeedLines() local displayHudTrails = Engine.GetDisplayHudTrails() local enableCockpit = Engine.GetCockpitEnabled() - local enableAutoSave = Engine.GetAutosaveEnabled() local c ui.text(lui.VIDEO_CONFIGURATION_RESTART_GAME_TO_APPLY) @@ -253,11 +254,6 @@ local function showVideoOptions() Engine.SetCockpitEnabled(enableCockpit) end - c,enableAutoSave = checkbox(lui.ENABLE_AUTOSAVE, enableAutoSave, lui.ENABLE_AUTOSAVE_DESC) - if c then - Engine.SetAutosaveEnabled(enableAutoSave) - end - c,starDensity = slider(lui.STAR_FIELD_DENSITY, starDensity, 0, 100) if c then needBackgroundStarRefresh = true @@ -637,11 +633,25 @@ local function showControlsOptions() end end +local function showGameplayOptions() + local enableAutoSave = Engine.GetAutosaveEnabled() + + local c + + c,enableAutoSave = checkbox(lui.ENABLE_AUTOSAVE, enableAutoSave, lui.ENABLE_AUTOSAVE_DESC) + if c then + Engine.SetAutosaveEnabled(enableAutoSave) + end + + c,difficulty = slider(lui.DIFFICULTY, difficulty, 0, 100) +end + local optionsTabs = { ["video"]=showVideoOptions, ["sound"]=showSoundOptions, ["language"]=showLanguageOptions, - ["controls"]=showControlsOptions + ["controls"]=showControlsOptions, + ["gameplay"]=showGameplayOptions, } ui.optionsWindow = ModalWindow.New("Options", function() @@ -660,6 +670,11 @@ ui.optionsWindow = ModalWindow.New("Options", function() mainButton(icons.controls, lui.CONTROLS, showTab=='controls', function() showTab = 'controls' end) + ui.sameLine() + -- TODO: localize + mainButton(icons.star, lui.GAMEPLAY, showTab=='gameplay', function() + showTab = 'gameplay' + end) ui.separator() @@ -690,6 +705,7 @@ ui.optionsWindow = ModalWindow.New("Options", function() if Game.player then ui.sameLine() optionTextButton(lui.SAVE, nil, Game.player.flightState ~= 'HYPERSPACE', function() + ui.optionsWindow:persist() ui.saveLoadWindow.mode = "SAVE" ui.saveLoadWindow:open() end) @@ -716,17 +732,25 @@ end function ui.optionsWindow:open() ModalWindow.open(self) + difficulty = Engine.GetDifficulty() if Game.player then Input.EnableBindings(false) Event.Queue("onPauseMenuOpen") end end +function ui.optionsWindow:persist() + Engine.SetDifficulty( difficulty ) + if Game.player then + Game.SetTimeAcceleration("1x") + end +end + function ui.optionsWindow:close() + self:persist() if not captureBindingWindow.isOpen then ModalWindow.close(self) if Game.player then - Game.SetTimeAcceleration("1x") Event.Queue("onPauseMenuClosed") end end diff --git a/src/Game.cpp b/src/Game.cpp index b70abf1098f..987b1c362ec 100644 --- a/src/Game.cpp +++ b/src/Game.cpp @@ -4,6 +4,7 @@ #include "buildopts.h" #include "Game.h" +#include "GameConfig.h" #include "Body.h" #include "DeathView.h" @@ -48,6 +49,10 @@ Game::Game(const SystemPath &path, const double startDateTime, const char *shipT m_forceTimeAccel(false) { PROFILE_SCOPED() + + int difficulty = Pi::config->Int("Difficulty", 50); + m_difficulty = difficulty / 100.0; + // Now that we have a Galaxy, check the starting location if (!path.IsBodyPath()) throw InvalidGameStartLocation("SystemPath is not a body path"); @@ -171,6 +176,17 @@ Game::Game(const Json &jsonObj) : m_hyperspaceDuration = jsonObj["hyperspace_duration"]; m_hyperspaceEndTime = jsonObj["hyperspace_end_time"]; +// Our version is too old for this (at least on windows)... +// if (jsonObj.contains("difficulty")) + if (jsonObj.cend() != jsonObj.find("difficulty")) + { + m_difficulty = jsonObj["difficulty"]; + } else + { + int difficulty = Pi::config->Int("Difficulty", 50); + m_difficulty = difficulty / 100.0; + } + // space, all the bodies and things m_space.reset(new Space(this, m_galaxy, jsonObj, m_time)); @@ -228,6 +244,8 @@ void Game::ToJson(Json &jsonObj) jsonObj["hyperspace_duration"] = m_hyperspaceDuration; jsonObj["hyperspace_end_time"] = m_hyperspaceEndTime; + jsonObj["difficulty"] = m_difficulty; + // Delete camera frame from frame structure: bool have_cam_frame = m_gameViews->m_worldView->GetCameraContext()->GetTempFrame().valid(); if (have_cam_frame) m_gameViews->m_worldView->EndCameraFrame(); @@ -759,6 +777,14 @@ ObjectViewerView *Game::GetObjectViewerView() const } #endif +void Game::SetDifficulty(double difficulty) +{ + difficulty = std::max(difficulty, 0.0); + difficulty = std::min(difficulty, 1.0); + m_difficulty = difficulty; +} + + Game::Views::Views() : m_sectorView(nullptr), m_systemView(nullptr), diff --git a/src/Game.h b/src/Game.h index 4548ff256c6..165c7add736 100644 --- a/src/Game.h +++ b/src/Game.h @@ -125,6 +125,12 @@ class Game { GameLog *log; + // for debug purposes only + unsigned int GetFrameNumber() const { return m_frameNumber; } + + double GetDifficulty() const { return m_difficulty; } + void SetDifficulty(double difficulty); + private: class Views { public: @@ -182,6 +188,10 @@ class Game { bool m_forceTimeAccel; static const float s_timeAccelRates[]; static const float s_timeInvAccelRates[]; + + double m_difficulty; + + unsigned int m_frameNumber = 0; }; #endif diff --git a/src/lua/LuaEngine.cpp b/src/lua/LuaEngine.cpp index f905f4193c0..9211a35666c 100644 --- a/src/lua/LuaEngine.cpp +++ b/src/lua/LuaEngine.cpp @@ -1035,6 +1035,43 @@ static int l_browse_user_folders(lua_State *l) return 0; } + +static int l_engine_set_difficulty(lua_State* l) +{ + int difficulty = LuaPull(l, 1); + difficulty = std::max(0, difficulty); + difficulty = std::min(100, difficulty); + if (Pi::game) + { + Pi::game->SetDifficulty(difficulty/100.0); + } + int old_difficulty = Pi::config->Int("Difficulty", 50); + if (old_difficulty != difficulty) + { + Pi::config->SetInt("Difficulty", difficulty); + Pi::config->Save(); + } + + return 0; +} + +// Return the difficulty of the current game, if there is one, else the saved, configured difficulty +static int l_engine_get_difficulty(lua_State* l) +{ + int difficulty; + if (Pi::game) + { + // add 0.5 to ensure rounding + difficulty = (int)(Pi::game->GetDifficulty()*100.0 + 0.5); + } + else + { + difficulty = Pi::config->Int("Difficulty", 50); + } + LuaPush(l, difficulty); + return 1; +} + void LuaEngine::Register() { lua_State *l = Lua::manager->GetLuaState(); @@ -1126,6 +1163,9 @@ void LuaEngine::Register() { "GetEnumValue", l_engine_get_enum_value }, { "RequestProfileFrame", l_engine_request_profile_frame }, + { "SetDifficulty", l_engine_set_difficulty }, + { "GetDifficulty", l_engine_get_difficulty }, + { 0, 0 } }; diff --git a/src/lua/LuaGame.cpp b/src/lua/LuaGame.cpp index 76af7dc5f56..dbcc79a7fa6 100644 --- a/src/lua/LuaGame.cpp +++ b/src/lua/LuaGame.cpp @@ -420,6 +420,15 @@ static int l_game_attr_paused(lua_State *l) return 1; } +static int l_game_attr_difficulty(lua_State* l) +{ + if (Pi::game) + lua_pushnumber(l, Pi::game->GetDifficulty()); + else + lua_pushnumber(l, 0.5); + return 1; +} + /* * Function: InHyperspace * @@ -678,6 +687,7 @@ void LuaGame::Register() { "sectorView", l_game_attr_sectorview }, { "time", l_game_attr_time }, { "paused", l_game_attr_paused }, + { "difficulty", l_game_attr_difficulty }, { 0, 0 } };