Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cheat to enable all buildings #1723

Merged
merged 3 commits into from
Jan 21, 2025
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
2 changes: 2 additions & 0 deletions libs/s25main/CheatCommandTracker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
37 changes: 32 additions & 5 deletions libs/s25main/Cheats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,58 @@ 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
{
if(isCheatModeOn())
GAMECLIENT.CheatArmageddon();
}

void Cheats::turnAllCheatsOff()
{
if(isAllVisible_)
toggleAllVisible();
if(areAllBuildingsEnabled_)
toggleAllBuildingsEnabled();
if(isHumanAIPlayer_)
toggleHumanAIPlayer();
}
9 changes: 8 additions & 1 deletion libs/s25main/Cheats.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_;
};
6 changes: 6 additions & 0 deletions libs/s25main/GamePlayer.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 "GamePlayer.h"
#include "Cheats.h"
#include "EventManager.h"
#include "FindWhConditions.h"
#include "GameInterface.h"
Expand Down Expand Up @@ -2254,6 +2255,11 @@ void GamePlayer::Trade(nobBaseWarehouse* goalWh, const boost_variant2<GoodType,
}
}

bool GamePlayer::IsBuildingEnabled(BuildingType type) const
{
return building_enabled[type] || (isHuman() && world.GetGameInterface()->GI_GetCheats().areAllBuildingsEnabled());
}

void GamePlayer::FillVisualSettings(VisualSettings& visualSettings) const
{
Distributions& visDistribution = visualSettings.distribution;
Expand Down
2 changes: 1 addition & 1 deletion libs/s25main/GamePlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<MapPoint>& GetRestrictedArea() { return restricted_area; }
Expand Down
36 changes: 28 additions & 8 deletions tests/s25Main/integration/testCheats.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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;
Expand All @@ -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()
Loading