Răsfoiți Sursa

Identifier remapping support for campaigns

Ivan Savenko 4 luni în urmă
părinte
comite
1ea2ce7959

+ 2 - 0
lib/CMakeLists.txt

@@ -172,6 +172,7 @@ set(lib_MAIN_SRCS
 	mapping/MapFormatH3M.cpp
 	mapping/MapReaderH3M.cpp
 	mapping/MapFormatJson.cpp
+	mapping/MapFormatSettings.cpp
 	mapping/ObstacleProxy.cpp
 
 	modding/ActiveModsInSaveList.cpp
@@ -587,6 +588,7 @@ set(lib_MAIN_HEADERS
 	mapping/MapFeaturesH3M.h
 	mapping/MapFormatH3M.h
 	mapping/MapFormat.h
+	mapping/MapFormatSettings.h
 	mapping/MapReaderH3M.h
 	mapping/MapFormatJson.h
 	mapping/ObstacleProxy.h

+ 3 - 0
lib/GameLibrary.cpp

@@ -26,6 +26,7 @@
 #include "entities/hero/CHeroClassHandler.h"
 #include "entities/hero/CHeroHandler.h"
 #include "texts/CGeneralTextHandler.h"
+#include "mapping/MapFormatSettings.h"
 #include "modding/CModHandler.h"
 #include "modding/IdentifierStorage.h"
 #include "modding/CModVersion.h"
@@ -192,6 +193,8 @@ void GameLibrary::initializeLibrary()
 
 	modh->load();
 	modh->afterLoad();
+
+	createHandler(mapFormat);
 }
 
 #if SCRIPTING_ENABLED

+ 2 - 0
lib/GameLibrary.h

@@ -41,6 +41,7 @@ class IGameSettings;
 class GameSettings;
 class CIdentifierStorage;
 class SpellSchoolHandler;
+class MapFormatSettings;
 
 #if SCRIPTING_ENABLED
 namespace scripting
@@ -97,6 +98,7 @@ public:
 	std::unique_ptr<ObstacleHandler> obstacleHandler;
 	std::unique_ptr<GameSettings> settingsHandler;
 	std::unique_ptr<ObstacleSetHandler> biomeHandler;
+	std::unique_ptr<MapFormatSettings> mapFormat;
 
 #if SCRIPTING_ENABLED
 	std::unique_ptr<scripting::ScriptHandler> scriptHandler;

+ 10 - 9
lib/campaign/CampaignBonus.cpp

@@ -11,6 +11,7 @@
 #include "CampaignBonus.h"
 
 #include "../filesystem/CBinaryReader.h"
+#include "../mapping/MapIdentifiersH3M.h"
 #include "../json/JsonNode.h"
 #include "../constants/StringConstants.h"
 
@@ -47,7 +48,7 @@ static const std::map<std::string, ui8> resourceTypeMap = {
 	{"rare", EGameResID::RARE}
 };
 
-CampaignBonus::CampaignBonus(CBinaryReader & reader, CampaignStartOptions mode)
+CampaignBonus::CampaignBonus(CBinaryReader & reader, const MapIdentifiersH3M & remapper, CampaignStartOptions mode)
 {
 	switch(mode)
 	{
@@ -65,7 +66,7 @@ CampaignBonus::CampaignBonus(CBinaryReader & reader, CampaignStartOptions mode)
 				{
 					HeroTypeID hero(reader.readUInt16());
 					SpellID spell(reader.readUInt8());
-					data = CampaignBonusSpell{hero, spell};
+					data = CampaignBonusSpell{remapper.remap(hero), spell};
 					break;
 				}
 				case CampaignBonusType::MONSTER:
@@ -73,27 +74,27 @@ CampaignBonus::CampaignBonus(CBinaryReader & reader, CampaignStartOptions mode)
 					HeroTypeID hero(reader.readUInt16());
 					CreatureID creature(reader.readUInt16());
 					int32_t amount = reader.readUInt16();
-					data = CampaignBonusCreatures{hero, creature, amount};
+					data = CampaignBonusCreatures{remapper.remap(hero), remapper.remap(creature), amount};
 					break;
 				}
 				case CampaignBonusType::BUILDING:
 				{
 					BuildingID building(reader.readUInt8());
-					data = CampaignBonusBuilding{building};
+					data = CampaignBonusBuilding{remapper.remapBuilding(std::nullopt, building)};
 					break;
 				}
 				case CampaignBonusType::ARTIFACT:
 				{
 					HeroTypeID hero(reader.readUInt16());
 					ArtifactID artifact(reader.readUInt16());
-					data = CampaignBonusArtifact{hero, artifact};
+					data = CampaignBonusArtifact{remapper.remap(hero), remapper.remap(artifact)};
 					break;
 				}
 				case CampaignBonusType::SPELL_SCROLL:
 				{
 					HeroTypeID hero(reader.readUInt16());
 					SpellID spell(reader.readUInt8());
-					data = CampaignBonusSpellScroll{hero, spell};
+					data = CampaignBonusSpellScroll{remapper.remap(hero), spell};
 					break;
 				}
 				case CampaignBonusType::PRIMARY_SKILL:
@@ -103,7 +104,7 @@ CampaignBonus::CampaignBonus(CBinaryReader & reader, CampaignStartOptions mode)
 					for(auto & value : amounts)
 						value = reader.readUInt8();
 
-					data = CampaignBonusPrimarySkill{hero, amounts};
+					data = CampaignBonusPrimarySkill{remapper.remap(hero), amounts};
 					break;
 				}
 				case CampaignBonusType::SECONDARY_SKILL:
@@ -111,7 +112,7 @@ CampaignBonus::CampaignBonus(CBinaryReader & reader, CampaignStartOptions mode)
 					HeroTypeID hero(reader.readUInt16());
 					SecondarySkill skill(reader.readUInt8());
 					int32_t skillMastery(reader.readUInt8());
-					data = CampaignBonusSecondarySkill{hero, skill, skillMastery};
+					data = CampaignBonusSecondarySkill{remapper.remap(hero), remapper.remap(skill), skillMastery};
 					break;
 				}
 				case CampaignBonusType::RESOURCE:
@@ -137,7 +138,7 @@ CampaignBonus::CampaignBonus(CBinaryReader & reader, CampaignStartOptions mode)
 		{
 			PlayerColor player(reader.readUInt8());
 			HeroTypeID hero(reader.readInt16());
-			data = CampaignBonusStartingHero{player, hero};
+			data = CampaignBonusStartingHero{player, remapper.remap(hero)};
 			break;
 		}
 		default:

+ 2 - 1
lib/campaign/CampaignBonus.h

@@ -15,6 +15,7 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 class CBinaryReader;
+class MapIdentifiersH3M;
 class JsonNode;
 
 struct CampaignBonusSpell
@@ -169,7 +170,7 @@ public:
 		:data(value)
 	{}
 
-	DLL_LINKAGE CampaignBonus(CBinaryReader & reader, CampaignStartOptions mode);
+	DLL_LINKAGE CampaignBonus(CBinaryReader & reader, const MapIdentifiersH3M & remapper, CampaignStartOptions mode);
 	DLL_LINKAGE CampaignBonus(const JsonNode & json, CampaignStartOptions mode);
 
 	template<typename T>

+ 10 - 7
lib/campaign/CampaignHandler.cpp

@@ -20,6 +20,7 @@
 #include "../GameLibrary.h"
 #include "../mapping/CMapHeader.h"
 #include "../mapping/CMapService.h"
+#include "../mapping/MapFormatSettings.h"
 #include "../modding/CModHandler.h"
 #include "../modding/IdentifierStorage.h"
 #include "../modding/ModScope.h"
@@ -425,14 +426,14 @@ CampaignScenario CampaignHandler::readScenarioFromMemory( CBinaryReader & reader
 }
 
 template<typename Identifier>
-static void readContainer(std::set<Identifier> & container, CBinaryReader & reader, int sizeBytes)
+static void readContainer(std::set<Identifier> & container, CBinaryReader & reader, const MapIdentifiersH3M & remapper, int sizeBytes)
 {
 	for(int iId = 0, byte = 0; iId < sizeBytes * 8; ++iId)
 	{
 		if(iId % 8 == 0)
 			byte = reader.readUInt8();
 		if(byte & (1 << iId % 8))
-			container.insert(Identifier(iId));
+			container.insert(remapper.remap(Identifier(iId)));
 	}
 }
 
@@ -446,16 +447,18 @@ CampaignTravel CampaignHandler::readScenarioTravelFromMemory(CBinaryReader & rea
 	ret.whatHeroKeeps.secondarySkills = whatHeroKeeps & 4;
 	ret.whatHeroKeeps.spells = whatHeroKeeps & 8;
 	ret.whatHeroKeeps.artifacts = whatHeroKeeps & 16;
+
+	const auto & mapping = LIBRARY->mapFormat->getMapping(version);
 	
 	if (version == CampaignVersion::HotA)
 	{
-		readContainer(ret.monstersKeptByHero, reader, 24);
-		readContainer(ret.artifactsKeptByHero, reader, 21);
+		readContainer(ret.monstersKeptByHero, reader, mapping, 24);
+		readContainer(ret.artifactsKeptByHero, reader, mapping, 21);
 	}
 	else
 	{
-		readContainer(ret.monstersKeptByHero, reader, 19);
-		readContainer(ret.artifactsKeptByHero, reader, version < CampaignVersion::SoD ? 17 : 18);
+		readContainer(ret.monstersKeptByHero, reader, mapping, 19);
+		readContainer(ret.artifactsKeptByHero, reader, mapping, version < CampaignVersion::SoD ? 17 : 18);
 	}
 
 	ret.startOptions = static_cast<CampaignStartOptions>(reader.readUInt8());
@@ -467,7 +470,7 @@ CampaignTravel CampaignHandler::readScenarioTravelFromMemory(CBinaryReader & rea
 	{
 		ui8 numOfBonuses = reader.readUInt8();
 		for (int g=0; g<numOfBonuses; ++g)
-			ret.bonusesToChoose.emplace_back(reader, ret.startOptions);
+			ret.bonusesToChoose.emplace_back(reader, mapping, ret.startOptions);
 	}
 
 	return ret;

+ 4 - 2
lib/mapping/MapFeaturesH3M.h

@@ -16,8 +16,6 @@ enum class EMapFormat : uint8_t;
 
 struct MapFormatFeaturesH3M
 {
-public:
-	static MapFormatFeaturesH3M find(EMapFormat format, uint32_t hotaVersion);
 	static MapFormatFeaturesH3M getFeaturesROE();
 	static MapFormatFeaturesH3M getFeaturesAB();
 	static MapFormatFeaturesH3M getFeaturesSOD();
@@ -27,6 +25,10 @@ public:
 
 	MapFormatFeaturesH3M() = default;
 
+public:
+	static MapFormatFeaturesH3M find(EMapFormat format, uint32_t hotaVersion);
+
+
 	// number of bytes in bitmask of appropriate type
 
 	int factionsBytes;

+ 3 - 51
lib/mapping/MapFormatH3M.cpp

@@ -13,7 +13,7 @@
 
 #include "CMap.h"
 #include "MapReaderH3M.h"
-#include "MapFormat.h"
+#include "MapFormatSettings.h"
 
 #include "../CCreatureHandler.h"
 #include "../texts/CGeneralTextHandler.h"
@@ -111,52 +111,6 @@ void CMapLoaderH3M::init()
 	//map->banWaterContent(); //Not sure if force this for custom scenarios
 }
 
-static MapIdentifiersH3M generateMapping(EMapFormat format)
-{
-	auto features = MapFormatFeaturesH3M::find(format, 0);
-	MapIdentifiersH3M identifierMapper;
-
-	if(features.levelROE)
-		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA));
-	if(features.levelAB)
-		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE));
-	if(features.levelSOD)
-		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH));
-	if(features.levelCHR)
-		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_CHRONICLES));
-	if(features.levelWOG)
-		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_IN_THE_WAKE_OF_GODS));
-	if(features.levelHOTA0)
-		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS));
-
-	return identifierMapper;
-}
-
-static std::map<EMapFormat, MapIdentifiersH3M> generateMappings()
-{
-	std::map<EMapFormat, MapIdentifiersH3M> result;
-	auto addMapping = [&result](EMapFormat format)
-	{
-		try
-		{
-			result[format] = generateMapping(format);
-		}
-		catch(const std::runtime_error &)
-		{
-			// unsupported map format - skip
-		}
-	};
-
-	addMapping(EMapFormat::ROE);
-	addMapping(EMapFormat::AB);
-	addMapping(EMapFormat::SOD);
-	addMapping(EMapFormat::CHR);
-	addMapping(EMapFormat::HOTA);
-	addMapping(EMapFormat::WOG);
-
-	return result;
-}
-
 void CMapLoaderH3M::readHeader()
 {
 	// Map version
@@ -233,12 +187,10 @@ void CMapLoaderH3M::readHeader()
 		reader->setFormatLevel(features);
 	}
 
-	// optimization - load mappings only once to avoid slow parsing of map headers for map list
-	static const std::map<EMapFormat, MapIdentifiersH3M> identifierMappers = generateMappings();
-	if (!identifierMappers.count(mapHeader->version))
+	if (!LIBRARY->mapFormat->isSupported(mapHeader->version))
 		throw std::runtime_error("Unsupported map format! Format ID " + std::to_string(static_cast<int>(mapHeader->version)));
 
-	const MapIdentifiersH3M & identifierMapper = identifierMappers.at(mapHeader->version);
+	const MapIdentifiersH3M & identifierMapper = LIBRARY->mapFormat->getMapping(mapHeader->version);
 
 	reader->setIdentifierRemapper(identifierMapper);
 

+ 85 - 0
lib/mapping/MapFormatSettings.cpp

@@ -0,0 +1,85 @@
+/*
+ * MapFormatSettings.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 "MapFormatSettings.h"
+
+#include "MapFeaturesH3M.h"
+
+#include "../GameLibrary.h"
+#include "../IGameSettings.h"
+
+MapIdentifiersH3M MapFormatSettings::generateMapping(EMapFormat format)
+{
+	auto features = MapFormatFeaturesH3M::find(format, 0);
+	MapIdentifiersH3M identifierMapper;
+
+	if(features.levelROE)
+		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA));
+	if(features.levelAB)
+		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_ARMAGEDDONS_BLADE));
+	if(features.levelSOD)
+		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH));
+	if(features.levelCHR)
+		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_CHRONICLES));
+	if(features.levelWOG)
+		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_IN_THE_WAKE_OF_GODS));
+	if(features.levelHOTA0)
+		identifierMapper.loadMapping(LIBRARY->engineSettings()->getValue(EGameSettings::MAP_FORMAT_HORN_OF_THE_ABYSS));
+
+	return identifierMapper;
+}
+
+std::map<CampaignVersion, EMapFormat> MapFormatSettings::generateCampaignMapping()
+{
+	return {
+		{CampaignVersion::RoE,  EMapFormat::ROE },
+		{CampaignVersion::AB,   EMapFormat::AB  },
+		{CampaignVersion::SoD,  EMapFormat::SOD },
+		{CampaignVersion::WoG,  EMapFormat::WOG },
+		{CampaignVersion::Chr,  EMapFormat::CHR },
+		{CampaignVersion::HotA, EMapFormat::HOTA}
+	};
+}
+
+std::map<EMapFormat, MapIdentifiersH3M> MapFormatSettings::generateMappings()
+{
+	std::map<EMapFormat, MapIdentifiersH3M> result;
+	auto addMapping = [&result](EMapFormat format)
+	{
+		try
+		{
+			result[format] = generateMapping(format);
+			logMod->trace("Loaded map format support for %d", static_cast<int>(format));
+		}
+		catch(const std::runtime_error &)
+		{
+			// unsupported map format - skip
+			logMod->debug("Failed to load map format support for %d", static_cast<int>(format));
+		}
+	};
+
+	addMapping(EMapFormat::ROE);
+	addMapping(EMapFormat::AB);
+	addMapping(EMapFormat::SOD);
+	addMapping(EMapFormat::CHR);
+	addMapping(EMapFormat::HOTA);
+	addMapping(EMapFormat::WOG);
+
+	return result;
+}
+
+MapFormatSettings::MapFormatSettings()
+	: mapping(generateMappings())
+	, campaignToMap(generateCampaignMapping())
+{
+}
+
+VCMI_LIB_NAMESPACE_END

+ 51 - 0
lib/mapping/MapFormatSettings.h

@@ -0,0 +1,51 @@
+/*
+ * MapFormatSettings.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 "MapIdentifiersH3M.h"
+#include "MapFormat.h"
+#include "../campaign/CampaignConstants.h"
+
+VCMI_LIB_NAMESPACE_BEGIN
+
+class MapFormatSettings
+{
+	static MapIdentifiersH3M generateMapping(EMapFormat format);
+	static std::map<EMapFormat, MapIdentifiersH3M> generateMappings();
+	static std::map<CampaignVersion, EMapFormat> generateCampaignMapping();
+
+	std::map<EMapFormat, MapIdentifiersH3M> mapping;
+	std::map<CampaignVersion, EMapFormat> campaignToMap;
+public:
+	MapFormatSettings();
+
+	bool isSupported(EMapFormat format) const
+	{
+		return mapping.count(format) != 0;
+	}
+
+	bool isSupported(CampaignVersion format) const
+	{
+		return isSupported(campaignToMap.at(format));
+	}
+
+	const MapIdentifiersH3M & getMapping(EMapFormat format) const
+	{
+		return mapping.at(format);
+	}
+
+	const MapIdentifiersH3M & getMapping(CampaignVersion format) const
+	{
+		return mapping.at(campaignToMap.at(format));
+	}
+};
+
+VCMI_LIB_NAMESPACE_END