mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-06 09:09:40 +02:00
Identifier remapping support for campaigns
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -447,15 +448,17 @@ CampaignTravel CampaignHandler::readScenarioTravelFromMemory(CBinaryReader & rea
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
lib/mapping/MapFormatSettings.cpp
Normal file
85
lib/mapping/MapFormatSettings.cpp
Normal file
@@ -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
lib/mapping/MapFormatSettings.h
Normal file
51
lib/mapping/MapFormatSettings.h
Normal file
@@ -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
|
||||
Reference in New Issue
Block a user