From 260f6d626c836266d12cc30aad4672813fbcf76f Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Sat, 25 Feb 2023 17:44:15 +0200 Subject: [PATCH] Implemented translations for H3M maps --- client/windows/GUIClasses.cpp | 8 ++- config/objects/generic.json | 4 ++ lib/CGeneralTextHandler.cpp | 12 ++++- lib/GameConstants.h | 1 + lib/mapping/CCampaignHandler.cpp | 18 ++++--- lib/mapping/CCampaignHandler.h | 6 ++- lib/mapping/CMapService.cpp | 18 +++---- lib/mapping/CMapService.h | 10 ++-- lib/mapping/MapFormatH3M.cpp | 85 ++++++++++++++++++-------------- lib/mapping/MapFormatH3M.h | 17 ++++--- 10 files changed, 110 insertions(+), 69 deletions(-) diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 1e5e89122..2d803c903 100644 --- a/client/windows/GUIClasses.cpp +++ b/client/windows/GUIClasses.cpp @@ -408,8 +408,12 @@ CLevelWindow::CLevelWindow(const CGHeroInstance * hero, PrimarySkill::PrimarySki mainTitle = std::make_shared(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(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(192, 162, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, levelTitleText); skillIcon = std::make_shared("PSKIL42", pskill, 0, 174, 190); diff --git a/config/objects/generic.json b/config/objects/generic.json index 859da47e9..5ec2d8c49 100644 --- a/config/objects/generic.json +++ b/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", diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 47cda45d8..b5b667b42 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/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()); diff --git a/lib/GameConstants.h b/lib/GameConstants.h index 6140991ac..d0869e24f 100644 --- a/lib/GameConstants.h +++ b/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, diff --git a/lib/mapping/CCampaignHandler.cpp b/lib/mapping/CCampaignHandler.cpp index ba26f3051..933ad499b 100644 --- a/lib/mapping/CCampaignHandler.cpp +++ b/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 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(VLC->generaltexth->getCampaignLength(ret->header.mapVersion)); for(int g=0; gheader.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 CCampaignHandler::getCampaign( const std::string & na reinterpret_cast(ret->mapPieces[scenarioID].c_str()), static_cast(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(mapContent.data()); CMapService mapService; - return mapService.loadMap(buffer, static_cast(mapContent.size()), scenarioName, "(unknown)").release(); + return mapService.loadMap(buffer, static_cast(mapContent.size()), scenarioName, camp->header.modName, camp->header.encoding).release(); } std::unique_ptr CCampaignState::getHeader(int scenarioId) const @@ -487,7 +489,7 @@ std::unique_ptr CCampaignState::getHeader(int scenarioId) const std::string & mapContent = camp->mapPieces.find(scenarioId)->second; const auto * buffer = reinterpret_cast(mapContent.data()); CMapService mapService; - return mapService.loadMapHeader(buffer, static_cast(mapContent.size()), scenarioName, "(unknown)"); + return mapService.loadMapHeader(buffer, static_cast(mapContent.size()), scenarioName, camp->header.modName, camp->header.encoding); } std::shared_ptr CCampaignState::getMapInfo(int scenarioId) const diff --git a/lib/mapping/CCampaignHandler.h b/lib/mapping/CCampaignHandler.h index aa102490d..0895834b3 100644 --- a/lib/mapping/CCampaignHandler.h +++ b/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 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 diff --git a/lib/mapping/CMapService.cpp b/lib/mapping/CMapService.cpp index a755cf74c..7f16f4525 100644 --- a/lib/mapping/CMapService.cpp +++ b/lib/mapping/CMapService.cpp @@ -33,7 +33,7 @@ std::unique_ptr 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 CMapService::loadMapHeader(const ResourceID & name) const @@ -43,13 +43,13 @@ std::unique_ptr 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 CMapService::loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const +std::unique_ptr 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 map(getMapLoader(stream, name, encoding)->loadMap()); + std::unique_ptr map(getMapLoader(stream, name, modName, encoding)->loadMap()); std::unique_ptr header(map.get()); //might be original campaign and require patch @@ -59,10 +59,10 @@ std::unique_ptr CMapService::loadMap(const ui8 * buffer, int size, const s return map; } -std::unique_ptr CMapService::loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const +std::unique_ptr 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 header = getMapLoader(stream, name, encoding)->loadMapHeader(); + std::unique_ptr 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 CMapService::getStreamFromMem(const ui8 * buffer, return std::unique_ptr(new CMemoryStream(buffer, size)); } -std::unique_ptr CMapService::getMapLoader(std::unique_ptr & stream, std::string mapName, std::string encoding) +std::unique_ptr CMapService::getMapLoader(std::unique_ptr & stream, std::string mapName, std::string modName, std::string encoding) { // Read map header CBinaryReader reader(stream.get()); @@ -119,12 +119,12 @@ std::unique_ptr CMapService::getMapLoader(std::unique_ptr(new CCompressedStream(std::move(stream), true)); - return std::unique_ptr(new CMapLoaderH3M(mapName, encoding, stream.get())); + return std::unique_ptr(new CMapLoaderH3M(mapName, modName, encoding, stream.get())); case EMapFormat::WOG : case EMapFormat::AB : case EMapFormat::ROE : case EMapFormat::SOD : - return std::unique_ptr(new CMapLoaderH3M(mapName, encoding, stream.get())); + return std::unique_ptr(new CMapLoaderH3M(mapName, modName, encoding, stream.get())); default : throw std::runtime_error("Unknown map format"); } diff --git a/lib/mapping/CMapService.h b/lib/mapping/CMapService.h index 6e35b0c53..ab671c8ba 100644 --- a/lib/mapping/CMapService.h +++ b/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 loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const = 0; + virtual std::unique_ptr 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 loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const = 0; + virtual std::unique_ptr 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 & map, boost::filesystem::path fullPath) const = 0; }; @@ -85,8 +85,8 @@ public: std::unique_ptr loadMap(const ResourceID & name) const override; std::unique_ptr loadMapHeader(const ResourceID & name) const override; - std::unique_ptr loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const override; - std::unique_ptr loadMapHeader(const ui8 * buffer, int size, const std::string & name, const std::string & encoding) const override; + std::unique_ptr loadMap(const ui8 * buffer, int size, const std::string & name, const std::string & modName, const std::string & encoding) const override; + std::unique_ptr 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 & 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 getMapLoader(std::unique_ptr & stream, std::string mapName, std::string encoding); + static std::unique_ptr getMapLoader(std::unique_ptr & stream, std::string mapName, std::string modName, std::string encoding); /** * Gets a map patcher for specified scenario diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index c9ac87356..1e29b62e9 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/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(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 diff --git a/lib/mapping/MapFormatH3M.h b/lib/mapping/MapFormatH3M.h index b90753161..955c7e386 100644 --- a/lib/mapping/MapFormatH3M.h +++ b/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 & 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; };