Prechádzať zdrojové kódy

Merge pull request #3397 from Laserlicht/cheat_allow

option for disabling cheats
Ivan Savenko 1 rok pred
rodič
commit
ae79c5e953

+ 8 - 0
Mods/vcmi/config/vcmi/english.json

@@ -305,6 +305,14 @@
 	"vcmi.optionsTab.simturns.months.1" : " %d month",
 	"vcmi.optionsTab.simturns.months.2" : " %d months",
 
+	"vcmi.optionsTab.extraOptions.hover" : "Extra Options",
+	"vcmi.optionsTab.extraOptions.help" : "Additional settings for the game",
+
+	"vcmi.optionsTab.cheatAllowed.hover" : "Allow cheats",
+	"vcmi.optionsTab.unlimitedReplay.hover" : "Unlimited battle replay",
+	"vcmi.optionsTab.cheatAllowed.help" : "{Allow cheats}\nAllows the inputs of cheats during the game.",
+	"vcmi.optionsTab.unlimitedReplay.help" : "{Unlimited battle replay}\nNo limit of replaying battles.",
+
 	// Custom victory conditions for H3 campaigns and HotA maps
 	"vcmi.map.victoryCondition.daysPassed.toOthers" : "The enemy has managed to survive till this day. Victory is theirs!",
 	"vcmi.map.victoryCondition.daysPassed.toSelf" : "Congratulations! You have managed to survive. Victory is yours!",

+ 8 - 0
Mods/vcmi/config/vcmi/german.json

@@ -299,6 +299,14 @@
 	"vcmi.optionsTab.simturns.months.1" : "%d Monat",
 	"vcmi.optionsTab.simturns.months.2" : "%d Monate",
 
+	"vcmi.optionsTab.extraOptions.hover" : "Extra Optionen",
+	"vcmi.optionsTab.extraOptions.help" : "Zusätzliche Einstellungen für das Spiel",
+	
+	"vcmi.optionsTab.cheatAllowed.hover" : "Cheats erlauben",
+	"vcmi.optionsTab.unlimitedReplay.hover" : "Unbegrenzte Kampfwiederholung",
+	"vcmi.optionsTab.cheatAllowed.help" : "{Cheats erlauben}\nErlaubt die Eingabe von Cheats während des Spiels.",
+	"vcmi.optionsTab.unlimitedReplay.help" : "{Unbegrenzte Kampfwiederholung}\nKämpfe lassen sich unbegrenzt wiederholen.",
+
 	// Custom victory conditions for H3 campaigns and HotA maps
 	"vcmi.map.victoryCondition.daysPassed.toOthers" : "Der Feind hat es geschafft, bis zum heutigen Tag zu überleben. Der Sieg gehört ihm!",
 	"vcmi.map.victoryCondition.daysPassed.toSelf" : "Herzlichen Glückwunsch! Ihr habt es geschafft, zu überleben. Der Sieg ist euer!",

+ 2 - 0
client/CMakeLists.txt

@@ -52,6 +52,7 @@ set(client_SRCS
 	lobby/CScenarioInfoScreen.cpp
 	lobby/CSelectionBase.cpp
 	lobby/TurnOptionsTab.cpp
+	lobby/ExtraOptionsTab.cpp
 	lobby/OptionsTab.cpp
 	lobby/OptionsTabBase.cpp
 	lobby/RandomMapTab.cpp
@@ -215,6 +216,7 @@ set(client_HEADERS
 	lobby/CScenarioInfoScreen.h
 	lobby/CSelectionBase.h
 	lobby/TurnOptionsTab.h
+	lobby/ExtraOptionsTab.h
 	lobby/OptionsTab.h
 	lobby/OptionsTabBase.h
 	lobby/RandomMapTab.h

+ 7 - 0
client/CServerHandler.cpp

@@ -507,6 +507,13 @@ void CServerHandler::setTurnTimerInfo(const TurnTimerInfo & info) const
 	sendLobbyPack(lstt);
 }
 
+void CServerHandler::setExtraOptionsInfo(const ExtraOptionsInfo & info) const
+{
+	LobbySetExtraOptions lseo;
+	lseo.extraOptionsInfo = info;
+	sendLobbyPack(lseo);
+}
+
 void CServerHandler::sendMessage(const std::string & txt) const
 {
 	std::istringstream readed;

+ 2 - 0
client/CServerHandler.h

@@ -72,6 +72,7 @@ public:
 	virtual void setDifficulty(int to) const = 0;
 	virtual void setTurnTimerInfo(const TurnTimerInfo &) const = 0;
 	virtual void setSimturnsInfo(const SimturnsInfo &) const = 0;
+	virtual void setExtraOptionsInfo(const ExtraOptionsInfo & info) const = 0;
 	virtual void sendMessage(const std::string & txt) const = 0;
 	virtual void sendGuiAction(ui8 action) const = 0; // TODO: possibly get rid of it?
 	virtual void sendStartGame(bool allowOnlyAI = false) const = 0;
@@ -158,6 +159,7 @@ public:
 	void setDifficulty(int to) const override;
 	void setTurnTimerInfo(const TurnTimerInfo &) const override;
 	void setSimturnsInfo(const SimturnsInfo &) const override;
+	void setExtraOptionsInfo(const ExtraOptionsInfo &) const override;
 	void sendMessage(const std::string & txt) const override;
 	void sendGuiAction(ui8 action) const override;
 	void sendRestartGame() const override;

+ 4 - 0
client/NetPacksLobbyClient.cpp

@@ -16,6 +16,7 @@
 #include "lobby/OptionsTab.h"
 #include "lobby/RandomMapTab.h"
 #include "lobby/TurnOptionsTab.h"
+#include "lobby/ExtraOptionsTab.h"
 #include "lobby/SelectionTab.h"
 #include "lobby/CBonusSelection.h"
 
@@ -99,6 +100,9 @@ void ApplyOnLobbyScreenNetPackVisitor::visitLobbyGuiAction(LobbyGuiAction & pack
 	case LobbyGuiAction::OPEN_TURN_OPTIONS:
 		lobby->toggleTab(lobby->tabTurnOptions);
 		break;
+	case LobbyGuiAction::OPEN_EXTRA_OPTIONS:
+		lobby->toggleTab(lobby->tabExtraOptions);
+		break;
 	}
 }
 

+ 1 - 1
client/battle/BattleInterfaceClasses.cpp

@@ -471,7 +471,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
 	exit = std::make_shared<CButton>(Point(384, 505), AnimationPath::builtin("iok6432.def"), std::make_pair("", ""), [&](){ bExitf();}, EShortcut::GLOBAL_ACCEPT);
 	exit->setBorderColor(Colors::METALLIC_GOLD);
 	
-	if(allowReplay)
+	if(allowReplay || owner.cb->getStartInfo()->extraOptionsInfo.unlimitedReplay)
 	{
 		repeat = std::make_shared<CButton>(Point(24, 505), AnimationPath::builtin("icn6432.def"), std::make_pair("", ""), [&](){ bRepeatf();}, EShortcut::GLOBAL_CANCEL);
 		repeat->setBorderColor(Colors::METALLIC_GOLD);

+ 19 - 2
client/lobby/CLobbyScreen.cpp

@@ -12,6 +12,7 @@
 
 #include "CBonusSelection.h"
 #include "TurnOptionsTab.h"
+#include "ExtraOptionsTab.h"
 #include "OptionsTab.h"
 #include "RandomMapTab.h"
 #include "SelectionTab.h"
@@ -53,7 +54,10 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
 
 		buttonOptions = std::make_shared<CButton>(Point(411, 510), AnimationPath::builtin("GSPBUTT.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabOpt), EShortcut::LOBBY_ADDITIONAL_OPTIONS);
 		if(settings["general"]["enableUiEnhancements"].Bool())
-			buttonTurnOptions = std::make_shared<CButton>(Point(619, 510), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabTurnOptions), EShortcut::NONE);
+		{
+			buttonTurnOptions = std::make_shared<CButton>(Point(619, 105), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabTurnOptions), EShortcut::NONE);
+			buttonExtraOptions = std::make_shared<CButton>(Point(619, 510), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[46], std::bind(&CLobbyScreen::toggleTab, this, tabExtraOptions), EShortcut::NONE);
+		}
 	};
 
 	buttonChat = std::make_shared<CButton>(Point(619, 80), AnimationPath::builtin("GSPBUT2.DEF"), CGI->generaltexth->zelp[48], std::bind(&CLobbyScreen::toggleChat, this), EShortcut::LOBBY_HIDE_CHAT);
@@ -65,6 +69,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
 	{
 		tabOpt = std::make_shared<OptionsTab>();
 		tabTurnOptions = std::make_shared<TurnOptionsTab>();
+		tabExtraOptions = std::make_shared<ExtraOptionsTab>();
 		tabRand = std::make_shared<RandomMapTab>();
 		tabRand->mapInfoChanged += std::bind(&IServerAPI::setMapInfo, CSH, _1, _2);
 		buttonRMG = std::make_shared<CButton>(Point(411, 105), AnimationPath::builtin("GSPBUTT.DEF"), CGI->generaltexth->zelp[47], 0, EShortcut::LOBBY_RANDOM_MAP);
@@ -84,6 +89,7 @@ CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)
 	{
 		tabOpt = std::make_shared<OptionsTab>();
 		tabTurnOptions = std::make_shared<TurnOptionsTab>();
+		tabExtraOptions = std::make_shared<ExtraOptionsTab>();
 		buttonStart = std::make_shared<CButton>(Point(411, 535), AnimationPath::builtin("SCNRLOD.DEF"), CGI->generaltexth->zelp[103], std::bind(&CLobbyScreen::startScenario, this, false), EShortcut::LOBBY_LOAD_GAME);
 		initLobby();
 		break;
@@ -122,6 +128,8 @@ void CLobbyScreen::toggleTab(std::shared_ptr<CIntObject> tab)
 		CSH->sendGuiAction(LobbyGuiAction::OPEN_RANDOM_MAP_OPTIONS);
 	else if(tab == tabTurnOptions)
 		CSH->sendGuiAction(LobbyGuiAction::OPEN_TURN_OPTIONS);
+	else if(tab == tabExtraOptions)
+		CSH->sendGuiAction(LobbyGuiAction::OPEN_EXTRA_OPTIONS);
 	CSelectionBase::toggleTab(tab);
 }
 
@@ -157,6 +165,9 @@ void CLobbyScreen::toggleMode(bool host)
 	if (buttonTurnOptions)
 		buttonTurnOptions->addTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.turnOptions.hover"), FONT_SMALL, buttonColor);
 
+	if (buttonExtraOptions)
+		buttonExtraOptions->addTextOverlay(CGI->generaltexth->translate("vcmi.optionsTab.extraOptions.hover"), FONT_SMALL, buttonColor);
+
 	if(buttonRMG)
 	{
 		buttonRMG->addTextOverlay(CGI->generaltexth->allTexts[740], FONT_SMALL, buttonColor);
@@ -168,10 +179,14 @@ void CLobbyScreen::toggleMode(bool host)
 	if (buttonTurnOptions)
 		buttonTurnOptions->block(!host);
 
+	if (buttonExtraOptions)
+		buttonExtraOptions->block(!host);
+
 	if(CSH->mi)
 	{
 		tabOpt->recreate();
 		tabTurnOptions->recreate();
+		tabExtraOptions->recreate();
 	}
 }
 
@@ -191,7 +206,9 @@ void CLobbyScreen::updateAfterStateChange()
 		if (tabOpt)
 			tabOpt->recreate();
 		if (tabTurnOptions)
-		tabTurnOptions->recreate();
+			tabTurnOptions->recreate();
+		if (tabExtraOptions)
+			tabExtraOptions->recreate();
 	}
 
 	buttonStart->block(CSH->mi == nullptr || CSH->isGuest());

+ 3 - 0
client/lobby/CSelectionBase.h

@@ -27,6 +27,7 @@ class CToggleGroup;
 class RandomMapTab;
 class OptionsTab;
 class TurnOptionsTab;
+class ExtraOptionsTab;
 class SelectionTab;
 class InfoCard;
 class CChatBox;
@@ -60,6 +61,7 @@ public:
 	std::shared_ptr<CButton> buttonRMG;
 	std::shared_ptr<CButton> buttonOptions;
 	std::shared_ptr<CButton> buttonTurnOptions;
+	std::shared_ptr<CButton> buttonExtraOptions;
 	std::shared_ptr<CButton> buttonStart;
 	std::shared_ptr<CButton> buttonBack;
 	std::shared_ptr<CButton> buttonSimturns;
@@ -67,6 +69,7 @@ public:
 	std::shared_ptr<SelectionTab> tabSel;
 	std::shared_ptr<OptionsTab> tabOpt;
 	std::shared_ptr<TurnOptionsTab> tabTurnOptions;
+	std::shared_ptr<ExtraOptionsTab> tabExtraOptions;
 	std::shared_ptr<RandomMapTab> tabRand;
 	std::shared_ptr<CIntObject> curTab;
 

+ 18 - 0
client/lobby/ExtraOptionsTab.cpp

@@ -0,0 +1,18 @@
+/*
+ * ExtraOptionsTab.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#include "StdInc.h"
+#include "ExtraOptionsTab.h"
+
+ExtraOptionsTab::ExtraOptionsTab()
+	: OptionsTabBase(JsonPath::builtin("config/widgets/extraOptionsTab.json"))
+{
+
+}

+ 18 - 0
client/lobby/ExtraOptionsTab.h

@@ -0,0 +1,18 @@
+/*
+ * ExtraOptionsTab.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#pragma once
+
+#include "OptionsTabBase.h"
+
+class ExtraOptionsTab : public OptionsTabBase
+{
+public:
+	ExtraOptionsTab();
+};

+ 18 - 0
client/lobby/OptionsTabBase.cpp

@@ -90,6 +90,18 @@ OptionsTabBase::OptionsTabBase(const JsonPath & configPath)
 		CSH->setSimturnsInfo(info);
 	});
 
+	addCallback("setCheatAllowed", [&](int index){
+		ExtraOptionsInfo info = SEL->getStartInfo()->extraOptionsInfo;
+		info.cheatsAllowed = index;
+		CSH->setExtraOptionsInfo(info);
+	});
+
+	addCallback("setUnlimitedReplay", [&](int index){
+		ExtraOptionsInfo info = SEL->getStartInfo()->extraOptionsInfo;
+		info.unlimitedReplay = index;
+		CSH->setExtraOptionsInfo(info);
+	});
+
 	addCallback("setTurnTimerAccumulate", [&](int index){
 		TurnTimerInfo info = SEL->getStartInfo()->turnTimerInfo;
 		info.accumulatingTurnTimer = index;
@@ -385,4 +397,10 @@ void OptionsTabBase::recreate()
 					w->setItem(1);
 		}
 	}
+
+	if(auto buttonCheatAllowed = widget<CToggleButton>("buttonCheatAllowed"))
+		buttonCheatAllowed->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.cheatsAllowed);
+
+	if(auto buttonUnlimitedReplay = widget<CToggleButton>("buttonUnlimitedReplay"))
+		buttonUnlimitedReplay->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.unlimitedReplay);
 }

+ 101 - 0
config/widgets/extraOptionsTab.json

@@ -0,0 +1,101 @@
+{
+	"library" : "config/widgets/settings/library.json",
+	
+	"items":
+	[
+		{
+			"name": "background",
+			"type": "picture",
+			"image": "ADVOPTBK",
+			"position": {"x": 0, "y": 6}
+		},
+		{
+			"name": "labelTitle",
+			"type": "label",
+			"font": "big",
+			"alignment": "center",
+			"color": "yellow",
+			"text": "vcmi.optionsTab.extraOptions.hover",
+			"position": {"x": 222, "y": 36}
+		},
+		{
+			"name": "labelSubTitle",
+			"type": "multiLineLabel",
+			"font": "small",
+			"alignment": "center",
+			"color": "white",
+			"text": "vcmi.optionsTab.extraOptions.help",
+			"rect": {"x": 60, "y": 48, "w": 320, "h": 0},
+			"adoptHeight": true
+		},
+		{
+			"type": "transparentFilledRectangle",
+			"rect": {"x": 54, "y": 127, "w": 335, "h": 1},
+			"color": [24, 41, 90, 255]
+		},
+		{
+			"type": "transparentFilledRectangle",
+			"rect": {"x": 158, "y": 90, "w": 2, "h": 37},
+			"color": [24, 41, 90, 255]
+		},
+		{
+			"type": "transparentFilledRectangle",
+			"rect": {"x": 234, "y": 90, "w": 2, "h": 37},
+			"color": [24, 41, 90, 255]
+		},
+		{
+			"type": "transparentFilledRectangle",
+			"rect": {"x": 310, "y": 90, "w": 2, "h": 37},
+			"color": [24, 41, 90, 255]
+		},
+		{
+			"type": "transparentFilledRectangle",
+			"rect": {"x": 55, "y": 556, "w": 334, "h": 18},
+			"color": [24, 41, 90, 255]
+		},
+		{
+			"name": "ExtraOptionsButtons",
+			"type" : "verticalLayout",
+			"customType" : "toggleButton",
+			"position": {"x": 70, "y": 100},
+			"items":
+			[
+				{
+					"name": "buttonCheatAllowed",
+					"image": "lobby/checkbox",
+					"callback" : "setCheatAllowed",
+					"selected" : true
+				},
+				{
+					"name": "buttonUnlimitedReplay",
+					"image": "lobby/checkbox",
+					"callback" : "setUnlimitedReplay",
+					"selected" : true
+				}
+			]
+		},
+		{
+			"name": "ExtraOptionsLabels",
+			"type" : "verticalLayout",
+			"customType" : "label",
+			"position": {"x": 110, "y": 103},
+			"items":
+			[
+				{
+					"name": "labelCheatAllowed",
+					"font": "small",
+					"alignment": "left",
+					"color": "yellow",
+					"text": "vcmi.optionsTab.cheatAllowed.hover"
+				},
+				{
+					"name": "labelUnlimitedReplay",
+					"font": "small",
+					"alignment": "left",
+					"color": "yellow",
+					"text": "vcmi.optionsTab.unlimitedReplay.hover"
+				}
+			]
+		}
+	]
+}

+ 1 - 1
config/widgets/turnOptionsTab.json

@@ -328,7 +328,7 @@
 			"alignment": "left",
 			"color": "yellow",
 			"text": "vcmi.optionsTab.simturnsAI.hover",
-			"position": {"x": 110, "y": 540}
+			"position": {"x": 110, "y": 538}
 		}
 	],
 	

+ 21 - 0
lib/ExtraOptionsInfo.cpp

@@ -0,0 +1,21 @@
+/*
+ * ExtraOptionsInfo.cpp, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+#include "StdInc.h"
+#include "ExtraOptionsInfo.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+bool ExtraOptionsInfo::operator == (const ExtraOptionsInfo & other) const
+{
+	return cheatsAllowed == other.cheatsAllowed &&
+			unlimitedReplay == other.unlimitedReplay;
+}
+
+VCMI_LIB_NAMESPACE_END

+ 30 - 0
lib/ExtraOptionsInfo.h

@@ -0,0 +1,30 @@
+/*
+ * ExtraOptionsInfo.h, part of VCMI engine
+ *
+ * Authors: listed in file AUTHORS in main folder
+ *
+ * License: GNU General Public License v2.0 or later
+ * Full text of license available in license.txt file, in main folder
+ *
+ */
+
+#pragma once
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+struct DLL_LINKAGE ExtraOptionsInfo
+{
+	bool cheatsAllowed = true;
+	bool unlimitedReplay = false;
+
+	bool operator == (const ExtraOptionsInfo & other) const;
+
+	template <typename Handler>
+	void serialize(Handler &h, const int version)
+	{
+		h & cheatsAllowed;
+		h & unlimitedReplay;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 6 - 0
lib/StartInfo.h

@@ -13,6 +13,7 @@
 
 #include "GameConstants.h"
 #include "TurnTimerInfo.h"
+#include "ExtraOptionsInfo.h"
 #include "campaign/CampaignConstants.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -115,6 +116,7 @@ struct DLL_LINKAGE StartInfo
 	std::string fileURI;
 	SimturnsInfo simturnsInfo;
 	TurnTimerInfo turnTimerInfo;
+	ExtraOptionsInfo extraOptionsInfo;
 	std::string mapname; // empty for random map, otherwise name of the map or savegame
 	bool createRandomMap() const { return mapGenOptions != nullptr; }
 	std::shared_ptr<CMapGenOptions> mapGenOptions;
@@ -141,6 +143,10 @@ struct DLL_LINKAGE StartInfo
 		h & fileURI;
 		h & simturnsInfo;
 		h & turnTimerInfo;
+		if(version >= 832)
+			h & extraOptionsInfo;
+		else
+			extraOptionsInfo = ExtraOptionsInfo();
 		h & mapname;
 		h & mapGenOptions;
 		h & campState;

+ 1 - 0
lib/networkPacks/NetPackVisitor.h

@@ -164,6 +164,7 @@ public:
 	virtual void visitLobbySetPlayerName(LobbySetPlayerName & pack) {}
 	virtual void visitLobbySetSimturns(LobbySetSimturns & pack) {}
 	virtual void visitLobbySetTurnTime(LobbySetTurnTime & pack) {}
+	virtual void visitLobbySetExtraOptions(LobbySetExtraOptions & pack) {}
 	virtual void visitLobbySetDifficulty(LobbySetDifficulty & pack) {}
 	virtual void visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack) {}
 	virtual void visitLobbyShowMessage(LobbyShowMessage & pack) {}

+ 5 - 0
lib/networkPacks/NetPacksLib.cpp

@@ -770,6 +770,11 @@ void LobbySetTurnTime::visitTyped(ICPackVisitor & visitor)
 	visitor.visitLobbySetTurnTime(*this);
 }
 
+void LobbySetExtraOptions::visitTyped(ICPackVisitor & visitor)
+{
+	visitor.visitLobbySetExtraOptions(*this);
+}
+
 void LobbySetDifficulty::visitTyped(ICPackVisitor & visitor)
 {
 	visitor.visitLobbySetDifficulty(*this);

+ 13 - 1
lib/networkPacks/PacksForLobby.h

@@ -86,7 +86,7 @@ struct DLL_LINKAGE LobbyChatMessage : public CLobbyPackToPropagate
 struct DLL_LINKAGE LobbyGuiAction : public CLobbyPackToPropagate
 {
 	enum EAction : ui8 {
-		NONE, NO_TAB, OPEN_OPTIONS, OPEN_SCENARIO_LIST, OPEN_RANDOM_MAP_OPTIONS, OPEN_TURN_OPTIONS
+		NONE, NO_TAB, OPEN_OPTIONS, OPEN_SCENARIO_LIST, OPEN_RANDOM_MAP_OPTIONS, OPEN_TURN_OPTIONS, OPEN_EXTRA_OPTIONS
 	} action = NONE;
 
 
@@ -287,6 +287,18 @@ struct DLL_LINKAGE LobbySetTurnTime : public CLobbyPackToServer
 	}
 };
 
+struct DLL_LINKAGE LobbySetExtraOptions : public CLobbyPackToServer
+{
+	ExtraOptionsInfo extraOptionsInfo;
+
+	void visitTyped(ICPackVisitor & visitor) override;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & extraOptionsInfo;
+	}
+};
+
 struct DLL_LINKAGE LobbySetDifficulty : public CLobbyPackToServer
 {
 	ui8 difficulty = 0;

+ 1 - 0
lib/registerTypes/RegisterTypesLobbyPacks.h

@@ -57,6 +57,7 @@ void registerTypesLobbyPacks(Serializer &s)
 	s.template registerType<CLobbyPackToServer, LobbySetSimturns>();
 	s.template registerType<CLobbyPackToServer, LobbySetDifficulty>();
 	s.template registerType<CLobbyPackToServer, LobbyForceSetPlayer>();
+	s.template registerType<CLobbyPackToServer, LobbySetExtraOptions>();
 }
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/serializer/CSerializer.h

@@ -14,7 +14,7 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-const ui32 SERIALIZATION_VERSION = 831;
+const ui32 SERIALIZATION_VERSION = 832;
 const ui32 MINIMAL_SERIALIZATION_VERSION = 831;
 const std::string SAVEGAME_MAGIC = "VCMISVG";
 

+ 1 - 0
server/LobbyNetPackVisitors.h

@@ -88,6 +88,7 @@ public:
 	virtual void visitLobbySetPlayer(LobbySetPlayer & pack) override;
 	virtual void visitLobbySetPlayerName(LobbySetPlayerName & pack) override;
 	virtual void visitLobbySetTurnTime(LobbySetTurnTime & pack) override;
+	virtual void visitLobbySetExtraOptions(LobbySetExtraOptions & pack) override;
 	virtual void visitLobbySetSimturns(LobbySetSimturns & pack) override;
 	virtual void visitLobbySetDifficulty(LobbySetDifficulty & pack) override;
 	virtual void visitLobbyForceSetPlayer(LobbyForceSetPlayer & pack) override;

+ 6 - 0
server/NetPacksLobbyServer.cpp

@@ -414,6 +414,12 @@ void ApplyOnServerNetPackVisitor::visitLobbySetTurnTime(LobbySetTurnTime & pack)
 	result = true;
 }
 
+void ApplyOnServerNetPackVisitor::visitLobbySetExtraOptions(LobbySetExtraOptions & pack)
+{
+	srv.si->extraOptionsInfo = pack.extraOptionsInfo;
+	result = true;
+}
+
 void ApplyOnServerNetPackVisitor::visitLobbySetDifficulty(LobbySetDifficulty & pack)
 {
 	srv.si->difficulty = std::clamp<uint8_t>(pack.difficulty, 0, 4);

+ 1 - 1
server/processors/PlayerMessageProcessor.cpp

@@ -440,7 +440,7 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
 	std::vector<std::string> words;
 	boost::split(words, cheat, boost::is_any_of("\t\r\n "));
 
-	if (words.empty())
+	if (words.empty() || !gameHandler->getStartInfo()->extraOptionsInfo.cheatsAllowed)
 		return false;
 
 	//Make cheat name case-insensitive, but keep words/parameters (e.g. creature name) as it