Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions libs/s25main/CheatCommandTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
15 changes: 15 additions & 0 deletions libs/s25main/Cheats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand All @@ -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())
Expand Down
6 changes: 6 additions & 0 deletions libs/s25main/Cheats.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
};
5 changes: 5 additions & 0 deletions libs/s25main/GameInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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<GameInterface&>(*this).GI_GetCheats(); }
};
2 changes: 2 additions & 0 deletions libs/s25main/desktops/dskGameInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion libs/s25main/world/GameWorldBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions libs/s25main/world/GameWorldViewer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
Expand Down
54 changes: 9 additions & 45 deletions tests/s25Main/integration/testCheatCommandTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
106 changes: 74 additions & 32 deletions tests/s25Main/integration/testCheats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <turtle/mock.hpp>

BOOST_AUTO_TEST_SUITE(CheatsTests)

namespace {
template<unsigned T_numPlayers>
struct CheatsFixture : WorldFixture<CreateEmptyWorld, T_numPlayers>
constexpr auto numPlayers = 1;
constexpr auto worldWidth = 64;
constexpr auto worldHeight = 64;
struct CheatsFixture : WorldFixture<CreateEmptyWorld, numPlayers, worldWidth, worldHeight>
{
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()
Loading