From 900a5179f78eea7c46821894a606098d945a1b45 Mon Sep 17 00:00:00 2001 From: florianessl Date: Mon, 27 Jan 2025 17:17:31 +0100 Subject: [PATCH 1/8] Refactor: Moved many of the commonly patched engine constants into namespace "Player::Constants" --- src/game_actor.cpp | 39 ++--------- src/game_actor.h | 2 +- src/game_battlealgorithm.cpp | 2 +- src/game_enemy.cpp | 24 ++----- src/game_party.cpp | 6 +- src/game_variables.h | 8 +-- src/player.cpp | 122 ++++++++++++++++++++++++++++++----- src/player.h | 27 ++++++++ src/scene_file.cpp | 2 +- 9 files changed, 153 insertions(+), 79 deletions(-) diff --git a/src/game_actor.cpp b/src/game_actor.cpp index 120646cb58..1ef1bba8bd 100644 --- a/src/game_actor.cpp +++ b/src/game_actor.cpp @@ -36,47 +36,24 @@ #include "algo.h" #include "game_message_terms.h" -constexpr int max_level_2k = 50; -constexpr int max_level_2k3 = 99; - int Game_Actor::MaxHpValue() const { - auto& val = lcf::Data::system.easyrpg_max_actor_hp; - if (val == -1) { - return Player::IsRPG2k() ? 999 : 9999; - } - return val; + return Player::Constants::MaxActorHpValue(); } int Game_Actor::MaxSpValue() const { - auto& val = lcf::Data::system.easyrpg_max_actor_sp; - if (val == -1) { - return 999; - } - return val; + return Player::Constants::MaxActorSpValue(); } int Game_Actor::MaxStatBattleValue() const { - auto& val = lcf::Data::system.easyrpg_max_stat_battle_value; - if (val == -1) { - return 9999; - } - return val; + return Player::Constants::MaxStatBattleValue(); } int Game_Actor::MaxStatBaseValue() const { - auto& val = lcf::Data::system.easyrpg_max_stat_base_value; - if (val == -1) { - return 999; - } - return val; + return Player::Constants::MaxStatBaseValue(); } int Game_Actor::MaxExpValue() const { - auto& val = lcf::Data::system.easyrpg_max_exp; - if (val == -1) { - return Player::IsRPG2k() ? 999999 : 9999999; - } - return val; + return Player::Constants::MaxExpValue(); } Game_Actor::Game_Actor(int actor_id) { @@ -745,11 +722,7 @@ int Game_Actor::GetAccessoryId() const { } int Game_Actor::GetMaxLevel() const { - int max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3; - if (lcf::Data::system.easyrpg_max_level > -1) { - max_level = lcf::Data::system.easyrpg_max_level; - } - return Utils::Clamp(max_level, 1, dbActor->final_level); + return Utils::Clamp(Player::Constants::MaxLevel(), 1, dbActor->final_level); } void Game_Actor::SetExp(int _exp) { diff --git a/src/game_actor.h b/src/game_actor.h index 2f15f0812c..cf2631573a 100644 --- a/src/game_actor.h +++ b/src/game_actor.h @@ -425,7 +425,7 @@ class Game_Actor final : public Game_Battler { /** * Sets exp of actor. - * The value is adjusted to the boundary 0 up 999999. + * The value is adjusted to the boundary 0 up to a maximum (dependent on engine type & patch). * Other actor attributes are not altered. Use ChangeExp to do a proper * experience change. * diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index 6770dafad1..6f796e26ae 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -52,7 +52,7 @@ #include "feature.h" static inline int MaxDamageValue() { - return lcf::Data::system.easyrpg_max_damage == -1 ? (Player::IsRPG2k() ? 999 : 9999) : lcf::Data::system.easyrpg_max_damage; + return Player::Constants::MaxDamageValue(); } Game_BattleAlgorithm::AlgorithmBase::AlgorithmBase(Type ty, Game_Battler* source, Game_Battler* target) : diff --git a/src/game_enemy.cpp b/src/game_enemy.cpp index 3da6b4a7fd..99531c405a 100644 --- a/src/game_enemy.cpp +++ b/src/game_enemy.cpp @@ -48,35 +48,19 @@ Game_Enemy::Game_Enemy(const lcf::rpg::TroopMember* member) } int Game_Enemy::MaxHpValue() const { - auto& val = lcf::Data::system.easyrpg_max_enemy_hp; - if (val == -1) { - return Player::IsRPG2k() ? 9999 : 99999; - } - return val; + return Player::Constants::MaxEnemyHpValue(); } int Game_Enemy::MaxSpValue() const { - auto& val = lcf::Data::system.easyrpg_max_enemy_sp; - if (val == -1) { - return 9999; - } - return val; + return Player::Constants::MaxEnemySpValue(); } int Game_Enemy::MaxStatBattleValue() const { - auto& val = lcf::Data::system.easyrpg_max_stat_battle_value; - if (val == -1) { - return 9999; - } - return val; + return Player::Constants::MaxStatBattleValue(); } int Game_Enemy::MaxStatBaseValue() const { - auto& val = lcf::Data::system.easyrpg_max_stat_base_value; - if (val == -1) { - return 999; - } - return val; + return Player::Constants::MaxStatBaseValue(); } int Game_Enemy::GetStateProbability(int state_id) const { diff --git a/src/game_party.cpp b/src/game_party.cpp index 745a3916ac..cd09e866a4 100644 --- a/src/game_party.cpp +++ b/src/game_party.cpp @@ -161,7 +161,7 @@ int Game_Party::GetItemTotalCount(int item_id) const { int Game_Party::GetMaxItemCount(int item_id) const { const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id); if (!item || item->easyrpg_max_count == -1) { - return (lcf::Data::system.easyrpg_max_item_count == -1 ? 99 : lcf::Data::system.easyrpg_max_item_count); + return Player::Constants::MaxItemCount(); } else { return item->easyrpg_max_count; } @@ -169,12 +169,12 @@ int Game_Party::GetMaxItemCount(int item_id) const { void Game_Party::GainGold(int n) { data.gold = data.gold + n; - data.gold = std::min(std::max(data.gold, 0), 999999); + data.gold = std::min(std::max(data.gold, 0), Player::Constants::MaxGoldValue()); } void Game_Party::LoseGold(int n) { data.gold = data.gold - n; - data.gold = std::min(std::max(data.gold, 0), 999999); + data.gold = std::min(std::max(data.gold, 0), Player::Constants::MaxGoldValue()); } void Game_Party::AddItem(int item_id, int amount) { diff --git a/src/game_variables.h b/src/game_variables.h index 1da0393a9d..bd4d5d5211 100644 --- a/src/game_variables.h +++ b/src/game_variables.h @@ -34,10 +34,10 @@ class Game_Variables { using Variables_t = std::vector; static constexpr int max_warnings = 10; - static constexpr Var_t min_2k = -999999; - static constexpr Var_t max_2k = 999999; - static constexpr Var_t min_2k3 = -9999999; - static constexpr Var_t max_2k3 = 9999999; + static constexpr Var_t min_2k = -999'999; + static constexpr Var_t max_2k = 999'999; + static constexpr Var_t min_2k3 = -9'999'999; + static constexpr Var_t max_2k3 = 9'999'999; Game_Variables(Var_t minval, Var_t maxval); diff --git a/src/player.cpp b/src/player.cpp index bb4799c4d7..6e03ea3b14 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -927,22 +927,9 @@ void Player::ResetGameObjects() { Main_Data::game_switches = std::make_unique(); Main_Data::game_switches->SetLowerLimit(lcf::Data::switches.size()); - auto min_var = lcf::Data::system.easyrpg_variable_min_value; - if (min_var == 0) { - if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { - min_var = Player::IsRPG2k3() ? Game_Variables::min_2k3 : Game_Variables::min_2k; - } else { - min_var = std::numeric_limits::min(); - } - } - auto max_var = lcf::Data::system.easyrpg_variable_max_value; - if (max_var == 0) { - if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { - max_var = Player::IsRPG2k3() ? Game_Variables::max_2k3 : Game_Variables::max_2k; - } else { - max_var = std::numeric_limits::max(); - } - } + int32_t min_var, max_var; + Player::Constants::GetVariableLimits(min_var, max_var); + Main_Data::game_variables = std::make_unique(min_var, max_var); Main_Data::game_variables->SetLowerLimit(lcf::Data::variables.size()); @@ -1638,3 +1625,106 @@ std::string Player::GetEngineVersion() { if (EngineVersion() > 0) return std::to_string(EngineVersion()); return std::string(); } + +void Player::Constants::GetVariableLimits(Var_t& min_var, Var_t& max_var) { + min_var = lcf::Data::system.easyrpg_variable_min_value; + if (min_var == 0) { + if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { + min_var = Player::IsRPG2k3() ? Game_Variables::min_2k3 : Game_Variables::min_2k; + } else { + min_var = std::numeric_limits::min(); + } + } + max_var = lcf::Data::system.easyrpg_variable_max_value; + if (max_var == 0) { + if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { + max_var = Player::IsRPG2k3() ? Game_Variables::max_2k3 : Game_Variables::max_2k; + } else { + max_var = std::numeric_limits::max(); + } + } +} + +int32_t Player::Constants::MaxActorHpValue() { + auto val = lcf::Data::system.easyrpg_max_actor_hp; + if (val == -1) { + return Player::IsRPG2k() ? 999 : 9'999; + } + return val; +} + +int32_t Player::Constants::MaxActorSpValue() { + auto val = lcf::Data::system.easyrpg_max_actor_sp; + if (val == -1) { + return 999; + } + return val; +} + +int32_t Player::Constants::MaxEnemyHpValue() { + auto val = lcf::Data::system.easyrpg_max_enemy_hp; + if (val == -1) { + return Player::IsRPG2k() ? 9'999 : 99'999; + } + return val; +} + +int32_t Player::Constants::MaxEnemySpValue() { + auto val = lcf::Data::system.easyrpg_max_enemy_sp; + if (val == -1) { + return 9'999; + } + return val; +} + +int32_t Player::Constants::MaxStatBaseValue() { + auto val = lcf::Data::system.easyrpg_max_stat_base_value; + if (val == -1) { + return 999; + } + return val; +} + +int32_t Player::Constants::MaxStatBattleValue() { + auto val = lcf::Data::system.easyrpg_max_stat_battle_value; + if (val == -1) { + return 9'999; + } + return val; +} + +int32_t Player::Constants::MaxDamageValue() { + auto val = lcf::Data::system.easyrpg_max_damage; + if (val == -1) { + return (Player::IsRPG2k() ? 999 : 9'999); + } + return val; +} + +int32_t Player::Constants::MaxExpValue() { + auto val = lcf::Data::system.easyrpg_max_exp; + if (val == -1) { + return Player::IsRPG2k() ? 999'999 : 9'999'999; + } + return val; +} + +int32_t Player::Constants::MaxLevel() { + int max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3; + if (lcf::Data::system.easyrpg_max_level > -1) { + max_level = lcf::Data::system.easyrpg_max_level; + } + return max_level; +} + +int32_t Player::Constants::MaxGoldValue() { + return 999'999; +} + +int32_t Player::Constants::MaxItemCount() { + return (lcf::Data::system.easyrpg_max_item_count == -1 ? 99 : lcf::Data::system.easyrpg_max_item_count); +} + +int32_t Player::Constants::MaxSaveFiles() { + return Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); +} diff --git a/src/player.h b/src/player.h index ece1b465e1..57bafe41ca 100644 --- a/src/player.h +++ b/src/player.h @@ -26,6 +26,7 @@ #include "game_config.h" #include "game_config_game.h" #include "game_interpreter_shared.h" +#include "game_variables.h" #include #include #include @@ -443,6 +444,32 @@ namespace Player { std::optional GetRuntimeFlag(Game_Interpreter_Shared::StateRuntimeFlagRef field_on, Game_Interpreter_Shared::StateRuntimeFlagRef field_off); #endif + + namespace Constants { + using Var_t = int32_t; + + static constexpr int32_t max_level_2k = 50; + static constexpr int32_t max_level_2k3 = 99; + + void GetVariableLimits(Var_t& min_var, Var_t& max_var); + + int32_t MaxActorHpValue(); + int32_t MaxActorSpValue(); + + int32_t MaxEnemyHpValue(); + int32_t MaxEnemySpValue(); + + int32_t MaxStatBaseValue(); + int32_t MaxStatBattleValue(); + int32_t MaxDamageValue(); + + int32_t MaxExpValue(); + int32_t MaxLevel(); + + int32_t MaxGoldValue(); + int32_t MaxItemCount(); + int32_t MaxSaveFiles(); + } } inline bool Player::IsRPG2k() { diff --git a/src/scene_file.cpp b/src/scene_file.cpp index 4e23f64c9c..c233a16135 100644 --- a/src/scene_file.cpp +++ b/src/scene_file.cpp @@ -123,7 +123,7 @@ void Scene_File::Start() { // Refresh File Finder Save Folder fs = FileFinder::Save(); - for (int i = 0; i < Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); i++) { + for (int i = 0; i < Player::Constants::MaxSaveFiles(); i++) { std::shared_ptr w(new Window_SaveFile(Player::menu_offset_x, 40 + i * 64, MENU_WIDTH, 64)); w->SetIndex(i); From 688058d88c8ad198c917905ce0dc31def75b6d48 Mon Sep 17 00:00:00 2001 From: florianessl Date: Thu, 30 Jan 2025 23:56:13 +0100 Subject: [PATCH 2/8] Implemented the ability to override game constants during runtime startup --- src/player.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++++--- src/player.h | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index 6e03ea3b14..50882fab3d 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1626,8 +1626,13 @@ std::string Player::GetEngineVersion() { return std::string(); } +namespace Player::Constants { + std::map constant_overrides; +} + void Player::Constants::GetVariableLimits(Var_t& min_var, Var_t& max_var) { min_var = lcf::Data::system.easyrpg_variable_min_value; + TryGetOverriddenConstant(GameConstantType::MinVarLimit, min_var); if (min_var == 0) { if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { min_var = Player::IsRPG2k3() ? Game_Variables::min_2k3 : Game_Variables::min_2k; @@ -1636,6 +1641,7 @@ void Player::Constants::GetVariableLimits(Var_t& min_var, Var_t& max_var) { } } max_var = lcf::Data::system.easyrpg_variable_max_value; + TryGetOverriddenConstant(GameConstantType::MaxVarLimit, max_var); if (max_var == 0) { if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { max_var = Player::IsRPG2k3() ? Game_Variables::max_2k3 : Game_Variables::max_2k; @@ -1647,6 +1653,7 @@ void Player::Constants::GetVariableLimits(Var_t& min_var, Var_t& max_var) { int32_t Player::Constants::MaxActorHpValue() { auto val = lcf::Data::system.easyrpg_max_actor_hp; + TryGetOverriddenConstant(GameConstantType::MaxActorHP, val); if (val == -1) { return Player::IsRPG2k() ? 999 : 9'999; } @@ -1655,6 +1662,7 @@ int32_t Player::Constants::MaxActorHpValue() { int32_t Player::Constants::MaxActorSpValue() { auto val = lcf::Data::system.easyrpg_max_actor_sp; + TryGetOverriddenConstant(GameConstantType::MaxActorSP, val); if (val == -1) { return 999; } @@ -1663,6 +1671,7 @@ int32_t Player::Constants::MaxActorSpValue() { int32_t Player::Constants::MaxEnemyHpValue() { auto val = lcf::Data::system.easyrpg_max_enemy_hp; + TryGetOverriddenConstant(GameConstantType::MaxEnemyHP, val); if (val == -1) { return Player::IsRPG2k() ? 9'999 : 99'999; } @@ -1671,6 +1680,7 @@ int32_t Player::Constants::MaxEnemyHpValue() { int32_t Player::Constants::MaxEnemySpValue() { auto val = lcf::Data::system.easyrpg_max_enemy_sp; + TryGetOverriddenConstant(GameConstantType::MaxEnemySP, val); if (val == -1) { return 9'999; } @@ -1679,6 +1689,7 @@ int32_t Player::Constants::MaxEnemySpValue() { int32_t Player::Constants::MaxStatBaseValue() { auto val = lcf::Data::system.easyrpg_max_stat_base_value; + TryGetOverriddenConstant(GameConstantType::MaxStatBaseValue, val); if (val == -1) { return 999; } @@ -1687,6 +1698,7 @@ int32_t Player::Constants::MaxStatBaseValue() { int32_t Player::Constants::MaxStatBattleValue() { auto val = lcf::Data::system.easyrpg_max_stat_battle_value; + TryGetOverriddenConstant(GameConstantType::MaxStatBattleValue, val); if (val == -1) { return 9'999; } @@ -1695,6 +1707,7 @@ int32_t Player::Constants::MaxStatBattleValue() { int32_t Player::Constants::MaxDamageValue() { auto val = lcf::Data::system.easyrpg_max_damage; + TryGetOverriddenConstant(GameConstantType::MaxDamageValue, val); if (val == -1) { return (Player::IsRPG2k() ? 999 : 9'999); } @@ -1703,6 +1716,7 @@ int32_t Player::Constants::MaxDamageValue() { int32_t Player::Constants::MaxExpValue() { auto val = lcf::Data::system.easyrpg_max_exp; + TryGetOverriddenConstant(GameConstantType::MaxExpValue, val); if (val == -1) { return Player::IsRPG2k() ? 999'999 : 9'999'999; } @@ -1711,6 +1725,9 @@ int32_t Player::Constants::MaxExpValue() { int32_t Player::Constants::MaxLevel() { int max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3; + if (TryGetOverriddenConstant(GameConstantType::MaxLevel, max_level)) { + return max_level; + } if (lcf::Data::system.easyrpg_max_level > -1) { max_level = lcf::Data::system.easyrpg_max_level; } @@ -1718,13 +1735,37 @@ int32_t Player::Constants::MaxLevel() { } int32_t Player::Constants::MaxGoldValue() { - return 999'999; + int32_t max_gold = 999'999; + if (TryGetOverriddenConstant(GameConstantType::MaxGoldValue, max_gold)) { + return max_gold; + } + return max_gold; } int32_t Player::Constants::MaxItemCount() { - return (lcf::Data::system.easyrpg_max_item_count == -1 ? 99 : lcf::Data::system.easyrpg_max_item_count); + int32_t max_item_count = (lcf::Data::system.easyrpg_max_item_count == -1 ? 99 : lcf::Data::system.easyrpg_max_item_count);; + TryGetOverriddenConstant(GameConstantType::MaxItemCount, max_item_count); + return max_item_count; } int32_t Player::Constants::MaxSaveFiles() { - return Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); + int32_t max_savefiles = Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); + TryGetOverriddenConstant(GameConstantType::MaxSaveFiles, max_savefiles); + return max_savefiles; +} + +bool Player::Constants::TryGetOverriddenConstant(GameConstantType const_type, int32_t& out_value) { + auto it = constant_overrides.find(const_type); + if (it != constant_overrides.end()) { + out_value = (*it).second; + } + return it != constant_overrides.end(); +} + +void Player::Constants::OverrideGameConstant(GameConstantType const_type, int32_t value) { + constant_overrides[const_type] = value; +} + +void Player::Constants::ResetOverrides() { + constant_overrides.clear(); } diff --git a/src/player.h b/src/player.h index 57bafe41ca..477da37968 100644 --- a/src/player.h +++ b/src/player.h @@ -61,6 +61,40 @@ namespace Player { PatchOverride = 1 << 16 }; + enum class GameConstantType { + MinVarLimit, + MaxVarLimit, + MaxActorHP, + MaxActorSP, + MaxEnemyHP, + MaxEnemySP, + MaxStatBaseValue, + MaxStatBattleValue, + MaxDamageValue, + MaxExpValue, + MaxLevel, + MaxGoldValue, + MaxItemCount, + MaxSaveFiles + }; + + static constexpr auto kGameConstantType = lcf::makeEnumTags( + "MinVarLimit", + "MaxVarLimit", + "MaxActorHP", + "MaxActorSP", + "MaxEnemyHP", + "MaxEnemySP", + "MaxStatBaseValue", + "MaxStatBattleValue", + "MaxDamageValue", + "MaxExpValue", + "MaxLevel", + "MaxGoldValue", + "MaxItemCount", + "MaxSaveFiles" + ); + /** * Initializes EasyRPG Player. * @@ -469,6 +503,12 @@ namespace Player { int32_t MaxGoldValue(); int32_t MaxItemCount(); int32_t MaxSaveFiles(); + + extern std::map constant_overrides; + + bool TryGetOverriddenConstant(GameConstantType const_type, int32_t& out_value); + void OverrideGameConstant(GameConstantType const_type, int32_t value); + void ResetOverrides(); } } From 712f2a28e1e7c1442b1664e3c10bad7e07eab98d Mon Sep 17 00:00:00 2001 From: florianessl Date: Sat, 15 Feb 2025 22:21:39 +0100 Subject: [PATCH 3/8] Config: Provide a new option for setting to the RPG_RT executable which is to be used by the engine detection (#3166) --- src/game_config_game.cpp | 11 +++++++++++ src/game_config_game.h | 1 + src/player.cpp | 8 +++++++- 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/game_config_game.cpp b/src/game_config_game.cpp index 0789bd17f9..0a590b81c7 100644 --- a/src/game_config_game.cpp +++ b/src/game_config_game.cpp @@ -77,6 +77,16 @@ void Game_ConfigGame::LoadFromArgs(CmdlineParser& cp) { } continue; } + if (cp.ParseNext(arg, 1, "--engine-path")) { + if (arg.NumValues() > 0) { + std::string path = arg.Value(0); + path = FileFinder::MakeCanonical(path, 0); + if (!path.empty()) { + engine_path.Set(path); + } + } + continue; + } if (cp.ParseNext(arg, 0, "--no-patch")) { patch_support.Set(false); patch_dynrpg.Lock(false); @@ -197,6 +207,7 @@ void Game_ConfigGame::LoadFromStream(Filesystem_Stream::InputStream& is) { new_game.FromIni(ini); engine_str.FromIni(ini); + engine_path.FromIni(ini); fake_resolution.FromIni(ini); if (patch_easyrpg.FromIni(ini)) { diff --git a/src/game_config_game.h b/src/game_config_game.h index 354d1d9bb8..5b097ba2bb 100644 --- a/src/game_config_game.h +++ b/src/game_config_game.h @@ -37,6 +37,7 @@ struct Game_ConfigGame { BoolConfigParam new_game{ "Start new game", "Skips the title screen and starts a new game directly", "Game", "NewGame", false }; StringConfigParam engine_str{ "Engine", "", "Game", "Engine", std::string() }; + StringConfigParam engine_path{ "Engine Path", "Sets the executable to be used by the engine auto-detection", "Game", "EnginePath", std::string() }; BoolConfigParam fake_resolution{ "Fake Metrics", "Makes games run on higher resolutions (with some success)", "Game", "FakeResolution", false }; BoolConfigParam patch_easyrpg{ "EasyRPG", "EasyRPG Engine Extensions", "Patch", "EasyRPG", false }; BoolConfigParam patch_destiny{ "Destiny Patch", "", "Patch", "Destiny", false }; diff --git a/src/player.cpp b/src/player.cpp index 50882fab3d..f6a11b43e3 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -777,7 +777,11 @@ void Player::CreateGameObjects() { #ifndef EMSCRIPTEN // Attempt reading ExFont and version information from RPG_RT.exe (not supported on Emscripten) std::unique_ptr exe_reader; - auto exeis = FileFinder::Game().OpenFile(EXE_NAME); + const auto exe_file = game_config.engine_path.Get().empty() ? EXE_NAME : game_config.engine_path.Get(); + if (!game_config.engine_path.Get().empty()) { + Output::Debug("Using specified .EXE '{}' for engine detection", exe_file); + } + auto exeis = FileFinder::Game().OpenFile(exe_file); if (exeis) { exe_reader.reset(new EXEReader(std::move(exeis))); @@ -1438,6 +1442,8 @@ Engine options: rpg2k3 - RPG Maker 2003 (v1.00 - v1.04) rpg2k3v105 - RPG Maker 2003 (v1.05 - v1.09a) rpg2k3e - RPG Maker 2003 (English release, v1.12) + --engine-path EXE Set a custom path for the executable which is to be used + by the automatic engine detection. --font1 FILE Font to use for the first font. The system graphic of the game determines whether font 1 or 2 is used. --font1-size PX Size of font 1 in pixel. The default is 12. From 8ff00023d50714e5b1ffc76aee316675956b8ccc Mon Sep 17 00:00:00 2001 From: florianessl Date: Mon, 28 Apr 2025 15:08:12 +0200 Subject: [PATCH 4/8] Minor: Use constant lookup method for fetching save file count in Scene_File & Scene_Save --- src/scene_file.cpp | 2 +- src/scene_save.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/scene_file.cpp b/src/scene_file.cpp index c233a16135..cd9fa341b9 100644 --- a/src/scene_file.cpp +++ b/src/scene_file.cpp @@ -168,7 +168,7 @@ void Scene_File::RefreshWindows() { } void Scene_File::Refresh() { - for (int i = 0; i < Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); i++) { + for (int i = 0; i < Player::Constants::MaxSaveFiles(); i++) { Window_SaveFile *w = file_windows[i].get(); PopulateSaveWindow(*w, i); w->Refresh(); diff --git a/src/scene_save.cpp b/src/scene_save.cpp index c802b103ad..a01a54766a 100644 --- a/src/scene_save.cpp +++ b/src/scene_save.cpp @@ -53,7 +53,7 @@ Scene_Save::Scene_Save() : void Scene_Save::Start() { Scene_File::Start(); - for (int i = 0; i < Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); i++) { + for (int i = 0; i < Player::Constants::MaxSaveFiles(); i++) { file_windows[i]->SetHasSave(true); file_windows[i]->Refresh(); } From 33292ecc0b2dd2daf017b1e4f531d4498f34cace Mon Sep 17 00:00:00 2001 From: florianessl Date: Mon, 28 Apr 2025 15:38:16 +0200 Subject: [PATCH 5/8] Cherry-picked all parts neccessary for support of "WhiteDragon" & "StatDelimiter" patches from experimental branch "PlayerLimits" --- src/exe_reader.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++ src/exe_reader.h | 5 +++-- src/font.cpp | 1 + src/player.cpp | 40 +++++++++++++++++++++++++++++++++++++ src/player.h | 38 +++++++++++++++++++++++++++++++++-- 5 files changed, 130 insertions(+), 4 deletions(-) diff --git a/src/exe_reader.cpp b/src/exe_reader.cpp index 8b1fd18936..4c26d7e4e3 100644 --- a/src/exe_reader.cpp +++ b/src/exe_reader.cpp @@ -122,6 +122,7 @@ EXEReader::EXEReader(Filesystem_Stream::InputStream core) : corefile(std::move(c uint32_t sectionsOfs = optional_header + GetU16(ofs + 0x14); // skip opt header uint32_t data_directory_ofs = (format_pe32 ? 0x60 : 0x70); resource_rva = GetU32(optional_header + data_directory_ofs + 16); + if (!resource_rva) { // Is some kind of encrypted EXE -> Give up return; @@ -138,6 +139,7 @@ EXEReader::EXEReader(Filesystem_Stream::InputStream core) : corefile(std::move(c if (secName == 0x45444F43) { // CODE file_info.code_size = sectVs; + file_info.code_ofs = GetU32(sectionsOfs + 0x14); } else if (secName == 0x52454843) { // CHER(RY) file_info.cherry_size = sectVs; } else if (secName == 0x50454547) { // GEEP @@ -563,4 +565,52 @@ int EXEReader::FileInfo::GetEngineType(int& mp_version) const { return Player::EngineNone; } +std::map EXEReader::GetOverriddenGameConstants() { + std::map game_constants; + + auto apply_known_config = [&](Player::Constants::KnownPatchConfigurations conf) { + Output::Debug("Assuming known patch config '{}'", Player::Constants::kKnownPatchConfigurations.tag(static_cast(conf))); + auto it_conf = Player::Constants::known_patch_configurations.find(conf); + assert(it_conf != Player::Constants::known_patch_configurations.end()); + + for (auto it = it_conf->second.begin(); it != it_conf->second.end(); ++it) { + game_constants[it->first] = it->second; + } + }; + + auto check_for_string = [&](uint32_t offset, const char* p) { + while (*p) { + if (GetU8(offset++) != *p++) + return false; + } + return true; + }; + + switch (file_info.code_size) { + case 0x9CC00: // RM2K 1.62 + if (check_for_string(file_info.code_ofs + 0x07DAA6, "XXX" /* 3x "POP EAX" */)) { + apply_known_config(Player::Constants::KnownPatchConfigurations::StatDelimiter); + } + break; + case 0xC8E00: // RM2K3 1.0.8.0 + // For all known Italian translations, the "WhiteDragon" patch seems to be the only one + // to translate this string in RPG_RT. So this segment can be used to reliably detect + // the patch without having to read all individual constant values from the EXE + if (check_for_string(file_info.code_ofs + 0x08EBE0, "NoTitolo")) { + apply_known_config(Player::Constants::KnownPatchConfigurations::Rm2k3_Italian_WD_108); + } + if (check_for_string(file_info.code_ofs + 0x09D279, "XXX" /* 3x "POP EAX" */)) { + apply_known_config(Player::Constants::KnownPatchConfigurations::StatDelimiter); + } + break; + case 0xC9000: // RM2K3 1.0.9.1 + if (check_for_string(file_info.code_ofs + 0x09C5AD, "XXX" /* 3x "POP EAX" */)) { + apply_known_config(Player::Constants::KnownPatchConfigurations::StatDelimiter); + } + break; + } + + return game_constants; +} + #endif diff --git a/src/exe_reader.h b/src/exe_reader.h index b5acb48843..cd1a3ea970 100644 --- a/src/exe_reader.h +++ b/src/exe_reader.h @@ -20,9 +20,7 @@ #include #include -#include #include -#include "bitmap.h" #include "player.h" /** @@ -65,6 +63,7 @@ class EXEReader { MachineType machine_type = MachineType::Unknown; bool is_easyrpg_player = false; int maniac_patch_version = 0; + uint32_t code_ofs = 0; int GetEngineType(int& mp_version) const; void Print() const; @@ -72,6 +71,8 @@ class EXEReader { const FileInfo& GetFileInfo(); + std::map GetOverriddenGameConstants(); + private: // Bounds-checked unaligned reader primitives. // In case of out-of-bounds, returns 0 - this will usually result in a harmless error at some other level, diff --git a/src/font.cpp b/src/font.cpp index c49c90a149..a806a5aedb 100644 --- a/src/font.cpp +++ b/src/font.cpp @@ -55,6 +55,7 @@ #include "cache.h" #include "player.h" #include "compiler.h" +#include "game_clock.h" // Static variables. namespace { diff --git a/src/player.cpp b/src/player.cpp index f6a11b43e3..6286ae45f9 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -773,6 +773,7 @@ void Player::CreateGameObjects() { } int& engine = game_config.engine; + std::map game_constant_overrides; #ifndef EMSCRIPTEN // Attempt reading ExFont and version information from RPG_RT.exe (not supported on Emscripten) @@ -797,6 +798,8 @@ void Player::CreateGameObjects() { } } + game_constant_overrides = exe_reader->GetOverriddenGameConstants(); + if (engine == EngineNone) { Output::Debug("Unable to detect version from exe"); } @@ -854,6 +857,14 @@ void Player::CreateGameObjects() { game_config.PrintActivePatches(); + Constants::ResetOverrides(); + if (game_constant_overrides.size() > 0) { + for (auto it = game_constant_overrides.begin(); it != game_constant_overrides.end();++it) { + Constants::OverrideGameConstant(it->first, it->second); + } + Constants::PrintActiveOverrides(); + } + ResetGameObjects(); LoadFonts(); @@ -1775,3 +1786,32 @@ void Player::Constants::OverrideGameConstant(GameConstantType const_type, int32_ void Player::Constants::ResetOverrides() { constant_overrides.clear(); } + +void Player::Constants::PrintActiveOverrides() { + std::vector> overridden_constants; + + auto it = Player::kGameConstantType.begin(); + int32_t value; + while (it != Player::kGameConstantType.end()) { + auto const_type = static_cast((*it).value); + if (!TryGetOverriddenConstant(const_type, value)) { + ++it; + continue; + } + overridden_constants.push_back(std::make_tuple((*it).name, value)); + ++it; + } + + if (!overridden_constants.empty()) { + std::string out = "Overridden Game Constants: "; + bool first = true; + for (const auto& p : overridden_constants) { + if (!first) { + out += ", "; + } + out += fmt::format("{}: {}", std::get(p), std::get(p)); + first = false; + } + Output::DebugStr(out); + } +} diff --git a/src/player.h b/src/player.h index 477da37968..60efe8390b 100644 --- a/src/player.h +++ b/src/player.h @@ -22,11 +22,9 @@ #include "fileext_guesser.h" #include "meta.h" #include "translation.h" -#include "game_clock.h" #include "game_config.h" #include "game_config_game.h" #include "game_interpreter_shared.h" -#include "game_variables.h" #include #include #include @@ -509,6 +507,42 @@ namespace Player { bool TryGetOverriddenConstant(GameConstantType const_type, int32_t& out_value); void OverrideGameConstant(GameConstantType const_type, int32_t value); void ResetOverrides(); + void PrintActiveOverrides(); + + enum class KnownPatchConfigurations { + Rm2k3_Italian_WD_108, // Italian "WhiteDragon" patch + StatDelimiter, + LAST + }; + static constexpr auto kKnownPatchConfigurations = lcf::makeEnumTags( + "Rm2k3 Italian 1.08", + "StatDelimiter" + ); + + static_assert(kKnownPatchConfigurations.size() == static_cast(KnownPatchConfigurations::LAST)); + + using T = Player::GameConstantType; + using patch_config = std::map; + inline const std::map known_patch_configurations = { + { + KnownPatchConfigurations::Rm2k3_Italian_WD_108, { + { T::MinVarLimit, -999999999 }, + { T::MaxVarLimit, 999999999 }, + { T::MaxEnemyHP, 999999999 }, + { T::MaxEnemySP, 999999999 }, + { T::MaxActorHP, 99999 }, + { T::MaxActorSP, 9999 }, + { T::MaxStatBaseValue, 9999 }, + { T::MaxDamageValue, 99999 }, + { T::MaxGoldValue, 9999999 } + }},{ + KnownPatchConfigurations::StatDelimiter, { + { T::MaxActorHP, 9999999 }, + { T::MaxActorSP, 9999999 }, + { T::MaxStatBaseValue, 999999 }, + { T::MaxStatBattleValue, 999999 } + }} + }; } } From 4446b13ac9b69adc78ffa064e9860803cec3b4dc Mon Sep 17 00:00:00 2001 From: florianessl Date: Mon, 28 Apr 2025 15:50:08 +0200 Subject: [PATCH 6/8] Fix: Removed upper limit for Enemy HP/SP (These are just editor limits in Vanilla RPG_RT) --- src/player.cpp | 8 ++++---- src/player.h | 6 ------ tests/game_enemy.cpp | 7 +++---- 3 files changed, 7 insertions(+), 14 deletions(-) diff --git a/src/player.cpp b/src/player.cpp index 6286ae45f9..34a54cd0d2 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1688,18 +1688,18 @@ int32_t Player::Constants::MaxActorSpValue() { int32_t Player::Constants::MaxEnemyHpValue() { auto val = lcf::Data::system.easyrpg_max_enemy_hp; - TryGetOverriddenConstant(GameConstantType::MaxEnemyHP, val); if (val == -1) { - return Player::IsRPG2k() ? 9'999 : 99'999; + // Upper bound is an editor limit, not enforced by the engine + return std::numeric_limits::max(); } return val; } int32_t Player::Constants::MaxEnemySpValue() { auto val = lcf::Data::system.easyrpg_max_enemy_sp; - TryGetOverriddenConstant(GameConstantType::MaxEnemySP, val); if (val == -1) { - return 9'999; + // Upper bound is an editor limit, not enforced by the engine + return std::numeric_limits::max(); } return val; } diff --git a/src/player.h b/src/player.h index 60efe8390b..bf30d3d850 100644 --- a/src/player.h +++ b/src/player.h @@ -64,8 +64,6 @@ namespace Player { MaxVarLimit, MaxActorHP, MaxActorSP, - MaxEnemyHP, - MaxEnemySP, MaxStatBaseValue, MaxStatBattleValue, MaxDamageValue, @@ -81,8 +79,6 @@ namespace Player { "MaxVarLimit", "MaxActorHP", "MaxActorSP", - "MaxEnemyHP", - "MaxEnemySP", "MaxStatBaseValue", "MaxStatBattleValue", "MaxDamageValue", @@ -528,8 +524,6 @@ namespace Player { KnownPatchConfigurations::Rm2k3_Italian_WD_108, { { T::MinVarLimit, -999999999 }, { T::MaxVarLimit, 999999999 }, - { T::MaxEnemyHP, 999999999 }, - { T::MaxEnemySP, 999999999 }, { T::MaxActorHP, 99999 }, { T::MaxActorSP, 9999 }, { T::MaxStatBaseValue, 9999 }, diff --git a/tests/game_enemy.cpp b/tests/game_enemy.cpp index 0044dfe57e..5921fce176 100644 --- a/tests/game_enemy.cpp +++ b/tests/game_enemy.cpp @@ -52,10 +52,9 @@ TEST_CASE("Default") { REQUIRE(e.IsInParty()); } -static void testLimits(int hp, int base, int battle) { +static void testLimits(int base, int battle) { auto& enemy = MakeEnemy(1, 100, 10, 11, 12, 13, 14); - REQUIRE_EQ(enemy.MaxHpValue(), hp); REQUIRE_EQ(enemy.MaxStatBaseValue(), base); REQUIRE_EQ(enemy.MaxStatBattleValue(), battle); @@ -86,12 +85,12 @@ TEST_CASE("Limits") { SUBCASE("2k") { const MockActor m(Player::EngineRpg2k); - testLimits(9999, 999, 9999); + testLimits(999, 9999); } SUBCASE("2k3") { const MockActor m(Player::EngineRpg2k3); - testLimits(99999, 999, 9999); + testLimits(999, 9999); } } From a1db578b2b39d8f98222fe540161cb266475dcbe Mon Sep 17 00:00:00 2001 From: florianessl Date: Mon, 28 Apr 2025 16:36:46 +0200 Subject: [PATCH 7/8] Fix 3DS Build --- src/player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/player.cpp b/src/player.cpp index 34a54cd0d2..3e14b799b2 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -1741,7 +1741,7 @@ int32_t Player::Constants::MaxExpValue() { } int32_t Player::Constants::MaxLevel() { - int max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3; + auto max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3; if (TryGetOverriddenConstant(GameConstantType::MaxLevel, max_level)) { return max_level; } From eaf645cf9b4684a790a6e3c6c458579f990bb685 Mon Sep 17 00:00:00 2001 From: Ghabry Date: Tue, 24 Feb 2026 11:27:07 +0100 Subject: [PATCH 8/8] Make Constants a class and move in seperate files Makes init/teardown simpler (and avoids even more global state) Header cleanup --- CMakeLists.txt | 2 + src/exe_reader.cpp | 25 ++--- src/exe_reader.h | 6 +- src/game_actor.cpp | 14 +-- src/game_actors.cpp | 3 + src/game_battlealgorithm.cpp | 7 +- src/game_constants.cpp | 189 ++++++++++++++++++++++++++++++++++ src/game_constants.h | 166 ++++++++++++++++++++++++++++++ src/game_enemy.cpp | 14 ++- src/game_party.cpp | 9 +- src/main_data.cpp | 3 + src/main_data.h | 4 +- src/platform/wiiu/main.cpp | 1 + src/player.cpp | 193 ++--------------------------------- src/player.h | 95 ----------------- src/scene_file.cpp | 7 +- src/scene_save.cpp | 4 +- tests/algo.cpp | 2 +- tests/mock_game.cpp | 4 + tests/parse.cpp | 3 + tests/test_mock_actor.h | 2 + 21 files changed, 429 insertions(+), 324 deletions(-) create mode 100644 src/game_constants.cpp create mode 100644 src/game_constants.h diff --git a/CMakeLists.txt b/CMakeLists.txt index af487985dc..de274b2a70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,8 @@ add_library(${PROJECT_NAME} OBJECT src/game_config.h src/game_config_game.cpp src/game_config_game.h + src/game_constants.h + src/game_constants.cpp src/game_destiny.cpp src/game_destiny.h src/game_dynrpg.cpp diff --git a/src/exe_reader.cpp b/src/exe_reader.cpp index 4c26d7e4e3..9eadff30b9 100644 --- a/src/exe_reader.cpp +++ b/src/exe_reader.cpp @@ -16,16 +16,17 @@ */ // All of this code is unused on EMSCRIPTEN. *Do not use it*! + #ifndef EMSCRIPTEN #include "exe_reader.h" #include "image_bmp.h" #include "output.h" -#include "utils.h" +#include "player.h" #include #include -#include #include +#include namespace { // hashes of known RPG_RT startup logos @@ -565,13 +566,13 @@ int EXEReader::FileInfo::GetEngineType(int& mp_version) const { return Player::EngineNone; } -std::map EXEReader::GetOverriddenGameConstants() { - std::map game_constants; +std::unordered_map EXEReader::GetOverriddenGameConstants() { + std::unordered_map game_constants; - auto apply_known_config = [&](Player::Constants::KnownPatchConfigurations conf) { - Output::Debug("Assuming known patch config '{}'", Player::Constants::kKnownPatchConfigurations.tag(static_cast(conf))); - auto it_conf = Player::Constants::known_patch_configurations.find(conf); - assert(it_conf != Player::Constants::known_patch_configurations.end()); + auto apply_known_config = [&](Game_Constants::KnownPatchConfigurations conf) { + Output::Debug("Assuming known patch config '{}'", Game_Constants::kKnownPatchConfigurations.tag(static_cast(conf))); + auto it_conf = Game_Constants::known_patch_configurations.find(conf); + assert(it_conf != Game_Constants::known_patch_configurations.end()); for (auto it = it_conf->second.begin(); it != it_conf->second.end(); ++it) { game_constants[it->first] = it->second; @@ -589,7 +590,7 @@ std::map EXEReader::GetOverriddenGameConstant switch (file_info.code_size) { case 0x9CC00: // RM2K 1.62 if (check_for_string(file_info.code_ofs + 0x07DAA6, "XXX" /* 3x "POP EAX" */)) { - apply_known_config(Player::Constants::KnownPatchConfigurations::StatDelimiter); + apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter); } break; case 0xC8E00: // RM2K3 1.0.8.0 @@ -597,15 +598,15 @@ std::map EXEReader::GetOverriddenGameConstant // to translate this string in RPG_RT. So this segment can be used to reliably detect // the patch without having to read all individual constant values from the EXE if (check_for_string(file_info.code_ofs + 0x08EBE0, "NoTitolo")) { - apply_known_config(Player::Constants::KnownPatchConfigurations::Rm2k3_Italian_WD_108); + apply_known_config(Game_Constants::KnownPatchConfigurations::Rm2k3_Italian_WD_108); } if (check_for_string(file_info.code_ofs + 0x09D279, "XXX" /* 3x "POP EAX" */)) { - apply_known_config(Player::Constants::KnownPatchConfigurations::StatDelimiter); + apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter); } break; case 0xC9000: // RM2K3 1.0.9.1 if (check_for_string(file_info.code_ofs + 0x09C5AD, "XXX" /* 3x "POP EAX" */)) { - apply_known_config(Player::Constants::KnownPatchConfigurations::StatDelimiter); + apply_known_config(Game_Constants::KnownPatchConfigurations::StatDelimiter); } break; } diff --git a/src/exe_reader.h b/src/exe_reader.h index cd1a3ea970..8d005ac2be 100644 --- a/src/exe_reader.h +++ b/src/exe_reader.h @@ -18,10 +18,12 @@ #ifndef EP_EXE_READER_H #define EP_EXE_READER_H +#include "filesystem_stream.h" +#include "game_constants.h" #include +#include #include #include -#include "player.h" /** * Extracts resources from an EXE. @@ -71,7 +73,7 @@ class EXEReader { const FileInfo& GetFileInfo(); - std::map GetOverriddenGameConstants(); + std::unordered_map GetOverriddenGameConstants(); private: // Bounds-checked unaligned reader primitives. diff --git a/src/game_actor.cpp b/src/game_actor.cpp index 1ef1bba8bd..24b5ff1361 100644 --- a/src/game_actor.cpp +++ b/src/game_actor.cpp @@ -17,10 +17,10 @@ // Headers #include -#include #include #include "game_actor.h" #include "game_battle.h" +#include "game_constants.h" #include "game_party.h" #include "sprite_actor.h" #include "main_data.h" @@ -37,23 +37,23 @@ #include "game_message_terms.h" int Game_Actor::MaxHpValue() const { - return Player::Constants::MaxActorHpValue(); + return Main_Data::game_constants->MaxActorHpValue(); } int Game_Actor::MaxSpValue() const { - return Player::Constants::MaxActorSpValue(); + return Main_Data::game_constants->MaxActorSpValue(); } int Game_Actor::MaxStatBattleValue() const { - return Player::Constants::MaxStatBattleValue(); + return Main_Data::game_constants->MaxStatBattleValue(); } int Game_Actor::MaxStatBaseValue() const { - return Player::Constants::MaxStatBaseValue(); + return Main_Data::game_constants->MaxStatBaseValue(); } int Game_Actor::MaxExpValue() const { - return Player::Constants::MaxExpValue(); + return Main_Data::game_constants->MaxExpValue(); } Game_Actor::Game_Actor(int actor_id) { @@ -722,7 +722,7 @@ int Game_Actor::GetAccessoryId() const { } int Game_Actor::GetMaxLevel() const { - return Utils::Clamp(Player::Constants::MaxLevel(), 1, dbActor->final_level); + return Utils::Clamp(Main_Data::game_constants->MaxLevel(), 1, dbActor->final_level); } void Game_Actor::SetExp(int _exp) { diff --git a/src/game_actors.cpp b/src/game_actors.cpp index 8f7a4ac6ef..de332ca774 100644 --- a/src/game_actors.cpp +++ b/src/game_actors.cpp @@ -17,12 +17,15 @@ // Headers #include "system.h" +#include #include #include "game_actors.h" #include "main_data.h" #include "output.h" Game_Actors::Game_Actors() { + assert(Main_Data::game_constants != nullptr && "Game Constants must be initialized"); + data.reserve(lcf::Data::actors.size()); for (size_t i = 0; i < lcf::Data::actors.size(); i++) { data.emplace_back(Game_Actor(i + 1)); diff --git a/src/game_battlealgorithm.cpp b/src/game_battlealgorithm.cpp index 6f796e26ae..053a681e57 100644 --- a/src/game_battlealgorithm.cpp +++ b/src/game_battlealgorithm.cpp @@ -17,23 +17,20 @@ */ #include -#include #include #include -#include #include "game_actor.h" #include "game_battle.h" #include "game_battlealgorithm.h" #include "game_battler.h" +#include "game_constants.h" #include "game_enemy.h" -#include "game_enemyparty.h" #include "game_party.h" #include "game_party_base.h" #include "game_switches.h" #include "game_system.h" #include "main_data.h" #include "game_message_terms.h" -#include "output.h" #include "player.h" #include #include @@ -52,7 +49,7 @@ #include "feature.h" static inline int MaxDamageValue() { - return Player::Constants::MaxDamageValue(); + return Main_Data::game_constants->MaxDamageValue(); } Game_BattleAlgorithm::AlgorithmBase::AlgorithmBase(Type ty, Game_Battler* source, Game_Battler* target) : diff --git a/src/game_constants.cpp b/src/game_constants.cpp new file mode 100644 index 0000000000..765a8e51f7 --- /dev/null +++ b/src/game_constants.cpp @@ -0,0 +1,189 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + +// Headers +#include "game_constants.h" +#include "game_variables.h" +#include "output.h" +#include "player.h" + +std::array Game_Constants::GetVariableLimits() { + Game_Variables::Var_t min_var = lcf::Data::system.easyrpg_variable_min_value; + TryGetOverriddenConstant(ConstantType::MinVarLimit, min_var); + if (min_var == 0) { + if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { + min_var = Player::IsRPG2k3() ? Game_Variables::min_2k3 : Game_Variables::min_2k; + } else { + min_var = std::numeric_limits::min(); + } + } + Game_Variables::Var_t max_var = lcf::Data::system.easyrpg_variable_max_value; + TryGetOverriddenConstant(ConstantType::MaxVarLimit, max_var); + if (max_var == 0) { + if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { + max_var = Player::IsRPG2k3() ? Game_Variables::max_2k3 : Game_Variables::max_2k; + } else { + max_var = std::numeric_limits::max(); + } + } + + return {min_var, max_var}; +} + +int32_t Game_Constants::MaxActorHpValue() { + auto val = lcf::Data::system.easyrpg_max_actor_hp; + TryGetOverriddenConstant(ConstantType::MaxActorHP, val); + if (val == -1) { + return Player::IsRPG2k() ? 999 : 9'999; + } + return val; +} + +int32_t Game_Constants::MaxActorSpValue() { + auto val = lcf::Data::system.easyrpg_max_actor_sp; + TryGetOverriddenConstant(ConstantType::MaxActorSP, val); + if (val == -1) { + return 999; + } + return val; +} + +int32_t Game_Constants::MaxEnemyHpValue() { + auto val = lcf::Data::system.easyrpg_max_enemy_hp; + if (val == -1) { + // Upper bound is an editor limit, not enforced by the engine + return std::numeric_limits::max(); + } + return val; +} + +int32_t Game_Constants::MaxEnemySpValue() { + auto val = lcf::Data::system.easyrpg_max_enemy_sp; + if (val == -1) { + // Upper bound is an editor limit, not enforced by the engine + return std::numeric_limits::max(); + } + return val; +} + +int32_t Game_Constants::MaxStatBaseValue() { + auto val = lcf::Data::system.easyrpg_max_stat_base_value; + TryGetOverriddenConstant(ConstantType::MaxStatBaseValue, val); + if (val == -1) { + return 999; + } + return val; +} + +int32_t Game_Constants::MaxStatBattleValue() { + auto val = lcf::Data::system.easyrpg_max_stat_battle_value; + TryGetOverriddenConstant(ConstantType::MaxStatBattleValue, val); + if (val == -1) { + return 9'999; + } + return val; +} + +int32_t Game_Constants::MaxDamageValue() { + auto val = lcf::Data::system.easyrpg_max_damage; + TryGetOverriddenConstant(ConstantType::MaxDamageValue, val); + if (val == -1) { + return (Player::IsRPG2k() ? 999 : 9'999); + } + return val; +} + +int32_t Game_Constants::MaxExpValue() { + auto val = lcf::Data::system.easyrpg_max_exp; + TryGetOverriddenConstant(ConstantType::MaxExpValue, val); + if (val == -1) { + return Player::IsRPG2k() ? 999'999 : 9'999'999; + } + return val; +} + +int32_t Game_Constants::MaxLevel() { + auto max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3; + if (TryGetOverriddenConstant(ConstantType::MaxLevel, max_level)) { + return max_level; + } + if (lcf::Data::system.easyrpg_max_level > -1) { + max_level = lcf::Data::system.easyrpg_max_level; + } + return max_level; +} + +int32_t Game_Constants::MaxGoldValue() { + int32_t max_gold = 999'999; + if (TryGetOverriddenConstant(ConstantType::MaxGoldValue, max_gold)) { + return max_gold; + } + return max_gold; +} + +int32_t Game_Constants::MaxItemCount() { + int32_t max_item_count = (lcf::Data::system.easyrpg_max_item_count == -1 ? 99 : lcf::Data::system.easyrpg_max_item_count);; + TryGetOverriddenConstant(ConstantType::MaxItemCount, max_item_count); + return max_item_count; +} + +int32_t Game_Constants::MaxSaveFiles() { + int32_t max_savefiles = Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); + TryGetOverriddenConstant(ConstantType::MaxSaveFiles, max_savefiles); + return max_savefiles; +} + +bool Game_Constants::TryGetOverriddenConstant(ConstantType const_type, int32_t& out_value) { + auto it = constant_overrides.find(const_type); + if (it != constant_overrides.end()) { + out_value = (*it).second; + } + return it != constant_overrides.end(); +} + +void Game_Constants::OverrideGameConstant(ConstantType const_type, int32_t value) { + constant_overrides[const_type] = value; +} + +void Game_Constants::PrintActiveOverrides() { + std::vector> overridden_constants; + + auto it = kConstantType.begin(); + int32_t value; + while (it != kConstantType.end()) { + auto const_type = static_cast((*it).value); + if (!TryGetOverriddenConstant(const_type, value)) { + ++it; + continue; + } + overridden_constants.push_back(std::make_tuple((*it).name, value)); + ++it; + } + + if (!overridden_constants.empty()) { + std::string out = "Overridden Game Constants: "; + bool first = true; + for (const auto& p : overridden_constants) { + if (!first) { + out += ", "; + } + out += fmt::format("{}: {}", std::get(p), std::get(p)); + first = false; + } + Output::DebugStr(out); + } +} diff --git a/src/game_constants.h b/src/game_constants.h new file mode 100644 index 0000000000..98f822ed82 --- /dev/null +++ b/src/game_constants.h @@ -0,0 +1,166 @@ +/* + * This file is part of EasyRPG Player. + * + * EasyRPG Player is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * EasyRPG Player is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with EasyRPG Player. If not, see . + */ + +#ifndef EP_GAME_CONSTANTS_H +#define EP_GAME_CONSTANTS_H + +// Headers +#include +#include +#include +#include "game_variables.h" + +/** + * Implements Constants (e.g. stat caps) used by the engine and allows + * overwriting them based on engine version or patches. + */ +class Game_Constants { +public: + Game_Constants() = default; + + /** RPG2k level cap */ + static const constexpr int32_t max_level_2k = 50; + /** RPG2k3 level cap */ + static const constexpr int32_t max_level_2k3 = 99; + + enum class ConstantType { + MinVarLimit, + MaxVarLimit, + MaxActorHP, + MaxActorSP, + MaxStatBaseValue, + MaxStatBattleValue, + MaxDamageValue, + MaxExpValue, + MaxLevel, + MaxGoldValue, + MaxItemCount, + MaxSaveFiles + }; + + static constexpr auto kConstantType = lcf::makeEnumTags( + "MinVarLimit", + "MaxVarLimit", + "MaxActorHP", + "MaxActorSP", + "MaxStatBaseValue", + "MaxStatBattleValue", + "MaxDamageValue", + "MaxExpValue", + "MaxLevel", + "MaxGoldValue", + "MaxItemCount", + "MaxSaveFiles" + ); + + /** @return Variable upper and lower limit; default: ±999,999 (2k), ±9,999,9999 (2k3) */ + std::array GetVariableLimits(); + + /** @return Max HP for Actors; default: 999 (2k), 9999 (2k3) */ + int32_t MaxActorHpValue(); + + /** @return Max SP for Actors (default: 999) */ + int32_t MaxActorSpValue(); + + /** @return Max HP for Enemies; default: uncapped */ + int32_t MaxEnemyHpValue(); + + /** @return Max SP for Enemies; default: uncapped */ + int32_t MaxEnemySpValue(); + + /** @return Max base stat (Attack, Defense, Spirit, Agility) value; default: 999 */ + int32_t MaxStatBaseValue(); + + /** @return Max stat (Attack, Defense, Spirit, Agility) with modifiers value; default: 9999 */ + int32_t MaxStatBattleValue(); + + /** @return Max attack damage; default: 999 (2k), 9999 (2k3) */ + int32_t MaxDamageValue(); + + /** @return Max Experience; default: 999,999 (2k), 9,999,999 (2k3) */ + int32_t MaxExpValue(); + + /** @return Level cap; default: 50 (2k), 99 (2k3) */ + int32_t MaxLevel(); + + /** @return Gold limit; default: 999,999 */ + int32_t MaxGoldValue(); + + /** @return Max amount per item; default: 99 */ + int32_t MaxItemCount(); + + /** @return Save slots displayed in the Save/Load scenes; default: 15 */ + int32_t MaxSaveFiles(); + + /** + * Overrides one of the constants. + * + * @param const_type Constant to override + * @param value new constant value + */ + void OverrideGameConstant(ConstantType const_type, int32_t value); + + /** + * Prints a list of overwritten constants to the log + */ + void PrintActiveOverrides(); + + /** Patches detected by the EXE Reader */ + enum class KnownPatchConfigurations { + Rm2k3_Italian_WD_108, // Italian "WhiteDragon" patch + StatDelimiter, + LAST + }; + + static constexpr auto kKnownPatchConfigurations = lcf::makeEnumTags( + "Rm2k3 Italian 1.08", + "StatDelimiter" + ); + + static_assert(kKnownPatchConfigurations.size() == static_cast(KnownPatchConfigurations::LAST)); + + using T = ConstantType; + + /** Constants of known patches */ + inline static const std::unordered_map> known_patch_configurations = { + { + KnownPatchConfigurations::Rm2k3_Italian_WD_108, { + { T::MinVarLimit, -999999999 }, + { T::MaxVarLimit, 999999999 }, + { T::MaxActorHP, 99999 }, + { T::MaxActorSP, 9999 }, + { T::MaxStatBaseValue, 9999 }, + { T::MaxDamageValue, 99999 }, + { T::MaxGoldValue, 9999999 } + } + },{ + KnownPatchConfigurations::StatDelimiter, { + { T::MaxActorHP, 9999999 }, + { T::MaxActorSP, 9999999 }, + { T::MaxStatBaseValue, 999999 }, + { T::MaxStatBattleValue, 999999 } + } + } + }; + + private: + bool TryGetOverriddenConstant(ConstantType const_type, int32_t& out_value); + + std::unordered_map constant_overrides; +}; + +#endif diff --git a/src/game_enemy.cpp b/src/game_enemy.cpp index 99531c405a..45d6a9e148 100644 --- a/src/game_enemy.cpp +++ b/src/game_enemy.cpp @@ -17,19 +17,17 @@ // Headers #include -#include #include #include #include "game_battle.h" #include "game_enemy.h" -#include "game_party.h" -#include "game_switches.h" +#include "game_constants.h" #include +#include "main_data.h" #include "output.h" #include "utils.h" #include "player.h" #include "attribute.h" -#include "rand.h" Game_Enemy::Game_Enemy(const lcf::rpg::TroopMember* member) : troop_member(member) @@ -48,19 +46,19 @@ Game_Enemy::Game_Enemy(const lcf::rpg::TroopMember* member) } int Game_Enemy::MaxHpValue() const { - return Player::Constants::MaxEnemyHpValue(); + return Main_Data::game_constants->MaxEnemyHpValue(); } int Game_Enemy::MaxSpValue() const { - return Player::Constants::MaxEnemySpValue(); + return Main_Data::game_constants->MaxEnemySpValue(); } int Game_Enemy::MaxStatBattleValue() const { - return Player::Constants::MaxStatBattleValue(); + return Main_Data::game_constants->MaxStatBattleValue(); } int Game_Enemy::MaxStatBaseValue() const { - return Player::Constants::MaxStatBaseValue(); + return Main_Data::game_constants->MaxStatBaseValue(); } int Game_Enemy::GetStateProbability(int state_id) const { diff --git a/src/game_party.cpp b/src/game_party.cpp index cd09e866a4..087e3d1dfc 100644 --- a/src/game_party.cpp +++ b/src/game_party.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include "game_constants.h" #include "system.h" #include "game_party.h" #include "game_actors.h" @@ -28,7 +28,6 @@ #include "game_battle.h" #include "game_targets.h" #include "game_system.h" -#include "scene_battle.h" #include #include "output.h" #include "algo.h" @@ -161,7 +160,7 @@ int Game_Party::GetItemTotalCount(int item_id) const { int Game_Party::GetMaxItemCount(int item_id) const { const lcf::rpg::Item* item = lcf::ReaderUtil::GetElement(lcf::Data::items, item_id); if (!item || item->easyrpg_max_count == -1) { - return Player::Constants::MaxItemCount(); + return Main_Data::game_constants->MaxItemCount(); } else { return item->easyrpg_max_count; } @@ -169,12 +168,12 @@ int Game_Party::GetMaxItemCount(int item_id) const { void Game_Party::GainGold(int n) { data.gold = data.gold + n; - data.gold = std::min(std::max(data.gold, 0), Player::Constants::MaxGoldValue()); + data.gold = std::min(std::max(data.gold, 0), Main_Data::game_constants->MaxGoldValue()); } void Game_Party::LoseGold(int n) { data.gold = data.gold - n; - data.gold = std::min(std::max(data.gold, 0), Player::Constants::MaxGoldValue()); + data.gold = std::min(std::max(data.gold, 0), Main_Data::game_constants->MaxGoldValue()); } void Game_Party::AddItem(int item_id, int amount) { diff --git a/src/main_data.cpp b/src/main_data.cpp index 34e9553f67..cc02e90f8e 100644 --- a/src/main_data.cpp +++ b/src/main_data.cpp @@ -19,6 +19,7 @@ #include #include "main_data.h" #include "filefinder_rtp.h" +#include "game_constants.h" #include "game_destiny.h" #include "game_system.h" #include "game_actors.h" @@ -70,6 +71,7 @@ namespace Main_Data { std::unique_ptr game_dynrpg; std::unique_ptr game_ineluki; std::unique_ptr game_destiny; + std::unique_ptr game_constants; std::unique_ptr game_switches_global; std::unique_ptr game_variables_global; @@ -112,6 +114,7 @@ void Main_Data::Init() { void Main_Data::Cleanup() { Game_Map::Quit(); + game_constants.reset(); game_switches.reset(); game_screen.reset(); game_pictures.reset(); diff --git a/src/main_data.h b/src/main_data.h index c0a1a4230d..336af641d3 100644 --- a/src/main_data.h +++ b/src/main_data.h @@ -42,10 +42,12 @@ class Game_Quit; class Game_DynRpg; class Game_Ineluki; class Game_Destiny; +class Game_Constants; class FileFinder_RTP; namespace Main_Data { // Dynamic Game lcf::Data + extern std::unique_ptr game_constants; extern std::unique_ptr game_system; extern std::unique_ptr game_switches; extern std::unique_ptr game_variables; @@ -62,11 +64,11 @@ namespace Main_Data { extern std::unique_ptr game_dynrpg; extern std::unique_ptr game_ineluki; extern std::unique_ptr game_destiny; + extern bool global_save_opened; extern std::unique_ptr game_switches_global; // Used by Global Save command extern std::unique_ptr game_variables_global; - extern std::unique_ptr filefinder_rtp; void Init(); diff --git a/src/platform/wiiu/main.cpp b/src/platform/wiiu/main.cpp index 02a93fc89f..5642c51bcd 100644 --- a/src/platform/wiiu/main.cpp +++ b/src/platform/wiiu/main.cpp @@ -23,6 +23,7 @@ #include "player.h" #include "utils.h" #include "output.h" +#include "game_clock.h" #ifdef USE_SDL // SDL might wrap main() # include diff --git a/src/player.cpp b/src/player.cpp index 3e14b799b2..7261a58765 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -16,11 +16,13 @@ */ // Headers +#include "game_constants.h" #include #include #include #include #include +#include #ifdef _WIN32 # include "platform/windows/utils.h" @@ -773,7 +775,7 @@ void Player::CreateGameObjects() { } int& engine = game_config.engine; - std::map game_constant_overrides; + std::unordered_map game_constant_overrides; #ifndef EMSCRIPTEN // Attempt reading ExFont and version information from RPG_RT.exe (not supported on Emscripten) @@ -857,16 +859,15 @@ void Player::CreateGameObjects() { game_config.PrintActivePatches(); - Constants::ResetOverrides(); - if (game_constant_overrides.size() > 0) { + ResetGameObjects(); + + if (!game_constant_overrides.empty()) { for (auto it = game_constant_overrides.begin(); it != game_constant_overrides.end();++it) { - Constants::OverrideGameConstant(it->first, it->second); + Main_Data::game_constants->OverrideGameConstant(it->first, it->second); } - Constants::PrintActiveOverrides(); + Main_Data::game_constants->PrintActiveOverrides(); } - ResetGameObjects(); - LoadFonts(); if (Player::IsPatchKeyPatch()) { @@ -939,11 +940,12 @@ void Player::ResetGameObjects() { Main_Data::Cleanup(); + Main_Data::game_constants = std::make_unique(); + Main_Data::game_switches = std::make_unique(); Main_Data::game_switches->SetLowerLimit(lcf::Data::switches.size()); - int32_t min_var, max_var; - Player::Constants::GetVariableLimits(min_var, max_var); + auto [min_var, max_var] = Main_Data::game_constants->GetVariableLimits(); Main_Data::game_variables = std::make_unique(min_var, max_var); Main_Data::game_variables->SetLowerLimit(lcf::Data::variables.size()); @@ -1642,176 +1644,3 @@ std::string Player::GetEngineVersion() { if (EngineVersion() > 0) return std::to_string(EngineVersion()); return std::string(); } - -namespace Player::Constants { - std::map constant_overrides; -} - -void Player::Constants::GetVariableLimits(Var_t& min_var, Var_t& max_var) { - min_var = lcf::Data::system.easyrpg_variable_min_value; - TryGetOverriddenConstant(GameConstantType::MinVarLimit, min_var); - if (min_var == 0) { - if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { - min_var = Player::IsRPG2k3() ? Game_Variables::min_2k3 : Game_Variables::min_2k; - } else { - min_var = std::numeric_limits::min(); - } - } - max_var = lcf::Data::system.easyrpg_variable_max_value; - TryGetOverriddenConstant(GameConstantType::MaxVarLimit, max_var); - if (max_var == 0) { - if (!Player::IsPatchManiac() || Player::game_config.patch_maniac.Get() == 2) { - max_var = Player::IsRPG2k3() ? Game_Variables::max_2k3 : Game_Variables::max_2k; - } else { - max_var = std::numeric_limits::max(); - } - } -} - -int32_t Player::Constants::MaxActorHpValue() { - auto val = lcf::Data::system.easyrpg_max_actor_hp; - TryGetOverriddenConstant(GameConstantType::MaxActorHP, val); - if (val == -1) { - return Player::IsRPG2k() ? 999 : 9'999; - } - return val; -} - -int32_t Player::Constants::MaxActorSpValue() { - auto val = lcf::Data::system.easyrpg_max_actor_sp; - TryGetOverriddenConstant(GameConstantType::MaxActorSP, val); - if (val == -1) { - return 999; - } - return val; -} - -int32_t Player::Constants::MaxEnemyHpValue() { - auto val = lcf::Data::system.easyrpg_max_enemy_hp; - if (val == -1) { - // Upper bound is an editor limit, not enforced by the engine - return std::numeric_limits::max(); - } - return val; -} - -int32_t Player::Constants::MaxEnemySpValue() { - auto val = lcf::Data::system.easyrpg_max_enemy_sp; - if (val == -1) { - // Upper bound is an editor limit, not enforced by the engine - return std::numeric_limits::max(); - } - return val; -} - -int32_t Player::Constants::MaxStatBaseValue() { - auto val = lcf::Data::system.easyrpg_max_stat_base_value; - TryGetOverriddenConstant(GameConstantType::MaxStatBaseValue, val); - if (val == -1) { - return 999; - } - return val; -} - -int32_t Player::Constants::MaxStatBattleValue() { - auto val = lcf::Data::system.easyrpg_max_stat_battle_value; - TryGetOverriddenConstant(GameConstantType::MaxStatBattleValue, val); - if (val == -1) { - return 9'999; - } - return val; -} - -int32_t Player::Constants::MaxDamageValue() { - auto val = lcf::Data::system.easyrpg_max_damage; - TryGetOverriddenConstant(GameConstantType::MaxDamageValue, val); - if (val == -1) { - return (Player::IsRPG2k() ? 999 : 9'999); - } - return val; -} - -int32_t Player::Constants::MaxExpValue() { - auto val = lcf::Data::system.easyrpg_max_exp; - TryGetOverriddenConstant(GameConstantType::MaxExpValue, val); - if (val == -1) { - return Player::IsRPG2k() ? 999'999 : 9'999'999; - } - return val; -} - -int32_t Player::Constants::MaxLevel() { - auto max_level = Player::IsRPG2k() ? max_level_2k : max_level_2k3; - if (TryGetOverriddenConstant(GameConstantType::MaxLevel, max_level)) { - return max_level; - } - if (lcf::Data::system.easyrpg_max_level > -1) { - max_level = lcf::Data::system.easyrpg_max_level; - } - return max_level; -} - -int32_t Player::Constants::MaxGoldValue() { - int32_t max_gold = 999'999; - if (TryGetOverriddenConstant(GameConstantType::MaxGoldValue, max_gold)) { - return max_gold; - } - return max_gold; -} - -int32_t Player::Constants::MaxItemCount() { - int32_t max_item_count = (lcf::Data::system.easyrpg_max_item_count == -1 ? 99 : lcf::Data::system.easyrpg_max_item_count);; - TryGetOverriddenConstant(GameConstantType::MaxItemCount, max_item_count); - return max_item_count; -} - -int32_t Player::Constants::MaxSaveFiles() { - int32_t max_savefiles = Utils::Clamp(lcf::Data::system.easyrpg_max_savefiles, 3, 99); - TryGetOverriddenConstant(GameConstantType::MaxSaveFiles, max_savefiles); - return max_savefiles; -} - -bool Player::Constants::TryGetOverriddenConstant(GameConstantType const_type, int32_t& out_value) { - auto it = constant_overrides.find(const_type); - if (it != constant_overrides.end()) { - out_value = (*it).second; - } - return it != constant_overrides.end(); -} - -void Player::Constants::OverrideGameConstant(GameConstantType const_type, int32_t value) { - constant_overrides[const_type] = value; -} - -void Player::Constants::ResetOverrides() { - constant_overrides.clear(); -} - -void Player::Constants::PrintActiveOverrides() { - std::vector> overridden_constants; - - auto it = Player::kGameConstantType.begin(); - int32_t value; - while (it != Player::kGameConstantType.end()) { - auto const_type = static_cast((*it).value); - if (!TryGetOverriddenConstant(const_type, value)) { - ++it; - continue; - } - overridden_constants.push_back(std::make_tuple((*it).name, value)); - ++it; - } - - if (!overridden_constants.empty()) { - std::string out = "Overridden Game Constants: "; - bool first = true; - for (const auto& p : overridden_constants) { - if (!first) { - out += ", "; - } - out += fmt::format("{}: {}", std::get(p), std::get(p)); - first = false; - } - Output::DebugStr(out); - } -} diff --git a/src/player.h b/src/player.h index bf30d3d850..0e5966cecc 100644 --- a/src/player.h +++ b/src/player.h @@ -59,36 +59,6 @@ namespace Player { PatchOverride = 1 << 16 }; - enum class GameConstantType { - MinVarLimit, - MaxVarLimit, - MaxActorHP, - MaxActorSP, - MaxStatBaseValue, - MaxStatBattleValue, - MaxDamageValue, - MaxExpValue, - MaxLevel, - MaxGoldValue, - MaxItemCount, - MaxSaveFiles - }; - - static constexpr auto kGameConstantType = lcf::makeEnumTags( - "MinVarLimit", - "MaxVarLimit", - "MaxActorHP", - "MaxActorSP", - "MaxStatBaseValue", - "MaxStatBattleValue", - "MaxDamageValue", - "MaxExpValue", - "MaxLevel", - "MaxGoldValue", - "MaxItemCount", - "MaxSaveFiles" - ); - /** * Initializes EasyRPG Player. * @@ -473,71 +443,6 @@ namespace Player { std::optional GetRuntimeFlag(Game_Interpreter_Shared::StateRuntimeFlagRef field_on, Game_Interpreter_Shared::StateRuntimeFlagRef field_off); #endif - namespace Constants { - using Var_t = int32_t; - - static constexpr int32_t max_level_2k = 50; - static constexpr int32_t max_level_2k3 = 99; - - void GetVariableLimits(Var_t& min_var, Var_t& max_var); - - int32_t MaxActorHpValue(); - int32_t MaxActorSpValue(); - - int32_t MaxEnemyHpValue(); - int32_t MaxEnemySpValue(); - - int32_t MaxStatBaseValue(); - int32_t MaxStatBattleValue(); - int32_t MaxDamageValue(); - - int32_t MaxExpValue(); - int32_t MaxLevel(); - - int32_t MaxGoldValue(); - int32_t MaxItemCount(); - int32_t MaxSaveFiles(); - - extern std::map constant_overrides; - - bool TryGetOverriddenConstant(GameConstantType const_type, int32_t& out_value); - void OverrideGameConstant(GameConstantType const_type, int32_t value); - void ResetOverrides(); - void PrintActiveOverrides(); - - enum class KnownPatchConfigurations { - Rm2k3_Italian_WD_108, // Italian "WhiteDragon" patch - StatDelimiter, - LAST - }; - static constexpr auto kKnownPatchConfigurations = lcf::makeEnumTags( - "Rm2k3 Italian 1.08", - "StatDelimiter" - ); - - static_assert(kKnownPatchConfigurations.size() == static_cast(KnownPatchConfigurations::LAST)); - - using T = Player::GameConstantType; - using patch_config = std::map; - inline const std::map known_patch_configurations = { - { - KnownPatchConfigurations::Rm2k3_Italian_WD_108, { - { T::MinVarLimit, -999999999 }, - { T::MaxVarLimit, 999999999 }, - { T::MaxActorHP, 99999 }, - { T::MaxActorSP, 9999 }, - { T::MaxStatBaseValue, 9999 }, - { T::MaxDamageValue, 99999 }, - { T::MaxGoldValue, 9999999 } - }},{ - KnownPatchConfigurations::StatDelimiter, { - { T::MaxActorHP, 9999999 }, - { T::MaxActorSP, 9999999 }, - { T::MaxStatBaseValue, 999999 }, - { T::MaxStatBattleValue, 999999 } - }} - }; - } } inline bool Player::IsRPG2k() { diff --git a/src/scene_file.cpp b/src/scene_file.cpp index cd9fa341b9..4117efaa90 100644 --- a/src/scene_file.cpp +++ b/src/scene_file.cpp @@ -19,11 +19,10 @@ #include #include #include -#include "baseui.h" #include "cache.h" #include +#include "game_constants.h" #include "game_system.h" -#include "game_party.h" #include "input.h" #include #include "player.h" @@ -123,7 +122,7 @@ void Scene_File::Start() { // Refresh File Finder Save Folder fs = FileFinder::Save(); - for (int i = 0; i < Player::Constants::MaxSaveFiles(); i++) { + for (int i = 0; i < Main_Data::game_constants->MaxSaveFiles(); i++) { std::shared_ptr w(new Window_SaveFile(Player::menu_offset_x, 40 + i * 64, MENU_WIDTH, 64)); w->SetIndex(i); @@ -168,7 +167,7 @@ void Scene_File::RefreshWindows() { } void Scene_File::Refresh() { - for (int i = 0; i < Player::Constants::MaxSaveFiles(); i++) { + for (int i = 0; i < Main_Data::game_constants->MaxSaveFiles(); i++) { Window_SaveFile *w = file_windows[i].get(); PopulateSaveWindow(*w, i); w->Refresh(); diff --git a/src/scene_save.cpp b/src/scene_save.cpp index a01a54766a..dd7bec8d57 100644 --- a/src/scene_save.cpp +++ b/src/scene_save.cpp @@ -16,7 +16,7 @@ */ // Headers -#include +#include "game_constants.h" #ifdef EMSCRIPTEN # include @@ -53,7 +53,7 @@ Scene_Save::Scene_Save() : void Scene_Save::Start() { Scene_File::Start(); - for (int i = 0; i < Player::Constants::MaxSaveFiles(); i++) { + for (int i = 0; i < Main_Data::game_constants->MaxSaveFiles(); i++) { file_windows[i]->SetHasSave(true); file_windows[i]->Refresh(); } diff --git a/tests/algo.cpp b/tests/algo.cpp index 3a9140afe6..597ea4f93f 100644 --- a/tests/algo.cpp +++ b/tests/algo.cpp @@ -1,3 +1,4 @@ +#include "main_data.h" #include "test_mock_actor.h" #include "algo.h" #include "rand.h" @@ -13,7 +14,6 @@ static void testRowAdj(lcf::rpg::SaveActor::RowType row, bool offense, bool none REQUIRE_EQ(pincers, Algo::IsRowAdjusted(row, lcf::rpg::System::BattleCondition_pincers, offense)); } - TEST_CASE("RowAdj") { const MockActor m; diff --git a/tests/mock_game.cpp b/tests/mock_game.cpp index 2a31343b19..ca2a99edc9 100644 --- a/tests/mock_game.cpp +++ b/tests/mock_game.cpp @@ -1,6 +1,9 @@ #include "mock_game.h" #include "game_actors.h" +#include "game_constants.h" #include "game_system.h" +#include "main_data.h" +#include static lcf::rpg::Terrain MakeTerrain() { return {}; @@ -22,6 +25,7 @@ MockGame::MockGame(MockMap maptag) { lcf::Data::terrains.push_back(MakeTerrain()); lcf::Data::chipsets.push_back(MakeChipset()); + Main_Data::game_constants = std::make_unique(); Main_Data::game_actors = std::make_unique(); Main_Data::game_party = std::make_unique(); diff --git a/tests/parse.cpp b/tests/parse.cpp index 2a72dc90e8..7e8b03d970 100644 --- a/tests/parse.cpp +++ b/tests/parse.cpp @@ -1,4 +1,5 @@ #include "game_actors.h" +#include "game_constants.h" #include "game_message.h" #include "game_party.h" #include "options.h" @@ -6,6 +7,7 @@ #include "game_variables.h" #include "main_data.h" #include +#include #include "doctest.h" TEST_SUITE_BEGIN("Parse"); @@ -22,6 +24,7 @@ struct DataInit { lcf::rpg::SaveInventory inventory; inventory.party.push_back(3); + Main_Data::game_constants = std::make_unique(); Main_Data::game_actors = std::make_unique(); Main_Data::game_party = std::make_unique(); Main_Data::game_party->SetupFromSave(std::move(inventory)); diff --git a/tests/test_mock_actor.h b/tests/test_mock_actor.h index 963a2149ed..9180516c06 100644 --- a/tests/test_mock_actor.h +++ b/tests/test_mock_actor.h @@ -2,6 +2,7 @@ #define EP_TEST_MOCK_ACTOR #include "game_actors.h" +#include "game_constants.h" #include "game_party.h" #include "game_enemyparty.h" #include "game_system.h" @@ -85,6 +86,7 @@ class MockActor { Main_Data::Cleanup(); + Main_Data::game_constants = std::make_unique(); Main_Data::game_system = std::make_unique(); Main_Data::game_actors = std::make_unique(); Main_Data::game_enemyparty = std::make_unique();