diff --git a/libs/s25main/CheatCommandTracker.cpp b/libs/s25main/CheatCommandTracker.cpp index a74733721..f1b4e1ace 100644 --- a/libs/s25main/CheatCommandTracker.cpp +++ b/libs/s25main/CheatCommandTracker.cpp @@ -34,6 +34,8 @@ void CheatCommandTracker::onChatCommand(const std::string& cmd) if(cmd == "apocalypsis") cheats_.armageddon(); + else if(cmd == "impulse9") + cheats_.toggleAllBuildingsEnabled(); } bool CheatCommandTracker::checkSpecialKeyEvent(const KeyEvent& ke) diff --git a/libs/s25main/Cheats.cpp b/libs/s25main/Cheats.cpp index d86f5d2b3..fedd5cf7e 100644 --- a/libs/s25main/Cheats.cpp +++ b/libs/s25main/Cheats.cpp @@ -16,27 +16,44 @@ bool Cheats::areCheatsAllowed() const void Cheats::toggleCheatMode() { + // In S2, if you enabled cheat mode, revealed the map and disabled cheat mode, the map would remain revealed and you + // would be unable to unreveal the map. + // In RTTR, disabling cheat mode turns all cheats off and they have to be turned on again manually. + if(isCheatModeOn_) + turnAllCheatsOff(); + isCheatModeOn_ = !isCheatModeOn_; } void Cheats::toggleAllVisible() { - // In the original game if you enabled cheats, revealed the map and disabled cheats, you would be unable to unreveal - // the map. In RTTR, with cheats disabled, you can unreveal the map but cannot reveal it. - if(isCheatModeOn() || isAllVisible()) + if(isCheatModeOn()) { isAllVisible_ = !isAllVisible_; - // The minimap in the original game is not updated immediately, but in RTTR this would mess up the minimap. + // In S2, the minimap is not updated immediately. + // In RTTR, the minimap would become messed up if it wasn't updated here. if(GameInterface* gi = world_.GetGameInterface()) gi->GI_UpdateMapVisibility(); } } -void Cheats::toggleHumanAIPlayer() const +void Cheats::toggleAllBuildingsEnabled() +{ + // In S2, if you enabled cheats you would automatically have all buildings enabled. + // In RTTR, because this may have unintended consequences when playing campaigns, the user must explicitly enable + // all buildings after enabling cheats. + if(isCheatModeOn()) + areAllBuildingsEnabled_ = !areAllBuildingsEnabled_; +} + +void Cheats::toggleHumanAIPlayer() { if(isCheatModeOn() && !GAMECLIENT.IsReplayModeOn()) + { GAMECLIENT.ToggleHumanAIPlayer(AI::Info{AI::Type::Default, AI::Level::Easy}); + isHumanAIPlayer_ = !isHumanAIPlayer_; + } } void Cheats::armageddon() const @@ -44,3 +61,13 @@ void Cheats::armageddon() const if(isCheatModeOn()) GAMECLIENT.CheatArmageddon(); } + +void Cheats::turnAllCheatsOff() +{ + if(isAllVisible_) + toggleAllVisible(); + if(areAllBuildingsEnabled_) + toggleAllBuildingsEnabled(); + if(isHumanAIPlayer_) + toggleHumanAIPlayer(); +} diff --git a/libs/s25main/Cheats.h b/libs/s25main/Cheats.h index c59e384d7..a850e892d 100644 --- a/libs/s25main/Cheats.h +++ b/libs/s25main/Cheats.h @@ -20,12 +20,19 @@ class Cheats void toggleAllVisible(); bool isAllVisible() const { return isAllVisible_; } + void toggleAllBuildingsEnabled(); + bool areAllBuildingsEnabled() const { return areAllBuildingsEnabled_; } + // RTTR cheats - void toggleHumanAIPlayer() const; + void toggleHumanAIPlayer(); void armageddon() const; private: + void turnAllCheatsOff(); + bool isCheatModeOn_ = false; bool isAllVisible_ = false; + bool areAllBuildingsEnabled_ = false; + bool isHumanAIPlayer_ = false; GameWorldBase& world_; }; diff --git a/libs/s25main/GamePlayer.cpp b/libs/s25main/GamePlayer.cpp index d00de03b9..18c09f310 100644 --- a/libs/s25main/GamePlayer.cpp +++ b/libs/s25main/GamePlayer.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "GamePlayer.h" +#include "Cheats.h" #include "EventManager.h" #include "FindWhConditions.h" #include "GameInterface.h" @@ -2254,6 +2255,11 @@ void GamePlayer::Trade(nobBaseWarehouse* goalWh, const boost_variant2GI_GetCheats().areAllBuildingsEnabled()); +} + void GamePlayer::FillVisualSettings(VisualSettings& visualSettings) const { Distributions& visDistribution = visualSettings.distribution; diff --git a/libs/s25main/GamePlayer.h b/libs/s25main/GamePlayer.h index 59ee84347..a646a12fd 100644 --- a/libs/s25main/GamePlayer.h +++ b/libs/s25main/GamePlayer.h @@ -285,7 +285,7 @@ class GamePlayer : public GamePlayerInfo void EnableBuilding(BuildingType type) { building_enabled[type] = true; } void DisableBuilding(BuildingType type) { building_enabled[type] = false; } - bool IsBuildingEnabled(BuildingType type) const { return building_enabled[type]; } + bool IsBuildingEnabled(BuildingType type) const; /// Set the area the player may have territory in /// Nothing means all is allowed. See Lua description std::vector& GetRestrictedArea() { return restricted_area; } diff --git a/tests/s25Main/integration/testCheats.cpp b/tests/s25Main/integration/testCheats.cpp index bbcf968fd..284b8276f 100644 --- a/tests/s25Main/integration/testCheats.cpp +++ b/tests/s25Main/integration/testCheats.cpp @@ -44,6 +44,20 @@ BOOST_FIXTURE_TEST_CASE(CanToggleCheatModeOnAndOffRepeatedly, CheatsFixture) BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == false); } +BOOST_FIXTURE_TEST_CASE(TurningCheatModeOffDisablesAllCheats, CheatsFixture) +{ + cheats.toggleCheatMode(); + cheats.toggleAllVisible(); + BOOST_TEST_REQUIRE(cheats.isAllVisible() == true); + cheats.toggleAllBuildingsEnabled(); + BOOST_TEST_REQUIRE(cheats.areAllBuildingsEnabled() == true); + cheats.toggleCheatMode(); + BOOST_TEST_REQUIRE(cheats.isAllVisible() == false); + BOOST_TEST_REQUIRE(cheats.areAllBuildingsEnabled() == false); + // testing toggleHumanAIPlayer would require GameClient::state==Loaded, which is guaranteed in code (because Cheats + // only exist after the game is loaded) but is not the case in tests - skipping +} + namespace { MOCK_BASE_CLASS(MockGameInterface, GameInterface) { @@ -65,11 +79,11 @@ MOCK_BASE_CLASS(MockGameInterface, GameInterface) }; } // namespace -BOOST_FIXTURE_TEST_CASE(CanToggleAllVisible_IfCheatModeIsOn_ButOnlyDisableAllVisible_IfCheatModeIsNotOn, CheatsFixture) +BOOST_FIXTURE_TEST_CASE(CanToggleAllVisible_IfCheatModeIsOn, CheatsFixture) { MockGameInterface mgi; MOCK_EXPECT(mgi.GI_GetCheats).returns(std::ref(gameDesktop.GI_GetCheats())); - MOCK_EXPECT(mgi.GI_UpdateMapVisibility).exactly(4); // because the actual toggling should occur 4 times + MOCK_EXPECT(mgi.GI_UpdateMapVisibility).exactly(3); // how many toggles should actually occur world.SetGameInterface(&mgi); MapPoint farawayPos = p1HQPos; @@ -88,17 +102,23 @@ BOOST_FIXTURE_TEST_CASE(CanToggleAllVisible_IfCheatModeIsOn_ButOnlyDisableAllVis BOOST_TEST_REQUIRE((viewer.GetVisibility(farawayPos) == Visibility::Visible) == true); cheats.toggleAllVisible(); - // now not visible BOOST_TEST_REQUIRE((viewer.GetVisibility(farawayPos) == Visibility::Visible) == false); - cheats.toggleAllVisible(); - // visible again BOOST_TEST_REQUIRE((viewer.GetVisibility(farawayPos) == Visibility::Visible) == true); +} +BOOST_FIXTURE_TEST_CASE(CanToggleAllBuildingsEnabled_IfCheatModeIsOn, CheatsFixture) +{ + BOOST_TEST_REQUIRE(cheats.areAllBuildingsEnabled() == false); + cheats.toggleAllBuildingsEnabled(); + BOOST_TEST_REQUIRE(cheats.areAllBuildingsEnabled() == false); cheats.toggleCheatMode(); - cheats.toggleAllVisible(); - // again not visible, despite cheat mode being off - BOOST_TEST_REQUIRE((viewer.GetVisibility(farawayPos) == Visibility::Visible) == false); + cheats.toggleAllBuildingsEnabled(); + BOOST_TEST_REQUIRE(cheats.areAllBuildingsEnabled() == true); + cheats.toggleAllBuildingsEnabled(); + BOOST_TEST_REQUIRE(cheats.areAllBuildingsEnabled() == false); + cheats.toggleAllBuildingsEnabled(); + BOOST_TEST_REQUIRE(cheats.areAllBuildingsEnabled() == true); } BOOST_AUTO_TEST_SUITE_END()