diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index f7b17e2f1..559eaf6bb 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/AI/Nullkiller/AIGateway.cpp @@ -77,6 +77,7 @@ AIGateway::AIGateway() destinationTeleport = ObjectInstanceID(); destinationTeleportPos = int3(-1); nullkiller.reset(new Nullkiller()); + announcedCheatingProblem = false; } AIGateway::~AIGateway() @@ -828,7 +829,14 @@ void AIGateway::makeTurn() boost::shared_lock gsLock(CGameState::mutex); setThreadName("AIGateway::makeTurn"); - cb->sendMessage("vcmieagles"); + if(cb->getStartInfo()->extraOptionsInfo.cheatsAllowed) + cb->sendMessage("vcmieagles"); + else + { + if(!announcedCheatingProblem) + cb->sendMessage("Nullkiller AI currently requires the ability to cheat in order to function correctly! Please enable!"); + announcedCheatingProblem = true; + } retrieveVisitableObjs(); diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index 2c157d0a0..00f1b7c2e 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -96,6 +96,7 @@ public: std::unique_ptr makingTurn; private: boost::mutex turnInterruptionMutex; + bool announcedCheatingProblem; public: ObjectInstanceID selectedObject; diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 1d917be60..47f490262 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -63,7 +63,7 @@ "vcmi.mainMenu.serverClosing" : "Closing...", "vcmi.mainMenu.hostTCP" : "Host TCP/IP game", "vcmi.mainMenu.joinTCP" : "Join TCP/IP game", - + "vcmi.lobby.filepath" : "File path", "vcmi.lobby.creationDate" : "Creation date", "vcmi.lobby.scenarioName" : "Scenario name", diff --git a/Mods/vcmi/config/vcmi/german.json b/Mods/vcmi/config/vcmi/german.json index a9d1421d8..0f6937a9f 100644 --- a/Mods/vcmi/config/vcmi/german.json +++ b/Mods/vcmi/config/vcmi/german.json @@ -63,7 +63,7 @@ "vcmi.mainMenu.serverClosing" : "Trenne...", "vcmi.mainMenu.hostTCP" : "Hoste TCP/IP Spiel", "vcmi.mainMenu.joinTCP" : "Trete TCP/IP Spiel bei", - + "vcmi.lobby.filepath" : "Dateipfad", "vcmi.lobby.creationDate" : "Erstellungsdatum", "vcmi.lobby.scenarioName" : "Szenario-Name", diff --git a/client/lobby/CLobbyScreen.cpp b/client/lobby/CLobbyScreen.cpp index 585f43a73..1a21834f3 100644 --- a/client/lobby/CLobbyScreen.cpp +++ b/client/lobby/CLobbyScreen.cpp @@ -230,6 +230,16 @@ void CLobbyScreen::toggleChat() void CLobbyScreen::updateAfterStateChange() { + if(CSH->isHost() && screenType == ESelectionScreen::newGame) + { + bool isMultiplayer = CSH->loadMode == ELoadMode::MULTI; + ExtraOptionsInfo info = SEL->getStartInfo()->extraOptionsInfo; + info.cheatsAllowed = isMultiplayer ? persistentStorage["startExtraOptions"]["multiPlayer"]["cheatsAllowed"].Bool() : !persistentStorage["startExtraOptions"]["singlePlayer"]["cheatsNotAllowed"].Bool(); + info.unlimitedReplay = persistentStorage["startExtraOptions"][isMultiplayer ? "multiPlayer" : "singlePlayer"]["unlimitedReplay"].Bool(); + if(info.cheatsAllowed != CSH->si->extraOptionsInfo.cheatsAllowed || info.unlimitedReplay != CSH->si->extraOptionsInfo.unlimitedReplay) + CSH->setExtraOptionsInfo(info); + } + if(CSH->mi) { if (tabOpt) diff --git a/client/lobby/OptionsTabBase.cpp b/client/lobby/OptionsTabBase.cpp index bb150efe7..f31bfb270 100644 --- a/client/lobby/OptionsTabBase.cpp +++ b/client/lobby/OptionsTabBase.cpp @@ -22,6 +22,7 @@ #include "../../lib/Languages.h" #include "../../lib/MetaString.h" #include "../../lib/CGeneralTextHandler.h" +#include "../../lib/CConfigHandler.h" static std::string timeToString(int time) { @@ -100,12 +101,18 @@ OptionsTabBase::OptionsTabBase(const JsonPath & configPath) }); addCallback("setCheatAllowed", [&](int index){ + bool isMultiplayer = CSH->loadMode == ELoadMode::MULTI; + Settings entry = persistentStorage.write["startExtraOptions"][isMultiplayer ? "multiPlayer" : "singlePlayer"][isMultiplayer ? "cheatsAllowed" : "cheatsNotAllowed"]; + entry->Bool() = isMultiplayer ? index : !index; ExtraOptionsInfo info = SEL->getStartInfo()->extraOptionsInfo; info.cheatsAllowed = index; CSH->setExtraOptionsInfo(info); }); addCallback("setUnlimitedReplay", [&](int index){ + bool isMultiplayer = CSH->loadMode == ELoadMode::MULTI; + Settings entry = persistentStorage.write["startExtraOptions"][isMultiplayer ? "multiPlayer" : "singlePlayer"]["unlimitedReplay"]; + entry->Bool() = index; ExtraOptionsInfo info = SEL->getStartInfo()->extraOptionsInfo; info.unlimitedReplay = index; CSH->setExtraOptionsInfo(info); @@ -410,13 +417,13 @@ void OptionsTabBase::recreate(bool campaign) if(auto buttonCheatAllowed = widget("buttonCheatAllowed")) { buttonCheatAllowed->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.cheatsAllowed); - buttonCheatAllowed->block(SEL->screenType == ESelectionScreen::loadGame); + buttonCheatAllowed->block(CSH->isGuest()); } if(auto buttonUnlimitedReplay = widget("buttonUnlimitedReplay")) { buttonUnlimitedReplay->setSelectedSilent(SEL->getStartInfo()->extraOptionsInfo.unlimitedReplay); - buttonUnlimitedReplay->block(SEL->screenType == ESelectionScreen::loadGame); + buttonUnlimitedReplay->block(CSH->isGuest()); } if(auto textureCampaignOverdraw = widget("textureCampaignOverdraw")) diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 69427e7fd..59c8f4c92 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -294,6 +294,7 @@ void CGameState::updateOnLoad(StartInfo * si) scenarioOps->playerInfos = si->playerInfos; for(auto & i : si->playerInfos) gs->players[i.first].human = i.second.isControlledByHuman(); + scenarioOps->extraOptionsInfo = si->extraOptionsInfo; } void CGameState::initNewGame(const IMapService * mapService, bool allowSavingRandomMap, Load::ProgressAccumulator & progressTracking) diff --git a/server/CVCMIServer.cpp b/server/CVCMIServer.cpp index 445be63d8..f908bc434 100644 --- a/server/CVCMIServer.cpp +++ b/server/CVCMIServer.cpp @@ -16,6 +16,7 @@ #include "processors/PlayerMessageProcessor.h" #include "../lib/CHeroHandler.h" +#include "../lib/CPlayerState.h" #include "../lib/MetaString.h" #include "../lib/registerTypes/RegisterTypesLobbyPacks.h" #include "../lib/serializer/CMemorySerializer.h" @@ -332,6 +333,8 @@ void CVCMIServer::startGameImmediately() setState(EServerState::GAMEPLAY); lastTimerUpdateTime = gameplayStartTime = std::chrono::steady_clock::now(); onTimer(); + + multiplayerWelcomeMessage(); } void CVCMIServer::onDisconnected(const std::shared_ptr & connection, const std::string & errorMessage) @@ -979,6 +982,38 @@ ui8 CVCMIServer::getIdOfFirstUnallocatedPlayer() const return 0; } +void CVCMIServer::multiplayerWelcomeMessage() +{ + int humanPlayer = 0; + for (auto & pi : si->playerInfos) + if(gh->getPlayerState(pi.first)->isHuman()) + humanPlayer++; + + if(humanPlayer < 2) // Singleplayer + return; + + std::vector optionIds; + if(si->extraOptionsInfo.cheatsAllowed) + optionIds.push_back("vcmi.optionsTab.cheatAllowed.hover"); + if(si->extraOptionsInfo.unlimitedReplay) + optionIds.push_back("vcmi.optionsTab.unlimitedReplay.hover"); + + if(!optionIds.size()) // No settings to publish + return; + + MetaString str; + str.appendTextID("vcmi.optionsTab.extraOptions.hover"); + str.appendRawString(": "); + for(int i = 0; i < optionIds.size(); i++) + { + str.appendTextID(optionIds[i]); + if(i < optionIds.size() - 1) + str.appendRawString(", "); + } + + gh->playerMessages->broadcastSystemMessage(str); +} + INetworkHandler & CVCMIServer::getNetworkHandler() { return *networkHandler; diff --git a/server/CVCMIServer.h b/server/CVCMIServer.h index 301fe0c43..6fa8f882b 100644 --- a/server/CVCMIServer.h +++ b/server/CVCMIServer.h @@ -130,4 +130,6 @@ public: void setCampaignBonus(int bonusId); ui8 getIdOfFirstUnallocatedPlayer() const; + + void multiplayerWelcomeMessage(); };