From 39131eba3ec299b5632c4d082869129d3e5bff5a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Mon, 16 Jan 2023 23:20:24 +0200 Subject: [PATCH 1/5] Added translation support for mods. VCMI translations are now in mod. Moved existing (English) translation to vcmi mod Added translations for: - German - Polish - Ukrainian --- .../vcmi/config/vcmi/english.json | 0 Mods/vcmi/mod.json | 25 ++- config/schemas/mod.json | 19 ++- lib/CGeneralTextHandler.cpp | 143 ++++++++++-------- lib/CGeneralTextHandler.h | 23 ++- lib/CModHandler.cpp | 32 +++- lib/CModHandler.h | 1 + 7 files changed, 154 insertions(+), 89 deletions(-) rename config/translate.json => Mods/vcmi/config/vcmi/english.json (100%) diff --git a/config/translate.json b/Mods/vcmi/config/vcmi/english.json similarity index 100% rename from config/translate.json rename to Mods/vcmi/config/vcmi/english.json diff --git a/Mods/vcmi/mod.json b/Mods/vcmi/mod.json index 84b36e8eb..e2083bb16 100644 --- a/Mods/vcmi/mod.json +++ b/Mods/vcmi/mod.json @@ -7,6 +7,21 @@ "description" : "Grundlegende Dateien, die für die korrekte Ausführung von VCMI erforderlich sind", "author" : "VCMI-Team", "modType" : "Grafik", + + "translations" : [ + "config/vcmi/german.json" + ] + }, + + "polish" : { + "name" : "VCMI essential files", + "description" : "Essential files required for VCMI to run correctly", + "author" : "VCMI Team", + "modType" : "Graphical", + + "translations" : [ + "config/vcmi/polish.json" + ] }, "ukrainian" : { @@ -14,15 +29,23 @@ "description" : "Ключові файли необхідні для повноцінної роботи VCMI", "author" : "Команда VCMI", "modType" : "Графіка", + + "translations" : [ + "config/vcmi/ukrainian.json" + ] }, - "version" : "1.1", + "version" : "1.1.1", "author" : "VCMI Team", "contact" : "http://forum.vcmi.eu/index.php", "modType" : "Graphical", "factions" : [ "config/vcmi/towerFactions" ], "creatures" : [ "config/vcmi/towerCreature" ], + + "translations" : [ + "config/vcmi/english.json" + ], "filesystem": { diff --git a/config/schemas/mod.json b/config/schemas/mod.json index 2cdf44246..0b357f734 100644 --- a/config/schemas/mod.json +++ b/config/schemas/mod.json @@ -8,7 +8,7 @@ "localizable" : { "type":"object", "additionalProperties" : false, - "required" : [ "name", "description", "author", "modType" ], + "required" : [ "name", "description", "modType" ], "properties":{ "name": { "type":"string", @@ -18,7 +18,6 @@ "type":"string", "description": "More lengthy description of mod. No hard limit" }, - "modType" : { "type":"string", "description": "Type of mod, e.g. Town, Artifacts, Graphical." @@ -27,6 +26,11 @@ "type":"string", "description": "Author of the mod. Can be nickname, real name or name of team" }, + "translations":{ + "type":"array", + "description": "List of files with translations for this language", + "items": { "type":"string", "format" : "textFile" } + }, "changelog" : { "type":"object", "description": "List of changes/new features in each version", @@ -112,27 +116,26 @@ "type":"boolean", "description": "If set to true, mod will not be enabled automatically on install" }, - "english" : { "$ref" : "#/definitions/localizable" }, - "german" : { "$ref" : "#/definitions/localizable" }, - "polish" : { "$ref" : "#/definitions/localizable" }, - "russian" : { "$ref" : "#/definitions/localizable" }, - "ukrainian" : { "$ref" : "#/definitions/localizable" }, - + "translations":{ + "type":"array", + "description": "List of files with translations for this language", + "items": { "type":"string", "format" : "textFile" } + }, "artifacts": { "type":"array", "description": "List of configuration files for artifacts", diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 59d0d5aa8..9c77f3cf9 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -103,42 +103,25 @@ bool Unicode::isValidString(const char * data, size_t size) return true; } -static std::string getSelectedEncoding() +/// Detects language and encoding of H3 text files based on matching against pregenerated footprints of H3 file +void CGeneralTextHandler::detectInstallParameters() const { - auto explicitSetting = settings["general"]["encoding"].String(); - if (explicitSetting != "auto") - return explicitSetting; - return settings["session"]["encoding"].String(); -} + struct LanguageFootprint + { + std::string language; + std::string encoding; + std::array footprint; + }; -/// Detects encoding of H3 text files based on matching against pregenerated footprints of H3 file -/// Can also detect language of H3 install, however right now this is not necessary -static void detectEncoding() -{ - static const size_t knownCount = 6; - - // "footprints" of data collected from known versions of H3 - static const std::array, knownCount> knownFootprints = - { { - { { 0.0559, 0.0000, 0.1983, 0.0051, 0.0222, 0.0183, 0.4596, 0.2405, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000 } }, - { { 0.0493, 0.0000, 0.1926, 0.0047, 0.0230, 0.0121, 0.4133, 0.2780, 0.0002, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0259, 0.0008 } }, - { { 0.0534, 0.0000, 0.1705, 0.0047, 0.0418, 0.0208, 0.4775, 0.2191, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0005, 0.0036, 0.0080 } }, - { { 0.0534, 0.0000, 0.1701, 0.0067, 0.0157, 0.0133, 0.4328, 0.2540, 0.0001, 0.0043, 0.0000, 0.0244, 0.0000, 0.0000, 0.0181, 0.0071 } }, - { { 0.0548, 0.0000, 0.1744, 0.0061, 0.0031, 0.0009, 0.0046, 0.0136, 0.0000, 0.0004, 0.0000, 0.0000, 0.0227, 0.0061, 0.4882, 0.2252 } }, - { { 0.0559, 0.0000, 0.1807, 0.0059, 0.0036, 0.0013, 0.0046, 0.0134, 0.0000, 0.0004, 0.0000, 0.0487, 0.0209, 0.0060, 0.4615, 0.1972 } } - } }; - - // languages of known footprints - static const std::array knownLanguages = - { { - "English", "French", "German", "Polish", "Russian", "Ukrainian" - } }; - - // encoding that should be used for known footprints - static const std::array knownEncodings = - { { - "CP1252", "CP1252", "CP1252", "CP1250", "CP1251", "CP1251" - } }; + static const std::vector knownFootprints = + { + { "English", "CP1252", { { 0.0559, 0.0000, 0.1983, 0.0051, 0.0222, 0.0183, 0.4596, 0.2405, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000 } } }, + { "French", "CP1252", { { 0.0493, 0.0000, 0.1926, 0.0047, 0.0230, 0.0121, 0.4133, 0.2780, 0.0002, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0259, 0.0008 } } }, + { "German", "CP1252", { { 0.0534, 0.0000, 0.1705, 0.0047, 0.0418, 0.0208, 0.4775, 0.2191, 0.0001, 0.0000, 0.0000, 0.0000, 0.0000, 0.0005, 0.0036, 0.0080 } } }, + { "Polish", "CP1250", { { 0.0534, 0.0000, 0.1701, 0.0067, 0.0157, 0.0133, 0.4328, 0.2540, 0.0001, 0.0043, 0.0000, 0.0244, 0.0000, 0.0000, 0.0181, 0.0071 } } }, + { "Russian", "CP1251", { { 0.0548, 0.0000, 0.1744, 0.0061, 0.0031, 0.0009, 0.0046, 0.0136, 0.0000, 0.0004, 0.0000, 0.0000, 0.0227, 0.0061, 0.4882, 0.2252 } } }, + { "Ukrainian", "CP1251", { { 0.0559, 0.0000, 0.1807, 0.0059, 0.0036, 0.0013, 0.0046, 0.0134, 0.0000, 0.0004, 0.0000, 0.0487, 0.0209, 0.0060, 0.4615, 0.1972 } } }, + }; // load file that will be used for footprint generation // this is one of the most text-heavy files in game and consists solely from translated texts @@ -146,11 +129,11 @@ static void detectEncoding() std::array charCount; std::array footprint; - std::array deviations; + std::vector deviations; boost::range::fill(charCount, 0); boost::range::fill(footprint, 0.0); - boost::range::fill(deviations, 0.0); + deviations.resize(knownFootprints.size(), 0.0); auto data = resource->readAll(); @@ -173,21 +156,24 @@ static void detectEncoding() for (size_t i = 0; i < deviations.size(); ++i) { for (size_t j = 0; j < footprint.size(); ++j) - deviations[i] += std::abs((footprint[j] - knownFootprints[i][j])); + deviations[i] += std::abs((footprint[j] - knownFootprints[i].footprint[j])); } size_t bestIndex = boost::range::min_element(deviations) - deviations.begin(); for (size_t i = 0; i < deviations.size(); ++i) - logGlobal->debug("Comparing to %s: %f", knownLanguages[i], deviations[i]); + logGlobal->debug("Comparing to %s: %f", knownFootprints[i].language, deviations[i]); - Settings s = settings.write["session"]["encoding"]; - s->String() = knownEncodings[bestIndex]; + Settings language = settings.write["session"]["language"]; + language->String() = knownFootprints[bestIndex].language; + + Settings encoding = settings.write["session"]["encoding"]; + encoding->String() = knownFootprints[bestIndex].encoding; } std::string Unicode::toUnicode(const std::string &text) { - return toUnicode(text, getSelectedEncoding()); + return toUnicode(text, CGeneralTextHandler::getInstalledEncoding()); } std::string Unicode::toUnicode(const std::string &text, const std::string &encoding) @@ -197,7 +183,7 @@ std::string Unicode::toUnicode(const std::string &text, const std::string &encod std::string Unicode::fromUnicode(const std::string & text) { - return fromUnicode(text, getSelectedEncoding()); + return fromUnicode(text, CGeneralTextHandler::getInstalledEncoding()); } std::string Unicode::fromUnicode(const std::string &text, const std::string &encoding) @@ -389,16 +375,14 @@ void CGeneralTextHandler::readToVector(std::string const & sourceID, std::string while (parser.endLine()); } -const std::string & CGeneralTextHandler::serialize(const std::string & identifier) const -{ - assert(stringsIdentifiers.count(identifier)); - return stringsIdentifiers.at(identifier); -} - const std::string & CGeneralTextHandler::deserialize(const TextIdentifier & identifier) const { + if(stringsOverrides.count(identifier.get())) + return stringsOverrides.at(identifier.get()); + if(stringsLocalizations.count(identifier.get())) return stringsLocalizations.at(identifier.get()); + logGlobal->error("Unable to find localization for string '%s'", identifier.get()); return identifier.get(); } @@ -406,11 +390,20 @@ const std::string & CGeneralTextHandler::deserialize(const TextIdentifier & iden void CGeneralTextHandler::registerString(const TextIdentifier & UID, const std::string & localized) { assert(UID.get().find("..") == std::string::npos); - - stringsIdentifiers[localized] = UID.get(); stringsLocalizations[UID.get()] = localized; } +void CGeneralTextHandler::registerStringOverride(const TextIdentifier & UID, const std::string & localized) +{ + stringsOverrides[UID.get()] = localized; +} + +void CGeneralTextHandler::loadTranslationOverrides(const JsonNode & config) +{ + for ( auto const & node : config.Struct()) + registerStringOverride(node.first, node.second.String()); +} + CGeneralTextHandler::CGeneralTextHandler(): victoryConditions(*this, "core.vcdesc" ), lossCondtions (*this, "core.lcdesc" ), @@ -441,8 +434,7 @@ CGeneralTextHandler::CGeneralTextHandler(): znpc00 (*this, "vcmi.znpc00" ), // technically - wog qeModCommands (*this, "vcmi.quickExchange" ) { - if (getSelectedEncoding().empty()) - detectEncoding(); + detectInstallParameters(); readToVector("core.vcdesc", "DATA/VCDESC.TXT" ); readToVector("core.lcdesc", "DATA/LCDESC.TXT" ); @@ -472,11 +464,6 @@ CGeneralTextHandler::CGeneralTextHandler(): if (CResourceHandler::get()->existsResource(ResourceID(QE_MOD_COMMANDS, EResType::TEXT))) readToVector("vcmi.quickExchange", QE_MOD_COMMANDS); - auto vcmiTexts = JsonNode(ResourceID("config/translate.json", EResType::TEXT)); - - for ( auto const & node : vcmiTexts.Struct()) - registerString(node.first, node.second.String()); - { CLegacyConfigParser parser("DATA/RANDTVRN.TXT"); parser.endLine(); @@ -638,18 +625,25 @@ int32_t CGeneralTextHandler::pluralText(const int32_t textIndex, const int32_t c void CGeneralTextHandler::dumpAllTexts() { + auto escapeString = [](std::string input) + { + boost::replace_all(input, "\\", "\\\\"); + boost::replace_all(input, "\n", "\\n"); + boost::replace_all(input, "\r", "\\r"); + boost::replace_all(input, "\t", "\\t"); + boost::replace_all(input, "\"", "\\\""); + + return input; + }; + 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"); - boost::replace_all(cleanString, "\"", "\\\""); + if (stringsOverrides.count(entry.first) == 0) + logGlobal->info("\"%s\" : \"%s\",", entry.first, escapeString(entry.second)); + + for ( auto const & entry : stringsOverrides) + logGlobal->info("\"%s\" : \"%s\",", entry.first, escapeString(entry.second)); - logGlobal->info("\"%s\" : \"%s\",", entry.first, cleanString); - } logGlobal->info("END TEXT EXPORT"); } @@ -662,6 +656,22 @@ size_t CGeneralTextHandler::getCampaignLength(size_t campaignID) const return 0; } +std::string CGeneralTextHandler::getInstalledLanguage() +{ + auto explicitSetting = settings["general"]["language"].String(); + if (explicitSetting != "auto") + return explicitSetting; + return settings["session"]["language"].String(); +} + +std::string CGeneralTextHandler::getInstalledEncoding() +{ + auto explicitSetting = settings["general"]["encoding"].String(); + if (explicitSetting != "auto") + return explicitSetting; + return settings["session"]["encoding"].String(); +} + std::vector CGeneralTextHandler::findStringsWithPrefix(std::string const & prefix) { std::vector result; @@ -698,5 +708,4 @@ std::pair LegacyHelpContainer::operator[](size_t index }; } - VCMI_LIB_NAMESPACE_END diff --git a/lib/CGeneralTextHandler.h b/lib/CGeneralTextHandler.h index 0e25fd38e..ad8eea9a0 100644 --- a/lib/CGeneralTextHandler.h +++ b/lib/CGeneralTextHandler.h @@ -149,17 +149,27 @@ class DLL_LINKAGE CGeneralTextHandler /// map identifier -> localization std::unordered_map stringsLocalizations; - /// map localization -> identifier - std::unordered_map stringsIdentifiers; + /// map identifier -> localization, high-priority strings from translation json + std::unordered_map stringsOverrides; void readToVector(const std::string & sourceID, const std::string & sourceName); /// number of scenarios in specific campaign. TODO: move to a better location std::vector scenariosCountPerCampaign; + + /// Attempts to detect encoding & language of H3 files + void detectInstallParameters() const; public: + /// Loads translation from provided json + /// Any entries loaded by this will have priority over texts registered normally + void loadTranslationOverrides(JsonNode const & file); + /// add selected string to internal storage void registerString(const TextIdentifier & UID, const std::string & localized); + /// add selected string to internal storage as high-priority strings + void registerStringOverride(const TextIdentifier & UID, const std::string & localized); + // returns true if identifier with such name was registered, even if not translated to current language // not required right now, can be added if necessary // bool identifierExists( const std::string identifier) const; @@ -172,9 +182,6 @@ public: return deserialize(id); } - /// converts translated string into locale-independent text that can be sent to another client - const std::string & serialize(const std::string & identifier) const; - /// converts identifier into user-readable string const std::string & deserialize(const TextIdentifier & identifier) const; @@ -226,6 +233,12 @@ public: CGeneralTextHandler(); CGeneralTextHandler(const CGeneralTextHandler&) = delete; CGeneralTextHandler operator=(const CGeneralTextHandler&) = delete; + + /// Returns name of language preferred by user + static std::string getInstalledLanguage(); + + /// Returns name of encoding of Heroes III text files + static std::string getInstalledEncoding(); }; VCMI_LIB_NAMESPACE_END diff --git a/lib/CModHandler.cpp b/lib/CModHandler.cpp index cd8148bce..9e2ed6677 100644 --- a/lib/CModHandler.cpp +++ b/lib/CModHandler.cpp @@ -24,6 +24,7 @@ #include "IHandlerBase.h" #include "spells/CSpellHandler.h" #include "CSkillHandler.h" +#include "CGeneralTextHandler.h" #include "ScriptHandler.h" #include "RoadHandler.h" #include "RiverHandler.h" @@ -455,7 +456,7 @@ CContentHandler::CContentHandler() void CContentHandler::init() { - handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass"))); + handlers.insert(std::make_pair("heroClasses", ContentTypeHandler(&VLC->heroh->classes, "heroClass"))); handlers.insert(std::make_pair("artifacts", ContentTypeHandler(VLC->arth, "artifact"))); handlers.insert(std::make_pair("creatures", ContentTypeHandler(VLC->creh, "creature"))); handlers.insert(std::make_pair("factions", ContentTypeHandler(VLC->townh, "faction"))); @@ -687,7 +688,7 @@ void CModInfo::loadLocalData(const JsonNode & data) validated = data["validated"].Bool(); checksum = strtol(data["checksum"].String().c_str(), nullptr, 16); } - + //check compatibility bool wasEnabled = enabled; enabled = enabled && (vcmiCompatibleMin.isNull() || Version::GameVersion().compatible(vcmiCompatibleMin)); @@ -704,10 +705,10 @@ void CModInfo::loadLocalData(const JsonNode & data) CModHandler::CModHandler() : content(std::make_shared()) { - modules.COMMANDERS = false; - modules.STACK_ARTIFACT = false; - modules.STACK_EXP = false; - modules.MITHRIL = false; + modules.COMMANDERS = false; + modules.STACK_ARTIFACT = false; + modules.STACK_EXP = false; + modules.MITHRIL = false; for (int i = 0; i < GameConstants::RESOURCE_QUANTITY; ++i) { identifiers.registerObject(CModHandler::scopeBuiltin(), "resource", GameConstants::RESOURCE_NAMES[i], i); @@ -841,7 +842,7 @@ std::vector CModHandler::validateAndSortDependencies(std::vector sortedValidMods; // Vector keeps order of elements (LIFO) + std::vector sortedValidMods; // Vector keeps order of elements (LIFO) sortedValidMods.reserve(modsToResolve.size()); // push_back calls won't cause memory reallocation std::set resolvedModIDs; // Use a set for validation for performance reason, but set does not keep order of elements @@ -876,7 +877,7 @@ std::vector CModHandler::validateAndSortDependencies(std::vector generaltexth->getInstalledLanguage(); + + for (auto const & config : mod.config["translations"].Vector()) + VLC->generaltexth->loadTranslationOverrides(JsonNode(ResourceID(config.String(), EResType::TEXT))); + + for (auto const & config : mod.config[language]["translations"].Vector()) + VLC->generaltexth->loadTranslationOverrides(JsonNode(ResourceID(config.String(), EResType::TEXT))); +} + void CModHandler::load() { CStopWatch totalTime, timer; @@ -1123,6 +1136,9 @@ void CModHandler::load() for(const TModID & modName : activeMods) content->load(allMods[modName]); + for(const TModID & modName : activeMods) + loadTranslation(modName); + #if SCRIPTING_ENABLED VLC->scriptHandler->performRegistration(VLC);//todo: this should be done before any other handlers load #endif diff --git a/lib/CModHandler.h b/lib/CModHandler.h index 64e4c420a..897766854 100644 --- a/lib/CModHandler.h +++ b/lib/CModHandler.h @@ -282,6 +282,7 @@ class DLL_LINKAGE CModHandler std::vector getModList(std::string path); void loadMods(std::string path, std::string parent, const JsonNode & modSettings, bool enableMods); void loadOneMod(std::string modName, std::string parent, const JsonNode & modSettings, bool enableMods); + void loadTranslation(TModID modName); public: /// returns true if scope is reserved for internal use and can not be used by mods From 35775b90f83897ec22e004c00f8d3f8ed58389c6 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 17 Jan 2023 12:42:43 +0200 Subject: [PATCH 2/5] Moved bonus names to translation --- Mods/vcmi/config/vcmi/english.json | 159 +++++++++- config/bonuses_texts.json | 476 ----------------------------- config/gameConfig.json | 3 +- config/sp_sounds.json | 13 - lib/CBonusTypeHandler.cpp | 151 +++------ lib/CBonusTypeHandler.h | 40 +-- lib/VCMI_Lib.cpp | 4 +- 7 files changed, 203 insertions(+), 643 deletions(-) delete mode 100644 config/bonuses_texts.json delete mode 100644 config/sp_sounds.json diff --git a/Mods/vcmi/config/vcmi/english.json b/Mods/vcmi/config/vcmi/english.json index 847c1203b..6071cc952 100644 --- a/Mods/vcmi/config/vcmi/english.json +++ b/Mods/vcmi/config/vcmi/english.json @@ -82,5 +82,162 @@ "vcmi.stackExperience.rank.8" : "Expert", "vcmi.stackExperience.rank.9" : "Elite", "vcmi.stackExperience.rank.10" : "Master", - "vcmi.stackExperience.rank.11" : "Ace" + "vcmi.stackExperience.rank.11" : "Ace", + + "core.bonus.ADDITIONAL_ATTACK.name": "Double Strike", + "core.bonus.ADDITIONAL_ATTACK.description": "Attacks twice", + "core.bonus.ADDITIONAL_RETALIATION.name": "Additional retaliations", + "core.bonus.ADDITIONAL_RETALIATION.description": "May Retaliate ${val} extra times", + "core.bonus.AIR_IMMUNITY.name": "Air immunity", + "core.bonus.AIR_IMMUNITY.description": "Immune to all Air school spells", + "core.bonus.ATTACKS_ALL_ADJACENT.name": "Attack all around", + "core.bonus.ATTACKS_ALL_ADJACENT.description": "Attacks all adjacent enemies", + "core.bonus.BLOCKS_RETALIATION.name": "No retaliation", + "core.bonus.BLOCKS_RETALIATION.description": "Enemy cannot Retaliate", + "core.bonus.BLOCKS_RANGED_RETALIATION.name": "No ranged retaliation", + "core.bonus.BLOCKS_RANGED_RETALIATION.description": "Enemy cannot Retaliate by shooting", + "core.bonus.CATAPULT.name": "Catapult", + "core.bonus.CATAPULT.description": "Attacks siege walls", + "core.bonus.CATAPULT_EXTRA_SHOTS.name": "Additional siege attacks", + "core.bonus.CATAPULT_EXTRA_SHOTS.description": "Can hit siege walls ${val} extra times per attack", + "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduce Casting Cost (${val})", + "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduces spell cost for hero", + "core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Magic Damper (${val})", + "core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Increases Cost of enemy spells", + "core.bonus.CHARGE_IMMUNITY.name": "Immune to Charge", + "core.bonus.CHARGE_IMMUNITY.description": "Immune to Champion charge", + "core.bonus.DARKNESS.name": "Darkness cover", + "core.bonus.DARKNESS.description": "Adds ${val} darkness radius", + "core.bonus.DEATH_STARE.name": "Death Stare (${val}%)", + "core.bonus.DEATH_STARE.description": "${val}% chance to kill single creature", + "core.bonus.DEFENSIVE_STANCE.name": "Defense Bonus", + "core.bonus.DEFENSIVE_STANCE.description": "+${val} Defense when defending", + "core.bonus.DESTRUCTION.name": "Destruction", + "core.bonus.DESTRUCTION.description": "Has ${val}% chance to kill extra units after attack", + "core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Death Blow", + "core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% chance for double damage", + "core.bonus.DRAGON_NATURE.name": "Dragon", + "core.bonus.DRAGON_NATURE.description": "Creature has a Dragon Nature", + "core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direct Damage Immunity", + "core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immune to direct damage spells", + "core.bonus.EARTH_IMMUNITY.name": "Earth immunity", + "core.bonus.EARTH_IMMUNITY.description": "Immune to all Earth school spells", + "core.bonus.ENCHANTER.name": "Enchanter", + "core.bonus.ENCHANTER.description": "Can cast mass ${subtype.spell} every turn", + "core.bonus.ENCHANTED.name": "Enchanted", + "core.bonus.ENCHANTED.description": "Affected by permanent ${subtype.spell}", + "core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignore Defense (${val}%)", + "core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignores part of Defence for the attack", + "core.bonus.FIRE_IMMUNITY.name": "Fire immunity", + "core.bonus.FIRE_IMMUNITY.description": "Immune to all Fire school spells", + "core.bonus.FIRE_SHIELD.name": "Fire Shield (${val}%)", + "core.bonus.FIRE_SHIELD.description": "Reflects part of melee damage", + "core.bonus.FIRST_STRIKE.name": "First Strike", + "core.bonus.FIRST_STRIKE.description": "This creature attacks first instead of retaliating", + "core.bonus.FEAR.name": "Fear", + "core.bonus.FEAR.description": "Causes Fear on an enemy stack", + "core.bonus.FEARLESS.name": "Fearless", + "core.bonus.FEARLESS.description": "Immune to Fear ability", + "core.bonus.FLYING.name": "Fly", + "core.bonus.FLYING.description": "Can Fly (ignores obstacles)", + "core.bonus.FREE_SHOOTING.name": "Shoot Close", + "core.bonus.FREE_SHOOTING.description": "Can shoot in Close Combat", + "core.bonus.FULL_HP_REGENERATION.name": "Regeneration", + "core.bonus.FULL_HP_REGENERATION.description": "May Regenerate to full Health", + "core.bonus.GARGOYLE.name": "Gargoyle", + "core.bonus.GARGOYLE.description": "Cannot be rised or healed", + "core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Reduce Damage (${val}%)", + "core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduces physical damage from ranged or melee", + "core.bonus.HATE.name": "Hates ${subtype.creature}", + "core.bonus.HATE.description": "Does ${val}% more damage", + "core.bonus.HEALER.name": "Healer", + "core.bonus.HEALER.description": "Heals allied units", + "core.bonus.HP_REGENERATION.name": "Regeneration", + "core.bonus.HP_REGENERATION.description": "Heals ${val} hit points every round", + "core.bonus.JOUSTING.name": "Champion Charge", + "core.bonus.JOUSTING.description": "+5% damage per hex travelled", + "core.bonus.KING1.name": "King 1", + "core.bonus.KING1.description": "Vulnerable to basic SLAYER", + "core.bonus.KING2.name": "King 2", + "core.bonus.KING2.description": "Vulnerable to advanced SLAYER", + "core.bonus.KING3.name": "King 3", + "core.bonus.KING3.description":"Vulnerable to expert SLAYER", + "core.bonus.LEVEL_SPELL_IMMUNITY.name": "Spell immunity 1-${val}", + "core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immune to spells of levels 1-${val}", + "core.bonus.LIMITED_SHOOTING_RANGE.name" : "Limited shooting range", + "core.bonus.LIMITED_SHOOTING_RANGE.description" : "", + "core.bonus.LIFE_DRAIN.name": "Drain life (${val}%)", + "core.bonus.LIFE_DRAIN.description": "Drains ${val}% of damage dealt", + "core.bonus.MANA_CHANNELING.name": "Magic Channel ${val}%", + "core.bonus.MANA_CHANNELING.description": "Gives your hero mana spent by enemy", + "core.bonus.MANA_DRAIN.name": "Mana Drain", + "core.bonus.MANA_DRAIN.description": "Drains ${val} mana every turn", + "core.bonus.MAGIC_MIRROR.name": "Magic Mirror (${val}%)", + "core.bonus.MAGIC_MIRROR.description": "${val}% chance to redirects an offensive spell to enemy", + "core.bonus.MAGIC_RESISTANCE.name": "Magic Resistance(${MR}%)", + "core.bonus.MAGIC_RESISTANCE.description": "${MR}% chance to resist enemy spell", + "core.bonus.MIND_IMMUNITY.name": "Mind Spell Immunity", + "core.bonus.MIND_IMMUNITY.description": "Immune to Mind-type spells", + "core.bonus.NO_DISTANCE_PENALTY.name": "No distance penalty", + "core.bonus.NO_DISTANCE_PENALTY.description": "Full damage from any distance", + "core.bonus.NO_MELEE_PENALTY.name": "No melee penalty", + "core.bonus.NO_MELEE_PENALTY.description": "Creature has no Melee Penalty", + "core.bonus.NO_MORALE.name": "Neutral Morale", + "core.bonus.NO_MORALE.description": "Creature is immune to morale effects", + "core.bonus.NO_WALL_PENALTY.name": "No wall penalty", + "core.bonus.NO_WALL_PENALTY.description": "Full damage during siege", + "core.bonus.NON_LIVING.name": "Non living", + "core.bonus.NON_LIVING.description": "Immunity to many effects", + "core.bonus.RANDOM_SPELLCASTER.name": "Random spellcaster", + "core.bonus.RANDOM_SPELLCASTER.description": "Can cast random spell", + "core.bonus.RANGED_RETALIATION.name": "Ranged retaliation", + "core.bonus.RANGED_RETALIATION.description": "Can perform ranged counterattack", + "core.bonus.RECEPTIVE.name": "Receptive", + "core.bonus.RECEPTIVE.description": "No Immunity to Friendly Spells", + "core.bonus.REBIRTH.name": "Rebirth (${val}%)", + "core.bonus.REBIRTH.description": "${val}% of stack will rise after death", + "core.bonus.RETURN_AFTER_STRIKE.name": "Attack and Return", + "core.bonus.RETURN_AFTER_STRIKE.description": "Returns after melee attack", + "core.bonus.SELF_LUCK.name": "Positive luck", + "core.bonus.SELF_LUCK.description": "Always has Positive Luck", + "core.bonus.SELF_MORALE.name": "Positive morale", + "core.bonus.SELF_MORALE.description": "Always has Positive Morale", + "core.bonus.SHOOTER.name": "Ranged", + "core.bonus.SHOOTER.description": "Creature can shoot", + "core.bonus.SHOOTS_ALL_ADJACENT.name": "Shoot all around", + "core.bonus.SHOOTS_ALL_ADJACENT.description": "This creature's ranged attacks strike all targets in a small area", + "core.bonus.SOUL_STEAL.name": "Soul Steal", + "core.bonus.SOUL_STEAL.description": "Gains ${val} new creatures for each enemy killed", + "core.bonus.SPELLCASTER.name": "Spellcaster", + "core.bonus.SPELLCASTER.description": "Can cast ${subtype.spell}", + "core.bonus.SPELL_AFTER_ATTACK.name": "Cast After Attack", + "core.bonus.SPELL_AFTER_ATTACK.description": "${val}% to cast ${subtype.spell} after attack", + "core.bonus.SPELL_BEFORE_ATTACK.name": "Cast Before Attack", + "core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% to cast ${subtype.spell} before attack", + "core.bonus.SPELL_DAMAGE_REDUCTION.name": "Spell Resistance", + "core.bonus.SPELL_DAMAGE_REDUCTION.description": "Damage from spells reduced ${val}%.", + "core.bonus.SPELL_IMMUNITY.name": "Spell immunity", + "core.bonus.SPELL_IMMUNITY.description": "Immune to ${subtype.spell}", + "core.bonus.SPELL_LIKE_ATTACK.name": "Spell-like attack", + "core.bonus.SPELL_LIKE_ATTACK.description": "Attacks with ${subtype.spell}", + "core.bonus.SPELL_RESISTANCE_AURA.name": "Aura of Resistance", + "core.bonus.SPELL_RESISTANCE_AURA.description": "Nearby stacks get ${val}% resistance", + "core.bonus.SUMMON_GUARDIANS.name": "Summon guardians", + "core.bonus.SUMMON_GUARDIANS.description": "At battle start summons ${subtype.creature} (${val}%)", + "core.bonus.SYNERGY_TARGET.name": "Synergizable", + "core.bonus.SYNERGY_TARGET.description": "This creature is vulnerable to synergy effect", + "core.bonus.TWO_HEX_ATTACK_BREATH.name": "Breath", + "core.bonus.TWO_HEX_ATTACK_BREATH.description": "Breath Attack (2-hex range)", + "core.bonus.THREE_HEADED_ATTACK.name": "Three-headed attack", + "core.bonus.THREE_HEADED_ATTACK.description": "Attacks three adjacent units", + "core.bonus.TRANSMUTATION.name": "Transmutation", + "core.bonus.TRANSMUTATION.description": "${val}% chance to transform attacked unit to other type", + "core.bonus.UNDEAD.name": "Undead", + "core.bonus.UNDEAD.description": "Creature is Undead", + "core.bonus.UNLIMITED_RETALIATIONS.name": "Unlimited retaliations", + "core.bonus.UNLIMITED_RETALIATIONS.description": "Retaliates any number of attacks", + "core.bonus.WATER_IMMUNITY.name": "Water immunity", + "core.bonus.WATER_IMMUNITY.description": "Immune to all Water school spells", + "core.bonus.WIDE_BREATH.name": "Wide breath", + "core.bonus.WIDE_BREATH.description": "Wide breath attack (multiple hexes)" } diff --git a/config/bonuses_texts.json b/config/bonuses_texts.json deleted file mode 100644 index d7f99bdb2..000000000 --- a/config/bonuses_texts.json +++ /dev/null @@ -1,476 +0,0 @@ -// macros: -// ${val} - value of bonuses; Selector: type,subtype -// ${subtype.creature} - creature name -// ${subtype.spell} - spell name -// ${MR} - magic resistance of bearer - - -{ - "ADDITIONAL_ATTACK": - { - "name": "Double Strike", - "description": "Attacks twice" - }, - - "ADDITIONAL_RETALIATION": - { - "name": "Additional retaliations", - "description": "May Retaliate ${val} extra times" - }, - - "AIR_IMMUNITY": - { - "name": "Air immunity", - "description": "Immune to all Air school spells" - }, - - "ATTACKS_ALL_ADJACENT": - { - "name": "Attack all around", - "description": "Attacks all adjacent enemies" - }, - - "BLOCKS_RETALIATION": - { - "name": "No retaliation", - "description": "Enemy cannot Retaliate" - }, - - "BLOCKS_RANGED_RETALIATION": - { - "name": "No ranged retaliation", - "description": "Enemy cannot Retaliate by shooting" - }, - - "CATAPULT": - { - "name": "Catapult", - "description": "Attacks siege walls" - }, - - "CATAPULT_EXTRA_SHOTS": - { - "name": "Additional siege attacks", - "description": "Can hit siege walls ${val} extra times per attack" - }, - - "CHANGES_SPELL_COST_FOR_ALLY": - { - "name": "Reduce Casting Cost (${val})", - "description": "Reduces spell cost for hero" - }, - - "CHANGES_SPELL_COST_FOR_ENEMY": - { - "name": "Magic Damper (${val})", - "description": "Increases Cost of enemy spells" - }, - - "CHARGE_IMMUNITY": - { - "name": "Immune to Charge", - "description": "Immune to Champion charge" - }, - - "DARKNESS": - { - "name": "Darkness cover", - "description": "Adds ${val} darkness radius" - }, - - "DEATH_STARE": - { - "name": "Death Stare (${val}%)", - "description": "${val}% chance to kill single creature" - }, - - "DEFENSIVE_STANCE": - { - "name": "Defense Bonus", - "description": "+${val} Defense when defending" - }, - - "DESTRUCTION": - { - "name": "Destruction", - "description": "Has ${val}% chance to kill extra units after attack" - }, - - "DOUBLE_DAMAGE_CHANCE": - { - "name": "Death Blow", - "description": "${val}% chance for double damage" - }, - - "DRAGON_NATURE": - { - "name": "Dragon", - "description": "Creature has a Dragon Nature" - }, - - "DIRECT_DAMAGE_IMMUNITY": - { - "name": "Direct Damage Immunity", - "description": "Immune to direct damage spells" - }, - - "EARTH_IMMUNITY": - { - "name": "Earth immunity", - "description": "Immune to all Earth school spells" - }, - - "ENCHANTER": - { - "name": "Enchanter", - "description": "Can cast mass ${subtype.spell} every turn" - }, - - "ENCHANTED": - { - "name": "Enchanted", - "description": "Affected by permanent ${subtype.spell}" - }, - - "ENEMY_DEFENCE_REDUCTION": - { - "name": "Ignore Defense (${val}%)", - "description": "Ignores part of Defence for the attack" - }, - - "FIRE_IMMUNITY": - { - "name": "Fire immunity", - "description": "Immune to all Fire school spells" - }, - - "FIRE_SHIELD": - { - "name": "Fire Shield (${val}%)", - "description": "Reflects part of melee damage" - }, - - "FIRST_STRIKE": - { - "name": "First Strike", - "description": "This creature attacks first instead of retaliating" - }, - - "FEAR": - { - "name": "Fear", - "description": "Causes Fear on an enemy stack" - }, - - "FEARLESS": - { - "name": "Fearless", - "description": "Immune to Fear ability" - }, - - "FLYING": - { - "name": "Fly", - "description": "Can Fly (ignores obstacles)" - }, - - "FREE_SHOOTING": - { - "name": "Shoot Close", - "description": "Can shoot in Close Combat" - }, - - "FULL_HP_REGENERATION": - { - "name": "Regeneration", - "description": "May Regenerate to full Health" - }, - - "GARGOYLE": - { - "name": "Gargoyle", - "description": "Cannot be rised or healed" - }, - - "GENERAL_DAMAGE_REDUCTION": - { - "name": "Reduce Damage (${val}%)", - "description": "Reduces physical damage from ranged or melee" - }, - - "HATE": - { - "name": "Hates ${subtype.creature}", - "description": "Does ${val}% more damage" - }, - - "HEALER": - { - "name": "Healer", - "description": "Heals allied units" - }, - - "HP_REGENERATION": - { - "name": "Regeneration", - "description": "Heals ${val} hit points every round" - }, - - "JOUSTING": - { - "name": "Champion Charge", - "description": "+5% damage per hex travelled" - }, - - "KING1": - { - "name": "King 1", - "description": "Vulnerable to basic SLAYER" - }, - - "KING2": - { - "name": "King 2", - "description": "Vulnerable to advanced SLAYER" - }, - - "KING3": - { - "name": "King 3", - "description":"Vulnerable to expert SLAYER" - }, - - "LEVEL_SPELL_IMMUNITY": - { - "name": "Spell immunity 1-${val}", - "description": "Immune to spells of levels 1-${val}" - }, - - "LIFE_DRAIN": - { - "name": "Drain life (${val}%)", - "description": "Drains ${val}% of damage dealt" - }, - - "LIMITED_SHOOTING_RANGE": - { - "name": "Limited shooting range", - "description": "Cannot shoot targets beyond ${val} hexes away" - }, - - "MANA_CHANNELING": - { - "name": "Magic Channel ${val}%", - "description": "Gives your hero mana spent by enemy" - }, - - "MANA_DRAIN": - { - "name": "Mana Drain", - "description": "Drains ${val} mana every turn" - }, - - "MAGIC_MIRROR": - { - "name": "Magic Mirror (${val}%)", - "description": "${val}% chance to redirects an offensive spell to enemy" - }, - - "MAGIC_RESISTANCE": - { - "name": "Magic Resistance(${MR}%)", - "description": "${MR}% chance to resist enemy spell" - }, - - "MIND_IMMUNITY": - { - "name": "Mind Spell Immunity", - "description": "Immune to Mind-type spells" - }, - - "NO_DISTANCE_PENALTY": - { - "name": "No distance penalty", - "description": "Full damage from any distance" - }, - - "NO_MELEE_PENALTY": - { - "name": "No melee penalty", - "description": "Creature has no Melee Penalty" - }, - - "NO_MORALE": - { - "name": "Neutral Morale", - "description": "Creature is immune to morale effects" - }, - - "NO_WALL_PENALTY": - { - "name": "No wall penalty", - "description": "Full damage during siege" - }, - - "NON_LIVING": - { - "name": "Non living", - "description": "Immunity to many effects" - }, - - "RANDOM_SPELLCASTER": - { - "name": "Random spellcaster", - "description": "Can cast random spell" - }, - - "RANGED_RETALIATION": - { - "name": "Ranged retaliation", - "description": "Can perform ranged counterattack" - }, - - "RECEPTIVE": - { - "name": "Receptive", - "description": "No Immunity to Friendly Spells" - }, - - "REBIRTH": - { - "name": "Rebirth (${val}%)", - "description": "${val}% of stack will rise after death" - }, - - "RETURN_AFTER_STRIKE": - { - "name": "Attack and Return", - "description": "Returns after melee attack" - }, - - "SELF_LUCK": - { - "name": "Positive luck", - "description": "Always has Positive Luck" - }, - - "SELF_MORALE": - { - "name": "Positive morale", - "description": "Always has Positive Morale" - }, - - "SHOOTER": - { - "name": "Ranged", - "description": "Creature can shoot" - }, - - "SHOOTS_ALL_ADJACENT": - { - "name": "Shoot all around", - "description": "This creature's ranged attacks strike all targets in a small area" - }, - - "SOUL_STEAL": - { - "name": "Soul Steal", - "description": "Gains ${val} new creatures for each enemy killed" - }, - - "SPELLCASTER": - { - "name": "Spellcaster", - "description": "Can cast ${subtype.spell}" - }, - - "SPELL_AFTER_ATTACK": - { - "name": "Cast After Attack", - "description": "${val}% to cast ${subtype.spell} after attack" - }, - - "SPELL_BEFORE_ATTACK": - { - "name": "Cast Before Attack", - "description": "${val}% to cast ${subtype.spell} before attack" - }, - - "SPELL_DAMAGE_REDUCTION": - { - "name": "Spell Resistance", - "description": "Damage from spells reduced ${val}%." - }, - - "SPELL_IMMUNITY": - { - "name": "Spell immunity", - "description": "Immune to ${subtype.spell}" - }, - - "SPELL_LIKE_ATTACK": - { - "name": "Spell-like attack", - "description": "Attacks with ${subtype.spell}" - }, - - "SPELL_RESISTANCE_AURA": - { - "name": "Aura of Resistance", - "description": "Nearby stacks get ${val}% resistance" - }, - - "SUMMON_GUARDIANS": - { - "name": "Summon guardians", - "description": "At battle start summons ${subtype.creature} (${val}%)" - }, - - "SYNERGY_TARGET": - { - "name": "Synergizable", - "description": "This creature is vulnerable to synergy effect" - }, - - "TWO_HEX_ATTACK_BREATH": - { - "name": "Breath", - "description": "Breath Attack (2-hex range)" - }, - - "THREE_HEADED_ATTACK": - { - "name": "Three-headed attack", - "description": "Attacks three adjacent units" - }, - - "TRANSMUTATION": - { - "name": "Transmutation", - "description": "${val}% chance to transform attacked unit to other type" - }, - - "UNDEAD": - { - "name": "Undead", - "description": "Creature is Undead" - }, - - "UNLIMITED_RETALIATIONS": - { - "name": "Unlimited retaliations", - "description": "Retaliates any number of attacks" - }, - - "WATER_IMMUNITY": - { - "name": "Water immunity", - "description": "Immune to all Water school spells" - }, - - "WIDE_BREATH": - { - "name": "Wide breath", - "description": "Wide breath attack (multiple hexes)" - } -} diff --git a/config/gameConfig.json b/config/gameConfig.json index edd8b7231..7c832b8ea 100644 --- a/config/gameConfig.json +++ b/config/gameConfig.json @@ -66,8 +66,7 @@ "bonuses" : [ - "config/bonuses.json", - "config/bonuses_texts.json" + "config/bonuses.json" ], "spells" : [ diff --git a/config/sp_sounds.json b/config/sp_sounds.json deleted file mode 100644 index 14aafc8fd..000000000 --- a/config/sp_sounds.json +++ /dev/null @@ -1,13 +0,0 @@ - -// Several probably missing sounds of creature abilities -// ACID.WAV Acid breath Rust Dragons -// DEATHBLO.WAV Deathblow Death knigts -// DRAINLIF.WAV Drain life Vampires -// FEAR.WAV Fear Azure dragons -// MAGCHDRN.WAV Steal mana Imps -// MAGCHFIL.WAV Steal mana Either for upgrades or for receiving mana by hero -// MAGICRES.WAV Magic resist Dwarves -// MANADRAI.WAV Mana drain Ghosts -// REGENER.WAV Regeneration Ghosts, Trolls -// RESURECT.WAV Resurrection Both archangels and Demons -// SPONTCOMB.WAV Fireball Magogs diff --git a/lib/CBonusTypeHandler.cpp b/lib/CBonusTypeHandler.cpp index 8e0047f34..51ae51e76 100644 --- a/lib/CBonusTypeHandler.cpp +++ b/lib/CBonusTypeHandler.cpp @@ -18,99 +18,29 @@ #include "GameConstants.h" #include "CCreatureHandler.h" +#include "CGeneralTextHandler.h" #include "spells/CSpellHandler.h" template class std::vector; VCMI_LIB_NAMESPACE_BEGIN -///MacroString - -MacroString::MacroString(const std::string & format) -{ - static const std::string MACRO_START = "${"; - static const std::string MACRO_END = "}"; - static const size_t MACRO_START_L = 2; - static const size_t MACRO_END_L = 1; - - size_t end_pos = 0; - size_t start_pos = std::string::npos; - do - { - start_pos = format.find(MACRO_START, end_pos); - - if(!(start_pos == std::string::npos)) - { - //chunk before macro - items.push_back(Item(Item::STRING, format.substr(end_pos, start_pos - end_pos))); - - start_pos += MACRO_START_L; - end_pos = format.find(MACRO_END, start_pos); - - if(end_pos == std::string::npos) - { - logBonus->warn("Format error in: %s", format); - end_pos = start_pos; - break; - } - else - { - items.push_back(Item(Item::MACRO, format.substr(start_pos, end_pos - start_pos))); - end_pos += MACRO_END_L; - } - } - } - while(start_pos != std::string::npos); - - //no more macros - items.push_back(Item(Item::STRING, format.substr(end_pos))); -} - -std::string MacroString::build(const GetValue & getValue) const -{ - std::string result; - - for(const Item & i : items) - { - switch(i.type) - { - case Item::MACRO: - { - result += getValue(i.value); - break; - } - case Item::STRING: - { - result += i.value; - break; - } - } - } - return result; -} - ///CBonusType -CBonusType::CBonusType() +CBonusType::CBonusType(): + hidden(true) +{} + +std::string CBonusType::getNameTextID() const { - hidden = true; - icon.clear(); - nameTemplate.clear(); - descriptionTemplate.clear(); + return TextIdentifier( "core", "bonus", identifier, "name").get(); } -CBonusType::~CBonusType() +std::string CBonusType::getDescriptionTextID() const { - + return TextIdentifier( "core", "bonus", identifier, "description").get(); } -void CBonusType::buildMacros() -{ - name = MacroString(nameTemplate); - description = MacroString(descriptionTemplate); -} - - ///CBonusTypeHandler CBonusTypeHandler::CBonusTypeHandler() @@ -136,39 +66,26 @@ CBonusTypeHandler::~CBonusTypeHandler() std::string CBonusTypeHandler::bonusToString(const std::shared_ptr & bonus, const IBonusBearer * bearer, bool description) const { - auto getValue = [=](const std::string & name) -> std::string - { - if(name == "val") - { - return boost::lexical_cast(bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype))); - } - else if(name == "subtype.creature") - { - const CreatureID cre(bonus->subtype); - return cre.toCreature()->getNamePluralTranslated(); - } - else if(name == "subtype.spell") - { - const SpellID sp(bonus->subtype); - return sp.toSpell()->getNameTranslated(); - } - else if(name == "MR") - { - return boost::lexical_cast(bearer->magicResistance()); - } - else - { - logBonus->warn("Unknown macro in bonus config: %s", name); - return "[error]"; - } - }; - const CBonusType & bt = bonusTypes[bonus->type]; if(bt.hidden) return ""; - const MacroString & macro = description ? bt.description : bt.name; - return macro.build(getValue); + std::string textID = description ? bt.getDescriptionTextID() : bt.getNameTextID(); + std::string text = VLC->generaltexth->translate(textID); + + if (text.find("${val}") != std::string::npos) + boost::algorithm::replace_all(text, "${val}", std::to_string(bearer->valOfBonuses(Selector::typeSubtype(bonus->type, bonus->subtype)))); + + if (text.find("${subtype.creature}") != std::string::npos) + boost::algorithm::replace_all(text, "${subtype.creature}", CreatureID(bonus->subtype).toCreature()->getNamePluralTranslated()); + + if (text.find("${subtype.spell}") != std::string::npos) + boost::algorithm::replace_all(text, "${subtype.spell}", SpellID(bonus->subtype).toSpell()->getNameTranslated()); + + if (text.find("${MR}") != std::string::npos) + boost::algorithm::replace_all(text, "${MR}", std::to_string(bearer->magicResistance())); + + return text; } std::string CBonusTypeHandler::bonusToGraphics(const std::shared_ptr & bonus) const @@ -304,31 +221,33 @@ void CBonusTypeHandler::load(const JsonNode & config) // // bonusTypes.push_back(bt); - logBonus->warn("Adding new bonuses not implemented (%s)", node.first); + logBonus->warn("Unrecognized bonus name! (%s)", node.first); } else { CBonusType & bt = bonusTypes[it->second]; - loadItem(node.second, bt); + loadItem(node.second, bt, node.first); logBonus->trace("Loaded bonus type %s", node.first); } } } -void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest) +void CBonusTypeHandler::loadItem(const JsonNode & source, CBonusType & dest, const std::string & name) { - dest.nameTemplate = source["name"].String(); - dest.descriptionTemplate = source["description"].String(); + dest.identifier = name; dest.hidden = source["hidden"].Bool(); //Null -> false + if (!dest.hidden) + { + VLC->generaltexth->registerString(dest.getNameTextID(), source["name"].String()); + VLC->generaltexth->registerString(dest.getDescriptionTextID(), source["description"].String()); + } + const JsonNode & graphics = source["graphics"]; if(!graphics.isNull()) - { dest.icon = graphics["icon"].String(); - } - dest.buildMacros(); } VCMI_LIB_NAMESPACE_END diff --git a/lib/CBonusTypeHandler.h b/lib/CBonusTypeHandler.h index 977efe051..61969e07b 100644 --- a/lib/CBonusTypeHandler.h +++ b/lib/CBonusTypeHandler.h @@ -21,53 +21,27 @@ class JsonNode; typedef Bonus::BonusType BonusTypeID; -class MacroString -{ - struct Item - { - enum ItemType - { - STRING, MACRO - }; - Item(ItemType _type, std::string _value): type(_type), value(_value){}; - ItemType type; - std::string value; //constant string or macro name - }; - std::vector items; -public: - typedef std::function GetValue; - - MacroString() = default; - ~MacroString() = default; - explicit MacroString(const std::string & format); - - std::string build(const GetValue & getValue) const; -}; - class DLL_LINKAGE CBonusType { public: CBonusType(); - ~CBonusType(); + + std::string getNameTextID() const; + std::string getDescriptionTextID() const; template void serialize(Handler & h, const int version) { h & icon; - h & nameTemplate; - h & descriptionTemplate; + h & identifier; h & hidden; - if (!h.saving) - buildMacros(); } private: - void buildMacros(); - MacroString name, description; - friend class CBonusTypeHandler; + std::string icon; - std::string nameTemplate, descriptionTemplate; + std::string identifier; bool hidden; }; @@ -91,7 +65,7 @@ public: private: void load(); void load(const JsonNode & config); - void loadItem(const JsonNode & source, CBonusType & dest); + void loadItem(const JsonNode & source, CBonusType & dest, const std::string & name); std::vector bonusTypes; //index = BonusTypeID }; diff --git a/lib/VCMI_Lib.cpp b/lib/VCMI_Lib.cpp index e8245d88a..04c1507fa 100644 --- a/lib/VCMI_Lib.cpp +++ b/lib/VCMI_Lib.cpp @@ -198,14 +198,14 @@ void LibClasses::init(bool onlyEssential) modh->initializeConfig(); + createHandler(generaltexth, "General text", pomtime); + createHandler(bth, "Bonus type", pomtime); createHandler(roadTypeHandler, "Road", pomtime); createHandler(riverTypeHandler, "River", pomtime); createHandler(terrainTypeHandler, "Terrain", pomtime); - createHandler(generaltexth, "General text", pomtime); - createHandler(heroh, "Hero", pomtime); createHandler(arth, "Artifact", pomtime); From 17b510703edda7943687b55732755dea37fc2527 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 17 Jan 2023 12:43:21 +0200 Subject: [PATCH 3/5] Added german, polish, and ukrainian translations --- Mods/vcmi/config/vcmi/german.json | 222 ++++++++++++++++++++++++++ Mods/vcmi/config/vcmi/polish.json | 70 +++++++++ Mods/vcmi/config/vcmi/ukrainian.json | 225 +++++++++++++++++++++++++++ 3 files changed, 517 insertions(+) create mode 100644 Mods/vcmi/config/vcmi/german.json create mode 100644 Mods/vcmi/config/vcmi/polish.json create mode 100644 Mods/vcmi/config/vcmi/ukrainian.json diff --git a/Mods/vcmi/config/vcmi/german.json b/Mods/vcmi/config/vcmi/german.json new file mode 100644 index 000000000..4fa051690 --- /dev/null +++ b/Mods/vcmi/config/vcmi/german.json @@ -0,0 +1,222 @@ +{ + "vcmi.adventureMap.monsterThreat.title" : "\n\n Bedrohung: ", + "vcmi.adventureMap.monsterThreat.levels.0" : "Mühelos", + "vcmi.adventureMap.monsterThreat.levels.1" : "Sehr schwach", + "vcmi.adventureMap.monsterThreat.levels.2" : "Schwach", + "vcmi.adventureMap.monsterThreat.levels.3" : "Ein bisschen schwächer", + "vcmi.adventureMap.monsterThreat.levels.4" : "Gleichauf", + "vcmi.adventureMap.monsterThreat.levels.5" : "Ein bisschen stärker", + "vcmi.adventureMap.monsterThreat.levels.6" : "Stark", + "vcmi.adventureMap.monsterThreat.levels.7" : "Sehr Stark", + "vcmi.adventureMap.monsterThreat.levels.8" : "Herausfordernd", + "vcmi.adventureMap.monsterThreat.levels.9" : "Überwältigend", + "vcmi.adventureMap.monsterThreat.levels.10" : "Tötlich", + "vcmi.adventureMap.monsterThreat.levels.11" : "Unmöglich", + + "vcmi.adventureMap.confirmRestartGame" : "Seid Ihr sicher, dass Ihr das Spiel neu starten wollt?", + "vcmi.adventureMap.noTownWithMarket" : "Kein Marktplatz verfügbar!", + "vcmi.adventureMap.noTownWithTavern" : "Keine Stadt mit Taverne verfügbar!", + "vcmi.adventureMap.spellUnknownProblem" : "Unbekanntes Problem mit diesem Zauberspruch, keine weiteren Informationen verfügbar.", + "vcmi.adventureMap.playerAttacked" : "Spieler wurde attackiert: %s", + + "vcmi.server.errors.existingProcess" : "Es läuft ein weiterer vcmiserver-Prozess, bitte beendet diesen zuerst", + "vcmi.server.errors.modsIncompatibility" : "Erforderliche Mods um das Spiel zu laden:", + + + "vcmi.systemOptions.fullscreenButton.hover" : "Vollbild", + "vcmi.systemOptions.fullscreenButton.help" : "{Fullscreen}\n\n Wenn ausgewählt wird VCMI im Vollbildmodus laufen, ansonsten im Fenstermodus", + "vcmi.systemOptions.resolutionButton.hover" : "Auflösung", + "vcmi.systemOptions.resolutionButton.help" : "{Select resolution}\n\n Ändert die Spielauflösung. Spielneustart ist erforderlich um neue Auflösung zu übernehmen.", + "vcmi.systemOptions.resolutionMenu.hover" : "Wähle Auflösung", + "vcmi.systemOptions.resolutionMenu.help" : "Ändere die Spielauflösung.", + + "vcmi.townHall.missingBase" : "Basis Gebäude %s muss als erstes gebaut werden", + "vcmi.townHall.noCreaturesToRecruit" : "Es gibt keine Kreaturen zu rekrutieren!", + "vcmi.townHall.greetingManaVortex" : "Wenn Ihr Euch den %s nähert, wird Euer Körper mit neuer Energie gefüllt. Ihr habt Eure normalen Zauberpunkte verdoppelt.", + "vcmi.townHall.greetingKnowledge" : "Ihr studiert die Glyphen auf dem %s und erhaltet Einblick in die Funktionsweise verschiedener Magie (+1 Wissen).", + "vcmi.townHall.greetingSpellPower" : "Der %s lehrt Euch neue Wege, Eure magischen Kräfte zu bündeln (+1 Kraft).", + "vcmi.townHall.greetingExperience" : "Ein Besuch bei den %s bringt Euch viele neue Fähigkeiten bei (+1000 Erfahrung).", + "vcmi.townHall.greetingAttack" : "Nach einiger Zeit im %s könnt Ihr effizientere Kampffertigkeiten erlernen (+1 Angriffsfertigkeit).", + "vcmi.townHall.greetingDefence" : "Wenn Ihr Zeit im %s verbringt, bringen Euch die erfahrenen Krieger dort zusätzliche Verteidigungsfähigkeiten bei (+1 Verteidigung).", + "vcmi.townHall.hasNotProduced" : "Die %s hat noch nichts produziert.", + "vcmi.townHall.hasProduced" : "Die %s hat diese Woche %d %s produziert.", + "vcmi.townHall.greetingCustomBonus" : "%s gibt Ihnen +%d %s%s", + "vcmi.townHall.greetingCustomUntil" : " bis zur nächsten Schlacht.", + "vcmi.townHall.greetingInTownMagicWell" : "%s hat Eure Zauberpunkte wieder auf das Maximum erhöht.", + + "vcmi.logicalExpressions.anyOf" : "Eines der folgenden:", + "vcmi.logicalExpressions.allOf" : "Alles der folgenden:", + "vcmi.logicalExpressions.noneOf" : "Keines der folgenden:", + + "vcmi.heroWindow.openCommander.hover" : "Öffne Kommandanten-Fenster", + "vcmi.heroWindow.openCommander.help" : "Zeige Informationen über Kommandanten dieses Helden", + + "vcmi.commanderWindow.artifactMessage" : "Möchtet Ihr diesen Artefakt dem Helden zurückgeben?", + + "vcmi.creatureWindow.showBonuses.hover" : "Wechsle zur Bonus-Ansicht", + "vcmi.creatureWindow.showBonuses.help" : "Zeige alle aktiven Boni des Kommandanten", + "vcmi.creatureWindow.showSkills.hover" : "Wechsle zur Fertigkeits-Ansicht", + "vcmi.creatureWindow.showSkills.help" : "Zeige alle erlernten Fertigkeiten des Kommandanten", + "vcmi.creatureWindow.returnArtifact.hover" : "Artefekt zurückgeben", + "vcmi.creatureWindow.returnArtifact.help" : "Nutze diese Schaltfläche, um Stapel-Artefakt in den Rucksack des Helden zurückzugeben", + + "vcmi.questLog.hideComplete.hover" : "Verstecke abgeschlossene Quests", + "vcmi.questLog.hideComplete.help" : "Verstecke alle Quests die bereits abgeschlossen sind", + + "core.bonus.ADDITIONAL_ATTACK.name": "Doppelschlag", + "core.bonus.ADDITIONAL_ATTACK.description": "Greift zweimal an", + "core.bonus.ADDITIONAL_RETALIATION.name": "Zusätzliche Vergeltungsmaßnahmen", + "core.bonus.ADDITIONAL_RETALIATION.description": "Kann ${val} zusätzliche Male vergelten", + "core.bonus.AIR_IMMUNITY.name": "Luftimmunität", + "core.bonus.AIR_IMMUNITY.description": "Immun gegen alle Luftschulzauber", + "core.bonus.ATTACKS_ALL_ADJACENT.name": "Rundum angreifen", + "core.bonus.ATTACKS_ALL_ADJACENT.description": "Greift alle benachbarten Gegner an", + "core.bonus.BLOCKS_RETALIATION.name": "Keine Vergeltung", + "core.bonus.BLOCKS_RETALIATION.description": "Feind kann nicht vergelten", + "core.bonus.BLOCKS_RANGED_RETALIATION.name": "Keine Reichweitenverschiebung", + "core.bonus.BLOCKS_RANGED_RETALIATION.description": "Feind kann nicht durch Schießen vergelten", + "core.bonus.CATAPULT.name": "Katapult", + "core.bonus.CATAPULT.description": "Greift Belagerungsmauern an", + "core.bonus.CATAPULT_EXTRA_SHOTS.name": "Zusätzliche Belagerungsangriffe", + "core.bonus.CATAPULT_EXTRA_SHOTS.description": "Kann Belagerungsmauern ${val} zusätzliche Male pro Angriff treffen", + "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "Reduziere Zauberkosten (${val})", + "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "Reduziert die Zauberkosten für den Helden", + "core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "Zauberdämpfer (${val})", + "core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "Erhöht die Kosten von gegnerischen Zaubern", + "core.bonus.CHARGE_IMMUNITY.name": "Immun gegen Aufladung", + "core.bonus.CHARGE_IMMUNITY.description": "Immune to Champion charge", + "core.bonus.DAEMON_SUMMONING.name": "Beschwörer (${subtype.creature})", + "core.bonus.DAEMON_SUMMONING.description": "Kann Kreaturen aus Leichen auferstehen lassen", + "core.bonus.DARKNESS.name": "Darkness cover", + "core.bonus.DARKNESS.description": "Fügt ${val} Dunkelheitsradius hinzu", + "core.bonus.DEATH_STARE.name": "Todesstarren (${val}%)", + "core.bonus.DEATH_STARE.description": "${val}% Chance, eine einzelne Kreatur zu töten", + "core.bonus.DEFENSIVE_STANCE.name": "Verteidigungsbonus", + "core.bonus.DEFENSIVE_STANCE.description": "+${val} Verteidigung beim Verteidigen", + "core.bonus.DESTRUCTION.name": "Zerstörung", + "core.bonus.DESTRUCTION.description": "Hat ${val}% Chance, zusätzliche Einheiten nach dem Angriff zu töten", + "core.bonus.DOUBLE_DAMAGE_CHANCE.name": "Todesstoß", + "core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% Chance auf doppelten Schaden", + "core.bonus.DRAGON_NATURE.name": "Drache", + "core.bonus.DRAGON_NATURE.description": "Kreatur hat eine Drachennatur", + "core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "Direkte Schadensimmunität", + "core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "Immun gegen Direktschadenszauber", + "core.bonus.EARTH_IMMUNITY.name": "Erdimmunität", + "core.bonus.EARTH_IMMUNITY.description": "Immun gegen alle Zauber der Erdschule", + "core.bonus.ENCHANTER.name": "Verzauberer", + "core.bonus.ENCHANTER.description": "Kann jede Runde eine Masse von ${subtype.spell} zaubern", + "core.bonus.ENCHANTED.name": "Verzaubert", + "core.bonus.ENCHANTED.description": "Beeinflusst von permanentem ${subtype.spell}", + "core.bonus.ENEMY_DEFENCE_REDUCTION.name": "Ignoriere Verteidigung (${val}%)", + "core.bonus.ENEMY_DEFENCE_REDUCTION.description": "Ignoriert einen Teil der Verteidigung für den Angriff", + "core.bonus.FIRE_IMMUNITY.name": "Feuerimmunität", + "core.bonus.FIRE_IMMUNITY.description": "Immun gegen alle Zauber der Schule des Feuers", + "core.bonus.FIRE_SHIELD.name": "Feuerschild (${val}%)", + "core.bonus.FIRE_SHIELD.description": "Reflektiert einen Teil des Nahkampfschadens", + "core.bonus.FIRST_STRIKE.name": "Erstschlag", + "core.bonus.FIRST_STRIKE.description": "Diese Kreatur greift zuerst an, anstatt zu vergelten", + "core.bonus.FEAR.name": "Furcht", + "core.bonus.FEAR.description": "Verursacht Furcht bei einem gegnerischen Stapel", + "core.bonus.FEARLESS.name": "Furchtlos", + "core.bonus.FEARLESS.description": "immun gegen die Fähigkeit Furcht", + "core.bonus.FLYING.name": "Fliegen", + "core.bonus.FLYING.description": "Kann fliegen (ignoriert Hindernisse)", + "core.bonus.FREE_SHOOTING.name": "Nah schießen", + "core.bonus.FREE_SHOOTING.description": "Kann im Nahkampf schießen", + "core.bonus.FULL_HP_REGENERATION.name": "Regeneration", + "core.bonus.FULL_HP_REGENERATION.description": "Kann auf volle Lebenspunkte regenerieren", + "core.bonus.GARGOYLE.name": "Gargoyle", + "core.bonus.GARGOYLE.description": "Kann nicht aufgerichtet oder geheilt werden", + "core.bonus.GENERAL_DAMAGE_REDUCTION.name": "Schaden vermindern (${val}%)", + "core.bonus.GENERAL_DAMAGE_REDUCTION.description": "Reduziert physischen Schaden aus dem Fern- oder Nahkampf", + "core.bonus.HATE.name": "Hasst ${subtype.creature}", + "core.bonus.HATE.description": "Macht ${val}% mehr Schaden", + "core.bonus.HEALER.name": "Heiler", + "core.bonus.HEALER.description": "Heilt verbündete Einheiten", + "core.bonus.HP_REGENERATION.name": "Regeneration", + "core.bonus.HP_REGENERATION.description": "Heilt ${val} Trefferpunkte jede Runde", + "core.bonus.JOUSTING.name": "Champion Charge", + "core.bonus.JOUSTING.description": "+5% Schaden pro zurückgelegtem Feld", + "core.bonus.KING1.name": "König 1", + "core.bonus.KING1.description": "Anfällig für grundlegende SLAYER", + "core.bonus.KING2.name": "König 2", + "core.bonus.KING2.description": "Anfällig für erweiterte SLAYER", + "core.bonus.KING3.name": "König3", + "core.bonus.KING3.description": "Anfällig für Experten-SLAYER", + "core.bonus.LEVEL_SPELL_IMMUNITY.name": "Zauberimmunität 1-${val}", + "core.bonus.LEVEL_SPELL_IMMUNITY.description": "Immun gegen Zaubersprüche der Stufen 1-${val}", + "core.bonus.LIFE_DRAIN.name": "Leben entziehen (${val}%)", + "core.bonus.LIFE_DRAIN.description": "Drainiert ${val}% des zugefügten Schadens", + "core.bonus.MANA_CHANNELING.name": "Magiekanal ${val}%", + "core.bonus.MANA_CHANNELING.description": "Gibt Ihrem Helden Mana, das vom Gegner ausgegeben wird", + "core.bonus.MANA_DRAIN.name": "Mana-Entzug", + "core.bonus.MANA_DRAIN.description": "Entzieht ${val} Mana jede Runde", + "core.bonus.MAGIC_MIRROR.name": "Zauberspiegel (${val}%)", + "core.bonus.MAGIC_MIRROR.description": "${val}% Chance, einen Angriffszauber auf den Gegner umzulenken", + "core.bonus.MAGIC_RESISTANCE.name": "Magie-Widerstand(${MR}%)", + "core.bonus.MAGIC_RESISTANCE.description": "${MR}% Chance, gegnerischem Zauber zu widerstehen", + "core.bonus.MIND_IMMUNITY.name": "Geist-Zauber-Immunität", + "core.bonus.MIND_IMMUNITY.description": "Immun gegen Zauber vom Typ Geist", + "core.bonus.NO_DISTANCE_PENALTY.name": "Keine Entfernungsstrafe", + "core.bonus.NO_DISTANCE_PENALTY.description": "Voller Schaden aus beliebiger Entfernung", + "core.bonus.NO_MELEE_PENALTY.name": "Keine Nahkampf-Strafe", + "core.bonus.NO_MELEE_PENALTY.description": "Kreatur hat keinen Nahkampf-Malus", + "core.bonus.NO_MORALE.name": "Neutrale Moral", + "core.bonus.NO_MORALE.description": "Kreatur ist immun gegen Moral-Effekte", + "core.bonus.NO_WALL_PENALTY.name": "Keine Wand-Strafe", + "core.bonus.NO_WALL_PENALTY.description": "Voller Schaden bei Belagerung", + "core.bonus.NON_LIVING.name": "Nicht lebend", + "core.bonus.NON_LIVING.description": "Immunität gegen viele Effekte", + "core.bonus.RANDOM_SPELLCASTER.name": "Zufälliger Zauberwirker", + "core.bonus.RANDOM_SPELLCASTER.description": "Kann einen zufälligen Zauberspruch wirken", + "core.bonus.RANGED_RETALIATION.name": "Fernkampf-Vergeltung", + "core.bonus.RANGED_RETALIATION.description": "Kann einen Fernkampf-Gegenangriff durchführen", + "core.bonus.RECEPTIVE.name": "Empfänglich", + "core.bonus.RECEPTIVE.description": "Keine Immunität gegen Freundschaftszauber", + "core.bonus.REBIRTH.name": "Wiedergeburt (${val}%)", + "core.bonus.REBIRTH.description": "${val}% des Stacks wird nach dem Tod auferstehen", + "core.bonus.RETURN_AFTER_STRIKE.name": "Angriff und Rückkehr", + "core.bonus.RETURN_AFTER_STRIKE.description": "Kehrt nach Nahkampfangriff zurück", + "core.bonus.SELF_LUCK.name": "Positives Glück", + "core.bonus.SELF_LUCK.description": "Hat immer positives Glück", + "core.bonus.SELF_MORALE.name": "Positive Moral", + "core.bonus.SELF_MORALE.description": "Hat immer positive Moral", + "core.bonus.SHOOTER.name": "Fernkämpfer", + "core.bonus.SHOOTER.description": "Kreatur kann schießen", + "core.bonus.SHOOTS_ALL_ADJACENT.name": "Schießt rundherum", + "core.bonus.SHOOTS_ALL_ADJACENT.description": "Die Fernkampfangriffe dieser Kreatur treffen alle Ziele in einem kleinen Bereich", + "core.bonus.SOUL_STEAL.name": "Seelenraub", + "core.bonus.SOUL_STEAL.description": "Gewinnt ${val} neue Kreaturen für jeden getöteten Gegner", + "core.bonus.SPELLCASTER.name": "Zauberer", + "core.bonus.SPELLCASTER.description": "Kann ${subtype.spell} zaubern", + "core.bonus.SPELL_AFTER_ATTACK.name": "Nach Angriff zaubern", + "core.bonus.SPELL_AFTER_ATTACK.description": "${val}%, um ${subtype.spell} nach dem Angriff zu wirken", + "core.bonus.SPELL_BEFORE_ATTACK.name": "Zauber vor Angriff", + "core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% um ${subtype.spell} vor dem Angriff zu wirken", + "core.bonus.SPELL_DAMAGE_REDUCTION.name": "Zauberwiderstand", + "core.bonus.SPELL_DAMAGE_REDUCTION.description": "Schaden von Zaubern reduziert ${val}%.", + "core.bonus.SPELL_IMMUNITY.name": "Zauberimmunität", + "core.bonus.SPELL_IMMUNITY.description": "Immun gegen ${subtype.spell}", + "core.bonus.SPELL_LIKE_ATTACK.name": "zauberähnlicher Angriff", + "core.bonus.SPELL_LIKE_ATTACK.description": "Angriffe mit ${subtype.spell}", + "core.bonus.SPELL_RESISTANCE_AURA.name": "Aura des Widerstands", + "core.bonus.SPELL_RESISTANCE_AURA.description": "Stapel in der Nähe erhalten ${val}% Widerstand", + "core.bonus.SUMMON_GUARDIANS.name": "Wächter beschwören", + "core.bonus.SUMMON_GUARDIANS.description": "Beschwört bei Kampfbeginn ${subtype.creature} (${val}%)", + "core.bonus.SYNERGY_TARGET.name": "Synergierbar", + "core.bonus.SYNERGY_TARGET.description": "Diese Kreatur ist anfällig für Synergieeffekte", + "core.bonus.TWO_HEX_ATTACK_BREATH.name": "Breath", + "core.bonus.TWO_HEX_ATTACK_BREATH.description": "Atem-Angriff (2-Hex-Bereich)", + "core.bonus.THREE_HEADED_ATTACK.name": "Dreiköpfiger Angriff", + "core.bonus.THREE_HEADED_ATTACK.description": "Greift drei benachbarte Einheiten an", + "core.bonus.TRANSMUTATION.name": "Transmutation", + "core.bonus.TRANSMUTATION.description": "${val}% Chance, angegriffene Einheit in einen anderen Typ zu verwandeln", + "core.bonus.UNDEAD.name": "Untot", + "core.bonus.UNDEAD.description": "Kreatur ist untot", + "core.bonus.UNLIMITED_RETALIATIONS.name": "Unbegrenzte Vergeltungsmaßnahmen", + "core.bonus.UNLIMITED_RETALIATIONS.description": "Vergeltungen für eine beliebige Anzahl von Angriffen", + "core.bonus.WATER_IMMUNITY.name": "Wasser-Immunität", + "core.bonus.WATER_IMMUNITY.description": "Immun gegen alle Zauber der Wasserschule", + "core.bonus.WIDE_BREATH.name": "Breiter Atem", + "core.bonus.WIDE_BREATH.description": "Breiter Atem-Angriff (mehrere Felder)" +} diff --git a/Mods/vcmi/config/vcmi/polish.json b/Mods/vcmi/config/vcmi/polish.json new file mode 100644 index 000000000..3ef6dbe93 --- /dev/null +++ b/Mods/vcmi/config/vcmi/polish.json @@ -0,0 +1,70 @@ +{ + "vcmi.adventureMap.monsterThreat.title" : "\n\n Poziom zagrożenia: ", + "vcmi.adventureMap.monsterThreat.levels.0" : "Zerowy", + "vcmi.adventureMap.monsterThreat.levels.1" : "Bardzo słaby", + "vcmi.adventureMap.monsterThreat.levels.2" : "Słaby", + "vcmi.adventureMap.monsterThreat.levels.3" : "Nieco słabszy", + "vcmi.adventureMap.monsterThreat.levels.4" : "Równie silny", + "vcmi.adventureMap.monsterThreat.levels.5" : "Nieco silniejszy", + "vcmi.adventureMap.monsterThreat.levels.6" : "Silny", + "vcmi.adventureMap.monsterThreat.levels.7" : "Bardzo silny", + "vcmi.adventureMap.monsterThreat.levels.8" : "Wyzwanie", + "vcmi.adventureMap.monsterThreat.levels.9" : "Przytłaczający", + "vcmi.adventureMap.monsterThreat.levels.10" : "Śmiertelny", + "vcmi.adventureMap.monsterThreat.levels.11" : "Nie do pokonania", + + "vcmi.adventureMap.confirmRestartGame" : "Czy na pewno chcesz zrestartować grę?", + "vcmi.adventureMap.noTownWithMarket" : "Brak dostępnego targowiska!", + "vcmi.adventureMap.noTownWithTavern" : "Brak dostępnego miasta z karczmą!", + "vcmi.adventureMap.spellUnknownProblem" : "Nieznany problem z zaklęciem, brak dodatkowych informacji.", + "vcmi.adventureMap.playerAttacked" : "Gracz został zaatakowany: %s", + + "vcmi.server.errors.existingProcess" : "Inny proces vcmiserver został już uruchomiony, zakończ go nim przejdziesz dalej", + "vcmi.server.errors.modsIncompatibility" : "Mody wymagane do wczytania gry:", + "vcmi.server.confirmReconnect" : "Połączyć z ostatnią sesją?", + + "vcmi.systemOptions.fullscreenButton.hover" : "Pełny ekran", + "vcmi.systemOptions.fullscreenButton.help" : "{Fullscreen}\n\n Po wybraniu VCMI uruchomi się w trybie pełnoekranowym, w przeciwnym wypadku uruchomi się w oknie", + "vcmi.systemOptions.resolutionButton.hover" : "Rozdzielczość", + "vcmi.systemOptions.resolutionButton.help" : "{Select resolution}\n\n Zmień rozdzielczość ekranu w grze. Restart gry jest wymagany, by zmiany zostały uwzględnione.", + "vcmi.systemOptions.resolutionMenu.hover" : "Wybierz rozdzielczość", + "vcmi.systemOptions.resolutionMenu.help" : "Zmień rozdzielczość ekranu w grze.", + + "vcmi.townHall.missingBase" : "Podstawowy budynek %s musi zostać najpierw wybudowany", + "vcmi.townHall.noCreaturesToRecruit" : "Brak stworzeń do rekrutacji!", + "vcmi.townHall.greetingManaVortex" : "Zbliżając się do %s czujesz jak twoje ciało wypełnia energia. Ilość pkt. magii, które posiadasz, zwiększa się dwukrotnie.", + "vcmi.townHall.greetingKnowledge" : "Studiując napisy na %s odkrywasz nowe aspekty stosowania magii (wiedza +1).", + "vcmi.townHall.greetingSpellPower" : "Odwiedzając %s dowiadujesz się, jak zwiększyć potęgę swojej mocy magicznej (moc +1).", + "vcmi.townHall.greetingExperience" : "Wizyta w %s zwiększa twoje doświadczenie (doświadczenie +1000).", + "vcmi.townHall.greetingAttack" : "Krótka wizyka w %s umożliwia ci polepszenie technik walki (atak +1).", + "vcmi.townHall.greetingDefence" : "Odwiedzasz %s. Doświadczeni żołnierze, którzy tam przebywają, uczą cię sztuki skutecznej obrony (obrona +1).", + "vcmi.townHall.hasNotProduced" : "%s nic jeszcze nie wyprodukował.", + "vcmi.townHall.hasProduced" : "%s wyprodukował w tym tygodniu: %d %s.", + "vcmi.townHall.greetingCustomBonus" : "%s daje tobie +%d %s%s", + "vcmi.townHall.greetingCustomUntil" : " do następnej bitwy.", + "vcmi.townHall.greetingInTownMagicWell" : "%s przywraca ci wszystkie punkty magii.", + + "vcmi.logicalExpressions.anyOf" : "Dowolne spośród:", + "vcmi.logicalExpressions.allOf" : "Wszystkie spośród:", + "vcmi.logicalExpressions.noneOf" : "Żadne spośród:", + + "vcmi.heroWindow.openCommander.hover" : "Otwórz okno dowódcy", + "vcmi.heroWindow.openCommander.help" : "Wyświetla informacje o dowódcy przynależącym do tego bohatera", + + "vcmi.commanderWindow.artifactMessage" : "Czy chcesz zwrócić ten artefakt bohaterowi?", + + "vcmi.creatureWindow.showBonuses.hover" : "Przełącz do widoku bonusów", + "vcmi.creatureWindow.showBonuses.help" : "Wyświetla wszystkie aktywne bonusy dowódcy", + "vcmi.creatureWindow.showSkills.hover" : "Przełącz do widoku umiejętności", + "vcmi.creatureWindow.showSkills.help" : "Wyświetla wszystkie nauczone umiejętności dowódcy", + "vcmi.creatureWindow.returnArtifact.hover" : "Zwróć artefakt", + "vcmi.creatureWindow.returnArtifact.help" : "Użyj tego przycisku by zwrócić artefakt do sakwy bohatera", + + "vcmi.questLog.hideComplete.hover" : "Ukryj ukończone misje", + "vcmi.questLog.hideComplete.help" : "Ukrywa wszystkie misje, które zostały zakończone", + + "vcmi.randomMapTab.widgets.defaultTemplate" : "domyślny", + "vcmi.randomMapTab.widgets.templateLabel" : "Szablon", + "vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Ustaw...", + "vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Sojusze" +} diff --git a/Mods/vcmi/config/vcmi/ukrainian.json b/Mods/vcmi/config/vcmi/ukrainian.json new file mode 100644 index 000000000..15db6fe94 --- /dev/null +++ b/Mods/vcmi/config/vcmi/ukrainian.json @@ -0,0 +1,225 @@ +{ + "vcmi.adventureMap.monsterThreat.title" : "\n\n Сила загону: ", + "vcmi.adventureMap.monsterThreat.levels.0" : "Безсилий", + "vcmi.adventureMap.monsterThreat.levels.1" : "Дуже слабкий", + "vcmi.adventureMap.monsterThreat.levels.2" : "Слабкий", + "vcmi.adventureMap.monsterThreat.levels.3" : "Трохи слабша", + "vcmi.adventureMap.monsterThreat.levels.4" : "Відповідна", + "vcmi.adventureMap.monsterThreat.levels.5" : "Трохи сильніша", + "vcmi.adventureMap.monsterThreat.levels.6" : "Сильніша", + "vcmi.adventureMap.monsterThreat.levels.7" : "Дуже сильна", + "vcmi.adventureMap.monsterThreat.levels.8" : "Кидає виклик", + "vcmi.adventureMap.monsterThreat.levels.9" : "Нездоланна", + "vcmi.adventureMap.monsterThreat.levels.10" : "Смертельна", + "vcmi.adventureMap.monsterThreat.levels.11" : "Неможлива", + + "vcmi.adventureMap.confirmRestartGame" : "Ви впевнені, що хочете перезапустити гру?", + "vcmi.adventureMap.noTownWithMarket" : "Немає доступних ринків!", + "vcmi.adventureMap.noTownWithTavern" : "Немає доступного міста з таверною!", + "vcmi.adventureMap.spellUnknownProblem" : "Невідома проблема з цим заклинанням, більше інформації немає.", + "vcmi.adventureMap.playerAttacked" : "Гравця атаковано: %s", + + "vcmi.server.errors.existingProcess" : "Працює інший процес vcmiserver, будь ласка, спочатку завершіть його", + "vcmi.server.errors.modsIncompatibility" : "Потрібні модифікації для завантаження гри:", + "vcmi.server.confirmReconnect" : "Підключитися до минулої сесії?", + + "vcmi.systemOptions.fullscreenButton.hover" : "Режим на весь екран", + "vcmi.systemOptions.fullscreenButton.help" : "{Режим на весь екран}\n\n Якщо обрано, VCMI буде запускатися в режимі на весь екран, інакше — віконний режим", + "vcmi.systemOptions.resolutionButton.hover" : "Розширення екрану", + "vcmi.systemOptions.resolutionButton.help" : "{Розширення екрану}\n\n Зміна розширення екрану в грі. Аби зміни набули чинності необхідно перезавантажити гру.", + "vcmi.systemOptions.resolutionMenu.hover" : "Обрати розширення екрану", + "vcmi.systemOptions.resolutionMenu.help" : "Змінити розширення екрану в грі.", + + "vcmi.townHall.missingBase" : "Спочатку необхідно звести початкову будівлю: %s", + "vcmi.townHall.noCreaturesToRecruit" : "Немає істот, яких можна завербувати!", + "vcmi.townHall.greetingManaVortex" : "Неподалік %s ваше тіло наповнюється новою силою. Ваша звична магічна енергія сьогодні подвоєна.", + "vcmi.townHall.greetingKnowledge" : "Ви вивчили знаки на %s, і на вас зійшло прозріння у справах магії. (+1 Knowledge).", + "vcmi.townHall.greetingSpellPower" : "В %s вас навчили новим методам концентрації магічної сили. (+1 Power).", + "vcmi.townHall.greetingExperience" : "Відвідавши %s, ви дізналися багато нового. (+1000 Experience).", + "vcmi.townHall.greetingAttack" : "Перебування у %s дозволило вам краще використовувати бойові навички (+1 Attack Skill).", + "vcmi.townHall.greetingDefence" : "У %s досвідчені воїни виклали вам свої захисні вміння. (+1 Defense).", + "vcmi.townHall.hasNotProduced" : "Поки що %s нічого не створило.", + "vcmi.townHall.hasProduced" : "Цього тижня %s створило %d одиниць, цього разу це %s.", + "vcmi.townHall.greetingCustomBonus" : "%s дає вам +%d %s%s", + "vcmi.townHall.greetingCustomUntil" : " до наступної битви.", + "vcmi.townHall.greetingInTownMagicWell" : "%s повністю відновлює ваш запас очків магії.", + + "vcmi.logicalExpressions.anyOf" : "Будь-що з перерахованого:", + "vcmi.logicalExpressions.allOf" : "Все з перерахованого:", + "vcmi.logicalExpressions.noneOf" : "Нічого з перерахованого:", + + "vcmi.heroWindow.openCommander.hover" : "Відкрити вікно командира", + "vcmi.heroWindow.openCommander.help" : "Показує інформацію про командира героя", + + "vcmi.commanderWindow.artifactMessage" : "Бажаєте передати цей артефакт герою?", + + "vcmi.creatureWindow.showBonuses.hover" : "Перейти до перегляду бонусів", + "vcmi.creatureWindow.showBonuses.help" : "Відображає всі активні бонуси командира", + "vcmi.creatureWindow.showSkills.hover" : "Перейдіть до перегляду вмінь", + "vcmi.creatureWindow.showSkills.help" : "Відображає всі вивчені командиром вміння", + "vcmi.creatureWindow.returnArtifact.hover" : "Повернути артефакт", + "vcmi.creatureWindow.returnArtifact.help" : "Використовуйте цю кнопку, щоб повернути артефакт загону назад у рюкзак героя", + + "vcmi.questLog.hideComplete.hover" : "Приховати завершені квести", + "vcmi.questLog.hideComplete.help" : "Приховує всі квести, які вже мають стан виконаних", + + "vcmi.randomMapTab.widgets.defaultTemplate" : "за замовчуванням", + "vcmi.randomMapTab.widgets.templateLabel" : "Шаблон", + "vcmi.randomMapTab.widgets.teamAlignmentsButton" : "Налаштувати...", + "vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "Розподіл команд", + + "core.bonus.ADDITIONAL_ATTACK.name" : "Подвійний удар", + "core.bonus.ADDITIONAL_ATTACK.description" : "Атакує двічі", + "core.bonus.ADDITIONAL_RETALIATION.name" : "Додаткові відплати", + "core.bonus.ADDITIONAL_RETALIATION.description" : "Може нанести ${val} додаткових ударів у відповідь", + "core.bonus.AIR_IMMUNITY.name" : "Імунітет до повітря", + "core.bonus.AIR_IMMUNITY.description" : "Імунітет до всіх заклять школи повітря", + "core.bonus.ATTACKS_ALL_ADJACENT.name" : "Атакує всіх навколо", + "core.bonus.ATTACKS_ALL_ADJACENT.description" : "Атакує всіх сусідніх ворогів", + "core.bonus.BLOCKS_RETALIATION.name" : "Ворог не відповідає", + "core.bonus.BLOCKS_RETALIATION.description" : "Ворог не може атакувати у відповідь", + "core.bonus.BLOCKS_RANGED_RETALIATION.name" : "Немає дальнього удару у відповідь", + "core.bonus.BLOCKS_RANGED_RETALIATION.description" : "Ворог не може відповісти пострілом", + "core.bonus.CATAPULT.name" : "Катапульта", + "core.bonus.CATAPULT.description" : "Атакує стіни фортеці", + "core.bonus.CATAPULT_EXTRA_SHOTS.name" : "Додаткові атаки стін", + "core.bonus.CATAPULT_EXTRA_SHOTS.description" : "Може вражати стіни фортеці ${val} додаткових разів за атаку", + "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name" : "Зменшує вартість закляття (${value})", + "core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description" : "Зменшує вартість закляття для героя", + "core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name" : "Демпфер магії (${value})", + "core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description" : "Збільшує вартість ворожих заклять", + "core.bonus.CHARGE_IMMUNITY.name" : "Імунітет до атак з розгону", + "core.bonus.CHARGE_IMMUNITY.description" : "Імунітет до турнірної переваги", + "core.bonus.DARKNESS.name" : "Покриття темряви", + "core.bonus.DARKNESS.description" : "Додає ${val} радіусу темряви", + "core.bonus.DEATH_STARE.name" : "Погляд смерті (${val}%)", + "core.bonus.DEATH_STARE.description" : "${val}% шанс вбити одну істоту", + "core.bonus.DEFENSIVE_STANCE.name" : "Бонус до захисту", + "core.bonus.DEFENSIVE_STANCE.description" : "+${val} Захист при обороні", + "core.bonus.DESTRUCTION.name" : "Знищення", + "core.bonus.DESTRUCTION.description" : "Має ${val}% шанс вбити додаткових юнітів після атаки", + "core.bonus.DOUBLE_DAMAGE_CHANCE.name" : "Смертельний удар", + "core.bonus.DOUBLE_DAMAGE_CHANCE.description" : "${val}% шанс нанести подвійний шкоди", + "core.bonus.DRAGON_NATURE.name" : "Дракон", + "core.bonus.DRAGON_NATURE.description" : "Істота має драконячу природу", + "core.bonus.DIRECT_DAMAGE_IMMUNITY.name" : "Імунітет до прямої шкоди", + "core.bonus.DIRECT_DAMAGE_IMMUNITY.description" : "Імунітет до заклять, що завдають прямої шкоди", + "core.bonus.EARTH_IMMUNITY.name" : "Імунітет Землі", + "core.bonus.EARTH_IMMUNITY.description" : "Імунітет до всіх заклять школи Землі", + "core.bonus.ENCHANTER.name" : "Чарівник", + "core.bonus.ENCHANTER.description" : "Може використовувати масове закляття ${subtype.spell} кожного ходу", + "core.bonus.ENCHANTED.name" : "Зачарований", + "core.bonus.ENCHANTED.description" : "Піддається впливу постійних закляття ${subtype.spell}", + "core.bonus.ENEMY_DEFENCE_REDUCTION.name" : "Ігнорує ${val}% захисту", + "core.bonus.ENEMY_DEFENCE_REDUCTION.description" : "Ігнорує частину захисту для атаки", + "core.bonus.FIRE_IMMUNITY.name" : "Імунітет до вогню", + "core.bonus.FIRE_IMMUNITY.description" : "Імунітет до всіх заклять школи вогню", + "core.bonus.FIRE_SHIELD.name" : "Вогняний щит (${value}%)", + "core.bonus.FIRE_SHIELD.description" : "Повертає частину шкоди ближнього бою тому, хто їх завдав", + "core.bonus.FIRST_STRIKE.name" : "Перший удар", + "core.bonus.FIRST_STRIKE.description" : "Цей загін атакує першим замість того, щоб відповідати", + "core.bonus.FEAR.name" : "Страх", + "core.bonus.FEAR.description" : "Спричиняє страх у загоні ворога", + "core.bonus.FEARLESS.name" : "Безстрашний", + "core.bonus.FEARLESS.description" : "Імунітет до страху", + "core.bonus.FLYING.name" : "Літає", + "core.bonus.FLYING.description" : "Може літати (ігнорує перешкоди)", + "core.bonus.FREE_SHOOTING.name" : "Стріляє впритул", + "core.bonus.FREE_SHOOTING.description" : "Може стріляти в ближньому бою", + "core.bonus.FULL_HP_REGENERATION.name" : "Регенерація", + "core.bonus.FULL_HP_REGENERATION.description" : "Може регенерувати до повного здоров'я", + "core.bonus.GARGOYLE.name" : "Горгулья", + "core.bonus.GARGOYLE.description" : "Не може бути відроджена або зцілена", + "core.bonus.GENERAL_DAMAGE_REDUCTION.name" : "Зменшує шкоду (${val}%)", + "core.bonus.GENERAL_DAMAGE_REDUCTION.description" : "Зменшує фізичний урон від ударів з дальньої та ближньої дистанції", + "core.bonus.HATE.name" : "Ненавидить ${subtype.creature}", + "core.bonus.HATE.description" : "Завдає на ${val}% більше шкоди", + "core.bonus.HEALER.name" : "Цілитель", + "core.bonus.HEALER.description" : "Лікує союзників", + "core.bonus.HP_REGENERATION.name" : "Регенерація", + "core.bonus.HP_REGENERATION.description" : "Відновлює ${val} очок здоров'я кожного раунду", + "core.bonus.JOUSTING.name" : "Турнірна перевага", + "core.bonus.JOUSTING.description" : "+5% шкоди за кожен пройдений гекс", + "core.bonus.KING1.name" : "Король 1", + "core.bonus.KING1.description" : "Вразливий до 1-го рівня закляття Вбивця", + "core.bonus.KING2.name" : "Король 2", + "core.bonus.KING2.description" : "Вразливий до 2-го рівня закляття Вбивця", + "core.bonus.KING3.name" : "Король 3", + "core.bonus.KING3.description" : "Вразливий до 3-го рівня закляття Вбивця", + "core.bonus.LEVEL_SPELL_IMMUNITY.name" : "Імунітет до заклять 1-${val}", + "core.bonus.LEVEL_SPELL_IMMUNITY.description" : "Імунітет до заклять рівнів 1-${val}", + "core.bonus.LIFE_DRAIN.name" : "Висмоктує життя (${val}%)", + "core.bonus.LIFE_DRAIN.description" : "Висмоктує ${val}% від завданої шкоди", + "core.bonus.MANA_CHANNELING.name" : "Магічний канал ${val}%", + "core.bonus.MANA_CHANNELING.description" : "Повертає вашому герою ману, витрачену ворогом", + "core.bonus.MANA_DRAIN.name" : "Викрадання мани", + "core.bonus.MANA_DRAIN.description" : "Викрадає ${val} мани кожного ходу", + "core.bonus.MAGIC_MIRROR.name" : "Магічне дзеркало (${val}%)", + "core.bonus.MAGIC_MIRROR.description" : "Відбиває ворожі заклинання до випадкової істоти ворога з силою в ${val}%", + "core.bonus.MAGIC_RESISTANCE.name" : "Опір магії (${MR}%)", + "core.bonus.MAGIC_RESISTANCE.description" : "${MR}% шанс протистояти ворожим закляттям", + "core.bonus.MIND_IMMUNITY.name" : "Імунітет до заклять розуму", + "core.bonus.MIND_IMMUNITY.description" : "Імунітет до заклять типу ", + "core.bonus.NO_DISTANCE_PENALTY.name" : "Немає штрафу за відстань", + "core.bonus.NO_DISTANCE_PENALTY.description" : "Повна шкода з будь-якої відстані", + "core.bonus.NO_MELEE_PENALTY.name" : "Немає штрафу за ближній бій", + "core.bonus.NO_MELEE_PENALTY.description" : "Загін не має штрафу за ближній бій", + "core.bonus.NO_MORALE.name" : "Нейтральний бойовий дух", + "core.bonus.NO_MORALE.description" : "Загін має імунітет до ефектів моралі", + "core.bonus.NO_WALL_PENALTY.name" : "Немає штрафу за перешкоди", + "core.bonus.NO_WALL_PENALTY.description" : "Повна шкода при пострілах через стіни", + "core.bonus.NON_LIVING.name" : "Не жива", + "core.bonus.NON_LIVING.description" : "Імунітет до багатьох ефектів", + "core.bonus.RANDOM_SPELLCASTER.name" : "Випадковий заклинатель", + "core.bonus.RANDOM_SPELLCASTER.description" : "Може накласти випадкове закляття", + "core.bonus.RANGED_RETALIATION.name" : "Дистанційне відплата", + "core.bonus.RANGED_RETALIATION.description" : "Може наносити контратаку пострілом", + "core.bonus.RECEPTIVE.name" : "Сприйнятливий", + "core.bonus.RECEPTIVE.description" : "Не має імунітету до дружніх заклять", + "core.bonus.REBIRTH.name" : "Відродження (${val}%)", + "core.bonus.REBIRTH.description" : "${val}% загону відродиться після смерті", + "core.bonus.RETURN_AFTER_STRIKE.name" : "Атакує і повертається", + "core.bonus.RETURN_AFTER_STRIKE.description" : "Повертається після атаки ближнього бою", + "core.bonus.SELF_LUCK.name" : "Позитивна удача", + "core.bonus.SELF_LUCK.description" : "Завжди має позитивну удачу", + "core.bonus.SELF_MORALE.name" : "Позитивний бойовий дух", + "core.bonus.SELF_MORALE.description" : "Завжди має позитивний бойовий дух", + "core.bonus.SHOOTER.name" : "Стрілок", + "core.bonus.SHOOTER.description" : "Істота може стріляти", + "core.bonus.SHOOTS_ALL_ADJACENT.name" : "Стріляйте по площі", + "core.bonus.SHOOTS_ALL_ADJACENT.description" : "Дистанційні атаки цієї істоти вражають всі цілі на невеликій площі", + "core.bonus.SOUL_STEAL.name" : "Викрадення душ", + "core.bonus.SOUL_STEAL.description" : "Отримує ${val} нових істот за кожного вбитого ворога", + "core.bonus.SPELLCASTER.name" : "Заклинатель", + "core.bonus.SPELLCASTER.description" : "Може використовувати закляття ${subtype.spell}", + "core.bonus.SPELL_AFTER_ATTACK.name" : "Після атаки", + "core.bonus.SPELL_AFTER_ATTACK.description" : "${val}%, щоб застосувати ${subtype.spell} після атаки", + "core.bonus.SPELL_BEFORE_ATTACK.name" : "закляття перед атакою", + "core.bonus.SPELL_BEFORE_ATTACK.description" : "Застосовує ${subtype.spell} з вірогідністю ${value}% перед атакою", + "core.bonus.SPELL_DAMAGE_REDUCTION.name" : "Стійкість до заклять", + "core.bonus.SPELL_DAMAGE_REDUCTION.description" : "Шкода від заклять зменшується на ${val}%.", + "core.bonus.SPELL_IMMUNITY.name" : "Імунітет до заклять", + "core.bonus.SPELL_IMMUNITY.description" : "Імунітет до ${subtype.spell}", + "core.bonus.SPELL_LIKE_ATTACK.name" : "Атака, схожа на закляття", + "core.bonus.SPELL_LIKE_ATTACK.description" : "Атакує за допомогою ${subtype.spell}", + "core.bonus.SPELL_RESISTANCE_AURA.name" : "Аура стійкості", + "core.bonus.SPELL_RESISTANCE_AURA.description" : "Поруч розташовані стеки отримують ${val}% опору", + "core.bonus.SUMMON_GUARDIANS.name" : "Закликати охоронців", + "core.bonus.SUMMON_GUARDIANS.description" : "На початку бою викликає ${subtype.creature} (${val}%)", + "core.bonus.SYNERGY_TARGET.name" : "Синергізм", + "core.bonus.SYNERGY_TARGET.description" : "Ця істота вразлива до ефекту синергії", + "core.bonus.TWO_HEX_ATTACK_BREATH.name" : "Подих", + "core.bonus.TWO_HEX_ATTACK_BREATH.description" : "Атакує додаткову ціль позаду", + "core.bonus.THREE_HEADED_ATTACK.name" : "Триголова атака", + "core.bonus.THREE_HEADED_ATTACK.description" : "Атакує до трьох сусідніх загонів", + "core.bonus.TRANSMUTATION.name" : "Трансмутація", + "core.bonus.TRANSMUTATION.description" : "${val}% шанс перетворити атакованого юніта в інший тип", + "core.bonus.UNDEAD.name" : "Нежить", + "core.bonus.UNDEAD.description" : "Істота є нежить", + "core.bonus.UNLIMITED_RETALIATIONS.name" : "Необмежена кількість ударів у відповідь", + "core.bonus.UNLIMITED_RETALIATIONS.description" : "Відбиває будь-яку кількість атак", + "core.bonus.WATER_IMMUNITY.name" : "Імунітет до води", + "core.bonus.WATER_IMMUNITY.description" : "Імунітет до всіх заклять школи Води", + "core.bonus.WIDE_BREATH.name" : "Широкий подих", + "core.bonus.WIDE_BREATH.description" : "Атака широким подихом", +} From 19cfb79c59c8da957e8f16de1ff7f9edb627b03d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 18 Jan 2023 00:14:26 +0200 Subject: [PATCH 4/5] Removed H3 language detection - should be set by Launcher --- lib/CGeneralTextHandler.cpp | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/CGeneralTextHandler.cpp b/lib/CGeneralTextHandler.cpp index 9c77f3cf9..0a0f9cbd3 100644 --- a/lib/CGeneralTextHandler.cpp +++ b/lib/CGeneralTextHandler.cpp @@ -164,9 +164,6 @@ void CGeneralTextHandler::detectInstallParameters() const for (size_t i = 0; i < deviations.size(); ++i) logGlobal->debug("Comparing to %s: %f", knownFootprints[i].language, deviations[i]); - Settings language = settings.write["session"]["language"]; - language->String() = knownFootprints[bestIndex].language; - Settings encoding = settings.write["session"]["encoding"]; encoding->String() = knownFootprints[bestIndex].encoding; } @@ -658,10 +655,7 @@ size_t CGeneralTextHandler::getCampaignLength(size_t campaignID) const std::string CGeneralTextHandler::getInstalledLanguage() { - auto explicitSetting = settings["general"]["language"].String(); - if (explicitSetting != "auto") - return explicitSetting; - return settings["session"]["language"].String(); + return settings["general"]["language"].String(); } std::string CGeneralTextHandler::getInstalledEncoding() From 4a8d1d9b5e3736ab1202fd8544d00a4d89c28674 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 18 Jan 2023 14:35:40 +0200 Subject: [PATCH 5/5] Serialization version bump --- lib/serializer/CSerializer.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/serializer/CSerializer.h b/lib/serializer/CSerializer.h index b4972e977..a99a4775d 100644 --- a/lib/serializer/CSerializer.h +++ b/lib/serializer/CSerializer.h @@ -14,8 +14,8 @@ VCMI_LIB_NAMESPACE_BEGIN -const ui32 SERIALIZATION_VERSION = 812; -const ui32 MINIMAL_SERIALIZATION_VERSION = 812; +const ui32 SERIALIZATION_VERSION = 813; +const ui32 MINIMAL_SERIALIZATION_VERSION = 813; const std::string SAVEGAME_MAGIC = "VCMISVG"; class CHero;