diff --git a/libs/s25main/CheatCommandTracker.cpp b/libs/s25main/CheatCommandTracker.cpp index b12a48bab4..a74733721d 100644 --- a/libs/s25main/CheatCommandTracker.cpp +++ b/libs/s25main/CheatCommandTracker.cpp @@ -43,6 +43,7 @@ bool CheatCommandTracker::checkSpecialKeyEvent(const KeyEvent& ke) switch(ke.kt) { + case KeyType::F7: cheats_.toggleAllVisible(); break; case KeyType::F10: cheats_.toggleHumanAIPlayer(); break; default: break; } diff --git a/libs/s25main/Cheats.cpp b/libs/s25main/Cheats.cpp index 724e5f60a3..d86f5d2b3d 100644 --- a/libs/s25main/Cheats.cpp +++ b/libs/s25main/Cheats.cpp @@ -3,6 +3,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "Cheats.h" +#include "GameInterface.h" #include "network/GameClient.h" #include "world/GameWorldBase.h" @@ -18,6 +19,20 @@ void Cheats::toggleCheatMode() 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()) + { + isAllVisible_ = !isAllVisible_; + + // The minimap in the original game is not updated immediately, but in RTTR this would mess up the minimap. + if(GameInterface* gi = world_.GetGameInterface()) + gi->GI_UpdateMapVisibility(); + } +} + void Cheats::toggleHumanAIPlayer() const { if(isCheatModeOn() && !GAMECLIENT.IsReplayModeOn()) diff --git a/libs/s25main/Cheats.h b/libs/s25main/Cheats.h index ae3ef52d15..c59e384d70 100644 --- a/libs/s25main/Cheats.h +++ b/libs/s25main/Cheats.h @@ -16,10 +16,16 @@ class Cheats void toggleCheatMode(); bool isCheatModeOn() const { return isCheatModeOn_; } + // Classic S2 cheats + void toggleAllVisible(); + bool isAllVisible() const { return isAllVisible_; } + + // RTTR cheats void toggleHumanAIPlayer() const; void armageddon() const; private: bool isCheatModeOn_ = false; + bool isAllVisible_ = false; GameWorldBase& world_; }; diff --git a/libs/s25main/GameInterface.h b/libs/s25main/GameInterface.h index 1bdb3a9b21..aad106750a 100644 --- a/libs/s25main/GameInterface.h +++ b/libs/s25main/GameInterface.h @@ -6,6 +6,8 @@ #include "gameTypes/MapCoordinates.h" +class Cheats; + /// Interface, welches vom Spiel angesprocehn werden kann, um beispielsweise GUI wichtige Nachrichten /// zu übermiteln class GameInterface @@ -34,4 +36,7 @@ class GameInterface virtual void GI_CancelRoadBuilding() = 0; /// Executes the construction of a road virtual void GI_BuildRoad() = 0; + + virtual Cheats& GI_GetCheats() = 0; + const Cheats& GI_GetCheats() const { return const_cast(*this).GI_GetCheats(); } }; diff --git a/libs/s25main/desktops/dskGameInterface.h b/libs/s25main/desktops/dskGameInterface.h index bd1d8a2025..c30bf86011 100644 --- a/libs/s25main/desktops/dskGameInterface.h +++ b/libs/s25main/desktops/dskGameInterface.h @@ -84,6 +84,8 @@ class dskGameInterface : /// Baut die gewünschte bis jetzt noch visuelle Straße (schickt Anfrage an Server) void GI_BuildRoad() override; + Cheats& GI_GetCheats() override { return cheats_; } + // Sucht einen Weg von road_point_x/y zu cselx/y und baut ihn ( nur visuell ) // Bei Wasserwegen kann die Reichweite nicht bis zum gewünschten // Punkt reichen. Dann werden die Zielkoordinaten geändert, daher diff --git a/libs/s25main/world/GameWorldBase.h b/libs/s25main/world/GameWorldBase.h index f47521e291..4d242e6929 100644 --- a/libs/s25main/world/GameWorldBase.h +++ b/libs/s25main/world/GameWorldBase.h @@ -79,7 +79,8 @@ class GameWorldBase : public World // Remaining initialization after loading (BQ...) void InitAfterLoad(); - /// Setzt GameInterface + GameInterface* GetGameInterface() { return gi; } + const GameInterface* GetGameInterface() const { return gi; } void SetGameInterface(GameInterface* const gi) { this->gi = gi; } /// Get the economy mode handler if set. diff --git a/libs/s25main/world/GameWorldViewer.cpp b/libs/s25main/world/GameWorldViewer.cpp index a51f6440be..c2360b6ad0 100644 --- a/libs/s25main/world/GameWorldViewer.cpp +++ b/libs/s25main/world/GameWorldViewer.cpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "world/GameWorldViewer.h" +#include "Cheats.h" +#include "GameInterface.h" #include "GamePlayer.h" #include "GlobalGameSettings.h" #include "RttrForeachPt.h" @@ -113,6 +115,10 @@ BuildingQuality GameWorldViewer::GetBQ(const MapPoint& pt) const Visibility GameWorldViewer::GetVisibility(const MapPoint pt) const { + const auto* const gi = GetWorld().GetGameInterface(); + if(gi && gi->GI_GetCheats().isAllVisible()) + return Visibility::Visible; + /// Replaymodus und FoW aus? Dann alles sichtbar if(GAMECLIENT.IsReplayModeOn() && GAMECLIENT.IsReplayFOWDisabled()) return Visibility::Visible; diff --git a/tests/s25Main/integration/testCheatCommandTracker.cpp b/tests/s25Main/integration/testCheatCommandTracker.cpp index 3127b18695..5193e7e312 100644 --- a/tests/s25Main/integration/testCheatCommandTracker.cpp +++ b/tests/s25Main/integration/testCheatCommandTracker.cpp @@ -30,78 +30,42 @@ using CheatCommandTrackerFixture1P = CheatCommandTrackerFixture<1>; using CheatCommandTrackerFixture2P = CheatCommandTrackerFixture<2>; } // namespace -BOOST_FIXTURE_TEST_CASE(CheatModeIsOffByDefault, CheatCommandTrackerFixture1P) -{ - BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); -} - -BOOST_FIXTURE_TEST_CASE(CheatModeCanBeTurnedOn, CheatCommandTrackerFixture1P) +BOOST_FIXTURE_TEST_CASE(CheatModeCanBeTurnedOnAndOffRepeatedly, CheatCommandTrackerFixture1P) { + BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); // initially off trackString("winter"); BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == true); -} - -BOOST_FIXTURE_TEST_CASE(CheatModeCannotBeTurnedOn_InMultiplayer, CheatCommandTrackerFixture2P) -{ trackString("winter"); BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); -} - -BOOST_FIXTURE_TEST_CASE(CheatModeCanBeTurnedOff, CheatCommandTrackerFixture1P) -{ trackString("winter"); BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == true); trackString("winter"); BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); } -BOOST_FIXTURE_TEST_CASE(CheatModeCanBeTurnedOnAndOffRepeatedly, CheatCommandTrackerFixture1P) +BOOST_FIXTURE_TEST_CASE(CheatModeCannotBeTurnedOn_InMultiplayer, CheatCommandTrackerFixture2P) { - trackString("winter"); - BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == true); - trackString("winter"); - BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); - trackString("winter"); - BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == true); trackString("winter"); BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); } -BOOST_FIXTURE_TEST_CASE(CheatModeIsNotTurnedOn_WhenIncomplete, CheatCommandTrackerFixture1P) +BOOST_FIXTURE_TEST_CASE(CheatModeIsNotTurnedOn_IfTheCheatStringIsWrong, CheatCommandTrackerFixture1P) { - trackString("winte"); + trackString("winte"); // incomplete BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); -} -BOOST_FIXTURE_TEST_CASE(CheatModeIsNotTurnedOn_WhenInterruptedByAnotherKeyType, CheatCommandTrackerFixture1P) -{ trackString("win"); - tracker_.onKeyEvent(makeKeyEvent(KeyType::F10)); + tracker_.onKeyEvent(makeKeyEvent(KeyType::F10)); // interrupted by another key type trackString("ter"); BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); -} - -BOOST_FIXTURE_TEST_CASE(CheatModeIsNotTurnedOn_WhenInterruptedByAnotherLetter, CheatCommandTrackerFixture1P) -{ - trackString("wainter"); - BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); -} -BOOST_FIXTURE_TEST_CASE(CheatModeIsNotTurnedOn_WhenOrderOfCharactersIsWrong, CheatCommandTrackerFixture1P) -{ - trackString("winetr"); + trackString("wainter"); // interrupted by another letter BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); -} -BOOST_FIXTURE_TEST_CASE(CheatModeIsNotTurnedOn_WhenOrderOfCharactersIsWrong_Wraparound, CheatCommandTrackerFixture1P) -{ - trackString("rwinte"); + trackString("rwinte"); // order of characters is wrong BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); -} -BOOST_FIXTURE_TEST_CASE(CheatModeIsNotTurnedOn_WhenACharacterIsRepeated, CheatCommandTrackerFixture1P) -{ - trackString("winnter"); + trackString("winnter"); // character repeated BOOST_TEST_REQUIRE(cheats_.isCheatModeOn() == false); } diff --git a/tests/s25Main/integration/testCheats.cpp b/tests/s25Main/integration/testCheats.cpp index b1238095d1..bbcf968fd4 100644 --- a/tests/s25Main/integration/testCheats.cpp +++ b/tests/s25Main/integration/testCheats.cpp @@ -3,60 +3,102 @@ // SPDX-License-Identifier: GPL-2.0-or-later #include "Cheats.h" +#include "GamePlayer.h" +#include "desktops/dskGameInterface.h" #include "worldFixtures/CreateEmptyWorld.h" #include "worldFixtures/WorldFixture.h" +#include BOOST_AUTO_TEST_SUITE(CheatsTests) namespace { -template -struct CheatsFixture : WorldFixture +constexpr auto numPlayers = 1; +constexpr auto worldWidth = 64; +constexpr auto worldHeight = 64; +struct CheatsFixture : WorldFixture { - Cheats cheats{this->world}; -}; -using CheatsFixture1P = CheatsFixture<1>; -using CheatsFixture2P = CheatsFixture<2>; -} // namespace + CheatsFixture() + : cheats{gameDesktop.GI_GetCheats()}, viewer{gameDesktop.GetView().GetViewer()}, p1HQPos{p1.GetHQPos()} + {} -BOOST_FIXTURE_TEST_CASE(CheatsAreAllowed_InSinglePlayer, CheatsFixture1P) -{ - BOOST_TEST_REQUIRE(cheats.areCheatsAllowed() == true); -} + dskGameInterface gameDesktop{game, nullptr, 0, false}; + Cheats& cheats; + const GameWorldViewer& viewer; -BOOST_FIXTURE_TEST_CASE(CheatsAreNotAllowed_InMultiplayer, CheatsFixture2P) -{ - BOOST_TEST_REQUIRE(cheats.areCheatsAllowed() == false); -} - -BOOST_FIXTURE_TEST_CASE(CheatModeIsOffByDefault, CheatsFixture1P) -{ - BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == false); -} + GamePlayer& getPlayer(unsigned id) { return world.GetPlayer(id); } + GamePlayer& p1 = getPlayer(0); + const MapPoint p1HQPos; +}; +} // namespace -BOOST_FIXTURE_TEST_CASE(CanToggleCheatModeOn, CheatsFixture1P) +BOOST_FIXTURE_TEST_CASE(CanToggleCheatModeOnAndOffRepeatedly, CheatsFixture) { + BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == false); // initially off cheats.toggleCheatMode(); BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == true); -} - -BOOST_FIXTURE_TEST_CASE(CanToggleCheatModeOnAndOff, CheatsFixture1P) -{ + cheats.toggleCheatMode(); + BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == false); cheats.toggleCheatMode(); BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == true); cheats.toggleCheatMode(); BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == false); } -BOOST_FIXTURE_TEST_CASE(CanToggleCheatModeOnAndOffRepeatedly, CheatsFixture1P) +namespace { +MOCK_BASE_CLASS(MockGameInterface, GameInterface) { + // LCOV_EXCL_START + MOCK_METHOD(GI_PlayerDefeated, 1) + MOCK_METHOD(GI_UpdateMinimap, 1) + MOCK_METHOD(GI_FlagDestroyed, 1) + MOCK_METHOD(GI_TreatyOfAllianceChanged, 1) + MOCK_METHOD(GI_UpdateMapVisibility, 0) + MOCK_METHOD(GI_Winner, 1) + MOCK_METHOD(GI_TeamWinner, 1) + MOCK_METHOD(GI_StartRoadBuilding, 2) + MOCK_METHOD(GI_CancelRoadBuilding, 0) + MOCK_METHOD(GI_BuildRoad, 0) + // clang-format off + MOCK_METHOD(GI_GetCheats, 0, Cheats&(void)) + // clang-format on + // LCOV_EXCL_STOP +}; +} // namespace + +BOOST_FIXTURE_TEST_CASE(CanToggleAllVisible_IfCheatModeIsOn_ButOnlyDisableAllVisible_IfCheatModeIsNotOn, 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 + world.SetGameInterface(&mgi); + + MapPoint farawayPos = p1HQPos; + farawayPos.x += 20; + + // initially farawayPos is not visible + BOOST_TEST_REQUIRE((viewer.GetVisibility(farawayPos) == Visibility::Visible) == false); + + cheats.toggleAllVisible(); + // still not visible - cheat mode is not on + BOOST_TEST_REQUIRE((viewer.GetVisibility(farawayPos) == Visibility::Visible) == false); + cheats.toggleCheatMode(); - BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == true); - cheats.toggleCheatMode(); - BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == false); - cheats.toggleCheatMode(); - BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == true); + cheats.toggleAllVisible(); + // now visible - cheat mode is on + 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); + cheats.toggleCheatMode(); - BOOST_TEST_REQUIRE(cheats.isCheatModeOn() == false); + cheats.toggleAllVisible(); + // again not visible, despite cheat mode being off + BOOST_TEST_REQUIRE((viewer.GetVisibility(farawayPos) == Visibility::Visible) == false); } BOOST_AUTO_TEST_SUITE_END()