diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 11d45d74e..1e05a3821 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -16,6 +16,7 @@ #include "CConfigHandler.h" #include "CModHandler.h" #include "GameConstants.h" +#include "mapObjects/CQuest.h" #include "VCMI_Lib.h" #include "Terrain.h" @@ -374,7 +375,10 @@ CGeneralTextHandler::CGeneralTextHandler(): tentColors (*this, "core.tentcolr" ), levels (*this, "core.skilllev" ), zelp (*this, "core.help" ), + allTexts (*this, "core.genrltxt" ), // pseudo-array, that don't have H3 file with same name + seerEmpty (*this, "core.seerhut.empty" ), + seerNames (*this, "core.seerhut.names" ), capColors (*this, "vcmi.capitalColors" ), qeModCommands (*this, "vcmi.quickExchange" ) { @@ -416,10 +420,11 @@ CGeneralTextHandler::CGeneralTextHandler(): { CLegacyConfigParser parser("DATA/GENRLTXT.TXT"); parser.endLine(); + size_t index = 0; do { - allTexts.push_back(parser.readString()); - registerH3String("core.genrltxt", allTexts.size(), allTexts.back()); + registerH3String("core.genrltxt", index, parser.readString()); + index += 1; } while (parser.endLine()); } @@ -458,33 +463,30 @@ CGeneralTextHandler::CGeneralTextHandler(): for (int i = 0; i < 6; ++i) { - seerEmpty.push_back(parser.readString()); - registerH3String("core.seerhut.empty", seerEmpty.size(), seerEmpty.back()); + registerH3String("core.seerhut.empty", i, parser.readString()); } parser.endLine(); - quests.resize(10); for (int i = 0; i < 9; ++i) //9 types of quests { - quests[i].resize(5); + std::string questName = CQuest::missionName(CQuest::Emission(1+i)); + for (int j = 0; j < 5; ++j) { + std::string questState = CQuest::missionState(j); + parser.readString(); //front description for (int k = 0; k < 6; ++k) { - quests[i][j].push_back(parser.readString()); - - registerH3String("core.seerhut.quest." + std::to_string(i) + "." + std::to_string(j), k, quests[i][j].back()); + registerH3String("core.seerhut.quest." + questName + "." + questState, k, parser.readString()); } - parser.endLine(); } } - quests[9].resize(1); for (int k = 0; k < 6; ++k) //Time limit { - quests[9][0].push_back(parser.readString()); + registerH3String("core.seerhut.time", k, parser.readString()); } parser.endLine(); @@ -493,7 +495,7 @@ CGeneralTextHandler::CGeneralTextHandler(): for (int i = 0; i < 48; ++i) { - seerNames.push_back(parser.readString()); + registerH3String("core.seerhut.names", i, parser.readString()); parser.endLine(); } } @@ -569,6 +571,8 @@ CGeneralTextHandler::CGeneralTextHandler(): logGlobal->warn("WoG file ZNPC00.TXT containing commander texts was not found"); } } + + dumpAllTexts(); } int32_t CGeneralTextHandler::pluralText(const int32_t textIndex, const int32_t count) const @@ -583,6 +587,22 @@ int32_t CGeneralTextHandler::pluralText(const int32_t textIndex, const int32_t c return textIndex + 1; } +void CGeneralTextHandler::dumpAllTexts() +{ + logGlobal->info("BEGIN TEXT EXPORT"); + for ( auto const & entry : stringsLocalizations) + { + auto cleanString = entry.second; + boost::replace_all(cleanString, "\\", "\\\\"); + boost::replace_all(cleanString, "\n", "\\n"); + boost::replace_all(cleanString, "\r", "\\r"); + boost::replace_all(cleanString, "\t", "\\t"); + + logGlobal->info("\"%s\" : \"%s\",", entry.first, cleanString); + } + logGlobal->info("END TEXT EXPORT"); +} + std::vector CGeneralTextHandler::findStringsWithPrefix(std::string const & prefix) { std::vector result; diff --git a/lib/CGeneralTextHandler.h b/lib/CGeneralTextHandler.h index d3c5f87e8..fd641f656 100644 --- a/lib/CGeneralTextHandler.h +++ b/lib/CGeneralTextHandler.h @@ -93,6 +93,7 @@ public: class CGeneralTextHandler; +/// Small wrapper that provides text access API compatible with old code class DLL_LINKAGE LegacyTextContainer { CGeneralTextHandler & owner; @@ -103,6 +104,7 @@ public: const std::string & operator[](size_t index) const; }; +/// Small wrapper that provides help text access API compatible with old code class DLL_LINKAGE LegacyHelpContainer { CGeneralTextHandler & owner; @@ -141,7 +143,10 @@ public: /// converts identifier into user-readable string, may be identical to 'translate' but reserved for serialization calls const std::string & deserialize(const std::string & identifier) const; - std::vector allTexts; + /// Debug methods, dumps all currently known texts into console using Json-like format + void dumpAllTexts(); + + LegacyTextContainer allTexts; LegacyTextContainer arraytxt; LegacyTextContainer primarySkillNames; @@ -171,10 +176,8 @@ public: LegacyTextContainer restypes; //names of resources LegacyTextContainer terrainNames; LegacyTextContainer randsign; - std::vector seerEmpty; - std::vector>> quests; //[quest][type][index] - //type: quest, progress, complete, rollover, log OR time limit //index: 0-2 seer hut, 3-5 border guard - std::vector seerNames; + LegacyTextContainer seerEmpty; + LegacyTextContainer seerNames; LegacyTextContainer tentColors; //sec skills diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 7cbe050b1..0d58ad14d 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -24,7 +24,7 @@ VCMI_LIB_NAMESPACE_BEGIN ///helpers -static std::string & visitedTxt(const bool visited) +static const std::string & visitedTxt(const bool visited) { int id = visited ? 352 : 353; return VLC->generaltexth->allTexts[id]; diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 837841715..a0a799b20 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -56,12 +56,48 @@ static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 showInfoDialog(playerID,txtID,soundID); } -static std::string & visitedTxt(const bool visited) +static const std::string & visitedTxt(const bool visited) { int id = visited ? 352 : 353; return VLC->generaltexth->allTexts[id]; } +const std::string & CQuest::missionName(CQuest::Emission mission) +{ + static const std::array names = { + "empty", + "heroLevel", + "primarySkill", + "killHero", + "killCreature", + "bringArt", + "bringCreature", + "bringResources", + "bringHero", + "bringPlayer", + "keymaster," + }; + + if (static_cast(mission) < names.size()) + return names[static_cast(mission)]; + return names[0]; +} + +const std::string & CQuest::missionState(int state) +{ + static const std::array states = { + "receive", + "visit", + "complete", + "hover", + "description", + }; + + if (state < states.size()) + return states[state]; + return states[0]; +} + bool CQuest::checkMissionArmy(const CQuest * q, const CCreatureSet * army) { std::vector::const_iterator cre; @@ -264,7 +300,10 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const if(onHover) ms << "\n\n"; - ms << VLC->generaltexth->quests[missionType-1][onHover ? 3 : 4][textOption]; + std::string questName = missionName(Emission(missionType-1)); + std::string questState = missionState(onHover ? 3 : 4); + + ms << VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + questState,textOption); switch(missionType) { @@ -535,7 +574,9 @@ void CGSeerHut::setObjToKill() void CGSeerHut::init(CRandomGenerator & rand) { - seerName = *RandomGeneratorUtil::nextItem(VLC->generaltexth->seerNames, rand); + auto names = VLC->generaltexth->findStringsWithPrefix("core.seerhut.names"); + + seerName = *RandomGeneratorUtil::nextItem(names, rand); quest->textOption = rand.nextInt(2); quest->completedOption = rand.nextInt(1, 3); } @@ -547,12 +588,14 @@ void CGSeerHut::initObj(CRandomGenerator & rand) quest->progress = CQuest::NOT_ACTIVE; if(quest->missionType) { + std::string questName = quest->missionName(CQuest::Emission(quest->missionType-1)); + if(!quest->isCustomFirst) - quest->firstVisitText = VLC->generaltexth->quests[quest->missionType-1][0][quest->textOption]; + quest->firstVisitText = VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + quest->missionState(0), quest->textOption); if(!quest->isCustomNext) - quest->nextVisitText = VLC->generaltexth->quests[quest->missionType-1][1][quest->textOption]; + quest->nextVisitText = VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + quest->missionState(1), quest->textOption); if(!quest->isCustomComplete) - quest->completedText = VLC->generaltexth->quests[quest->missionType-1][2][quest->textOption]; + quest->completedText = VLC->generaltexth->translate("core.seerhut.quest." + questName + "." + quest->missionState(2), quest->textOption); } else { diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 12312bac3..83c55bf96 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -24,9 +24,28 @@ class DLL_LINKAGE CQuest final mutable std::unordered_map artifactsRequirements; // artifact ID -> required count public: - enum Emission {MISSION_NONE = 0, MISSION_LEVEL = 1, MISSION_PRIMARY_STAT = 2, MISSION_KILL_HERO = 3, MISSION_KILL_CREATURE = 4, - MISSION_ART = 5, MISSION_ARMY = 6, MISSION_RESOURCES = 7, MISSION_HERO = 8, MISSION_PLAYER = 9, MISSION_KEYMASTER = 10}; - enum Eprogress {NOT_ACTIVE, IN_PROGRESS, COMPLETE}; + enum Emission { + MISSION_NONE = 0, + MISSION_LEVEL = 1, + MISSION_PRIMARY_STAT = 2, + MISSION_KILL_HERO = 3, + MISSION_KILL_CREATURE = 4, + MISSION_ART = 5, + MISSION_ARMY = 6, + MISSION_RESOURCES = 7, + MISSION_HERO = 8, + MISSION_PLAYER = 9, + MISSION_KEYMASTER = 10 + }; + + enum Eprogress { + NOT_ACTIVE, + IN_PROGRESS, + COMPLETE + }; + + static const std::string & missionName(Emission mission); + static const std::string & missionState(int index); si32 qid; //unique quest id for serialization / identification diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index e6452f53d..7e031765a 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -402,7 +402,7 @@ Component CRewardInfo::getDisplayedComponent(const CGHeroInstance * h) const } // FIXME: copy-pasted from CObjectHandler -static std::string & visitedTxt(const bool visited) +static const std::string & visitedTxt(const bool visited) { int id = visited ? 352 : 353; return VLC->generaltexth->allTexts[id]; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 290bdeba9..5ea0cd76e 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -58,7 +58,7 @@ static void showInfoDialog(const CGHeroInstance* h, const ui32 txtID, const ui16 showInfoDialog(playerID,txtID,soundID); } -static std::string & visitedTxt(const bool visited) +static const std::string & visitedTxt(const bool visited) { int id = visited ? 352 : 353; return VLC->generaltexth->allTexts[id]; diff --git a/lib/spells/effects/Damage.cpp b/lib/spells/effects/Damage.cpp index 88b0d1848..3f641cfb4 100644 --- a/lib/spells/effects/Damage.cpp +++ b/lib/spells/effects/Damage.cpp @@ -179,7 +179,7 @@ void Damage::describeEffect(std::vector & log, const Mechanics * m, { MetaString line; //todo: handle newlines in metastring - std::string text = VLC->generaltexth->allTexts.at(343); //Does %d points of damage. + std::string text = VLC->generaltexth->allTexts[343]; //Does %d points of damage. boost::algorithm::trim(text); line << text; line.addReplacement((int)damage); //no more text afterwards diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index b38cb8a9c..a2bee3141 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1300,7 +1300,7 @@ void CGameHandler::addGenericKilledLog(BattleLogMessage & blm, const CStack * de if(killed > 0) { const int32_t txtIndex = (killed > 1) ? 379 : 378; - std::string formatString = VLC->generaltexth->allTexts.at(txtIndex); + std::string formatString = VLC->generaltexth->allTexts[txtIndex]; // these default h3 texts have unnecessary new lines, so get rid of them before displaying (and trim just in case, trimming newlines does not works for some reason) formatString.erase(std::remove(formatString.begin(), formatString.end(), '\n'), formatString.end()); @@ -5209,7 +5209,7 @@ void CGameHandler::playerMessage(PlayerColor player, const std::string &message, if (cheated) { - SystemMessage temp_message(VLC->generaltexth->allTexts.at(260)); + SystemMessage temp_message(VLC->generaltexth->allTexts[260]); sendAndApply(&temp_message); if(!player.isSpectator())