From 4d37fe631b445cd77bf1ea624c50aa12c702b514 Mon Sep 17 00:00:00 2001 From: Laserlicht <13953785+Laserlicht@users.noreply.github.com> Date: Sat, 18 Oct 2025 21:43:05 +0200 Subject: [PATCH] battle only basic implementation --- .../Content/Sprites/lobby/battle-normal.png | Bin 0 -> 873 bytes .../Content/Sprites/lobby/battle-pressed.png | Bin 0 -> 863 bytes .../Content/Sprites/lobby/battleButton.json | 8 ++ Mods/vcmi/Content/config/english.json | 1 + Mods/vcmi/Content/config/german.json | 1 + client/CMakeLists.txt | 2 + client/CPlayerInterface.cpp | 3 +- client/NetPacksClient.cpp | 9 +- client/lobby/BattleOnlyMode.cpp | 124 ++++++++++++++++++ client/lobby/BattleOnlyMode.h | 32 +++++ client/lobby/SelectionTab.cpp | 11 ++ client/lobby/SelectionTab.h | 2 + lib/mapping/CMapHeader.cpp | 1 + lib/mapping/CMapHeader.h | 4 + lib/mapping/MapEditUtils.cpp | 8 +- lib/mapping/MapFormatJson.cpp | 4 + lib/modding/IdentifierStorage.cpp | 4 +- lib/networkPacks/PacksForClient.h | 2 + lib/serializer/ESerializationVersion.h | 3 +- server/CGameHandler.cpp | 10 ++ server/processors/TurnOrderProcessor.cpp | 10 ++ 21 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 Mods/vcmi/Content/Sprites/lobby/battle-normal.png create mode 100644 Mods/vcmi/Content/Sprites/lobby/battle-pressed.png create mode 100644 Mods/vcmi/Content/Sprites/lobby/battleButton.json create mode 100644 client/lobby/BattleOnlyMode.cpp create mode 100644 client/lobby/BattleOnlyMode.h diff --git a/Mods/vcmi/Content/Sprites/lobby/battle-normal.png b/Mods/vcmi/Content/Sprites/lobby/battle-normal.png new file mode 100644 index 0000000000000000000000000000000000000000..ad6e918e5ea88b8fb3901ab8461df7c7f062d354 GIT binary patch literal 873 zcmV-v1D5=WP)^^yOQ8?(m2-N~mq;zJkQ z2^S)>X2jWx-uYoC(`0maHE!N8i|<+&*}gfkBon#toijM>1x_L`5h%p*;m4J4FF96R zb-$XJuD>yB^lWOJJ4fXUl_MWH^^^jw7+j;riqbz&-t#uvTFh;)B|Pu;-70chlUAJb zKU8)N*rR_*4TJ#N^WppT4gGmr`u3Iw-a&h-+1ry8ff?f~g-YKZd#qRwV*mt}njQj6 zTNs~|H{Z7lBX`Ug$4&m}7dO{m8Y@aBN%c4aq24k8tbBw&{UG&RhkkaAv21BL^OD6z zw$sS2(kBk3o^DlP+X4VuX4EbG>f5&8-8DCv&}NMDg^KG+TZ_4)J6Zg@Iyvd(uZ64j zMFhmmP#jjDa4~qsSuoGA;ym)>o!q;nH{P=^=PPlOn-&^_XPn~1tjE_PkXrRMPpFrk zw}?=(JXYX1>xTQRL(&Ts7q$^FNiaR=wG z*j6;01;E9t6(U;q%$%LMM(xi60U6v69CuEhDyLIwWvda+ z?D_CTKKp9w=imMrx?CNfp}%`<+`)n4&Nr8)zQ5`r_>((xTN^Y5C#tDIQ$$fzQB+an z{)Ye}P*jI93334l#7YE_@XyTDLg1OI3V?qBeu+yf2|szt00000NkvXXu0mjf&|aQH literal 0 HcmV?d00001 diff --git a/Mods/vcmi/Content/Sprites/lobby/battle-pressed.png b/Mods/vcmi/Content/Sprites/lobby/battle-pressed.png new file mode 100644 index 0000000000000000000000000000000000000000..f52afa391b42979652d7dfba78fcefa6b492825a GIT binary patch literal 863 zcmV-l1EBngP)9Q#QZ;RAe3DiE zObic%SVUTzrShUR{3MzHN!1WA5lsvsMtRZrf~G*=iINoMCoHZ-)cR8ueORS?x4p|P zd(QB1Z!ZvHd~kAdGG{X1%zWSE0RO`Xz_z}ZB!K~KAO!>nfMzCw0BL4sC_xMuX2VEY zqQ)!;3V<{@s~Pt7G>f zH|DJUyVFm0B+s1nCw&kFU?M>PaO%7s9`UkGR!?{R=Cya>yH~~fHm4S3V`tBKLnAt% zDga0UqCg#d@NxC)3!amdJujr9{u^^v@22|Mvt*%IJ@}Cq1WmwMYcPi-$4k6(u(IcM zw6)kTznIec`pp{hThdNa6uznM8g$403NvR`1vYmG^v87`*jpLgjuowTe{Y%@c0yb( zRtNUD6C!Dg?|=03JImuG&Zh57oKZXK-2Q+#@?Ped z4z;q=T6BNNdBJ=u*Jb4%SBDQ|o@xyjs~HM_6r^NWviOs6UDMM&JDpN?LKKQs-{-a# zduva+^mlD)N*Au`yE-$&%(R~LF?7;voMSmjG5Xz&{EqS~Z@3o=)ub(&=2}B1z0&0E zu@wLlX$^EfDxY8D5TWd9#}Tt+p^lyob)E-uzR>s%cxpDyTttOU^6zPxd6)A8YN zyPD-de|>hI^~LeZ`-dw_7OBSjRJKVn6g<#{QS?Ei?s-OuT6N^cgXwd!t|vpI$X~{|CTyMaYc2C p>J-H!#U#a5c*kcV(Fr01+yaBMLuxJJKmz~(002ovPDHLkV1ibsl{WwY literal 0 HcmV?d00001 diff --git a/Mods/vcmi/Content/Sprites/lobby/battleButton.json b/Mods/vcmi/Content/Sprites/lobby/battleButton.json new file mode 100644 index 000000000..c198750c7 --- /dev/null +++ b/Mods/vcmi/Content/Sprites/lobby/battleButton.json @@ -0,0 +1,8 @@ +{ + "basepath" : "lobby/", + "images" : + [ + { "frame" : 0, "file" : "battle-normal.png"}, + { "frame" : 1, "file" : "battle-pressed.png"} + ] +} diff --git a/Mods/vcmi/Content/config/english.json b/Mods/vcmi/Content/config/english.json index 28c1bec88..42abde7d5 100644 --- a/Mods/vcmi/Content/config/english.json +++ b/Mods/vcmi/Content/config/english.json @@ -138,6 +138,7 @@ "vcmi.lobby.deleteFile" : "Do you want to delete following file?", "vcmi.lobby.deleteFolder" : "Do you want to delete following folder?", "vcmi.lobby.deleteMode" : "Switch to delete mode and back", + "vcmi.lobby.battleOnlyMode" : "Battle only mode", "vcmi.broadcast.failedLoadGame" : "Failed to load game", "vcmi.broadcast.command" : "Use '!help' to list available commands", diff --git a/Mods/vcmi/Content/config/german.json b/Mods/vcmi/Content/config/german.json index a2a956ef5..ce2e916cb 100644 --- a/Mods/vcmi/Content/config/german.json +++ b/Mods/vcmi/Content/config/german.json @@ -138,6 +138,7 @@ "vcmi.lobby.deleteFile" : "Möchtet Ihr folgende Datei löschen?", "vcmi.lobby.deleteFolder" : "Möchtet Ihr folgenden Ordner löschen?", "vcmi.lobby.deleteMode" : "In den Löschmodus wechseln und zurück", + "vcmi.lobby.battleOnlyMode" : "Nur Kämpfen Modus", "vcmi.broadcast.failedLoadGame" : "Spiel konnte nicht geladen werden", "vcmi.broadcast.command" : "Benutze '!help' um alle verfügbaren Befehle aufzulisten", diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt index 165326f5f..25b009a59 100644 --- a/client/CMakeLists.txt +++ b/client/CMakeLists.txt @@ -52,6 +52,7 @@ set(vcmiclientcommon_SRCS gui/ShortcutHandler.cpp gui/WindowHandler.cpp + lobby/BattleOnlyMode.cpp lobby/CBonusSelection.cpp lobby/CCampaignInfoScreen.cpp lobby/CLobbyScreen.cpp @@ -262,6 +263,7 @@ set(vcmiclientcommon_HEADERS gui/TextAlignment.h gui/WindowHandler.h + lobby/BattleOnlyMode.h lobby/CBonusSelection.h lobby/CCampaignInfoScreen.h lobby/CLobbyScreen.h diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index acff46b6c..04170c63a 100644 --- a/client/CPlayerInterface.cpp +++ b/client/CPlayerInterface.cpp @@ -97,6 +97,7 @@ #include "../lib/mapObjects/MiscObjects.h" #include "../lib/mapObjects/ObjectTemplate.h" +#include "../lib/mapping/CMap.h" #include "../lib/mapping/CMapHeader.h" #include "../lib/networkPacks/PacksForClient.h" @@ -658,7 +659,7 @@ void CPlayerInterface::battleStart(const BattleID & battleID, const CCreatureSet { EVENT_HANDLER_CALLED_BY_CLIENT; - bool useQuickCombat = settings["adventure"]["quickCombat"].Bool(); + bool useQuickCombat = settings["adventure"]["quickCombat"].Bool() || GAME->map().getMap()->battleOnly; bool forceQuickCombat = settings["adventure"]["forceQuickCombat"].Bool(); if ((replayAllowed && useQuickCombat) || forceQuickCombat) diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index 29a8558f9..d6f6e8fc9 100644 --- a/client/NetPacksClient.cpp +++ b/client/NetPacksClient.cpp @@ -15,6 +15,7 @@ #include "windows/GUIClasses.h" #include "windows/CCastleInterface.h" #include "mapView/mapHandler.h" +#include "mainmenu/CMainMenu.h" #include "adventureMap/AdventureMapInterface.h" #include "adventureMap/CInGameConsole.h" #include "battle/BattleInterface.h" @@ -409,7 +410,13 @@ void ApplyClientNetPackVisitor::visitPlayerEndsGame(PlayerEndsGame & pack) adventureInt.reset(); } - GAME->server().showHighScoresAndEndGameplay(pack.player, pack.victoryLossCheckResult.victory(), pack.statistic); + if(!pack.silentEnd) + GAME->server().showHighScoresAndEndGameplay(pack.player, pack.victoryLossCheckResult.victory(), pack.statistic); + else + { + GAME->server().endGameplay(); + GAME->mainmenu()->menu->switchToTab("main"); + } } // In auto testing pack.mode we always close client if red pack.player won or lose diff --git a/client/lobby/BattleOnlyMode.cpp b/client/lobby/BattleOnlyMode.cpp new file mode 100644 index 000000000..9a851d878 --- /dev/null +++ b/client/lobby/BattleOnlyMode.cpp @@ -0,0 +1,124 @@ +/* + * BattleOnlyMode.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 "BattleOnlyMode.h" + +#include "../CServerHandler.h" +#include "../GameEngine.h" +#include "../GameInstance.h" + +#include "../gui/Shortcut.h" +#include "../gui/WindowHandler.h" +#include "../widgets/Buttons.h" +#include "../widgets/Images.h" + +#include "../../lib/gameState/CGameState.h" +#include "../../lib/StartInfo.h" +#include "../../lib/VCMIDirs.h" +#include "../../lib/CRandomGenerator.h" +#include "../../lib/callback/EditorCallback.h" +#include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapObjectConstructors/AObjectTypeHandler.h" +#include "../../lib/mapObjectConstructors/CObjectClassesHandler.h" +#include "../../lib/mapping/CMap.h" +#include "../../lib/mapping/CMapInfo.h" +#include "../../lib/mapping/CMapEditManager.h" +#include "../../lib/mapping/CMapService.h" +#include "../../lib/mapping/MapFormat.h" +#include "../../lib/filesystem/Filesystem.h" + +void BattleOnlyMode::openBattleWindow() +{ + ENGINE->windows().createAndPushWindow(); +} + +BattleOnlyModeWindow::BattleOnlyModeWindow() + : CWindowObject(BORDERED) +{ + OBJECT_CONSTRUCTION; + + pos.w = 640; + pos.h = 480; + + updateShadow(); + center(); + + backgroundTexture = std::make_shared(Rect(0, 0, pos.w, pos.h)); + backgroundTexture->setPlayerColor(PlayerColor(1)); + buttonOk = std::make_shared(Point(183, 388), AnimationPath::builtin("MuBchck"), CButton::tooltip(), [this](){ startBattle(); }, EShortcut::GLOBAL_ACCEPT); + buttonAbort = std::make_shared(Point(250, 388), AnimationPath::builtin("MuBcanc"), CButton::tooltip(), [this](){ close(); }, EShortcut::GLOBAL_CANCEL); +} + +void BattleOnlyModeWindow::startBattle() +{ + auto map = std::make_unique(nullptr); + map->version = EMapFormat::VCMI; + map->creationDateTime = std::time(nullptr); + map->width = 4; + map->height = 3; + map->mapLevels = 1; + map->battleOnly = true; + + map->initTerrain(); + map->getEditManager()->clearTerrain(&CRandomGenerator::getDefault()); + + //auto terrain = LIBRARY->terrainTypeHandler->objects; + map->getEditManager()->getTerrainSelection().selectAll(); + map->getEditManager()->drawTerrain(TerrainId::GRASS, 0, &CRandomGenerator::getDefault()); + + map->players[0].canComputerPlay = true; + map->players[0].canHumanPlay = true; + map->players[1] = map->players[0]; + + auto cb = std::make_unique(map.get()); + + auto knownHeroes = LIBRARY->objtypeh->knownSubObjects(Obj::HERO); + + auto addHero = [&](MapObjectSubID heroType, PlayerColor color, const int3 & position) + { + auto it = knownHeroes.find(heroType); + if (it == knownHeroes.end()) + return; // unknown hero type, nothing to add + + auto firstHeroType = *it; + auto factory = LIBRARY->objtypeh->getHandlerFor(Obj::HERO, firstHeroType); + auto templates = factory->getTemplates(); + + auto obj = std::dynamic_pointer_cast(factory->create(cb.get(), templates.front())); + obj->setOwner(color); + obj->pos = position; + /*if(heroType == MapObjectSubID(1)) + {*/ + obj->pushPrimSkill(PrimarySkill::ATTACK, 100); + obj->getArmy()->setCreature(SlotID(0), CreatureID(1), 10000); + //} + map->getEditManager()->insertObject(obj); + }; + + addHero(MapObjectSubID(1), PlayerColor(0), int3(2, 1, 0)); + addHero(MapObjectSubID(2), PlayerColor(1), int3(3, 1, 0)); + + auto path = VCMIDirs::get().userDataPath() / "Maps"; + boost::filesystem::create_directories(path); + const std::string fileName = "BattleOnlyMode.vmap"; + const auto fullPath = path / fileName; + CMapService mapService; + mapService.saveMap(map, fullPath); + CResourceHandler::get()->updateFilteredFiles([&](const std::string & mount) { return true; }); + + auto mapInfo = std::make_shared(); + mapInfo->mapInit("Maps/BattleOnlyMode"); + GAME->server().setMapInfo(mapInfo); + ExtraOptionsInfo extraOptions; + extraOptions.unlimitedReplay = true; + GAME->server().setExtraOptionsInfo(extraOptions); + GAME->server().sendStartGame(); +} diff --git a/client/lobby/BattleOnlyMode.h b/client/lobby/BattleOnlyMode.h new file mode 100644 index 000000000..3293e50e5 --- /dev/null +++ b/client/lobby/BattleOnlyMode.h @@ -0,0 +1,32 @@ +/* + * BattleOnlyMode.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 "../windows/CWindowObject.h" + +class FilledTexturePlayerColored; +class CButton; + +class BattleOnlyMode +{ +public: + static void openBattleWindow(); +}; +class BattleOnlyModeWindow : public CWindowObject +{ +private: + std::shared_ptr backgroundTexture; + std::shared_ptr buttonOk; + std::shared_ptr buttonAbort; + + void startBattle(); +public: + BattleOnlyModeWindow(); +}; \ No newline at end of file diff --git a/client/lobby/SelectionTab.cpp b/client/lobby/SelectionTab.cpp index a35922ba0..9fd8213fb 100644 --- a/client/lobby/SelectionTab.cpp +++ b/client/lobby/SelectionTab.cpp @@ -12,6 +12,7 @@ #include "SelectionTab.h" #include "CSelectionBase.h" #include "CLobbyScreen.h" +#include "BattleOnlyMode.h" #include "../CPlayerInterface.h" #include "../CServerHandler.h" @@ -241,6 +242,15 @@ SelectionTab::SelectionTab(ESelectionScreen Type) sortByDate->setOverlay(std::make_shared(ImagePath::builtin("lobby/selectionTabSortDate"))); buttonsSortBy.push_back(sortByDate); + bool isMultiplayer = GAME->server().loadMode == ELoadMode::MULTI; + + if(tabType == ESelectionScreen::newGame && !isMultiplayer) + { + buttonBattleOnlyMode = std::make_shared(Point(23, 18), AnimationPath::builtin("lobby/battleButton"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.battleOnlyMode")), [this, tabTitle, tabTitleDelete](){ + BattleOnlyMode::openBattleWindow(); + }); + } + if(tabType == ESelectionScreen::loadGame || tabType == ESelectionScreen::newGame) { buttonDeleteMode = std::make_shared(Point(367, 18), AnimationPath::builtin("lobby/deleteButton"), CButton::tooltip("", LIBRARY->generaltexth->translate("vcmi.lobby.deleteMode")), [this, tabTitle, tabTitleDelete](){ @@ -325,6 +335,7 @@ void SelectionTab::toggleMode() inputName->disable(); auto files = getFiles("Maps/", EResType::MAP); files.erase(ResourcePath("Maps/Tutorial.tut", EResType::MAP)); + files.erase(ResourcePath("Maps/BattleOnlyMode.vmap", EResType::MAP)); parseMaps(files); break; } diff --git a/client/lobby/SelectionTab.h b/client/lobby/SelectionTab.h index 1718173a6..de3768323 100644 --- a/client/lobby/SelectionTab.h +++ b/client/lobby/SelectionTab.h @@ -128,6 +128,8 @@ private: std::shared_ptr buttonDeleteMode; bool deleteMode; + std::shared_ptr buttonBattleOnlyMode; + bool enableUiEnhancements; std::shared_ptr buttonCampaignSet; diff --git a/lib/mapping/CMapHeader.cpp b/lib/mapping/CMapHeader.cpp index 7fc258be3..f26f6b3ed 100644 --- a/lib/mapping/CMapHeader.cpp +++ b/lib/mapping/CMapHeader.cpp @@ -129,6 +129,7 @@ CMapHeader::CMapHeader() , defeatIconIndex(0) , howManyTeams(0) , areAnyPlayers(false) + , battleOnly(false) { setupEvents(); allowedHeroes = LIBRARY->heroh->getDefaultAllowed(); diff --git a/lib/mapping/CMapHeader.h b/lib/mapping/CMapHeader.h index dbb8c0c71..c86ffc142 100644 --- a/lib/mapping/CMapHeader.h +++ b/lib/mapping/CMapHeader.h @@ -272,6 +272,8 @@ public: bool areAnyPlayers; /// Unused. True if there are any playable players on the map. + bool battleOnly; /// Battle only mode + /// "main quests" of the map that describe victory and loss conditions std::vector triggeredEvents; @@ -316,6 +318,8 @@ public: h & levelLimit; h & areAnyPlayers; + if (h.version >= Handler::Version::BATTLE_ONLY) + h & battleOnly; h & players; h & howManyTeams; h & allowedHeroes; diff --git a/lib/mapping/MapEditUtils.cpp b/lib/mapping/MapEditUtils.cpp index 70de36ad1..800740067 100644 --- a/lib/mapping/MapEditUtils.cpp +++ b/lib/mapping/MapEditUtils.cpp @@ -120,14 +120,14 @@ void CTerrainSelection::setSelection(const std::vector & vec) void CTerrainSelection::selectAll() { - selectRange(MapRect(int3(0, 0, 0), getMap()->width, getMap()->height)); - selectRange(MapRect(int3(0, 0, 1), getMap()->width, getMap()->height)); + for(int i = 0; i < getMap()->mapLevels; i++) + selectRange(MapRect(int3(0, 0, i), getMap()->width, getMap()->height)); } void CTerrainSelection::clearSelection() { - deselectRange(MapRect(int3(0, 0, 0), getMap()->width, getMap()->height)); - deselectRange(MapRect(int3(0, 0, 1), getMap()->width, getMap()->height)); + for(int i = 0; i < getMap()->mapLevels; i++) + deselectRange(MapRect(int3(0, 0, i), getMap()->width, getMap()->height)); } CObjectSelection::CObjectSelection(CMap * map) : CMapSelection(map) diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index ca84d3d6e..3415efbd3 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -870,6 +870,8 @@ void CMapLoaderJson::readHeader(const bool complete) readTeams(handler); //TODO: check mods + mapHeader->battleOnly = header["battleOnly"].Bool(); + if(complete) readOptions(handler); @@ -1223,6 +1225,8 @@ void CMapSaverJson::writeHeader() writeTeams(handler); + header["battleOnly"].Bool() = mapHeader->battleOnly; + writeOptions(handler); writeTranslations(); diff --git a/lib/modding/IdentifierStorage.cpp b/lib/modding/IdentifierStorage.cpp index 9206e13ce..966169f73 100644 --- a/lib/modding/IdentifierStorage.cpp +++ b/lib/modding/IdentifierStorage.cpp @@ -94,7 +94,7 @@ void CIdentifierStorage::requestIdentifier(const ObjectCallback & callback) cons checkIdentifier(callback.type); checkIdentifier(callback.name); - assert(!callback.localScope.empty()); + //assert(!callback.localScope.empty()); if (state != ELoadingState::FINISHED) // enqueue request if loading is still in progress scheduledRequests.push_back(callback); @@ -126,7 +126,7 @@ CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameW CIdentifierStorage::ObjectCallback CIdentifierStorage::ObjectCallback::fromNameAndType(const std::string & scope, const std::string & type, const std::string & fullName, const std::function & callback, bool optional, bool bypassDependenciesCheck, bool caseSensitive) { - assert(!scope.empty()); + //assert(!scope.empty()); auto scopeAndFullName = vstd::splitStringToPair(fullName, ':'); auto typeAndName = vstd::splitStringToPair(scopeAndFullName.second, '.'); diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 0a6bf1051..0159729ad 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -524,6 +524,7 @@ struct DLL_LINKAGE PlayerEndsGame : public CPackForClient PlayerColor player; EVictoryLossCheckResult victoryLossCheckResult; StatisticDataSet statistic; + bool silentEnd = false; void visitTyped(ICPackVisitor & visitor) override; @@ -532,6 +533,7 @@ struct DLL_LINKAGE PlayerEndsGame : public CPackForClient h & player; h & victoryLossCheckResult; h & statistic; + h & silentEnd; } }; diff --git a/lib/serializer/ESerializationVersion.h b/lib/serializer/ESerializationVersion.h index 45b11eb20..afd163772 100644 --- a/lib/serializer/ESerializationVersion.h +++ b/lib/serializer/ESerializationVersion.h @@ -50,8 +50,9 @@ enum class ESerializationVersion : int32_t BONUS_HIDDEN, // hidden bonus MORE_MAP_LAYERS, // more map layers CONFIGURABLE_RESOURCES, // configurable resources + BATTLE_ONLY, // battle only mode - CURRENT = CONFIGURABLE_RESOURCES, + CURRENT = BATTLE_ONLY, }; static_assert(ESerializationVersion::MINIMAL <= ESerializationVersion::CURRENT, "Invalid serialization version definition!"); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 722c601d6..db06c10b6 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3478,6 +3478,16 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player) if(!p || p->status != EPlayerStatus::INGAME) return; + if(gameState().getMap().battleOnly) + { + PlayerEndsGame peg; + peg.player = player; + peg.silentEnd = true; + sendAndApply(peg); + gameServer().setState(EServerState::SHUTDOWN); + return; + } + auto victoryLossCheckResult = gameState().checkForVictoryAndLoss(player); if (victoryLossCheckResult.victory() || victoryLossCheckResult.loss()) diff --git a/server/processors/TurnOrderProcessor.cpp b/server/processors/TurnOrderProcessor.cpp index 04ddfb683..46657a478 100644 --- a/server/processors/TurnOrderProcessor.cpp +++ b/server/processors/TurnOrderProcessor.cpp @@ -19,6 +19,7 @@ #include "../../lib/CPlayerState.h" #include "../../lib/mapping/CMap.h" #include "../../lib/mapObjects/CGObjectInstance.h" +#include "../../lib/mapObjects/CGHeroInstance.h" #include "../../lib/gameState/CGameState.h" #include "../../lib/pathfinder/CPathfinder.h" #include "../../lib/pathfinder/PathfinderOptions.h" @@ -364,6 +365,15 @@ bool TurnOrderProcessor::onPlayerEndsTurn(PlayerColor which) void TurnOrderProcessor::onGameStarted() { + if(gameHandler->gameInfo().getMapHeader()->battleOnly) + { + auto heroes = gameHandler->gameState().getMap().getObjects(); + auto hero1 = heroes.at(0); + auto hero2 = heroes.at(1); + gameHandler->startBattle(hero1, hero2); + return; + } + if (actingPlayers.empty()) blockedContacts = computeContactStatus();