mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-24 22:14:36 +02:00
Merge pull request #4824 from IvanSavenko/translate_fix
Fixes for issues with translations
This commit is contained in:
commit
a97deea563
@ -42,6 +42,12 @@
|
||||
"vcmi.heroOverview.secondarySkills" : "Secondary Skills",
|
||||
"vcmi.heroOverview.spells" : "Spells",
|
||||
|
||||
"vcmi.quickExchange.moveUnit" : "Move Unit",
|
||||
"vcmi.quickExchange.moveAllUnits" : "Move All Units",
|
||||
"vcmi.quickExchange.swapAllUnits" : "Swap Armies",
|
||||
"vcmi.quickExchange.moveAllArtifacts" : "Move All Artifacts",
|
||||
"vcmi.quickExchange.swapAllArtifacts" : "Swap Artifact",
|
||||
|
||||
"vcmi.radialWheel.mergeSameUnit" : "Merge same creatures",
|
||||
"vcmi.radialWheel.fillSingleUnit" : "Fill with single creatures",
|
||||
"vcmi.radialWheel.splitSingleUnit" : "Split off single creature",
|
||||
@ -60,6 +66,16 @@
|
||||
"vcmi.radialWheel.moveUp" : "Move up",
|
||||
"vcmi.radialWheel.moveDown" : "Move down",
|
||||
"vcmi.radialWheel.moveBottom" : "Move to bottom",
|
||||
|
||||
"vcmi.randomMap.description" : "Map created by the Random Map Generator.\nTemplate was %s, size %dx%d, levels %d, players %d, computers %d, water %s, monster %s, VCMI map",
|
||||
"vcmi.randomMap.description.isHuman" : ", %s is human",
|
||||
"vcmi.randomMap.description.townChoice" : ", %s town choice is %s",
|
||||
"vcmi.randomMap.description.water.none" : "none",
|
||||
"vcmi.randomMap.description.water.normal" : "normal",
|
||||
"vcmi.randomMap.description.water.islands" : "islands",
|
||||
"vcmi.randomMap.description.monster.weak" : "weak",
|
||||
"vcmi.randomMap.description.monster.normal" : "normal",
|
||||
"vcmi.randomMap.description.monster.strong" : "strong",
|
||||
|
||||
"vcmi.spellBook.search" : "search...",
|
||||
|
||||
|
66
Mods/vcmi/config/vcmi/spells.json
Normal file
66
Mods/vcmi/config/vcmi/spells.json
Normal file
@ -0,0 +1,66 @@
|
||||
{
|
||||
"core:summonDemons" : {
|
||||
"name": "Summon Demons"
|
||||
},
|
||||
"core:firstAid" : {
|
||||
"name": "First Aid"
|
||||
},
|
||||
"core:catapultShot" : {
|
||||
"name": "Catapult shot"
|
||||
},
|
||||
"core:cyclopsShot" : {
|
||||
"name": "Siege shot"
|
||||
},
|
||||
|
||||
"core:fireWallTrigger" : {
|
||||
"name" : "Fire Wall"
|
||||
},
|
||||
"core:landMineTrigger" : {
|
||||
"name" : "Land Mine",
|
||||
},
|
||||
"core:castleMoatTrigger" : {
|
||||
"name": "Moat"
|
||||
},
|
||||
"core:castleMoat": {
|
||||
"name": "Moat"
|
||||
},
|
||||
"core:rampartMoatTrigger" : {
|
||||
"name": "Brambles"
|
||||
},
|
||||
"core:rampartMoat": {
|
||||
"name": "Brambles"
|
||||
},
|
||||
"core:towerMoat": {
|
||||
"name": "Land Mine"
|
||||
},
|
||||
"core:infernoMoatTrigger" : {
|
||||
"name": "Lava"
|
||||
},
|
||||
"core:infernoMoat": {
|
||||
"name": "Lava"
|
||||
},
|
||||
"core:necropolisMoatTrigger" : {
|
||||
"name": "Boneyard"
|
||||
},
|
||||
"core:necropolisMoat": {
|
||||
"name": "Boneyard"
|
||||
},
|
||||
"core:dungeonMoatTrigger" : {
|
||||
"name": "Boiling Oil"
|
||||
},
|
||||
"core:dungeonMoat": {
|
||||
"name": "Boiling Oil"
|
||||
},
|
||||
"core:strongholdMoatTrigger" : {
|
||||
"name": "Wooden Spikes"
|
||||
},
|
||||
"core:strongholdMoat": {
|
||||
"name": "Wooden Spikes"
|
||||
},
|
||||
"core:fortressMoatTrigger" : {
|
||||
"name": "Boiling Tar"
|
||||
},
|
||||
"core:fortressMoat": {
|
||||
"name": "Boiling Tar"
|
||||
}
|
||||
}
|
@ -612,18 +612,5 @@
|
||||
"core.bonus.WIDE_BREATH.name" : "Широкий подих",
|
||||
"core.bonus.WIDE_BREATH.description" : "Атака широким подихом",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "Обмежена дальність стрільби",
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Не може стріляти по цілях на відстані більше ${val} гексів",
|
||||
|
||||
"vcmi.stackExperience.description" : "» S t a c k E x p e r i e n c e D e t a i l s «\n\nCreature Type ................... : %s\nExperience Rank ................. : %s (%i)\nExperience Points ............... : %i\nExperience Points to Next Rank .. : %i\nMaximum Experience per Battle ... : %i%% (%i)\nNumber of Creatures in stack .... : %i\nMaximum New Recruits\n without losing current Rank .... : %i\nExperience Multiplier ........... : %.2f\nUpgrade Multiplier .............. : %.2f\nExperience after Rank 10 ........ : %i\nMaximum New Recruits to remain at\n Rank 10 if at Maximum Experience : %i",
|
||||
"vcmi.stackExperience.rank.0" : "Початковий",
|
||||
"vcmi.stackExperience.rank.1" : "Новачок",
|
||||
"vcmi.stackExperience.rank.2" : "Підготовлений",
|
||||
"vcmi.stackExperience.rank.3" : "Досвідчений",
|
||||
"vcmi.stackExperience.rank.4" : "Випробуваний",
|
||||
"vcmi.stackExperience.rank.5" : "Ветеран",
|
||||
"vcmi.stackExperience.rank.6" : "Адепт",
|
||||
"vcmi.stackExperience.rank.7" : "Експерт",
|
||||
"vcmi.stackExperience.rank.8" : "Еліта",
|
||||
"vcmi.stackExperience.rank.9" : "Майстер",
|
||||
"vcmi.stackExperience.rank.10" : "Профі"
|
||||
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "Не може стріляти по цілях на відстані більше ${val} гексів"
|
||||
}
|
||||
|
@ -127,6 +127,7 @@
|
||||
|
||||
"factions" : [ "config/vcmi/towerFactions" ],
|
||||
"creatures" : [ "config/vcmi/towerCreature" ],
|
||||
"spells" : [ "config/vcmi/spells" ],
|
||||
|
||||
"translations" : [
|
||||
"config/vcmi/english.json"
|
||||
|
@ -185,12 +185,12 @@ void ClientCommandManager::handleRedrawCommand()
|
||||
GH.windows().totalRedraw();
|
||||
}
|
||||
|
||||
void ClientCommandManager::handleTranslateGameCommand()
|
||||
void ClientCommandManager::handleTranslateGameCommand(bool onlyMissing)
|
||||
{
|
||||
std::map<std::string, std::map<std::string, std::string>> textsByMod;
|
||||
VLC->generaltexth->exportAllTexts(textsByMod);
|
||||
VLC->generaltexth->exportAllTexts(textsByMod, onlyMissing);
|
||||
|
||||
const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / "translation";
|
||||
const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / ( onlyMissing ? "translationMissing" : "translation");
|
||||
boost::filesystem::create_directories(outPath);
|
||||
|
||||
for(const auto & modEntry : textsByMod)
|
||||
@ -254,13 +254,20 @@ void ClientCommandManager::handleTranslateMapsCommand()
|
||||
logGlobal->info("Loading campaigns for export");
|
||||
for (auto const & campaignName : campaignList)
|
||||
{
|
||||
loadedCampaigns.push_back(CampaignHandler::getCampaign(campaignName.getName()));
|
||||
for (auto const & part : loadedCampaigns.back()->allScenarios())
|
||||
loadedCampaigns.back()->getMap(part, nullptr);
|
||||
try
|
||||
{
|
||||
loadedCampaigns.push_back(CampaignHandler::getCampaign(campaignName.getName()));
|
||||
for (auto const & part : loadedCampaigns.back()->allScenarios())
|
||||
loadedCampaigns.back()->getMap(part, nullptr);
|
||||
}
|
||||
catch(std::exception & e)
|
||||
{
|
||||
logGlobal->warn("Campaign %s is invalid. Message: %s", campaignName.getName(), e.what());
|
||||
}
|
||||
}
|
||||
|
||||
std::map<std::string, std::map<std::string, std::string>> textsByMod;
|
||||
VLC->generaltexth->exportAllTexts(textsByMod);
|
||||
VLC->generaltexth->exportAllTexts(textsByMod, false);
|
||||
|
||||
const boost::filesystem::path outPath = VCMIDirs::get().userExtractedPath() / "translation";
|
||||
boost::filesystem::create_directories(outPath);
|
||||
@ -591,7 +598,10 @@ void ClientCommandManager::processCommand(const std::string & message, bool call
|
||||
handleRedrawCommand();
|
||||
|
||||
else if(message=="translate" || message=="translate game")
|
||||
handleTranslateGameCommand();
|
||||
handleTranslateGameCommand(false);
|
||||
|
||||
else if(message=="translate missing")
|
||||
handleTranslateGameCommand(true);
|
||||
|
||||
else if(message=="translate maps")
|
||||
handleTranslateMapsCommand();
|
||||
|
@ -46,7 +46,7 @@ class ClientCommandManager //take mantis #2292 issue about account if thinking a
|
||||
void handleRedrawCommand();
|
||||
|
||||
// Extracts all translateable game texts into Translation directory, separating files on per-mod basis
|
||||
void handleTranslateGameCommand();
|
||||
void handleTranslateGameCommand(bool onlyMissing);
|
||||
|
||||
// Extracts all translateable texts from maps and campaigns into Translation directory, separating files on per-mod basis
|
||||
void handleTranslateMapsCommand();
|
||||
|
@ -393,7 +393,7 @@ CStackWindow::CommanderMainSection::CommanderMainSection(CStackWindow * owner, i
|
||||
|
||||
auto getSkillDescription = [this](int skillIndex) -> std::string
|
||||
{
|
||||
return CGI->generaltexth->znpc00[152 + (12 * skillIndex) + (parent->info->commander->secondarySkills[skillIndex] * 2)];
|
||||
return parent->getCommanderSkillDescription(skillIndex, parent->info->commander->secondarySkills[skillIndex]);
|
||||
};
|
||||
|
||||
for(int index = ECommander::ATTACK; index <= ECommander::SPELL_POWER; ++index)
|
||||
@ -905,14 +905,30 @@ std::string CStackWindow::generateStackExpDescription()
|
||||
return expText;
|
||||
}
|
||||
|
||||
std::string CStackWindow::getCommanderSkillDescription(int skillIndex, int skillLevel)
|
||||
{
|
||||
constexpr std::array skillNames = {
|
||||
"attack",
|
||||
"defence",
|
||||
"health",
|
||||
"damage",
|
||||
"speed",
|
||||
"magic"
|
||||
};
|
||||
|
||||
std::string textID = TextIdentifier("vcmi", "commander", "skill", skillNames.at(skillIndex), skillLevel).get();
|
||||
|
||||
return CGI->generaltexth->translate(textID);
|
||||
}
|
||||
|
||||
void CStackWindow::setSelection(si32 newSkill, std::shared_ptr<CCommanderSkillIcon> newIcon)
|
||||
{
|
||||
auto getSkillDescription = [this](int skillIndex, bool selected) -> std::string
|
||||
{
|
||||
if(selected)
|
||||
return CGI->generaltexth->znpc00[152 + (12 * skillIndex) + ((info->commander->secondarySkills[skillIndex] + 1) * 2)]; //upgrade description
|
||||
return getCommanderSkillDescription(skillIndex, info->commander->secondarySkills[skillIndex] + 1); //upgrade description
|
||||
else
|
||||
return CGI->generaltexth->znpc00[152 + (12 * skillIndex) + (info->commander->secondarySkills[skillIndex] * 2)];
|
||||
return getCommanderSkillDescription(skillIndex, info->commander->secondarySkills[skillIndex]);
|
||||
};
|
||||
|
||||
auto getSkillImage = [this](int skillIndex)
|
||||
|
@ -189,6 +189,7 @@ class CStackWindow : public CWindowObject
|
||||
void init();
|
||||
|
||||
std::string generateStackExpDescription();
|
||||
std::string getCommanderSkillDescription(int skillIndex, int skillLevel);
|
||||
|
||||
public:
|
||||
// for battles
|
||||
|
@ -192,18 +192,52 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
|
||||
if(qeLayout)
|
||||
{
|
||||
buttonMoveUnitsFromLeftToRight = std::make_shared<CButton>(Point(325, 118), AnimationPath::builtin("quick-exchange/armRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), [this](){ this->moveUnitsShortcut(true); });
|
||||
buttonMoveUnitsFromRightToLeft = std::make_shared<CButton>(Point(425, 118), AnimationPath::builtin("quick-exchange/armLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[1]), [this](){ this->moveUnitsShortcut(false); });
|
||||
buttonMoveArtifactsFromLeftToRight = std::make_shared<CButton>(Point(325, 154), AnimationPath::builtin("quick-exchange/artRight.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), [this](){ this->moveArtifactsCallback(true);});
|
||||
buttonMoveArtifactsFromRightToLeft = std::make_shared<CButton>(Point(425, 154), AnimationPath::builtin("quick-exchange/artLeft.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[3]), [this](){ this->moveArtifactsCallback(false);});
|
||||
buttonMoveUnitsFromLeftToRight = std::make_shared<CButton>(
|
||||
Point(325, 118),
|
||||
AnimationPath::builtin("quick-exchange/armRight.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.moveAllUnits")),
|
||||
[this](){ this->moveUnitsShortcut(true); });
|
||||
|
||||
exchangeUnitsButton = std::make_shared<CButton>(Point(377, 118), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[2]), [this](){ controller.swapArmy(); });
|
||||
exchangeArtifactsButton = std::make_shared<CButton>(Point(377, 154), AnimationPath::builtin("quick-exchange/swapAll.DEF"), CButton::tooltip(CGI->generaltexth->qeModCommands[4]), [this](){ this->swapArtifactsCallback(); });
|
||||
buttonMoveUnitsFromRightToLeft = std::make_shared<CButton>(
|
||||
Point(425, 118),
|
||||
AnimationPath::builtin("quick-exchange/armLeft.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.moveAllUnits")),
|
||||
[this](){ this->moveUnitsShortcut(false); });
|
||||
|
||||
backpackButtonLeft = std::make_shared<CButton>(Point(325, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
|
||||
buttonMoveArtifactsFromLeftToRight = std::make_shared<CButton>(
|
||||
Point(325, 154), AnimationPath::builtin("quick-exchange/artRight.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.moveAllArtifacts")),
|
||||
[this](){ this->moveArtifactsCallback(true);});
|
||||
|
||||
buttonMoveArtifactsFromRightToLeft = std::make_shared<CButton>(
|
||||
Point(425, 154), AnimationPath::builtin("quick-exchange/artLeft.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.moveAllArtifacts")),
|
||||
[this](){ this->moveArtifactsCallback(false);});
|
||||
|
||||
exchangeUnitsButton = std::make_shared<CButton>(
|
||||
Point(377, 118),
|
||||
AnimationPath::builtin("quick-exchange/swapAll.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.swapAllUnits")),
|
||||
[this](){ controller.swapArmy(); });
|
||||
|
||||
exchangeArtifactsButton = std::make_shared<CButton>(
|
||||
Point(377, 154),
|
||||
AnimationPath::builtin("quick-exchange/swapAll.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.swapAllArtifacts")),
|
||||
[this](){ this->swapArtifactsCallback(); });
|
||||
|
||||
backpackButtonLeft = std::make_shared<CButton>(
|
||||
Point(325, 518),
|
||||
AnimationPath::builtin("heroBackpack"),
|
||||
CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
|
||||
[this](){ this->backpackShortcut(true); });
|
||||
backpackButtonRight = std::make_shared<CButton>(Point(419, 518), AnimationPath::builtin("heroBackpack"), CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
|
||||
|
||||
backpackButtonRight = std::make_shared<CButton>(
|
||||
Point(419, 518),
|
||||
AnimationPath::builtin("heroBackpack"),
|
||||
CButton::tooltipLocalized("vcmi.heroWindow.openBackpack"),
|
||||
[this](){ this->backpackShortcut(false); });
|
||||
|
||||
backpackButtonLeft->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
|
||||
backpackButtonRight->setOverlay(std::make_shared<CPicture>(ImagePath::builtin("heroWindow/backpackButtonIcon")));
|
||||
|
||||
@ -227,7 +261,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
std::make_shared<CButton>(
|
||||
Point(484 + 35 * i, 154),
|
||||
AnimationPath::builtin("quick-exchange/unitLeft.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.moveUnit")),
|
||||
std::bind(&CExchangeController::moveStack, &controller, false, SlotID(i))));
|
||||
moveUnitFromRightToLeftButtons.back()->block(leftHeroBlock);
|
||||
|
||||
@ -235,7 +269,7 @@ CExchangeWindow::CExchangeWindow(ObjectInstanceID hero1, ObjectInstanceID hero2,
|
||||
std::make_shared<CButton>(
|
||||
Point(66 + 35 * i, 154),
|
||||
AnimationPath::builtin("quick-exchange/unitRight.DEF"),
|
||||
CButton::tooltip(CGI->generaltexth->qeModCommands[1]),
|
||||
CButton::tooltip(CGI->generaltexth->translate("vcmi.quickExchange.moveUnit")),
|
||||
std::bind(&CExchangeController::moveStack, &controller, true, SlotID(i))));
|
||||
moveUnitFromLeftToRightButtons.back()->block(rightHeroBlock);
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -55,7 +55,7 @@
|
||||
{
|
||||
"targetType" : "CREATURE",
|
||||
"type": "combat",
|
||||
"name": "Land Mine",
|
||||
"name": "",
|
||||
"school":
|
||||
{
|
||||
"air": false,
|
||||
@ -237,7 +237,7 @@
|
||||
"fireWallTrigger" : {
|
||||
"targetType" : "CREATURE",
|
||||
"type": "combat",
|
||||
"name": "Fire Wall",
|
||||
"name": "",
|
||||
"school":
|
||||
{
|
||||
"air": false,
|
||||
|
@ -1,8 +1,8 @@
|
||||
{
|
||||
"summonDemons" : {
|
||||
"summonDemons" : {
|
||||
"type": "ability",
|
||||
"targetType" : "CREATURE",
|
||||
"name": "Summon Demons",
|
||||
"name": "",
|
||||
"school" : {},
|
||||
"level": 2,
|
||||
"power": 50,
|
||||
@ -46,11 +46,11 @@
|
||||
"bonus.GARGOYLE" : "absolute"
|
||||
}
|
||||
}
|
||||
},
|
||||
"firstAid" : {
|
||||
},
|
||||
"firstAid" : {
|
||||
"targetType" : "CREATURE",
|
||||
"type": "ability",
|
||||
"name": "First Aid",
|
||||
"name": "",
|
||||
"school" : {},
|
||||
"level": 1,
|
||||
"power": 10,
|
||||
@ -106,7 +106,7 @@
|
||||
"catapultShot" : {
|
||||
"targetType" : "LOCATION",
|
||||
"type": "ability",
|
||||
"name": "Catapult shot",
|
||||
"name": "",
|
||||
"school" : {},
|
||||
"level": 1,
|
||||
"power": 1,
|
||||
@ -187,7 +187,7 @@
|
||||
"cyclopsShot" : {
|
||||
"targetType" : "LOCATION",
|
||||
"type": "ability",
|
||||
"name": "Siege shot",
|
||||
"name": "",
|
||||
"school" : {},
|
||||
"level": 1,
|
||||
"power": 1,
|
||||
|
@ -121,6 +121,7 @@ Below a list of supported commands, with their arguments wrapped in `<>`
|
||||
|
||||
#### Extract commands
|
||||
`translate` - save game texts into json files
|
||||
`translate missing` - save untranslated game texts into json files
|
||||
`translate maps` - save map and campaign texts into json files
|
||||
`get config` - save game objects data into json files
|
||||
`get scripts` - dumps lua script stuff into files (currently inactive due to scripting disabled for default builds)
|
||||
|
@ -136,6 +136,8 @@ After that, start Launcher, switch to Help tab and open "log files directory". Y
|
||||
|
||||
If your mod also contains maps or campaigns that you want to translate, then use '/translate maps' command instead.
|
||||
|
||||
If you want to update existing translation, you can use '/translate missing' command that will export only strings that were not translated
|
||||
|
||||
### Translating mod information
|
||||
In order to display information in Launcher in language selected by user add following block into your `mod.json`:
|
||||
```
|
||||
|
@ -136,6 +136,9 @@ si64 CCompressedStream::readMore(ui8 *data, si64 size)
|
||||
{
|
||||
if (inflateState->avail_in == 0)
|
||||
{
|
||||
if (gzipStream == nullptr)
|
||||
throw std::runtime_error("Potentially truncated gzip file");
|
||||
|
||||
//inflate ran out of available data or was not initialized yet
|
||||
// get new input data and update state accordingly
|
||||
si64 availSize = gzipStream->read(compressedBuffer.data(), compressedBuffer.size());
|
||||
|
@ -158,40 +158,58 @@ bool JsonParser::extractEscaping(std::string & str)
|
||||
|
||||
switch(input[pos])
|
||||
{
|
||||
case '\r':
|
||||
if(settings.mode == JsonParsingSettings::JsonFormatMode::JSON5 && input.size() > pos && input[pos+1] == '\n')
|
||||
{
|
||||
pos += 2;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case '\n':
|
||||
if(settings.mode == JsonParsingSettings::JsonFormatMode::JSON5)
|
||||
{
|
||||
pos += 1;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case '\"':
|
||||
str += '\"';
|
||||
break;
|
||||
pos++;
|
||||
return true;
|
||||
case '\\':
|
||||
str += '\\';
|
||||
break;
|
||||
pos++;
|
||||
return true;
|
||||
case 'b':
|
||||
str += '\b';
|
||||
break;
|
||||
pos++;
|
||||
return true;
|
||||
case 'f':
|
||||
str += '\f';
|
||||
break;
|
||||
pos++;
|
||||
return true;
|
||||
case 'n':
|
||||
str += '\n';
|
||||
break;
|
||||
pos++;
|
||||
return true;
|
||||
case 'r':
|
||||
str += '\r';
|
||||
break;
|
||||
pos++;
|
||||
return true;
|
||||
case 't':
|
||||
str += '\t';
|
||||
break;
|
||||
pos++;
|
||||
return true;
|
||||
case '/':
|
||||
str += '/';
|
||||
break;
|
||||
default:
|
||||
return error("Unknown escape sequence!", true);
|
||||
pos++;
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
return error("Unknown escape sequence!", true);
|
||||
}
|
||||
|
||||
bool JsonParser::extractString(std::string & str)
|
||||
{
|
||||
//TODO: JSON5 - line breaks escaping
|
||||
|
||||
if(settings.mode < JsonParsingSettings::JsonFormatMode::JSON5)
|
||||
{
|
||||
if(input[pos] != '\"')
|
||||
@ -216,27 +234,30 @@ bool JsonParser::extractString(std::string & str)
|
||||
pos++;
|
||||
return true;
|
||||
}
|
||||
if(input[pos] == '\\') // Escaping
|
||||
else if(input[pos] == '\\') // Escaping
|
||||
{
|
||||
str.append(&input[first], pos - first);
|
||||
pos++;
|
||||
if(pos == input.size())
|
||||
break;
|
||||
|
||||
extractEscaping(str);
|
||||
first = pos + 1;
|
||||
first = pos;
|
||||
}
|
||||
if(input[pos] == '\n') // end-of-line
|
||||
else if(input[pos] == '\n') // end-of-line
|
||||
{
|
||||
str.append(&input[first], pos - first);
|
||||
return error("Closing quote not found!", true);
|
||||
}
|
||||
if(static_cast<unsigned char>(input[pos]) < ' ') // control character
|
||||
else if(static_cast<unsigned char>(input[pos]) < ' ') // control character
|
||||
{
|
||||
str.append(&input[first], pos - first);
|
||||
first = pos + 1;
|
||||
pos++;
|
||||
first = pos;
|
||||
error("Illegal character in the string!", true);
|
||||
}
|
||||
pos++;
|
||||
else
|
||||
pos++;
|
||||
}
|
||||
return error("Unterminated string!");
|
||||
}
|
||||
|
@ -144,7 +144,7 @@ void CBank::onHeroVisit(const CGHeroInstance * h) const
|
||||
bd.player = h->getOwner();
|
||||
bd.text.appendLocalString(EMetaText::ADVOB_TXT, 32);
|
||||
bd.components = getPopupComponents(h->getOwner());
|
||||
bd.text.replaceRawString(getObjectName());
|
||||
bd.text.replaceTextID(getObjectHandler()->getNameTextID());
|
||||
cb->showBlockingDialog(this, &bd);
|
||||
}
|
||||
|
||||
@ -158,7 +158,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
|
||||
if (!bankConfig)
|
||||
{
|
||||
iw.text.appendRawString(VLC->generaltexth->advobtxt[33]);// This was X, now is completely empty
|
||||
iw.text.replaceRawString(getObjectName());
|
||||
iw.text.replaceTextID(getObjectHandler()->getNameTextID());
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
|
||||
|
@ -208,6 +208,9 @@ void CMapLoaderH3M::readHeader()
|
||||
|
||||
// optimization - load mappings only once to avoid slow parsing of map headers for map list
|
||||
static const std::map<EMapFormat, MapIdentifiersH3M> identifierMappers = generateMappings();
|
||||
if (!identifierMappers.count(mapHeader->version))
|
||||
throw std::runtime_error("Unsupported map format! Format ID " + std::to_string(static_cast<int>(mapHeader->version)));
|
||||
|
||||
const MapIdentifiersH3M & identifierMapper = identifierMappers.at(mapHeader->version);
|
||||
|
||||
reader->setIdentifierRemapper(identifierMapper);
|
||||
|
@ -1014,8 +1014,6 @@ void CMapLoaderJson::readTerrain()
|
||||
const JsonNode underground = getFromArchive(TERRAIN_FILE_NAMES[1]);
|
||||
readTerrainLevel(underground, 1);
|
||||
}
|
||||
|
||||
map->calculateWaterContent();
|
||||
}
|
||||
|
||||
CMapLoaderJson::MapObjectLoader::MapObjectLoader(CMapLoaderJson * _owner, JsonMap::value_type & json):
|
||||
|
@ -410,9 +410,11 @@ bool MapReaderH3M::readBool()
|
||||
int8_t MapReaderH3M::readInt8Checked(int8_t lowerLimit, int8_t upperLimit)
|
||||
{
|
||||
int8_t result = readInt8();
|
||||
assert(result >= lowerLimit);
|
||||
assert(result <= upperLimit);
|
||||
return std::clamp(result, lowerLimit, upperLimit);
|
||||
int8_t resultClamped = std::clamp(result, lowerLimit, upperLimit);
|
||||
if (result != resultClamped)
|
||||
logGlobal->warn("Map contains out of range value %d! Expected %d-%d", static_cast<int>(result), static_cast<int>(lowerLimit), static_cast<int>(upperLimit));
|
||||
|
||||
return resultClamped;
|
||||
}
|
||||
|
||||
uint8_t MapReaderH3M::readUInt8()
|
||||
|
@ -446,8 +446,8 @@ void CModHandler::loadTranslation(const TModID & modName)
|
||||
JsonNode baseTranslation = JsonUtils::assembleFromFiles(mod.config["translations"]);
|
||||
JsonNode extraTranslation = JsonUtils::assembleFromFiles(mod.config[preferredLanguage]["translations"]);
|
||||
|
||||
VLC->generaltexth->loadTranslationOverrides(modName, baseTranslation);
|
||||
VLC->generaltexth->loadTranslationOverrides(modName, extraTranslation);
|
||||
VLC->generaltexth->loadTranslationOverrides(modName, modBaseLanguage, baseTranslation);
|
||||
VLC->generaltexth->loadTranslationOverrides(modName, preferredLanguage, extraTranslation);
|
||||
}
|
||||
|
||||
void CModHandler::load()
|
||||
|
@ -152,41 +152,55 @@ std::unique_ptr<CMap> CMapGenerator::generate()
|
||||
return std::move(map->mapInstance);
|
||||
}
|
||||
|
||||
std::string CMapGenerator::getMapDescription() const
|
||||
MetaString CMapGenerator::getMapDescription() const
|
||||
{
|
||||
assert(map);
|
||||
const TextIdentifier mainPattern("vcmi", "randomMap", "description");
|
||||
const TextIdentifier isHuman("vcmi", "randomMap", "description", "isHuman");
|
||||
const TextIdentifier townChoiceIs("vcmi", "randomMap", "description", "townChoice");
|
||||
const std::array waterContent = {
|
||||
TextIdentifier("vcmi", "randomMap", "description", "water", "none"),
|
||||
TextIdentifier("vcmi", "randomMap", "description", "water", "normal"),
|
||||
TextIdentifier("vcmi", "randomMap", "description", "water", "islands")
|
||||
};
|
||||
const std::array monsterStrength = {
|
||||
TextIdentifier("vcmi", "randomMap", "description", "monster", "weak"),
|
||||
TextIdentifier("vcmi", "randomMap", "description", "monster", "normal"),
|
||||
TextIdentifier("vcmi", "randomMap", "description", "monster", "strong")
|
||||
};
|
||||
|
||||
const std::string waterContentStr[3] = { "none", "normal", "islands" };
|
||||
const std::string monsterStrengthStr[3] = { "weak", "normal", "strong" };
|
||||
|
||||
int monsterStrengthIndex = mapGenOptions.getMonsterStrength() - EMonsterStrength::GLOBAL_WEAK; //does not start from 0
|
||||
const auto * mapTemplate = mapGenOptions.getMapTemplate();
|
||||
int monsterStrengthIndex = mapGenOptions.getMonsterStrength() - EMonsterStrength::GLOBAL_WEAK; //does not start from 0
|
||||
|
||||
if(!mapTemplate)
|
||||
throw rmgException("Map template for Random Map Generator is not found. Could not start the game.");
|
||||
MetaString result = MetaString::createFromTextID(mainPattern.get());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << boost::str(boost::format(std::string("Map created by the Random Map Generator.\nTemplate was %s, size %dx%d") +
|
||||
", levels %d, players %d, computers %d, water %s, monster %s, VCMI map") % mapTemplate->getName() %
|
||||
map->width() % map->height() % static_cast<int>(map->levels()) % static_cast<int>(mapGenOptions.getHumanOrCpuPlayerCount()) %
|
||||
static_cast<int>(mapGenOptions.getCompOnlyPlayerCount()) % waterContentStr[mapGenOptions.getWaterContent()] %
|
||||
monsterStrengthStr[monsterStrengthIndex]);
|
||||
result.replaceRawString(mapTemplate->getName());
|
||||
result.replaceNumber(map->width());
|
||||
result.replaceNumber(map->height());
|
||||
result.replaceNumber(map->levels());
|
||||
result.replaceNumber(mapGenOptions.getHumanOrCpuPlayerCount());
|
||||
result.replaceNumber(mapGenOptions.getCompOnlyPlayerCount());
|
||||
result.replaceTextID(waterContent.at(mapGenOptions.getWaterContent()).get());
|
||||
result.replaceTextID(monsterStrength.at(monsterStrengthIndex).get());
|
||||
|
||||
for(const auto & pair : mapGenOptions.getPlayersSettings())
|
||||
{
|
||||
const auto & pSettings = pair.second;
|
||||
|
||||
if(pSettings.getPlayerType() == EPlayerType::HUMAN)
|
||||
{
|
||||
ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor().getNum()] << " is human";
|
||||
result.appendTextID(isHuman.get());
|
||||
result.replaceName(pSettings.getColor());
|
||||
}
|
||||
|
||||
if(pSettings.getStartingTown() != FactionID::RANDOM)
|
||||
{
|
||||
ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor().getNum()]
|
||||
<< " town choice is " << (*VLC->townh)[pSettings.getStartingTown()]->getNameTranslated();
|
||||
result.appendTextID(townChoiceIs.get());
|
||||
result.replaceName(pSettings.getColor());
|
||||
result.replaceName(pSettings.getStartingTown());
|
||||
}
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
return result;
|
||||
}
|
||||
|
||||
void CMapGenerator::addPlayerInfo()
|
||||
@ -451,7 +465,7 @@ void CMapGenerator::addHeaderInfo()
|
||||
m.height = mapGenOptions.getHeight();
|
||||
m.twoLevel = mapGenOptions.getHasTwoLevels();
|
||||
m.name.appendLocalString(EMetaText::GENERAL_TXT, 740);
|
||||
m.description.appendRawString(getMapDescription());
|
||||
m.description = getMapDescription();
|
||||
m.difficulty = EMapDifficulty::NORMAL;
|
||||
addPlayerInfo();
|
||||
m.waterMap = (mapGenOptions.getWaterContent() != EWaterContent::EWaterContent::NONE);
|
||||
|
@ -10,14 +10,12 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../GameConstants.h"
|
||||
#include "CMapGenOptions.h"
|
||||
#include "../int3.h"
|
||||
#include "CRmgTemplate.h"
|
||||
#include "../LoadProgress.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class MetaString;
|
||||
class CRmgTemplate;
|
||||
class CMapGenOptions;
|
||||
class JsonNode;
|
||||
@ -93,7 +91,7 @@ private:
|
||||
/// Generation methods
|
||||
void loadConfig();
|
||||
|
||||
std::string getMapDescription() const;
|
||||
MetaString getMapDescription() const;
|
||||
|
||||
void initPrisonsRemaining();
|
||||
void initQuestArtsRemaining();
|
||||
|
@ -139,9 +139,7 @@ CGeneralTextHandler::CGeneralTextHandler():
|
||||
// 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" ),
|
||||
znpc00 (*this, "vcmi.znpc00" ), // technically - wog
|
||||
qeModCommands (*this, "vcmi.quickExchange" )
|
||||
capColors (*this, "vcmi.capitalColors" )
|
||||
{
|
||||
readToVector("core.vcdesc", "DATA/VCDESC.TXT" );
|
||||
readToVector("core.lcdesc", "DATA/LCDESC.TXT" );
|
||||
@ -166,10 +164,6 @@ CGeneralTextHandler::CGeneralTextHandler():
|
||||
readToVector("core.mineevnt", "DATA/MINEEVNT.TXT" );
|
||||
readToVector("core.xtrainfo", "DATA/XTRAINFO.TXT" );
|
||||
|
||||
static const std::string QE_MOD_COMMANDS = "DATA/QECOMMANDS.TXT";
|
||||
if (CResourceHandler::get()->existsResource(TextPath::builtin(QE_MOD_COMMANDS)))
|
||||
readToVector("vcmi.quickExchange", QE_MOD_COMMANDS);
|
||||
|
||||
{
|
||||
CLegacyConfigParser parser(TextPath::builtin("DATA/RANDTVRN.TXT"));
|
||||
parser.endLine();
|
||||
@ -298,11 +292,6 @@ CGeneralTextHandler::CGeneralTextHandler():
|
||||
scenariosCountPerCampaign.push_back(region);
|
||||
}
|
||||
}
|
||||
if (VLC->engineSettings()->getBoolean(EGameSettings::MODULE_COMMANDERS))
|
||||
{
|
||||
if(CResourceHandler::get()->existsResource(TextPath::builtin("DATA/ZNPC00.TXT")))
|
||||
readToVector("vcmi.znpc00", "DATA/ZNPC00.TXT" );
|
||||
}
|
||||
}
|
||||
|
||||
int32_t CGeneralTextHandler::pluralText(const int32_t textIndex, const int32_t count) const
|
||||
|
@ -62,8 +62,6 @@ public:
|
||||
LegacyTextContainer fcommands; // fort screen
|
||||
LegacyTextContainer tavernInfo;
|
||||
|
||||
LegacyTextContainer qeModCommands;
|
||||
|
||||
LegacyHelpContainer zelp;
|
||||
|
||||
//objects
|
||||
@ -75,8 +73,6 @@ public:
|
||||
|
||||
//sec skills
|
||||
LegacyTextContainer levels;
|
||||
//commanders
|
||||
LegacyTextContainer znpc00; //more or less useful content of that file
|
||||
|
||||
std::vector<std::string> findStringsWithPrefix(const std::string & prefix);
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "CArtHandler.h"
|
||||
#include "CCreatureHandler.h"
|
||||
#include "CCreatureSet.h"
|
||||
#include "entities/faction/CFaction.h"
|
||||
#include "texts/CGeneralTextHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "GameConstants.h"
|
||||
@ -387,6 +388,11 @@ void MetaString::replaceName(const ArtifactID & id)
|
||||
replaceTextID(id.toEntity(VLC)->getNameTextID());
|
||||
}
|
||||
|
||||
void MetaString::replaceName(const FactionID & id)
|
||||
{
|
||||
replaceTextID(id.toEntity(VLC)->getNameTextID());
|
||||
}
|
||||
|
||||
void MetaString::replaceName(const MapObjectID& id)
|
||||
{
|
||||
replaceTextID(VLC->objtypeh->getObjectName(id, 0));
|
||||
|
@ -21,6 +21,7 @@ class MapObjectSubID;
|
||||
class PlayerColor;
|
||||
class SecondarySkill;
|
||||
class SpellID;
|
||||
class FactionID;
|
||||
class GameResID;
|
||||
using TQuantity = si32;
|
||||
|
||||
@ -97,6 +98,7 @@ public:
|
||||
void replacePositiveNumber(int64_t txt);
|
||||
|
||||
void replaceName(const ArtifactID & id);
|
||||
void replaceName(const FactionID& id);
|
||||
void replaceName(const MapObjectID& id);
|
||||
void replaceName(const PlayerColor& id);
|
||||
void replaceName(const SecondarySkill& id);
|
||||
|
@ -22,7 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
std::recursive_mutex TextLocalizationContainer::globalTextMutex;
|
||||
|
||||
void TextLocalizationContainer::registerStringOverride(const std::string & modContext, const TextIdentifier & UID, const std::string & localized)
|
||||
void TextLocalizationContainer::registerStringOverride(const std::string & modContext, const TextIdentifier & UID, const std::string & localized, const std::string & language)
|
||||
{
|
||||
std::lock_guard globalLock(globalTextMutex);
|
||||
|
||||
@ -42,6 +42,11 @@ void TextLocalizationContainer::registerStringOverride(const std::string & modCo
|
||||
entry.identifierModContext = modContext;
|
||||
entry.baseStringModContext = modContext;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (language == VLC->generaltexth->getPreferredLanguage())
|
||||
entry.overriden = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -127,10 +132,10 @@ void TextLocalizationContainer::registerString(const std::string & identifierMod
|
||||
}
|
||||
}
|
||||
|
||||
void TextLocalizationContainer::loadTranslationOverrides(const std::string & modContext, const JsonNode & config)
|
||||
void TextLocalizationContainer::loadTranslationOverrides(const std::string & modContext, const std::string & language, const JsonNode & config)
|
||||
{
|
||||
for(const auto & node : config.Struct())
|
||||
registerStringOverride(modContext, node.first, node.second.String());
|
||||
registerStringOverride(modContext, node.first, node.second.String(), language);
|
||||
}
|
||||
|
||||
bool TextLocalizationContainer::identifierExists(const TextIdentifier & UID) const
|
||||
@ -140,15 +145,18 @@ bool TextLocalizationContainer::identifierExists(const TextIdentifier & UID) con
|
||||
return stringsLocalizations.count(UID.get());
|
||||
}
|
||||
|
||||
void TextLocalizationContainer::exportAllTexts(std::map<std::string, std::map<std::string, std::string>> & storage) const
|
||||
void TextLocalizationContainer::exportAllTexts(std::map<std::string, std::map<std::string, std::string>> & storage, bool onlyMissing) const
|
||||
{
|
||||
std::lock_guard globalLock(globalTextMutex);
|
||||
|
||||
for (auto const & subContainer : subContainers)
|
||||
subContainer->exportAllTexts(storage);
|
||||
subContainer->exportAllTexts(storage, onlyMissing);
|
||||
|
||||
for (auto const & entry : stringsLocalizations)
|
||||
{
|
||||
if (onlyMissing && entry.second.overriden)
|
||||
continue;
|
||||
|
||||
std::string textToWrite;
|
||||
std::string modName = entry.second.baseStringModContext;
|
||||
|
||||
|
@ -32,6 +32,8 @@ protected:
|
||||
/// Different from identifierModContext if mod has modified object from another mod (e.g. rebalance mods)
|
||||
std::string baseStringModContext;
|
||||
|
||||
bool overriden = false;
|
||||
|
||||
template <typename Handler>
|
||||
void serialize(Handler & h)
|
||||
{
|
||||
@ -47,7 +49,7 @@ protected:
|
||||
std::vector<const TextLocalizationContainer *> subContainers;
|
||||
|
||||
/// add selected string to internal storage as high-priority strings
|
||||
void registerStringOverride(const std::string & modContext, const TextIdentifier & UID, const std::string & localized);
|
||||
void registerStringOverride(const std::string & modContext, const TextIdentifier & UID, const std::string & localized, const std::string & language);
|
||||
|
||||
std::string getModLanguage(const std::string & modContext);
|
||||
|
||||
@ -57,7 +59,7 @@ protected:
|
||||
public:
|
||||
/// Loads translation from provided json
|
||||
/// Any entries loaded by this will have priority over texts registered normally
|
||||
void loadTranslationOverrides(const std::string & modContext, JsonNode const & file);
|
||||
void loadTranslationOverrides(const std::string & modContext, const std::string & language, JsonNode const & file);
|
||||
|
||||
/// add selected string to internal storage
|
||||
void registerString(const std::string & modContext, const TextIdentifier & UID, const JsonNode & localized);
|
||||
@ -77,7 +79,7 @@ public:
|
||||
|
||||
/// Debug method, returns all currently stored texts
|
||||
/// Format: [mod ID][string ID] -> human-readable text
|
||||
void exportAllTexts(std::map<std::string, std::map<std::string, std::string>> & storage) const;
|
||||
void exportAllTexts(std::map<std::string, std::map<std::string, std::string>> & storage, bool onlyMissing) const;
|
||||
|
||||
/// Add or override subcontainer which can store identifiers
|
||||
void addSubContainer(const TextLocalizationContainer & container);
|
||||
|
Loading…
Reference in New Issue
Block a user