diff --git a/lib/CGameState.cpp b/lib/CGameState.cpp
index 27cf29b6c..4284c334f 100644
--- a/lib/CGameState.cpp
+++ b/lib/CGameState.cpp
@@ -1266,7 +1266,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 
 	// TODO replace magic numbers with named constants
 	// TODO this logic (what should be kept) should be part of CScenarioTravel and be exposed via some clean set of methods
-	if(!(travelOptions.whatHeroKeeps & 1))
+	if(!travelOptions.whatHeroKeeps.experience)
 	{
 		//trimming experience
 		for(CGHeroInstance * cgh : crossoverHeroes)
@@ -1275,7 +1275,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 		}
 	}
 
-	if(!(travelOptions.whatHeroKeeps & 2))
+	if(!travelOptions.whatHeroKeeps.primarySkills)
 	{
 		//trimming prim skills
 		for(CGHeroInstance * cgh : crossoverHeroes)
@@ -1291,7 +1291,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 		}
 	}
 
-	if(!(travelOptions.whatHeroKeeps & 4))
+	if(!travelOptions.whatHeroKeeps.secondarySkills)
 	{
 		//trimming sec skills
 		for(CGHeroInstance * cgh : crossoverHeroes)
@@ -1301,7 +1301,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 		}
 	}
 
-	if(!(travelOptions.whatHeroKeeps & 8))
+	if(!travelOptions.whatHeroKeeps.spells)
 	{
 		for(CGHeroInstance * cgh : crossoverHeroes)
 		{
@@ -1309,7 +1309,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 		}
 	}
 
-	if(!(travelOptions.whatHeroKeeps & 16))
+	if(!travelOptions.whatHeroKeeps.artifacts)
 	{
 		//trimming artifacts
 		for(CGHeroInstance * hero : crossoverHeroes)
@@ -1329,9 +1329,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 				if(!art)
 					continue;
 
-				int id  = art->artType->getId();
-				assert( 8*18 > id );//number of arts that fits into h3m format
-				bool takeable = travelOptions.artifsKeptByHero[id / 8] & ( 1 << (id%8) );
+				bool takeable = travelOptions.artifactsKeptByHero.count(art->artType->getId());
 
 				ArtifactLocation al(hero, artifactPosition);
 				if(!takeable  &&  !al.getSlot()->locked)  //don't try removing locked artifacts -> it crashes #1719
@@ -1346,7 +1344,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 		auto shouldSlotBeErased = [&](const std::pair<SlotID, CStackInstance *> & j) -> bool
 		{
 			CreatureID::ECreatureID crid = j.second->getCreatureID().toEnum();
-			return !(travelOptions.monstersKeptByHero[crid / 8] & (1 << (crid % 8)));
+			return !travelOptions.monstersKeptByHero.count(crid);
 		};
 
 		auto stacksCopy = cgh->stacks; //copy of the map, so we can iterate iover it and remove stacks
diff --git a/lib/mapping/CCampaignHandler.cpp b/lib/mapping/CCampaignHandler.cpp
index f7760d37c..a91712494 100644
--- a/lib/mapping/CCampaignHandler.cpp
+++ b/lib/mapping/CCampaignHandler.cpp
@@ -282,13 +282,6 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
 {
 	CScenarioTravel ret;
 
-	std::map<std::string, ui8> heroKeepsMap = {
-		{"experience", 1},
-		{"primarySkill", 2},
-		{"secondarySkill", 4},
-		{"spells", 8},
-		{"artifacts", 16}
-	};
 	std::map<std::string, ui8> startOptionsMap = {
 		{"none", 0},
 		{"bonus", 1},
@@ -337,31 +330,25 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromJson(JsonNode & reader)
 	};
 	
 	for(auto & k : reader["heroKeeps"].Vector())
-		ret.whatHeroKeeps |= heroKeepsMap[k.String()];
+	{
+		if(k.String() == "experience") ret.whatHeroKeeps.experience = true;
+		if(k.String() == "primarySkills") ret.whatHeroKeeps.primarySkills = true;
+		if(k.String() == "secondarySkills") ret.whatHeroKeeps.secondarySkills = true;
+		if(k.String() == "spells") ret.whatHeroKeeps.spells = true;
+		if(k.String() == "artifacts") ret.whatHeroKeeps.artifacts = true;
+	}
 	
 	for(auto & k : reader["keepCreatures"].Vector())
 	{
 		if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "creature", k.String()))
-		{
-			int creId = identifier.get();
-			if(creId >= ret.monstersKeptByHero.size())
-				logGlobal->warn("VCMP Loading: creature %s with id %d isn't supported yet", k.String(), creId);
-			else
-				ret.monstersKeptByHero[creId / 8] |= (1 << creId % 8);
-		}
+			ret.monstersKeptByHero.insert(CreatureID(identifier.get()));
 		else
 			logGlobal->warn("VCMP Loading: keepCreatures contains unresolved identifier %s", k.String());
 	}
 	for(auto & k : reader["keepArtifacts"].Vector())
 	{
 		if(auto identifier = VLC->modh->identifiers.getIdentifier(CModHandler::scopeMap(), "artifact", k.String()))
-		{
-			int artId = identifier.get();
-			if(artId >= ret.artifsKeptByHero.size())
-				logGlobal->warn("VCMP Loading: artifact %s with id %d isn't supported yet", k.String(), artId);
-			else
-				ret.artifsKeptByHero[artId / 8] |= (1 << artId % 8);
-		}
+			ret.artifactsKeptByHero.insert(ArtifactID(identifier.get()));
 		else
 			logGlobal->warn("VCMP Loading: keepArtifacts contains unresolved identifier %s", k.String());
 	}
@@ -491,8 +478,8 @@ CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader,
 	if (ret.version > CampaignVersion::RoE)
 		ret.difficultyChoosenByPlayer = reader.readInt8();
 	else
-		ret.difficultyChoosenByPlayer = 0;
-	ret.music = reader.readInt8();
+		ret.difficultyChoosenByPlayer = false;
+	reader.readInt8(); //music - skip as unused
 	ret.filename = filename;
 	ret.modName = modName;
 	ret.encoding = encoding;
@@ -518,7 +505,7 @@ CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & read
 	CCampaignScenario ret;
 	ret.conquered = false;
 	ret.mapName = reader.readBaseString();
-	ret.packedMapSize = reader.readUInt32();
+	reader.readUInt32(); //packedMapSize - not used
 	if(header.numberOfScenarios > 8) //unholy alliance
 	{
 		ret.loadPreconditionRegions(reader.readUInt16());
@@ -551,18 +538,29 @@ CScenarioTravel CCampaignHandler::readScenarioTravelFromMemory(CBinaryReader & r
 {
 	CScenarioTravel ret;
 
-	ret.whatHeroKeeps = reader.readUInt8();
-	reader.getStream()->read(ret.monstersKeptByHero.data(), ret.monstersKeptByHero.size());
-
-	if (version < CampaignVersion::SoD)
+	ui8 whatHeroKeeps = reader.readUInt8();
+	ret.whatHeroKeeps.experience = whatHeroKeeps & 1;
+	ret.whatHeroKeeps.primarySkills = whatHeroKeeps & 2;
+	ret.whatHeroKeeps.secondarySkills = whatHeroKeeps & 4;
+	ret.whatHeroKeeps.spells = whatHeroKeeps & 8;
+	ret.whatHeroKeeps.artifacts = whatHeroKeeps & 16;
+		
+	auto bitMaskToId = [&reader]<typename T>(std::set<T> & container, int size)
 	{
-		ret.artifsKeptByHero.fill(0);
-		reader.getStream()->read(ret.artifsKeptByHero.data(), ret.artifsKeptByHero.size() - 1);
-	}
+		for(int iId = 0, byte = 0; iId < size * 8; ++iId)
+		{
+			if(iId % 8 == 0)
+				byte = reader.readUInt8();
+			if(byte & (1 << iId % 8))
+				container.insert(T(iId));
+		}
+	};
+	
+	bitMaskToId(ret.monstersKeptByHero, 19);
+	if(version < CampaignVersion::SoD)
+		bitMaskToId(ret.artifactsKeptByHero, 17);
 	else
-	{
-		reader.getStream()->read(ret.artifsKeptByHero.data(), ret.artifsKeptByHero.size());
-	}
+		bitMaskToId(ret.artifactsKeptByHero, 18);
 
 	ret.startOptions = reader.readUInt8();
 
diff --git a/lib/mapping/CCampaignHandler.h b/lib/mapping/CCampaignHandler.h
index bb9e9e765..9dde5cae1 100644
--- a/lib/mapping/CCampaignHandler.h
+++ b/lib/mapping/CCampaignHandler.h
@@ -77,8 +77,7 @@ public:
 	CampaignRegions campaignRegions;
 	int numberOfScenarios = 0;
 	std::string name, description;
-	ui8 difficultyChoosenByPlayer = 0;
-	ui8 music = 0; //CmpMusic.txt, start from 0, field is unused in vcmi
+	bool difficultyChoosenByPlayer = false;
 	bool valid = false;
 
 	std::string filename;
@@ -90,34 +89,44 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int formatVersion)
 	{
 		h & version;
-		if(!h.saving && formatVersion < 821)
-		{
-			ui8 campId = 0; //legacy field
-			h & campId;
-			loadLegacyData(campId);
-		}
-		else
-		{
-			h & campaignRegions;
-			h & numberOfScenarios;
-		}
+		h & campaignRegions;
+		h & numberOfScenarios;
 		h & name;
 		h & description;
 		h & difficultyChoosenByPlayer;
-		if(formatVersion < 821)
-			h & music; //deprecated
 		h & filename;
 		h & modName;
 		h & encoding;
+		h & valid;
 	}
 };
 
 class DLL_LINKAGE CScenarioTravel
 {
 public:
-	ui8 whatHeroKeeps = 0; //bitfield [0] - experience, [1] - prim skills, [2] - sec skills, [3] - spells, [4] - artifacts
-	std::array<ui8, 19> monstersKeptByHero;
-	std::array<ui8, 18> artifsKeptByHero;
+	
+	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;
+		}
+	};
+	
+	WhatHeroKeeps whatHeroKeeps;
+	
+	std::set<CreatureID> monstersKeptByHero;
+	std::set<ArtifactID> artifactsKeptByHero;
 
 	ui8 startOptions = 0; //1 - start bonus, 2 - traveling hero, 3 - hero options
 
@@ -147,7 +156,7 @@ public:
 	{
 		h & whatHeroKeeps;
 		h & monstersKeptByHero;
-		h & artifsKeptByHero;
+		h & artifactsKeptByHero;
 		h & startOptions;
 		h & playerColor;
 		h & bonusesToChoose;
@@ -176,7 +185,6 @@ public:
 
 	std::string mapName; //*.h3m
 	std::string scenarioName; //from header. human-readble
-	ui32 packedMapSize = 0; //generally not used
 	std::set<ui8> preconditionRegions; //what we need to conquer to conquer this one (stored as bitfield in h3c)
 	ui8 regionColor = 0;
 	ui8 difficulty = 0;
@@ -200,7 +208,6 @@ public:
 	{
 		h & mapName;
 		h & scenarioName;
-		h & packedMapSize;
 		h & preconditionRegions;
 		h & regionColor;
 		h & difficulty;