瀏覽代碼

Implemented translations for H3M maps

Ivan Savenko 2 年之前
父節點
當前提交
260f6d626c

+ 6 - 2
client/windows/GUIClasses.cpp

@@ -408,8 +408,12 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill::PrimarySki
 	mainTitle = std::make_shared<CLabel>(192, 33, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->allTexts[444]) % hero->getNameTranslated()));
 
 	//%s is now a level %d %s.
-	levelTitle = std::make_shared<CLabel>(192, 162, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE,
-		boost::str(boost::format(CGI->generaltexth->allTexts[445]) % hero->getNameTranslated() % hero->level % hero->type->heroClass->getNameTranslated()));
+	std::string levelTitleText = CGI->generaltexth->translate("core.genrltxt.445");
+	boost::replace_first(levelTitleText, "%s", hero->getNameTranslated());
+	boost::replace_first(levelTitleText, "%d", std::to_string(hero->level));
+	boost::replace_first(levelTitleText, "%s", hero->type->heroClass->getNameTranslated());
+
+	levelTitle = std::make_shared<CLabel>(192, 162, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, levelTitleText);
 
 	skillIcon = std::make_shared<CAnimImage>("PSKIL42", pskill, 0, 174, 190);
 

+ 4 - 0
config/objects/generic.json

@@ -840,6 +840,10 @@
 			}
 		}
 	},
+	"marketOfTime" : { // Unused/not implemented H3 object present on some maps RoE maps
+		"index" :50,
+		"handler": "generic"
+	},
 	"tavern" : {
 		"index" :95,
 		"handler": "generic",

+ 11 - 1
lib/CGeneralTextHandler.cpp

@@ -257,7 +257,17 @@ const std::string & CGeneralTextHandler::deserialize(const TextIdentifier & iden
 void CGeneralTextHandler::registerString(const std::string & modContext, const TextIdentifier & UID, const std::string & localized)
 {
 	assert(UID.get().find("..") == std::string::npos); // invalid identifier - there is section that was evaluated to empty string
-	assert(stringsLocalizations.count(UID.get()) == 0); // registering already registered string?
+	//assert(stringsLocalizations.count(UID.get()) == 0); // registering already registered string?
+
+	if (stringsLocalizations.count(UID.get()) > 0)
+	{
+		std::string oldValue = stringsLocalizations[UID.get()].baseValue;
+
+		if (oldValue != localized)
+			logMod->warn("Duplicate registered string '%s' found! Old value: '%s', new value: '%s'", UID.get(), oldValue, localized);
+		return;
+	}
+
 	assert(!modContext.empty());
 	assert(!getModLanguage(modContext).empty());
 

+ 1 - 0
lib/GameConstants.h

@@ -792,6 +792,7 @@ public:
 		SCHOOL_OF_MAGIC = 47,
 		MAGIC_SPRING = 48,
 		MAGIC_WELL = 49,
+		MARKET_OF_TIME = 50,
 		MERCENARY_CAMP = 51,
 		MERMAID = 52,
 		MINE = 53,

+ 10 - 8
lib/mapping/CCampaignHandler.cpp

@@ -53,7 +53,7 @@ CCampaignHeader CCampaignHandler::getHeader( const std::string & name)
 
 	CMemoryStream stream(cmpgn.data(), cmpgn.size());
 	CBinaryReader reader(&stream);
-	CCampaignHeader ret = readHeaderFromMemory(reader, name, encoding);
+	CCampaignHeader ret = readHeaderFromMemory(reader, name, modName, encoding);
 
 	return ret;
 }
@@ -72,12 +72,12 @@ std::unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & na
 
 	CMemoryStream stream(file[0].data(), file[0].size());
 	CBinaryReader reader(&stream);
-	ret->header = readHeaderFromMemory(reader, name, encoding);
+	ret->header = readHeaderFromMemory(reader, name, modName, encoding);
 
 	int howManyScenarios = static_cast<int>(VLC->generaltexth->getCampaignLength(ret->header.mapVersion));
 	for(int g=0; g<howManyScenarios; ++g)
 	{
-		CCampaignScenario sc = readScenarioFromMemory(reader, name, encoding, ret->header.version, ret->header.mapVersion);
+		CCampaignScenario sc = readScenarioFromMemory(reader, encoding, ret->header.version, ret->header.mapVersion);
 		ret->scenarios.push_back(sc);
 	}
 
@@ -102,7 +102,8 @@ std::unique_ptr<CCampaign> CCampaignHandler::getCampaign( const std::string & na
 			reinterpret_cast<const ui8 *>(ret->mapPieces[scenarioID].c_str()),
 			static_cast<int>(ret->mapPieces[scenarioID].size()),
 			scenarioName,
-			"(unknown)");
+			modName,
+			encoding);
 		ret->scenarios[scenarioID].scenarioName = hdr->name;
 		scenarioID++;
 	}
@@ -129,7 +130,7 @@ std::string CCampaignHandler::readLocalizedString(CBinaryReader & reader, std::s
 	return TextOperations::toUnicode(reader.readBaseString(), encoding);
 }
 
-CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader, std::string filename, std::string encoding )
+CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader, std::string filename, std::string modName, std::string encoding )
 {
 	CCampaignHeader ret;
 
@@ -143,11 +144,12 @@ CCampaignHeader CCampaignHandler::readHeaderFromMemory( CBinaryReader & reader,
 		ret.difficultyChoosenByPlayer = 0;
 	ret.music = reader.readInt8();
 	ret.filename = filename;
+	ret.modName = modName;
 	ret.encoding = encoding;
 	return ret;
 }
 
-CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & reader, std::string filename, std::string encoding, int version, int mapVersion )
+CCampaignScenario CCampaignHandler::readScenarioFromMemory( CBinaryReader & reader, std::string encoding, int version, int mapVersion )
 {
 	auto prologEpilogReader = [&]() -> CCampaignScenario::SScenarioPrologEpilog
 	{
@@ -473,7 +475,7 @@ CMap * CCampaignState::getMap(int scenarioId) const
 	std::string & mapContent = camp->mapPieces.find(scenarioId)->second;
 	const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
 	CMapService mapService;
-	return mapService.loadMap(buffer, static_cast<int>(mapContent.size()), scenarioName, "(unknown)").release();
+	return mapService.loadMap(buffer, static_cast<int>(mapContent.size()), scenarioName, camp->header.modName, camp->header.encoding).release();
 }
 
 std::unique_ptr<CMapHeader> CCampaignState::getHeader(int scenarioId) const
@@ -487,7 +489,7 @@ std::unique_ptr<CMapHeader> CCampaignState::getHeader(int scenarioId) const
 	std::string & mapContent = camp->mapPieces.find(scenarioId)->second;
 	const auto * buffer = reinterpret_cast<const ui8 *>(mapContent.data());
 	CMapService mapService;
-	return mapService.loadMapHeader(buffer, static_cast<int>(mapContent.size()), scenarioName, "(unknown)");
+	return mapService.loadMapHeader(buffer, static_cast<int>(mapContent.size()), scenarioName, camp->header.modName, camp->header.encoding);
 }
 
 std::shared_ptr<CMapInfo> CCampaignState::getMapInfo(int scenarioId) const

+ 4 - 2
lib/mapping/CCampaignHandler.h

@@ -43,6 +43,7 @@ public:
 	ui8 music = 0; //CmpMusic.txt, start from 0
 
 	std::string filename;
+	std::string modName;
 	std::string encoding;
 
 	template <typename Handler> void serialize(Handler &h, const int formatVersion)
@@ -54,6 +55,7 @@ public:
 		h & difficultyChoosenByPlayer;
 		h & music;
 		h & filename;
+		h & modName;
 		h & encoding;
 	}
 };
@@ -220,8 +222,8 @@ class DLL_LINKAGE CCampaignHandler
 
 	static std::string readLocalizedString(CBinaryReader & reader, std::string encoding);
 
-	static CCampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string encoding);
-	static CCampaignScenario readScenarioFromMemory(CBinaryReader & reader, std::string filename, std::string encoding, int version, int mapVersion );
+	static CCampaignHeader readHeaderFromMemory(CBinaryReader & reader, std::string filename, std::string modName, std::string encoding);
+	static CCampaignScenario readScenarioFromMemory(CBinaryReader & reader, std::string encoding, int version, int mapVersion );
 	static CScenarioTravel readScenarioTravelFromMemory(CBinaryReader & reader, int 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

+ 9 - 9
lib/mapping/CMapService.cpp

@@ -33,7 +33,7 @@ std::unique_ptr<CMap> CMapService::loadMap(const ResourceID & name) const
 	std::string encoding = Languages::getLanguageOptions(language).encoding;
 
 	auto stream = getStreamFromFS(name);
-	return getMapLoader(stream, name.getName(), encoding)->loadMap();
+	return getMapLoader(stream, name.getName(), modName, encoding)->loadMap();
 }
 
 std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ResourceID & name) const
@@ -43,13 +43,13 @@ std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ResourceID & name)
 	std::string encoding = Languages::getLanguageOptions(language).encoding;
 
 	auto stream = getStreamFromFS(name);
-	return getMapLoader(stream, name.getName(), encoding)->loadMapHeader();
+	return getMapLoader(stream, name.getName(), modName, encoding)->loadMapHeader();
 }
 
-std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const
+std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size, const std::string & name,  const std::string & modName, const std::string & encoding) const
 {
 	auto stream = getStreamFromMem(buffer, size);
-	std::unique_ptr<CMap> map(getMapLoader(stream, name, encoding)->loadMap());
+	std::unique_ptr<CMap> map(getMapLoader(stream, name, modName, encoding)->loadMap());
 	std::unique_ptr<CMapHeader> header(map.get());
 
 	//might be original campaign and require patch
@@ -59,10 +59,10 @@ std::unique_ptr<CMap> CMapService::loadMap(const ui8 * buffer, int size, const s
 	return map;
 }
 
-std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const
+std::unique_ptr<CMapHeader> CMapService::loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const
 {
 	auto stream = getStreamFromMem(buffer, size);
-	std::unique_ptr<CMapHeader> header = getMapLoader(stream, name, encoding)->loadMapHeader();
+	std::unique_ptr<CMapHeader> header = getMapLoader(stream, name, modName, encoding)->loadMapHeader();
 
 	//might be original campaign and require patch
 	getMapPatcher(name)->patchMapHeader(header);
@@ -96,7 +96,7 @@ std::unique_ptr<CInputStream> CMapService::getStreamFromMem(const ui8 * buffer,
 	return std::unique_ptr<CInputStream>(new CMemoryStream(buffer, size));
 }
 
-std::unique_ptr<IMapLoader> CMapService::getMapLoader(std::unique_ptr<CInputStream> & stream, std::string mapName, std::string encoding)
+std::unique_ptr<IMapLoader> CMapService::getMapLoader(std::unique_ptr<CInputStream> & stream, std::string mapName, std::string modName, std::string encoding)
 {
 	// Read map header
 	CBinaryReader reader(stream.get());
@@ -119,12 +119,12 @@ std::unique_ptr<IMapLoader> CMapService::getMapLoader(std::unique_ptr<CInputStre
 			// gzip header magic number, reversed for LE
 			case 0x00088B1F:
 				stream = std::unique_ptr<CInputStream>(new CCompressedStream(std::move(stream), true));
-				return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, encoding, stream.get()));
+				return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, modName, encoding, stream.get()));
 			case EMapFormat::WOG :
 			case EMapFormat::AB  :
 			case EMapFormat::ROE :
 			case EMapFormat::SOD :
-				return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, encoding, stream.get()));
+				return std::unique_ptr<IMapLoader>(new CMapLoaderH3M(mapName, modName, encoding, stream.get()));
 			default :
 				throw std::runtime_error("Unknown map format");
 		}

+ 5 - 5
lib/mapping/CMapService.h

@@ -58,7 +58,7 @@ public:
 	 * @param name indicates name of file that will be used during map header patching
 	 * @return a unique ptr to the loaded map class
 	 */
-	virtual std::unique_ptr<CMap> loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const = 0;
+	virtual std::unique_ptr<CMap> loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const = 0;
 
 	/**
 	 * Loads the VCMI/H3 map header from a buffer. This method is temporarily
@@ -72,7 +72,7 @@ public:
 	 * @param name indicates name of file that will be used during map header patching
 	 * @return a unique ptr to the loaded map class
 	 */
-	virtual std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const = 0;
+	virtual std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const = 0;
 
 	virtual void saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath) const = 0;
 };
@@ -85,8 +85,8 @@ public:
 
 	std::unique_ptr<CMap> loadMap(const ResourceID & name) const override;
 	std::unique_ptr<CMapHeader> loadMapHeader(const ResourceID & name) const override;
-	std::unique_ptr<CMap> loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const override;
-	std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const override;
+	std::unique_ptr<CMap> loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const override;
+	std::unique_ptr<CMapHeader> loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const override;
 	void saveMap(const std::unique_ptr<CMap> & map, boost::filesystem::path fullPath) const override;
 private:
 	/**
@@ -113,7 +113,7 @@ private:
 	 * @param stream the input map stream
 	 * @return the constructed map loader
 	 */
-	static std::unique_ptr<IMapLoader> getMapLoader(std::unique_ptr<CInputStream> & stream, std::string mapName, std::string encoding);
+	static std::unique_ptr<IMapLoader> getMapLoader(std::unique_ptr<CInputStream> & stream, std::string mapName, std::string modName, std::string encoding);
 
 	/**
 	 * Gets a map patcher for specified scenario

+ 49 - 36
lib/mapping/MapFormatH3M.cpp

@@ -35,11 +35,12 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 const bool CMapLoaderH3M::IS_PROFILING_ENABLED = false;
 
-CMapLoaderH3M::CMapLoaderH3M(const std::string & mapName, const std::string & encodingName, CInputStream * stream)
+CMapLoaderH3M::CMapLoaderH3M(const std::string & mapName, const std::string & modName, const std::string & encodingName, CInputStream * stream)
 	: map(nullptr)
 	, reader(new CBinaryReader(stream))
 	, inputStream(stream)
-	, mapName(mapName)
+	, mapName(boost::algorithm::to_lower_copy(mapName))
+	, modName(modName)
 	, fileEncoding(encodingName)
 {
 }
@@ -161,8 +162,8 @@ void CMapLoaderH3M::readHeader()
 	mapHeader->areAnyPlayers = reader->readBool();
 	mapHeader->height = mapHeader->width = reader->readUInt32();
 	mapHeader->twoLevel = reader->readBool();
-	mapHeader->name = readLocalizedString();
-	mapHeader->description = readLocalizedString();
+	mapHeader->name = readLocalizedString("header.name");
+	mapHeader->description = readLocalizedString("header.description");
 	mapHeader->difficulty = reader->readInt8();
 	if(mapHeader->version != EMapFormat::ROE)
 	{
@@ -270,7 +271,7 @@ void CMapLoaderH3M::readPlayerInfo()
 			if (mapHeader->players[i].mainCustomHeroPortrait == 0xff)
 				mapHeader->players[i].mainCustomHeroPortrait = -1; //correct 1-byte -1 (0xff) into 4-byte -1
 
-			mapHeader->players[i].mainCustomHeroName = readLocalizedString();
+			mapHeader->players[i].mainCustomHeroName = readLocalizedString(TextIdentifier("header", "player", i, "mainHeroName"));
 		}
 		else
 			mapHeader->players[i].mainCustomHeroId = -1; //correct 1-byte -1 (0xff) into 4-byte -1
@@ -284,7 +285,7 @@ void CMapLoaderH3M::readPlayerInfo()
 			{
 				SHeroName vv;
 				vv.heroId = reader->readUInt8();
-				vv.heroName = readLocalizedString();
+				vv.heroName = readLocalizedString( TextIdentifier("header", "heroNames", vv.heroId));
 
 				mapHeader->players[i].heroesNames.push_back(vv);
 			}
@@ -650,7 +651,7 @@ void CMapLoaderH3M::readDisposedHeroes()
 		{
 			map->disposedHeroes[g].heroId = reader->readUInt8();
 			map->disposedHeroes[g].portrait = reader->readUInt8();
-			map->disposedHeroes[g].name = readLocalizedString();
+			map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId));
 			map->disposedHeroes[g].players = reader->readUInt8();
 		}
 	}
@@ -738,8 +739,8 @@ void CMapLoaderH3M::readRumors()
 	for(int it = 0; it < rumNr; it++)
 	{
 		Rumor ourRumor;
-		ourRumor.name = readLocalizedString();
-		ourRumor.text = readLocalizedString();
+		ourRumor.name = readLocalizedString(TextIdentifier("header", "rumor", it, "name"));
+		ourRumor.text = readLocalizedString(TextIdentifier("header", "rumor", it, "text"));
 		map->rumors.push_back(ourRumor);
 	}
 }
@@ -788,7 +789,7 @@ void CMapLoaderH3M::readPredefinedHeroes()
 				bool hasCustomBio = reader->readBool();
 				if(hasCustomBio)
 				{
-					hero->biographyCustom = readLocalizedString();
+					hero->biographyCustom = readLocalizedString(TextIdentifier("heroes", z, "biography"));
 				}
 
 				// 0xFF is default, 00 male, 01 female
@@ -994,7 +995,7 @@ void CMapLoaderH3M::readObjects()
 				auto * evnt = new CGEvent();
 				nobj = evnt;
 
-				readMessageAndGuards(evnt->message, evnt);
+				readMessageAndGuards(evnt->message, evnt, objPos);
 
 				evnt->gainedExp = reader->readUInt32();
 				evnt->manaDiff = reader->readUInt32();
@@ -1084,7 +1085,7 @@ void CMapLoaderH3M::readObjects()
 				bool hasMessage = reader->readBool();
 				if(hasMessage)
 				{
-					cre->message = readLocalizedString();
+					cre->message = readLocalizedString(TextIdentifier("monster", objPos.x, objPos.y, objPos.z, "message"));
 					readResourses(cre->resources);
 
 					int artID = 0;
@@ -1130,13 +1131,13 @@ void CMapLoaderH3M::readObjects()
 			{
 				auto * sb = new CGSignBottle();
 				nobj = sb;
-				sb->message = readLocalizedString();
+				sb->message = readLocalizedString(TextIdentifier("sign", objPos.x, objPos.y, objPos.z, "message"));
 				reader->skip(4);
 				break;
 			}
 		case Obj::SEER_HUT:
 			{
-				nobj = readSeerHut();
+				nobj = readSeerHut(objPos);
 				break;
 			}
 		case Obj::WITCH_HUT:
@@ -1217,7 +1218,7 @@ void CMapLoaderH3M::readObjects()
 				auto * art = new CGArtifact();
 				nobj = art;
 
-				readMessageAndGuards(art->message, art);
+				readMessageAndGuards(art->message, art, objPos);
 
 				if(objTempl->id == Obj::SPELL_SCROLL)
 				{
@@ -1239,7 +1240,7 @@ void CMapLoaderH3M::readObjects()
 				auto * res = new CGResource();
 				nobj = res;
 
-				readMessageAndGuards(res->message, res);
+				readMessageAndGuards(res->message, res, objPos);
 
 				res->amount = reader->readUInt32();
 				if(objTempl->subid == Res::GOLD)
@@ -1253,7 +1254,7 @@ void CMapLoaderH3M::readObjects()
 		case Obj::RANDOM_TOWN:
 		case Obj::TOWN:
 			{
-				nobj = readTown(objTempl->subid);
+				nobj = readTown(objTempl->subid, objPos);
 				break;
 			}
 		case Obj::MINE:
@@ -1298,7 +1299,7 @@ void CMapLoaderH3M::readObjects()
 			{
 				auto * box = new CGPandoraBox();
 				nobj = box;
-				readMessageAndGuards(box->message, box);
+				readMessageAndGuards(box->message, box, objPos);
 
 				box->gainedExp = reader->readUInt32();
 				box->manaDiff = reader->readUInt32();
@@ -1412,7 +1413,7 @@ void CMapLoaderH3M::readObjects()
 		case Obj::QUEST_GUARD:
 			{
 				auto * guard = new CGQuestGuard();
-				readQuest(guard);
+				readQuest(guard, objPos);
 				nobj = guard;
 				break;
 			}
@@ -1604,7 +1605,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const ObjectInstanceID & idToBeGiven,
 	bool hasName = reader->readBool();
 	if(hasName)
 	{
-		nhi->nameCustom = readLocalizedString();
+		nhi->nameCustom = readLocalizedString(TextIdentifier("heroes", nhi->subID, "name"));
 	}
 	if(map->version > EMapFormat::AB)
 	{
@@ -1669,7 +1670,7 @@ CGObjectInstance * CMapLoaderH3M::readHero(const ObjectInstanceID & idToBeGiven,
 		bool hasCustomBiography = reader->readBool();
 		if(hasCustomBiography)
 		{
-			nhi->biographyCustom = readLocalizedString();
+			nhi->biographyCustom = readLocalizedString(TextIdentifier("heroes", nhi->subID, "biography"));
 		}
 		nhi->sex = reader->readUInt8();
 
@@ -1740,13 +1741,13 @@ CGObjectInstance * CMapLoaderH3M::readHero(const ObjectInstanceID & idToBeGiven,
 	return nhi;
 }
 
-CGSeerHut * CMapLoaderH3M::readSeerHut()
+CGSeerHut * CMapLoaderH3M::readSeerHut(const int3 & position)
 {
 	auto * hut = new CGSeerHut();
 
 	if(map->version > EMapFormat::ROE)
 	{
-		readQuest(hut);
+		readQuest(hut, position);
 	}
 	else
 	{
@@ -1854,7 +1855,7 @@ CGSeerHut * CMapLoaderH3M::readSeerHut()
 	return hut;
 }
 
-void CMapLoaderH3M::readQuest(IQuestObject * guard)
+void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
 {
 	guard->quest->missionType = static_cast<CQuest::Emission>(reader->readUInt8());
 
@@ -1926,15 +1927,15 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard)
 	{
 		guard->quest->lastDay = limit;
 	}
-	guard->quest->firstVisitText = readLocalizedString();
-	guard->quest->nextVisitText = readLocalizedString();
-	guard->quest->completedText = readLocalizedString();
+	guard->quest->firstVisitText = readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "firstVisit"));
+	guard->quest->nextVisitText = readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "nextVisit"));
+	guard->quest->completedText = readLocalizedString(TextIdentifier("quest", position.x, position.y, position.z, "completed"));
 	guard->quest->isCustomFirst = !guard->quest->firstVisitText.empty();
 	guard->quest->isCustomNext = !guard->quest->nextVisitText.empty();
 	guard->quest->isCustomComplete = !guard->quest->completedText.empty();
 }
 
-CGTownInstance * CMapLoaderH3M::readTown(int castleID)
+CGTownInstance * CMapLoaderH3M::readTown(int castleID, const int3 & position)
 {
 	auto * nt = new CGTownInstance();
 	if(map->version > EMapFormat::ROE)
@@ -1945,7 +1946,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 	bool hasName = reader->readBool();
 	if(hasName)
 	{
-		nt->setNameTranslated( readLocalizedString());
+		nt->setNameTranslated(readLocalizedString(TextIdentifier("town", position.x, position.y, position.z, "name")));
 	}
 
 	bool hasGarrison = reader->readBool();
@@ -2025,8 +2026,8 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 	{
 		CCastleEvent nce;
 		nce.town = nt;
-		nce.name = readLocalizedString();
-		nce.message = readLocalizedString();
+		nce.name = readBasicString();
+		nce.message = readLocalizedString(TextIdentifier("town", position.x, position.y, position.z, "event", gh, "description"));
 
 		readResourses(nce.resources);
 
@@ -2136,8 +2137,8 @@ void CMapLoaderH3M::readEvents()
 	for(int yyoo = 0; yyoo < numberOfEvents; ++yyoo)
 	{
 		CMapEvent ne;
-		ne.name = readLocalizedString();
-		ne.message = readLocalizedString();
+		ne.name = readBasicString();
+		ne.message = readLocalizedString(TextIdentifier("event", yyoo, "description"));
 
 		readResourses(ne.resources);
 		ne.players = reader->readUInt8();
@@ -2159,12 +2160,12 @@ void CMapLoaderH3M::readEvents()
 	}
 }
 
-void CMapLoaderH3M::readMessageAndGuards(std::string& message, CCreatureSet* guards)
+void CMapLoaderH3M::readMessageAndGuards(std::string& message, CCreatureSet* guards, const int3 & position)
 {
 	bool hasMessage = reader->readBool();
 	if(hasMessage)
 	{
-		message = readLocalizedString();
+		message = readLocalizedString(TextIdentifier("guards", position.x, position.y, position.z, "message"));
 		bool hasGuards = reader->readBool();
 		if(hasGuards)
 		{
@@ -2243,11 +2244,23 @@ int3 CMapLoaderH3M::readInt3()
 	return p;
 }
 
-std::string CMapLoaderH3M::readLocalizedString()
+std::string CMapLoaderH3M::readBasicString()
 {
 	return TextOperations::toUnicode(reader->readBaseString(), fileEncoding);
 }
 
+std::string CMapLoaderH3M::readLocalizedString(const TextIdentifier & stringIdentifier)
+{
+	std::string mapString = TextOperations::toUnicode(reader->readBaseString(), fileEncoding);
+	TextIdentifier fullIdentifier(mapName, stringIdentifier.get());
+
+	if (mapString.empty())
+		return "";
+
+	VLC->generaltexth->registerString(modName, fullIdentifier, mapString);
+	return VLC->generaltexth->translate(fullIdentifier.get());
+}
+
 void CMapLoaderH3M::afterRead()
 {
 	//convert main town positions for all players to actual object position, in H3M it is position of active tile

+ 11 - 6
lib/mapping/MapFormatH3M.h

@@ -29,6 +29,7 @@ class IQuestObject;
 class CGTownInstance;
 class CCreatureSet;
 class CInputStream;
+class TextIdentifier;
 
 
 class DLL_LINKAGE CMapLoaderH3M : public IMapLoader
@@ -39,7 +40,7 @@ public:
 	 *
 	 * @param stream a stream containing the map data
 	 */
-	CMapLoaderH3M(const std::string & mapName, const std::string & encodingName, CInputStream * stream);
+	CMapLoaderH3M(const std::string & mapName, const std::string & modName, const std::string & encodingName, CInputStream * stream);
 
 	/**
 	 * Destructor.
@@ -171,14 +172,14 @@ private:
 	 *
 	 * @return the initialized seer hut object
 	 */
-	CGSeerHut * readSeerHut();
+	CGSeerHut * readSeerHut(const int3 & position);
 
 	/**
 	 * Reads a quest for the given quest guard.
 	 *
 	 * @param guard the quest guard where that quest should be applied to
 	 */
-	void readQuest(IQuestObject * guard);
+	void readQuest(IQuestObject * guard, const int3 & position);
 
 	/**
 	 * Reads a town.
@@ -186,7 +187,7 @@ private:
 	 * @param castleID the id of the castle type
 	 * @return the loaded town object
 	 */
-	CGTownInstance * readTown(int castleID);
+	CGTownInstance * readTown(int castleID, const int3 & position);
 
 	/**
 	 * Converts buildings to the specified castle id.
@@ -206,7 +207,7 @@ private:
 	/**
 	* read optional message and optional guards
 	*/
-	void readMessageAndGuards(std::string& message, CCreatureSet * guards);
+	void readMessageAndGuards(std::string & message, CCreatureSet * guards, const int3 & position);
 
 	void readSpells(std::set<SpellID> & dest);
 
@@ -237,7 +238,10 @@ private:
 	int3 readInt3();
 
 	/// reads string from input stream and converts it to unicode
-	std::string readLocalizedString();
+	std::string readBasicString();
+
+	/// reads string from input stream, converts it to unicode and attempts to translate it
+	std::string readLocalizedString(const TextIdentifier & identifier);
 
 	void afterRead();
 
@@ -257,6 +261,7 @@ private:
 	CInputStream * inputStream;
 
 	std::string mapName;
+	std::string modName;
 	std::string fileEncoding;
 
 };