Bläddra i källkod

Reorganization of campaigns code

Ivan Savenko 2 år sedan
förälder
incheckning
8420a90aa4
38 ändrade filer med 659 tillägg och 611 borttagningar
  1. 1 1
      client/CServerHandler.cpp
  2. 1 1
      client/ClientCommandManager.cpp
  3. 0 1
      client/NetPacksClient.cpp
  4. 2 0
      client/eventsSDL/UserEventHandler.cpp
  5. 1 1
      client/lobby/CBonusSelection.cpp
  6. 2 1
      client/lobby/CBonusSelection.h
  7. 1 1
      client/lobby/CLobbyScreen.cpp
  8. 1 1
      client/lobby/SelectionTab.cpp
  9. 3 3
      client/mainmenu/CCampaignScreen.cpp
  10. 1 1
      client/mainmenu/CMainMenu.cpp
  11. 0 2
      client/mainmenu/CPrologEpilogVideo.cpp
  12. 2 1
      client/mainmenu/CPrologEpilogVideo.h
  13. 1 1
      client/windows/CCastleInterface.cpp
  14. 8 2
      cmake_modules/VCMI_lib.cmake
  15. 1 1
      lib/IGameCallback.cpp
  16. 1 1
      lib/NetPacksLib.cpp
  17. 1 1
      lib/StartInfo.cpp
  18. 1 1
      lib/StartInfo.h
  19. 58 0
      lib/campaign/CampaignConstants.h
  20. 8 248
      lib/campaign/CampaignHandler.cpp
  21. 43 0
      lib/campaign/CampaignHandler.h
  22. 26 0
      lib/campaign/CampaignScenarioPrologEpilog.h
  23. 252 0
      lib/campaign/CampaignState.cpp
  24. 236 322
      lib/campaign/CampaignState.h
  25. 1 1
      lib/gameState/CGameState.cpp
  26. 1 1
      lib/gameState/CGameStateCampaign.cpp
  27. 3 5
      lib/mapping/CMapInfo.cpp
  28. 0 1
      lib/registerTypes/RegisterTypes.cpp
  29. 0 1
      lib/registerTypes/TypesClientPacks1.cpp
  30. 0 1
      lib/registerTypes/TypesClientPacks2.cpp
  31. 1 1
      lib/registerTypes/TypesLobbyPacks.cpp
  32. 0 1
      lib/registerTypes/TypesMapObjects1.cpp
  33. 0 1
      lib/registerTypes/TypesMapObjects2.cpp
  34. 0 1
      lib/registerTypes/TypesMapObjects3.cpp
  35. 0 1
      lib/registerTypes/TypesServerPacks.cpp
  36. 0 1
      server/CGameHandler.cpp
  37. 1 1
      server/CVCMIServer.cpp
  38. 1 3
      server/NetPacksLobbyServer.cpp

+ 1 - 1
client/CServerHandler.cpp

@@ -41,7 +41,7 @@
 #include "../lib/NetPackVisitor.h"
 #include "../lib/StartInfo.h"
 #include "../lib/VCMIDirs.h"
-#include "../lib/mapping/CCampaignHandler.h"
+#include "../lib/campaign/CampaignState.h"
 #include "../lib/mapping/CMapInfo.h"
 #include "../lib/mapObjects/MiscObjects.h"
 #include "../lib/rmg/CMapGenOptions.h"

+ 1 - 1
client/ClientCommandManager.cpp

@@ -23,9 +23,9 @@
 #include "../lib/gameState/CGameState.h"
 #include "../lib/CPlayerState.h"
 #include "../lib/StringConstants.h"
+#include "../lib/campaign/CampaignHandler.h"
 #include "../lib/mapping/CMapService.h"
 #include "../lib/mapping/CMap.h"
-#include "../lib/mapping/CCampaignHandler.h"
 #include "windows/CCastleInterface.h"
 #include "render/CAnimation.h"
 #include "../CCallback.h"

+ 0 - 1
client/NetPacksClient.cpp

@@ -37,7 +37,6 @@
 #include "../lib/StartInfo.h"
 #include "../lib/CConfigHandler.h"
 #include "../lib/mapObjects/CGMarket.h"
-#include "../lib/mapping/CCampaignHandler.h"
 #include "../lib/gameState/CGameState.h"
 #include "../lib/CStack.h"
 #include "../lib/battle/BattleInfo.h"

+ 2 - 0
client/eventsSDL/UserEventHandler.cpp

@@ -20,6 +20,8 @@
 #include "../mainmenu/CPrologEpilogVideo.h"
 #include "../gui/EventDispatcher.h"
 
+#include "../../lib/campaign/CampaignState.h"
+
 #include <SDL_events.h>
 
 void UserEventHandler::handleUserEvent(const SDL_UserEvent & user)

+ 1 - 1
client/lobby/CBonusSelection.cpp

@@ -47,7 +47,7 @@
 #include "../../lib/CCreatureHandler.h"
 #include "../../lib/StartInfo.h"
 
-#include "../../lib/mapping/CCampaignHandler.h"
+#include "../../lib/campaign/CampaignState.h"
 #include "../../lib/mapping/CMapService.h"
 #include "../../lib/mapping/CMapInfo.h"
 

+ 2 - 1
client/lobby/CBonusSelection.h

@@ -10,7 +10,8 @@
 #pragma once
 
 #include "../windows/CWindowObject.h"
-#include "../lib/mapping/CCampaignHandler.h"
+
+#include "../lib/campaign/CampaignConstants.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 1 - 1
client/lobby/CLobbyScreen.cpp

@@ -27,8 +27,8 @@
 #include "../../lib/CModHandler.h"
 #include "../../lib/NetPacksLobby.h"
 #include "../../lib/CGeneralTextHandler.h"
+#include "../../lib/campaign/CampaignHandler.h"
 #include "../../lib/mapping/CMapInfo.h"
-#include "../../lib/mapping/CCampaignHandler.h"
 #include "../../lib/rmg/CMapGenOptions.h"
 
 CLobbyScreen::CLobbyScreen(ESelectionScreen screenType)

+ 1 - 1
client/lobby/SelectionTab.cpp

@@ -36,10 +36,10 @@
 #include "../../lib/CModHandler.h"
 #include "../../lib/GameSettings.h"
 #include "../../lib/filesystem/Filesystem.h"
+#include "../../lib/campaign/CampaignState.h"
 #include "../../lib/mapping/CMapInfo.h"
 #include "../../lib/mapping/CMapHeader.h"
 #include "../../lib/mapping/MapFormat.h"
-#include "../../lib/mapping/CCampaignHandler.h"
 #include "../../lib/serializer/Connection.h"
 
 bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::shared_ptr<CMapInfo> bbb)

+ 3 - 3
client/mainmenu/CCampaignScreen.cpp

@@ -42,7 +42,7 @@
 #include "../../lib/CHeroHandler.h"
 #include "../../lib/CCreatureHandler.h"
 
-#include "../../lib/mapping/CCampaignHandler.h"
+#include "../../lib/campaign/CampaignHandler.h"
 #include "../../lib/mapping/CMapService.h"
 
 #include "../../lib/mapObjects/CGHeroInstance.h"
@@ -96,8 +96,8 @@ CCampaignScreen::CCampaignButton::CCampaignButton(const JsonNode & config)
 
 	status = config["open"].Bool() ? CCampaignScreen::ENABLED : CCampaignScreen::DISABLED;
 
-	CampaignHeader header = CampaignHandler::getHeader(campFile);
-	hoverText = header.name;
+	auto header = CampaignHandler::getHeader(campFile);
+	hoverText = header->name;
 
 	if(status != CCampaignScreen::DISABLED)
 	{

+ 1 - 1
client/mainmenu/CMainMenu.cpp

@@ -42,6 +42,7 @@
 
 #include "../../lib/CGeneralTextHandler.h"
 #include "../../lib/JsonNode.h"
+#include "../../lib/campaign/CampaignHandler.h"
 #include "../../lib/serializer/Connection.h"
 #include "../../lib/serializer/CTypeList.h"
 #include "../../lib/filesystem/Filesystem.h"
@@ -54,7 +55,6 @@
 #include "../../lib/GameConstants.h"
 #include "../../lib/CRandomGenerator.h"
 #include "../../lib/CondSh.h"
-#include "../../lib/mapping/CCampaignHandler.h"
 
 #if defined(SINGLE_PROCESS_APP) && defined(VCMI_ANDROID)
 #include "../../server/CVCMIServer.h"

+ 0 - 2
client/mainmenu/CPrologEpilogVideo.cpp

@@ -18,8 +18,6 @@
 #include "../widgets/TextControls.h"
 #include "../render/Canvas.h"
 
-#include "../../lib/mapping/CCampaignHandler.h"
-
 
 CPrologEpilogVideo::CPrologEpilogVideo(CampaignScenarioPrologEpilog _spe, std::function<void()> callback)
 	: CWindowObject(BORDERED), spe(_spe), positionCounter(0), voiceSoundHandle(-1), exitCb(callback)

+ 2 - 1
client/mainmenu/CPrologEpilogVideo.h

@@ -9,7 +9,8 @@
  */
 #pragma once
 #include "../windows/CWindowObject.h"
-#include "../../lib/mapping/CCampaignHandler.h"
+
+#include "../../lib/campaign/CampaignScenarioPrologEpilog.h"
 
 class CMultiLineLabel;
 

+ 1 - 1
client/windows/CCastleInterface.cpp

@@ -45,7 +45,7 @@
 #include "../../lib/CTownHandler.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/StartInfo.h"
-#include "../../lib/mapping/CCampaignHandler.h"
+#include "../../lib/campaign/CampaignState.h"
 #include "../../lib/mapObjects/CGHeroInstance.h"
 #include "../../lib/mapObjects/CGTownInstance.h"
 

+ 8 - 2
cmake_modules/VCMI_lib.cmake

@@ -39,6 +39,9 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/bonuses/Propagators.cpp
 		${MAIN_LIB_DIR}/bonuses/Updaters.cpp
 
+		${MAIN_LIB_DIR}/campaign/CampaignHandler.cpp
+		${MAIN_LIB_DIR}/campaign/CampaignState.cpp
+
 		${MAIN_LIB_DIR}/events/ApplyDamage.cpp
 		${MAIN_LIB_DIR}/events/GameResumed.cpp
 		${MAIN_LIB_DIR}/events/ObjectVisitEnded.cpp
@@ -97,7 +100,6 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapObjects/MiscObjects.cpp
 		${MAIN_LIB_DIR}/mapObjects/ObjectTemplate.cpp
 
-		${MAIN_LIB_DIR}/mapping/CCampaignHandler.cpp
 		${MAIN_LIB_DIR}/mapping/CDrawRoadsOperation.cpp
 		${MAIN_LIB_DIR}/mapping/CMap.cpp
 		${MAIN_LIB_DIR}/mapping/CMapHeader.cpp
@@ -353,6 +355,11 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/bonuses/Propagators.h
 		${MAIN_LIB_DIR}/bonuses/Updaters.h
 
+		${MAIN_LIB_DIR}/campaign/CampaignConstants.h
+		${MAIN_LIB_DIR}/campaign/CampaignHandler.h
+		${MAIN_LIB_DIR}/campaign/CampaignScenarioPrologEpilog.h
+		${MAIN_LIB_DIR}/campaign/CampaignState.h
+
 		${MAIN_LIB_DIR}/events/ApplyDamage.h
 		${MAIN_LIB_DIR}/events/GameResumed.h
 		${MAIN_LIB_DIR}/events/ObjectVisitEnded.h
@@ -423,7 +430,6 @@ macro(add_main_lib TARGET_NAME LIBRARY_TYPE)
 		${MAIN_LIB_DIR}/mapObjects/MiscObjects.h
 		${MAIN_LIB_DIR}/mapObjects/ObjectTemplate.h
 
-		${MAIN_LIB_DIR}/mapping/CCampaignHandler.h
 		${MAIN_LIB_DIR}/mapping/CDrawRoadsOperation.h
 		${MAIN_LIB_DIR}/mapping/CMapDefines.h
 		${MAIN_LIB_DIR}/mapping/CMapEditManager.h

+ 1 - 1
lib/IGameCallback.cpp

@@ -32,7 +32,7 @@
 #include "mapObjectConstructors/CObjectClassesHandler.h"
 #include "mapObjects/CObjectHandler.h"
 #include "mapObjects/ObjectTemplate.h"
-#include "mapping/CCampaignHandler.h"
+#include "campaign/CampaignState.h"
 #include "StartInfo.h"
 #include "gameState/CGameState.h"
 #include "gameState/CGameStateCampaign.h"

+ 1 - 1
lib/NetPacksLib.cpp

@@ -31,7 +31,7 @@
 #include "mapObjects/CGMarket.h"
 #include "mapObjectConstructors/AObjectTypeHandler.h"
 #include "mapObjectConstructors/CObjectClassesHandler.h"
-#include "mapping/CCampaignHandler.h"
+#include "campaign/CampaignState.h"
 #include "GameSettings.h"
 
 

+ 1 - 1
lib/StartInfo.cpp

@@ -15,7 +15,7 @@
 #include "VCMI_Lib.h"
 #include "rmg/CMapGenOptions.h"
 #include "mapping/CMapInfo.h"
-#include "mapping/CCampaignHandler.h"
+#include "campaign/CampaignState.h"
 #include "mapping/CMapHeader.h"
 #include "mapping/CMapService.h"
 

+ 1 - 1
lib/StartInfo.h

@@ -10,7 +10,7 @@
 #pragma once
 
 #include "GameConstants.h"
-#include "../lib/mapping/CCampaignHandler.h"
+#include "campaign/CampaignConstants.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 

+ 58 - 0
lib/campaign/CampaignConstants.h

@@ -0,0 +1,58 @@
+/*
+ * CampaignConstants.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
+
+enum class CampaignVersion : uint8_t
+{
+	NONE = 0,
+	RoE = 4,
+	AB = 5,
+	SoD = 6,
+	WoG = 6,
+	//		Chr = 7, // Heroes Chronicles, likely identical to SoD, untested
+
+	VCMI = 1,
+	VCMI_MIN = 1,
+	VCMI_MAX = 1,
+};
+
+enum class CampaignStartOptions: int8_t
+{
+	NONE = 0,
+	START_BONUS,
+	HERO_CROSSOVER,
+	HERO_OPTIONS
+};
+
+enum class CampaignBonusType : int8_t
+{
+	NONE = -1,
+	SPELL,
+	MONSTER,
+	BUILDING,
+	ARTIFACT,
+	SPELL_SCROLL,
+	PRIMARY_SKILL,
+	SECONDARY_SKILL,
+	RESOURCE,
+	HEROES_FROM_PREVIOUS_SCENARIO,
+	HERO
+};
+
+enum class CampaignScenarioID : int8_t
+{
+	NONE = -1,
+	// no members - fake enum to create integer type that is not implicitly convertible to int
+};
+
+
+VCMI_LIB_NAMESPACE_END

+ 8 - 248
lib/mapping/CCampaignHandler.cpp → lib/campaign/CampaignHandler.cpp

@@ -8,86 +8,26 @@
  *
  */
 #include "StdInc.h"
-#include "CCampaignHandler.h"
+#include "CampaignHandler.h"
+
+#include "CampaignState.h"
 
 #include "../filesystem/Filesystem.h"
 #include "../filesystem/CCompressedStream.h"
 #include "../filesystem/CMemoryStream.h"
 #include "../filesystem/CBinaryReader.h"
 #include "../VCMI_Lib.h"
-#include "../vcmi_endian.h"
 #include "../CGeneralTextHandler.h"
 #include "../TextOperations.h"
-#include "../StartInfo.h"
 #include "../CModHandler.h"
-#include "../CArtHandler.h" //for hero crossover
-#include "../mapObjects/CGHeroInstance.h"//for hero crossover
-#include "../CHeroHandler.h"
 #include "../Languages.h"
 #include "../StringConstants.h"
-#include "CMapService.h"
-#include "CMap.h"
-#include "CMapInfo.h"
-
-// For hero crossover
-#include "serializer/CSerializer.h"
-#include "serializer/JsonDeserializer.h"
-#include "serializer/JsonSerializer.h"
+#include "../mapping/CMapHeader.h"
+#include "../mapping/CMapService.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
-CampaignRegions::RegionDescription CampaignRegions::RegionDescription::fromJson(const JsonNode & node)
-{
-	CampaignRegions::RegionDescription rd;
-	rd.infix = node["infix"].String();
-	rd.xpos = static_cast<int>(node["x"].Float());
-	rd.ypos = static_cast<int>(node["y"].Float());
-	return rd;
-}
-
-CampaignRegions CampaignRegions::fromJson(const JsonNode & node)
-{
-	CampaignRegions cr;
-	cr.campPrefix = node["prefix"].String();
-	cr.colorSuffixLength = static_cast<int>(node["color_suffix_length"].Float());
-
-	for(const JsonNode & desc : node["desc"].Vector())
-		cr.regions.push_back(CampaignRegions::RegionDescription::fromJson(desc));
-	
-	return cr;
-}
-
-CampaignRegions CampaignRegions::getLegacy(int campId)
-{
-	static std::vector<CampaignRegions> campDescriptions;
-	if(campDescriptions.empty()) //read once
-	{
-		const JsonNode config(ResourceID("config/campaign_regions.json"));
-		for(const JsonNode & campaign : config["campaign_regions"].Vector())
-			campDescriptions.push_back(CampaignRegions::fromJson(campaign));
-	}
-	
-	return campDescriptions.at(campId);
-}
-
-
-bool CampaignBonus::isBonusForHero() const
-{
-	return type == CampaignBonusType::SPELL ||
-		   type == CampaignBonusType::MONSTER ||
-		   type == CampaignBonusType::ARTIFACT ||
-		   type == CampaignBonusType::SPELL_SCROLL ||
-		   type == CampaignBonusType::PRIMARY_SKILL ||
-		   type == CampaignBonusType::SECONDARY_SKILL;
-}
-
-void CampaignHeader::loadLegacyData(ui8 campId)
-{
-	campaignRegions = CampaignRegions::getLegacy(campId);
-	numberOfScenarios = VLC->generaltexth->getCampaignLength(campId);
-}
-
-CampaignHeader CampaignHandler::getHeader( const std::string & name)
+std::unique_ptr<CampaignHeader> CampaignHandler::getHeader( const std::string & name)
 {
 	ResourceID resourceID(name, EResType::CAMPAIGN);
 	std::string modName = VLC->modh->findResourceOrigin(resourceID);
@@ -102,11 +42,11 @@ CampaignHeader CampaignHandler::getHeader( const std::string & name)
 		//legacy OH3 campaign (*.h3c)
 		CMemoryStream stream(cmpgn.data(), cmpgn.size());
 		CBinaryReader reader(&stream);
-		return readHeaderFromMemory(reader, resourceID.getName(), modName, encoding);
+		return std::make_unique<CampaignHeader>(readHeaderFromMemory(reader, resourceID.getName(), modName, encoding));
 	}
 	
 	//VCMI (*.vcmp)
-	return readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding);
+	return std::make_unique<CampaignHeader>(readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding));
 }
 
 std::shared_ptr<CampaignState> CampaignHandler::getCampaign( const std::string & name )
@@ -139,7 +79,6 @@ std::shared_ptr<CampaignState> CampaignHandler::getCampaign( const std::string &
 		JsonNode jsonCampaign((const char*)files[0].data(), files[0].size());
 		ret->header = readHeaderFromJson(jsonCampaign, resourceID.getName(), modName, encoding);
 
-
 		for(auto & scenario : jsonCampaign["scenarios"].Vector())
 		{
 			auto scenarioID = static_cast<CampaignScenarioID>(ret->scenarios.size());
@@ -230,7 +169,6 @@ CampaignHeader CampaignHandler::readHeaderFromJson(JsonNode & reader, std::strin
 	ret.filename = filename;
 	ret.modName = modName;
 	ret.encoding = encoding;
-	ret.valid = true;
 	return ret;
 }
 
@@ -471,7 +409,6 @@ CampaignHeader CampaignHandler::readHeaderFromMemory( CBinaryReader & reader, st
 	ret.filename = filename;
 	ret.modName = modName;
 	ret.encoding = encoding;
-	ret.valid = true;
 	return ret;
 }
 
@@ -513,15 +450,6 @@ CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader
 	return ret;
 }
 
-void CampaignScenario::loadPreconditionRegions(ui32 regions)
-{
-	for (int i=0; i<32; i++) //for each bit in region. h3c however can only hold up to 16
-	{
-		if ( (1 << i) & regions)
-			preconditionRegions.insert(static_cast<CampaignScenarioID>(i));
-	}
-}
-
 template<typename Identifier>
 static void readContainer(std::set<Identifier> container, CBinaryReader & reader, int sizeBytes)
 {
@@ -679,174 +607,6 @@ std::vector< std::vector<ui8> > CampaignHandler::getFile(std::unique_ptr<CInputS
 	return ret;
 }
 
-bool CampaignState::conquerable(CampaignScenarioID whichScenario) const
-{
-	//check for void scenraio
-	if (!scenarios.at(whichScenario).isNotVoid())
-	{
-		return false;
-	}
-
-	if (scenarios.at(whichScenario).conquered)
-	{
-		return false;
-	}
-	//check preconditioned regions
-	for (auto const & it : scenarios.at(whichScenario).preconditionRegions)
-	{
-		if (!scenarios.at(it).conquered)
-			return false;
-	}
-	return true;
-}
-
-bool CampaignScenario::isNotVoid() const
-{
-	return !mapName.empty();
-}
-
-const CGHeroInstance * CampaignScenario::strongestHero(const PlayerColor & owner)
-{
-	std::function<bool(JsonNode & node)> isOwned = [owner](JsonNode & node)
-	{
-		auto * h = CampaignState::crossoverDeserialize(node);
-		bool result = h->tempOwner == owner;
-		vstd::clear_pointer(h);
-		return result;
-	};
-	auto ownedHeroes = crossoverHeroes | boost::adaptors::filtered(isOwned);
-
-	auto i = vstd::maxElementByFun(ownedHeroes, [](JsonNode & node)
-	{
-		auto * h = CampaignState::crossoverDeserialize(node);
-		double result = h->getHeroStrength();
-		vstd::clear_pointer(h);
-		return result;
-	});
-	return i == ownedHeroes.end() ? nullptr : CampaignState::crossoverDeserialize(*i);
-}
-
-std::vector<CGHeroInstance *> CampaignScenario::getLostCrossoverHeroes()
-{
-	std::vector<CGHeroInstance *> lostCrossoverHeroes;
-	if(conquered)
-	{
-		for(auto node2 : placedCrossoverHeroes)
-		{
-			auto * hero = CampaignState::crossoverDeserialize(node2);
-			auto it = range::find_if(crossoverHeroes, [hero](JsonNode node)
-			{
-				auto * h = CampaignState::crossoverDeserialize(node);
-				bool result = hero->subID == h->subID;
-				vstd::clear_pointer(h);
-				return result;
-			});
-			if(it == crossoverHeroes.end())
-			{
-				lostCrossoverHeroes.push_back(hero);
-			}
-		}
-	}
-	return lostCrossoverHeroes;
-}
-
-void CampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *> & heroes)
-{
-	scenarios.at(*currentMap).crossoverHeroes.clear();
-	for(CGHeroInstance * hero : heroes)
-	{
-		scenarios.at(*currentMap).crossoverHeroes.push_back(crossoverSerialize(hero));
-	}
-
-	mapsConquered.push_back(*currentMap);
-	mapsRemaining -= *currentMap;
-	scenarios.at(*currentMap).conquered = true;
-}
-
-std::optional<CampaignBonus> CampaignState::getBonusForCurrentMap() const
-{
-	auto bonuses = getCurrentScenario().travelOptions.bonusesToChoose;
-	assert(chosenCampaignBonuses.count(*currentMap) || bonuses.size() == 0);
-
-	if(bonuses.empty())
-		return std::optional<CampaignBonus>();
-	else
-		return bonuses[currentBonusID()];
-}
-
-const CampaignScenario & CampaignState::getCurrentScenario() const
-{
-	return scenarios.at(*currentMap);
-}
-
-CampaignScenario & CampaignState::getCurrentScenario()
-{
-	return scenarios.at(*currentMap);
-}
-
-ui8 CampaignState::currentBonusID() const
-{
-	return chosenCampaignBonuses.at(*currentMap);
-}
-
-std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
-{
-	// FIXME: there is certainly better way to handle maps inside campaigns
-	if(scenarioId == CampaignScenarioID::NONE)
-		scenarioId = currentMap.value();
-	
-	CMapService mapService;
-	std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
-	boost::to_lower(scenarioName);
-	scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
-	const std::string & mapContent = mapPieces.find(scenarioId)->second;
-	const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
-	return mapService.loadMap(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding);
-}
-
-std::unique_ptr<CMapHeader> CampaignState::getHeader(CampaignScenarioID scenarioId) const
-{
-	if(scenarioId == CampaignScenarioID::NONE)
-		scenarioId = currentMap.value();
-	
-	CMapService mapService;
-	std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
-	boost::to_lower(scenarioName);
-	scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
-	const std::string & mapContent = mapPieces.find(scenarioId)->second;
-	const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
-	return mapService.loadMapHeader(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding);
-}
-
-std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioId) const
-{
-	if(scenarioId == CampaignScenarioID::NONE)
-		scenarioId = currentMap.value();
-
-	auto mapInfo = std::make_shared<CMapInfo>();
-	mapInfo->fileURI = header.filename;
-	mapInfo->mapHeader = getHeader(scenarioId);
-	mapInfo->countPlayers();
-	return mapInfo;
-}
-
-JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero)
-{
-	JsonNode node;
-	JsonSerializer handler(nullptr, node);
-	hero->serializeJsonOptions(handler);
-	return node;
-}
-
-CGHeroInstance * CampaignState::crossoverDeserialize(JsonNode & node)
-{
-	JsonDeserializer handler(nullptr, node);
-	auto * hero = new CGHeroInstance();
-	hero->ID = Obj::HERO;
-	hero->serializeJsonOptions(handler);
-	return hero;
-}
-
 std::string CampaignHandler::prologVideoName(ui8 index)
 {
 	JsonNode config(ResourceID(std::string("CONFIG/campaignMedia"), EResType::TEXT));

+ 43 - 0
lib/campaign/CampaignHandler.h

@@ -0,0 +1,43 @@
+/*
+ * CampaignHandler.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 "CampaignState.h" // Convenience include - not required for build, but required for any user of CampaignHandler
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class DLL_LINKAGE CampaignHandler
+{
+	static std::string readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier);
+	
+	//parsers for VCMI campaigns (*.vcmp)
+	static CampaignHeader readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding);
+	static CampaignScenario readScenarioFromJson(JsonNode & reader);
+	static CampaignTravel readScenarioTravelFromJson(JsonNode & reader);
+
+	//parsers for original H3C campaigns
+	static CampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding);
+	static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CampaignHeader & header);
+	static CampaignTravel readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version);
+	/// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m)
+	/// headerOnly - only header will be decompressed, returned vector wont have any maps
+	static std::vector<std::vector<ui8>> getFile(std::unique_ptr<CInputStream> file, bool headerOnly);
+
+	static std::string prologVideoName(ui8 index);
+	static std::string prologMusicName(ui8 index);
+	static std::string prologVoiceName(ui8 index);
+
+public:
+	static std::unique_ptr<CampaignHeader> getHeader( const std::string & name); //name - name of appropriate file
+
+	static std::shared_ptr<CampaignState> getCampaign(const std::string & name); //name - name of appropriate file
+};
+
+VCMI_LIB_NAMESPACE_END

+ 26 - 0
lib/campaign/CampaignScenarioPrologEpilog.h

@@ -0,0 +1,26 @@
+/*
+ * CampaignScenarioPrologEpilog.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
+
+struct DLL_LINKAGE CampaignScenarioPrologEpilog
+{
+	bool hasPrologEpilog = false;
+	std::string prologVideo; // from CmpMovie.txt
+	std::string prologMusic; // from CmpMusic.txt
+	std::string prologText;
+
+	template <typename Handler> void serialize(Handler &h, const int formatVersion)
+	{
+		h & hasPrologEpilog;
+		h & prologVideo;
+		h & prologMusic;
+		h & prologText;
+	}
+};

+ 252 - 0
lib/campaign/CampaignState.cpp

@@ -0,0 +1,252 @@
+/*
+ * CCampaignHandler.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 "CampaignState.h"
+
+#include "../JsonNode.h"
+#include "../filesystem/ResourceID.h"
+#include "../VCMI_Lib.h"
+#include "../CGeneralTextHandler.h"
+#include "../mapping/CMapService.h"
+#include "../mapping/CMapInfo.h"
+#include "../mapping/CMap.h"
+#include "../mapObjects/CGHeroInstance.h"
+#include "../serializer/JsonDeserializer.h"
+#include "../serializer/JsonSerializer.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+void CampaignScenario::loadPreconditionRegions(ui32 regions)
+{
+	for (int i=0; i<32; i++) //for each bit in region. h3c however can only hold up to 16
+	{
+		if ( (1 << i) & regions)
+			preconditionRegions.insert(static_cast<CampaignScenarioID>(i));
+	}
+}
+
+CampaignRegions::RegionDescription CampaignRegions::RegionDescription::fromJson(const JsonNode & node)
+{
+	CampaignRegions::RegionDescription rd;
+	rd.infix = node["infix"].String();
+	rd.xpos = static_cast<int>(node["x"].Float());
+	rd.ypos = static_cast<int>(node["y"].Float());
+	return rd;
+}
+
+CampaignRegions CampaignRegions::fromJson(const JsonNode & node)
+{
+	CampaignRegions cr;
+	cr.campPrefix = node["prefix"].String();
+	cr.colorSuffixLength = static_cast<int>(node["color_suffix_length"].Float());
+
+	for(const JsonNode & desc : node["desc"].Vector())
+		cr.regions.push_back(CampaignRegions::RegionDescription::fromJson(desc));
+
+	return cr;
+}
+
+CampaignRegions CampaignRegions::getLegacy(int campId)
+{
+	static std::vector<CampaignRegions> campDescriptions;
+	if(campDescriptions.empty()) //read once
+	{
+		const JsonNode config(ResourceID("config/campaign_regions.json"));
+		for(const JsonNode & campaign : config["campaign_regions"].Vector())
+			campDescriptions.push_back(CampaignRegions::fromJson(campaign));
+	}
+
+	return campDescriptions.at(campId);
+}
+
+
+bool CampaignBonus::isBonusForHero() const
+{
+	return type == CampaignBonusType::SPELL ||
+		   type == CampaignBonusType::MONSTER ||
+		   type == CampaignBonusType::ARTIFACT ||
+		   type == CampaignBonusType::SPELL_SCROLL ||
+		   type == CampaignBonusType::PRIMARY_SKILL ||
+		   type == CampaignBonusType::SECONDARY_SKILL;
+}
+
+void CampaignHeader::loadLegacyData(ui8 campId)
+{
+	campaignRegions = CampaignRegions::getLegacy(campId);
+	numberOfScenarios = VLC->generaltexth->getCampaignLength(campId);
+}
+
+bool CampaignState::conquerable(CampaignScenarioID whichScenario) const
+{
+	//check for void scenraio
+	if (!scenarios.at(whichScenario).isNotVoid())
+	{
+		return false;
+	}
+
+	if (scenarios.at(whichScenario).conquered)
+	{
+		return false;
+	}
+	//check preconditioned regions
+	for (auto const & it : scenarios.at(whichScenario).preconditionRegions)
+	{
+		if (!scenarios.at(it).conquered)
+			return false;
+	}
+	return true;
+}
+
+bool CampaignScenario::isNotVoid() const
+{
+	return !mapName.empty();
+}
+
+const CGHeroInstance * CampaignScenario::strongestHero(const PlayerColor & owner)
+{
+	std::function<bool(JsonNode & node)> isOwned = [owner](JsonNode & node)
+	{
+		auto * h = CampaignState::crossoverDeserialize(node);
+		bool result = h->tempOwner == owner;
+		vstd::clear_pointer(h);
+		return result;
+	};
+	auto ownedHeroes = crossoverHeroes | boost::adaptors::filtered(isOwned);
+
+	auto i = vstd::maxElementByFun(ownedHeroes, [](JsonNode & node)
+	{
+		auto * h = CampaignState::crossoverDeserialize(node);
+		double result = h->getHeroStrength();
+		vstd::clear_pointer(h);
+		return result;
+	});
+	return i == ownedHeroes.end() ? nullptr : CampaignState::crossoverDeserialize(*i);
+}
+
+std::vector<CGHeroInstance *> CampaignScenario::getLostCrossoverHeroes()
+{
+	std::vector<CGHeroInstance *> lostCrossoverHeroes;
+	if(conquered)
+	{
+		for(auto node2 : placedCrossoverHeroes)
+		{
+			auto * hero = CampaignState::crossoverDeserialize(node2);
+			auto it = range::find_if(crossoverHeroes, [hero](JsonNode node)
+			{
+				auto * h = CampaignState::crossoverDeserialize(node);
+				bool result = hero->subID == h->subID;
+				vstd::clear_pointer(h);
+				return result;
+			});
+			if(it == crossoverHeroes.end())
+			{
+				lostCrossoverHeroes.push_back(hero);
+			}
+		}
+	}
+	return lostCrossoverHeroes;
+}
+
+void CampaignState::setCurrentMapAsConquered(const std::vector<CGHeroInstance *> & heroes)
+{
+	scenarios.at(*currentMap).crossoverHeroes.clear();
+	for(CGHeroInstance * hero : heroes)
+	{
+		scenarios.at(*currentMap).crossoverHeroes.push_back(crossoverSerialize(hero));
+	}
+
+	mapsConquered.push_back(*currentMap);
+	mapsRemaining -= *currentMap;
+	scenarios.at(*currentMap).conquered = true;
+}
+
+std::optional<CampaignBonus> CampaignState::getBonusForCurrentMap() const
+{
+	auto bonuses = getCurrentScenario().travelOptions.bonusesToChoose;
+	assert(chosenCampaignBonuses.count(*currentMap) || bonuses.size() == 0);
+
+	if(bonuses.empty())
+		return std::optional<CampaignBonus>();
+	else
+		return bonuses[currentBonusID()];
+}
+
+const CampaignScenario & CampaignState::getCurrentScenario() const
+{
+	return scenarios.at(*currentMap);
+}
+
+CampaignScenario & CampaignState::getCurrentScenario()
+{
+	return scenarios.at(*currentMap);
+}
+
+ui8 CampaignState::currentBonusID() const
+{
+	return chosenCampaignBonuses.at(*currentMap);
+}
+
+std::unique_ptr<CMap> CampaignState::getMap(CampaignScenarioID scenarioId) const
+{
+	// FIXME: there is certainly better way to handle maps inside campaigns
+	if(scenarioId == CampaignScenarioID::NONE)
+		scenarioId = currentMap.value();
+
+	CMapService mapService;
+	std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
+	boost::to_lower(scenarioName);
+	scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
+	const std::string & mapContent = mapPieces.find(scenarioId)->second;
+	const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
+	return mapService.loadMap(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding);
+}
+
+std::unique_ptr<CMapHeader> CampaignState::getMapHeader(CampaignScenarioID scenarioId) const
+{
+	if(scenarioId == CampaignScenarioID::NONE)
+		scenarioId = currentMap.value();
+
+	CMapService mapService;
+	std::string scenarioName = header.filename.substr(0, header.filename.find('.'));
+	boost::to_lower(scenarioName);
+	scenarioName += ':' + std::to_string(static_cast<int>(scenarioId));
+	const std::string & mapContent = mapPieces.find(scenarioId)->second;
+	const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
+	return mapService.loadMapHeader(buffer, static_cast<int>(mapContent.size()), scenarioName, header.modName, header.encoding);
+}
+
+std::shared_ptr<CMapInfo> CampaignState::getMapInfo(CampaignScenarioID scenarioId) const
+{
+	if(scenarioId == CampaignScenarioID::NONE)
+		scenarioId = currentMap.value();
+
+	auto mapInfo = std::make_shared<CMapInfo>();
+	mapInfo->fileURI = header.filename;
+	mapInfo->mapHeader = getMapHeader(scenarioId);
+	mapInfo->countPlayers();
+	return mapInfo;
+}
+
+JsonNode CampaignState::crossoverSerialize(CGHeroInstance * hero)
+{
+	JsonNode node;
+	JsonSerializer handler(nullptr, node);
+	hero->serializeJsonOptions(handler);
+	return node;
+}
+
+CGHeroInstance * CampaignState::crossoverDeserialize(JsonNode & node)
+{
+	JsonDeserializer handler(nullptr, node);
+	auto * hero = new CGHeroInstance();
+	hero->ID = Obj::HERO;
+	hero->serializeJsonOptions(handler);
+	return hero;
+}

+ 236 - 322
lib/mapping/CCampaignHandler.h → lib/campaign/CampaignState.h

@@ -1,322 +1,236 @@
-/*
- * CCampaignHandler.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 "../../lib/GameConstants.h"
-
-VCMI_LIB_NAMESPACE_BEGIN
-
-struct StartInfo;
-class CGHeroInstance;
-class CBinaryReader;
-class CInputStream;
-class CMap;
-class CMapHeader;
-class CMapInfo;
-class JsonNode;
-
-enum class CampaignVersion : uint8_t
-{
-	NONE = 0,
-	RoE = 4,
-	AB = 5,
-	SoD = 6,
-	WoG = 6,
-	//		Chr = 7, // Heroes Chronicles, likely identical to SoD, untested
-
-	VCMI = 1,
-	VCMI_MIN = 1,
-	VCMI_MAX = 1,
-};
-
-struct DLL_LINKAGE CampaignRegions
-{
-	std::string campPrefix;
-	int colorSuffixLength;
-
-	struct DLL_LINKAGE RegionDescription
-	{
-		std::string infix;
-		int xpos, ypos;
-		
-		template <typename Handler> void serialize(Handler &h, const int formatVersion)
-		{
-			h & infix;
-			h & xpos;
-			h & ypos;
-		}
-		
-		static CampaignRegions::RegionDescription fromJson(const JsonNode & node);
-	};
-
-	std::vector<RegionDescription> regions;
-	
-	template <typename Handler> void serialize(Handler &h, const int formatVersion)
-	{
-		h & campPrefix;
-		h & colorSuffixLength;
-		h & regions;
-	}
-	
-	static CampaignRegions fromJson(const JsonNode & node);
-	static CampaignRegions getLegacy(int campId);
-};
-
-class DLL_LINKAGE CampaignHeader
-{
-public:
-	CampaignVersion version = CampaignVersion::NONE;
-	CampaignRegions campaignRegions;
-	int numberOfScenarios = 0;
-	std::string name, description;
-	bool difficultyChoosenByPlayer = false;
-	bool valid = false;
-
-	std::string filename;
-	std::string modName;
-	std::string encoding;
-	
-	void loadLegacyData(ui8 campId);
-
-	template <typename Handler> void serialize(Handler &h, const int formatVersion)
-	{
-		h & version;
-		h & campaignRegions;
-		h & numberOfScenarios;
-		h & name;
-		h & description;
-		h & difficultyChoosenByPlayer;
-		h & filename;
-		h & modName;
-		h & encoding;
-		h & valid;
-	}
-};
-
-enum class CampaignStartOptions: int8_t
-{
-	NONE = 0,
-	START_BONUS,
-	HERO_CROSSOVER,
-	HERO_OPTIONS
-};
-
-enum class CampaignBonusType : int8_t
-{
-	NONE = -1,
-	SPELL,
-	MONSTER,
-	BUILDING,
-	ARTIFACT,
-	SPELL_SCROLL,
-	PRIMARY_SKILL,
-	SECONDARY_SKILL,
-	RESOURCE,
-	HEROES_FROM_PREVIOUS_SCENARIO,
-	HERO
-};
-
-enum class CampaignScenarioID : int8_t
-{
-	NONE = -1,
-	// no members - fake enum to create integer type that is not implicitly convertible to int
-};
-
-struct DLL_LINKAGE CampaignBonus
-{
-	CampaignBonusType type = CampaignBonusType::NONE; //uses EBonusType
-
-	//purpose depends on type
-	int32_t info1 = 0;
-	int32_t info2 = 0;
-	int32_t info3 = 0;
-
-	bool isBonusForHero() const;
-
-	template <typename Handler> void serialize(Handler &h, const int formatVersion)
-	{
-		h & type;
-		h & info1;
-		h & info2;
-		h & info3;
-	}
-};
-
-class DLL_LINKAGE CampaignTravel
-{
-public:
-	
-	struct DLL_LINKAGE WhatHeroKeeps
-	{
-		bool experience = false;
-		bool primarySkills = false;
-		bool secondarySkills = false;
-		bool spells = false;
-		bool artifacts = false;
-		
-		template <typename Handler> void serialize(Handler &h, const int formatVersion)
-		{
-			h & experience;
-			h & primarySkills;
-			h & secondarySkills;
-			h & spells;
-			h & artifacts;
-		}
-	};
-	
-	std::set<CreatureID> monstersKeptByHero;
-	std::set<ArtifactID> artifactsKeptByHero;
-	std::vector<CampaignBonus> bonusesToChoose;
-
-	WhatHeroKeeps whatHeroKeeps;
-	CampaignStartOptions startOptions = CampaignStartOptions::NONE; //1 - start bonus, 2 - traveling hero, 3 - hero options
-	PlayerColor playerColor = PlayerColor::NEUTRAL; //only for startOptions == 1
-
-	template <typename Handler> void serialize(Handler &h, const int formatVersion)
-	{
-		h & whatHeroKeeps;
-		h & monstersKeptByHero;
-		h & artifactsKeptByHero;
-		h & startOptions;
-		h & playerColor;
-		h & bonusesToChoose;
-	}
-};
-
-struct DLL_LINKAGE CampaignScenarioPrologEpilog
-{
-	bool hasPrologEpilog = false;
-	std::string prologVideo; // from CmpMovie.txt
-	std::string prologMusic; // from CmpMusic.txt
-	std::string prologText;
-
-	template <typename Handler> void serialize(Handler &h, const int formatVersion)
-	{
-		h & hasPrologEpilog;
-		h & prologVideo;
-		h & prologMusic;
-		h & prologText;
-	}
-};
-
-class DLL_LINKAGE CampaignScenario
-{
-public:
-	std::string mapName; //*.h3m
-	std::string scenarioName; //from header. human-readble
-	std::set<CampaignScenarioID> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c)
-	ui8 regionColor = 0;
-	ui8 difficulty = 0;
-	bool conquered = false;
-
-	std::string regionText;
-	CampaignScenarioPrologEpilog prolog;
-	CampaignScenarioPrologEpilog epilog;
-
-	CampaignTravel travelOptions;
-	std::vector<HeroTypeID> keepHeroes; // contains list of heroes which should be kept for next scenario (doesn't matter if they lost)
-	std::vector<JsonNode> crossoverHeroes; // contains all heroes with the same state when the campaign scenario was finished
-	std::vector<JsonNode> placedCrossoverHeroes; // contains all placed crossover heroes defined by hero placeholders when the scenario was started
-
-	void loadPreconditionRegions(ui32 regions);
-	bool isNotVoid() const;
-	// FIXME: due to usage of JsonNode I can't make these methods const
-	const CGHeroInstance * strongestHero(const PlayerColor & owner);
-	std::vector<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it
-
-	template <typename Handler> void serialize(Handler &h, const int formatVersion)
-	{
-		h & mapName;
-		h & scenarioName;
-		h & preconditionRegions;
-		h & regionColor;
-		h & difficulty;
-		h & conquered;
-		h & regionText;
-		h & prolog;
-		h & epilog;
-		h & travelOptions;
-		h & crossoverHeroes;
-		h & placedCrossoverHeroes;
-		h & keepHeroes;
-	}
-};
-
-class DLL_LINKAGE CampaignState
-{
-public:
-	CampaignHeader header;
-	std::map<CampaignScenarioID, CampaignScenario> scenarios;
-	std::map<CampaignScenarioID, std::string > mapPieces; //binary h3ms, scenario number -> map data
-
-	std::string fileEncoding;
-	std::vector<CampaignScenarioID> mapsConquered;
-	std::vector<CampaignScenarioID> mapsRemaining;
-	std::optional<CampaignScenarioID> currentMap;
-
-	std::map<CampaignScenarioID, ui8> chosenCampaignBonuses;
-
-public:
-	std::optional<CampaignBonus> getBonusForCurrentMap() const;
-	const CampaignScenario & getCurrentScenario() const;
-	ui8 currentBonusID() const;
-	bool conquerable(CampaignScenarioID whichScenario) const;
-
-	std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const;
-	std::unique_ptr<CMapHeader> getHeader(CampaignScenarioID scenarioId) const;
-	std::shared_ptr<CMapInfo> getMapInfo(CampaignScenarioID scenarioId) const;
-
-	CampaignScenario & getCurrentScenario();
-	void setCurrentMapAsConquered(const std::vector<CGHeroInstance*> & heroes);
-	static JsonNode crossoverSerialize(CGHeroInstance * hero);
-	static CGHeroInstance * crossoverDeserialize(JsonNode & node);
-
-	CampaignState() = default;
-
-	template <typename Handler> void serialize(Handler &h, const int version)
-	{
-		h & header;
-		h & scenarios;
-		h & mapPieces;
-		h & mapsRemaining;
-		h & mapsConquered;
-		h & currentMap;
-		h & chosenCampaignBonuses;
-	}
-};
-
-class DLL_LINKAGE CampaignHandler
-{
-	static std::string readLocalizedString(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding, std::string identifier);
-	
-	//parsers for VCMI campaigns (*.vcmp)
-	static CampaignHeader readHeaderFromJson(JsonNode & reader, std::string filename, std::string modName, std::string encoding);
-	static CampaignScenario readScenarioFromJson(JsonNode & reader);
-	static CampaignTravel readScenarioTravelFromJson(JsonNode & reader);
-
-	//parsers for original H3C campaigns
-	static CampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding);
-	static CampaignScenario readScenarioFromMemory(CBinaryReader & reader, const CampaignHeader & header);
-	static CampaignTravel readScenarioTravelFromMemory(CBinaryReader & reader, CampaignVersion version);
-	/// returns h3c split in parts. 0 = h3c header, 1-end - maps (binary h3m)
-	/// headerOnly - only header will be decompressed, returned vector wont have any maps
-	static std::vector<std::vector<ui8>> getFile(std::unique_ptr<CInputStream> file, bool headerOnly);
-
-	static std::string prologVideoName(ui8 index);
-	static std::string prologMusicName(ui8 index);
-	static std::string prologVoiceName(ui8 index);
-
-public:
-	static CampaignHeader getHeader( const std::string & name); //name - name of appropriate file
-
-	static std::shared_ptr<CampaignState> getCampaign(const std::string & name); //name - name of appropriate file
-};
-
-VCMI_LIB_NAMESPACE_END
+/*
+ * CampaignState.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 "../../lib/GameConstants.h"
+#include "CampaignConstants.h"
+#include "CampaignScenarioPrologEpilog.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+struct StartInfo;
+class CGHeroInstance;
+class CBinaryReader;
+class CInputStream;
+class CMap;
+class CMapHeader;
+class CMapInfo;
+class JsonNode;
+
+struct DLL_LINKAGE CampaignRegions
+{
+	std::string campPrefix;
+	int colorSuffixLength;
+
+	struct DLL_LINKAGE RegionDescription
+	{
+		std::string infix;
+		int xpos, ypos;
+
+		template <typename Handler> void serialize(Handler &h, const int formatVersion)
+		{
+			h & infix;
+			h & xpos;
+			h & ypos;
+		}
+
+		static CampaignRegions::RegionDescription fromJson(const JsonNode & node);
+	};
+
+	std::vector<RegionDescription> regions;
+
+	template <typename Handler> void serialize(Handler &h, const int formatVersion)
+	{
+		h & campPrefix;
+		h & colorSuffixLength;
+		h & regions;
+	}
+
+	static CampaignRegions fromJson(const JsonNode & node);
+	static CampaignRegions getLegacy(int campId);
+};
+
+class DLL_LINKAGE CampaignHeader
+{
+public:
+	int numberOfScenarios = 0;
+	CampaignVersion version = CampaignVersion::NONE;
+	CampaignRegions campaignRegions;
+	std::string name;
+	std::string description;
+	bool difficultyChoosenByPlayer = false;
+
+	void loadLegacyData(ui8 campId);
+
+	std::string filename;
+	std::string modName;
+	std::string encoding;
+
+	template <typename Handler> void serialize(Handler &h, const int formatVersion)
+	{
+		h & version;
+		h & campaignRegions;
+		h & numberOfScenarios;
+		h & name;
+		h & description;
+		h & difficultyChoosenByPlayer;
+		h & filename;
+		h & modName;
+		h & encoding;
+	}
+};
+
+struct DLL_LINKAGE CampaignBonus
+{
+	CampaignBonusType type = CampaignBonusType::NONE; //uses EBonusType
+
+	//purpose depends on type
+	int32_t info1 = 0;
+	int32_t info2 = 0;
+	int32_t info3 = 0;
+
+	bool isBonusForHero() const;
+
+	template <typename Handler> void serialize(Handler &h, const int formatVersion)
+	{
+		h & type;
+		h & info1;
+		h & info2;
+		h & info3;
+	}
+};
+
+class DLL_LINKAGE CampaignTravel
+{
+public:
+
+	struct DLL_LINKAGE WhatHeroKeeps
+	{
+		bool experience = false;
+		bool primarySkills = false;
+		bool secondarySkills = false;
+		bool spells = false;
+		bool artifacts = false;
+
+		template <typename Handler> void serialize(Handler &h, const int formatVersion)
+		{
+			h & experience;
+			h & primarySkills;
+			h & secondarySkills;
+			h & spells;
+			h & artifacts;
+		}
+	};
+
+	std::set<CreatureID> monstersKeptByHero;
+	std::set<ArtifactID> artifactsKeptByHero;
+	std::vector<CampaignBonus> bonusesToChoose;
+
+	WhatHeroKeeps whatHeroKeeps;
+	CampaignStartOptions startOptions = CampaignStartOptions::NONE; //1 - start bonus, 2 - traveling hero, 3 - hero options
+	PlayerColor playerColor = PlayerColor::NEUTRAL; //only for startOptions == 1
+
+	template <typename Handler> void serialize(Handler &h, const int formatVersion)
+	{
+		h & whatHeroKeeps;
+		h & monstersKeptByHero;
+		h & artifactsKeptByHero;
+		h & startOptions;
+		h & playerColor;
+		h & bonusesToChoose;
+	}
+};
+
+class DLL_LINKAGE CampaignScenario
+{
+public:
+	std::string mapName; //*.h3m
+	std::string scenarioName; //from header. human-readble
+	std::set<CampaignScenarioID> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c)
+	ui8 regionColor = 0;
+	ui8 difficulty = 0;
+	bool conquered = false;
+
+	std::string regionText;
+	CampaignScenarioPrologEpilog prolog;
+	CampaignScenarioPrologEpilog epilog;
+
+	CampaignTravel travelOptions;
+	std::vector<HeroTypeID> keepHeroes; // contains list of heroes which should be kept for next scenario (doesn't matter if they lost)
+	std::vector<JsonNode> crossoverHeroes; // contains all heroes with the same state when the campaign scenario was finished
+	std::vector<JsonNode> placedCrossoverHeroes; // contains all placed crossover heroes defined by hero placeholders when the scenario was started
+
+	void loadPreconditionRegions(ui32 regions);
+	bool isNotVoid() const;
+	// FIXME: due to usage of JsonNode I can't make these methods const
+	const CGHeroInstance * strongestHero(const PlayerColor & owner);
+	std::vector<CGHeroInstance *> getLostCrossoverHeroes(); /// returns a list of crossover heroes which started the scenario, but didn't complete it
+
+	template <typename Handler> void serialize(Handler &h, const int formatVersion)
+	{
+		h & mapName;
+		h & scenarioName;
+		h & preconditionRegions;
+		h & regionColor;
+		h & difficulty;
+		h & conquered;
+		h & regionText;
+		h & prolog;
+		h & epilog;
+		h & travelOptions;
+		h & crossoverHeroes;
+		h & placedCrossoverHeroes;
+		h & keepHeroes;
+	}
+};
+
+class DLL_LINKAGE CampaignState
+{
+public:
+	CampaignHeader header;
+	std::map<CampaignScenarioID, CampaignScenario> scenarios;
+	std::map<CampaignScenarioID, std::string > mapPieces; //binary h3ms, scenario number -> map data
+
+	std::vector<CampaignScenarioID> mapsConquered;
+	std::vector<CampaignScenarioID> mapsRemaining;
+	std::optional<CampaignScenarioID> currentMap;
+
+	std::map<CampaignScenarioID, ui8> chosenCampaignBonuses;
+
+public:
+	std::optional<CampaignBonus> getBonusForCurrentMap() const;
+	const CampaignScenario & getCurrentScenario() const;
+	ui8 currentBonusID() const;
+	bool conquerable(CampaignScenarioID whichScenario) const;
+
+	std::unique_ptr<CMap> getMap(CampaignScenarioID scenarioId) const;
+	std::unique_ptr<CMapHeader> getMapHeader(CampaignScenarioID scenarioId) const;
+	std::shared_ptr<CMapInfo> getMapInfo(CampaignScenarioID scenarioId) const;
+
+	CampaignScenario & getCurrentScenario();
+	void setCurrentMapAsConquered(const std::vector<CGHeroInstance*> & heroes);
+	static JsonNode crossoverSerialize(CGHeroInstance * hero);
+	static CGHeroInstance * crossoverDeserialize(JsonNode & node);
+
+	CampaignState() = default;
+
+	template <typename Handler> void serialize(Handler &h, const int version)
+	{
+		h & header;
+		h & scenarios;
+		h & mapPieces;
+		h & mapsRemaining;
+		h & mapsConquered;
+		h & currentMap;
+		h & chosenCampaignBonuses;
+	}
+};
+
+VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/gameState/CGameState.cpp

@@ -26,13 +26,13 @@
 #include "../VCMIDirs.h"
 #include "../VCMI_Lib.h"
 #include "../battle/BattleInfo.h"
+#include "../campaign/CampaignState.h"
 #include "../filesystem/ResourceID.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"
 #include "../mapObjectConstructors/CObjectClassesHandler.h"
 #include "../mapObjectConstructors/DwellingInstanceConstructor.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../mapObjects/CGTownInstance.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../mapping/CMap.h"
 #include "../mapping/CMapEditManager.h"
 #include "../mapping/CMapService.h"

+ 1 - 1
lib/gameState/CGameStateCampaign.cpp

@@ -13,8 +13,8 @@
 #include "CGameState.h"
 #include "QuestInfo.h"
 
+#include "../campaign/CampaignState.h"
 #include "../mapping/CMapEditManager.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../mapObjects/CGHeroInstance.h"
 #include "../registerTypes/RegisterTypes.h"
 #include "../mapObjectConstructors/AObjectTypeHandler.h"

+ 3 - 5
lib/mapping/CMapInfo.cpp

@@ -15,9 +15,9 @@
 #include "../GameConstants.h"
 #include "CMapService.h"
 #include "CMapHeader.h"
-#include "CCampaignHandler.h"
 #include "MapFormat.h"
 
+#include "../campaign/CampaignHandler.h"
 #include "../filesystem/Filesystem.h"
 #include "../serializer/CMemorySerializer.h"
 #include "../CGeneralTextHandler.h"
@@ -66,9 +66,7 @@ void CMapInfo::saveInit(const ResourceID & file)
 
 void CMapInfo::campaignInit()
 {
-	campaignHeader = std::make_unique<CampaignHeader>(CampaignHandler::getHeader(fileURI));
-	if(!campaignHeader->valid)
-		campaignHeader.reset();
+	campaignHeader = CampaignHandler::getHeader(fileURI);
 }
 
 void CMapInfo::countPlayers()
@@ -94,7 +92,7 @@ void CMapInfo::countPlayers()
 
 std::string CMapInfo::getName() const
 {
-	if(campaignHeader && campaignHeader->name.length())
+	if(campaignHeader && !campaignHeader->name.empty())
 		return campaignHeader->name;
 	else if(mapHeader && mapHeader->name.length())
 		return mapHeader->name;

+ 0 - 1
lib/registerTypes/RegisterTypes.cpp

@@ -16,7 +16,6 @@
 #include "../mapObjects/CObjectHandler.h"
 #include "../CCreatureHandler.h"
 #include "../spells/CSpellHandler.h"
-#include "../mapping/CCampaignHandler.h"
 
 #include "../serializer/BinaryDeserializer.h"
 #include "../serializer/BinarySerializer.h"

+ 0 - 1
lib/registerTypes/TypesClientPacks1.cpp

@@ -19,7 +19,6 @@
 #include "../CHeroHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"
 
 #include "../serializer/BinaryDeserializer.h"

+ 0 - 1
lib/registerTypes/TypesClientPacks2.cpp

@@ -21,7 +21,6 @@
 #include "../CHeroHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"
 
 #include "../serializer/BinaryDeserializer.h"

+ 1 - 1
lib/registerTypes/TypesLobbyPacks.cpp

@@ -26,7 +26,7 @@
 #include "../RoadHandler.h"
 #include "../RiverHandler.h"
 #include "../TerrainHandler.h"
-#include "../mapping/CCampaignHandler.h"
+#include "../campaign/CampaignState.h"
 #include "../NetPacks.h"
 #include "../rmg/CMapGenOptions.h"
 

+ 0 - 1
lib/registerTypes/TypesMapObjects1.cpp

@@ -19,7 +19,6 @@
 #include "../CHeroHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"
 
 #include "../serializer/BinaryDeserializer.h"

+ 0 - 1
lib/registerTypes/TypesMapObjects2.cpp

@@ -21,7 +21,6 @@
 #include "../CHeroHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"
 
 #include "../serializer/BinaryDeserializer.h"

+ 0 - 1
lib/registerTypes/TypesMapObjects3.cpp

@@ -19,7 +19,6 @@
 #include "../CHeroHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"
 
 #include "../serializer/BinaryDeserializer.h"

+ 0 - 1
lib/registerTypes/TypesServerPacks.cpp

@@ -19,7 +19,6 @@
 #include "../CHeroHandler.h"
 #include "../spells/CSpellHandler.h"
 #include "../CTownHandler.h"
-#include "../mapping/CCampaignHandler.h"
 #include "../NetPacks.h"
 
 #include "../serializer/BinaryDeserializer.h"

+ 0 - 1
server/CGameHandler.cpp

@@ -12,7 +12,6 @@
 #include "../lib/filesystem/Filesystem.h"
 #include "../lib/filesystem/FileInfo.h"
 #include "../lib/int3.h"
-#include "../lib/mapping/CCampaignHandler.h"
 #include "../lib/ArtifactUtils.h"
 #include "../lib/StartInfo.h"
 #include "../lib/CModHandler.h"

+ 1 - 1
server/CVCMIServer.cpp

@@ -11,7 +11,7 @@
 #include <boost/asio.hpp>
 
 #include "../lib/filesystem/Filesystem.h"
-#include "../lib/mapping/CCampaignHandler.h"
+#include "../lib/campaign/CampaignState.h"
 #include "../lib/CThreadHelper.h"
 #include "../lib/serializer/Connection.h"
 #include "../lib/CModHandler.h"

+ 1 - 3
server/NetPacksLobbyServer.cpp

@@ -18,9 +18,7 @@
 #include "../lib/StartInfo.h"
 
 // Campaigns
-#include "../lib/mapping/CCampaignHandler.h"
-#include "../lib/mapping/CMapService.h"
-#include "../lib/mapping/CMapInfo.h"
+#include "../lib/campaign/CampaignState.h"
 
 void ClientPermissionsCheckerNetPackVisitor::visitForLobby(CPackForLobby & pack)
 {