Browse Source

Win/loss condition texts use MetaString

Ivan Savenko 2 years ago
parent
commit
d51fe62804

+ 1 - 1
AI/Nullkiller/AIGateway.cpp

@@ -188,7 +188,7 @@ void AIGateway::showShipyardDialog(const IShipyard * obj)
 
 void AIGateway::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult)
 {
-	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf);
+	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf.toString());
 	NET_EVENT_HANDLER;
 	logAi->debug("Player %d (%s): I heard that player %d (%s) %s.", playerID, playerID.getStr(), player, player.getStr(), (victoryLossCheckResult.victory() ? "won" : "lost"));
 

+ 1 - 1
AI/VCAI/VCAI.cpp

@@ -202,7 +202,7 @@ void VCAI::showShipyardDialog(const IShipyard * obj)
 
 void VCAI::gameOver(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult)
 {
-	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf);
+	LOG_TRACE_PARAMS(logAi, "victoryLossCheckResult '%s'", victoryLossCheckResult.messageToSelf.toString());
 	NET_EVENT_HANDLER;
 	logAi->debug("Player %d (%s): I heard that player %d (%s) %s.", playerID, playerID.getStr(), player, player.getStr(), (victoryLossCheckResult.victory() ? "won" : "lost"));
 	if(player == playerID)

+ 3 - 3
client/CPlayerInterface.cpp

@@ -1585,9 +1585,9 @@ void CPlayerInterface::gameOver(PlayerColor player, const EVictoryLossCheckResul
 	{
 		if (victoryLossCheckResult.loss() && cb->getPlayerStatus(playerID) == EPlayerStatus::INGAME) //enemy has lost
 		{
-			std::string str = CGI->generaltexth->translate(victoryLossCheckResult.messageToSelf);
-			boost::algorithm::replace_first(str, "%s", CGI->generaltexth->capColors[player.getNum()]);
-			showInfoDialog(str, std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(CComponent::flag, player.getNum(), 0)));
+			MetaString message = victoryLossCheckResult.messageToSelf;
+			message.appendLocalString(EMetaText::COLOR, player.getNum());
+			showInfoDialog(message.toString(), std::vector<std::shared_ptr<CComponent>>(1, std::make_shared<CComponent>(CComponent::flag, player.getNum(), 0)));
 		}
 	}
 }

+ 2 - 2
client/lobby/CSelectionBase.cpp

@@ -210,9 +210,9 @@ void InfoCard::changeSelection()
 	iconsMapSizes->setFrame(mapInfo->getMapSizeIconId());
 	const CMapHeader * header = mapInfo->mapHeader.get();
 	iconsVictoryCondition->setFrame(header->victoryIconIndex);
-	labelVictoryConditionText->setText(header->victoryMessage);
+	labelVictoryConditionText->setText(header->victoryMessage.toString());
 	iconsLossCondition->setFrame(header->defeatIconIndex);
-	labelLossConditionText->setText(header->defeatMessage);
+	labelLossConditionText->setText(header->defeatMessage.toString());
 	flagbox->recreate();
 	labelDifficulty->setText(CGI->generaltexth->arraytxt[142 + mapInfo->mapHeader->difficulty]);
 	iconDifficulty->setSelected(SEL->getCurrentDifficulty());

+ 2 - 2
client/lobby/SelectionTab.cpp

@@ -54,7 +54,7 @@ bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::share
 			return (a->version < b->version);
 			break;
 		case _loscon: //by loss conditions
-			return (a->defeatMessage < b->defeatMessage);
+			return (a->defeatIconIndex < b->defeatIconIndex);
 			break;
 		case _playerAm: //by player amount
 			int playerAmntB, humenPlayersB, playerAmntA, humenPlayersA;
@@ -89,7 +89,7 @@ bool mapSorter::operator()(const std::shared_ptr<CMapInfo> aaa, const std::share
 			return (a->width < b->width);
 			break;
 		case _viccon: //by victory conditions
-			return (a->victoryMessage < b->victoryMessage);
+			return (a->victoryIconIndex < b->victoryIconIndex);
 			break;
 		case _name: //by name
 			return boost::ilexicographical_compare(a->name, b->name);

+ 4 - 4
lib/CGameState.cpp

@@ -2035,10 +2035,10 @@ bool CGameState::checkForVisitableDir(const int3 & src, const int3 & dst) const
 
 EVictoryLossCheckResult CGameState::checkForVictoryAndLoss(const PlayerColor & player) const
 {
-	const std::string & messageWonSelf = VLC->generaltexth->allTexts[659];
-	const std::string & messageWonOther = VLC->generaltexth->allTexts[5];
-	const std::string & messageLostSelf = VLC->generaltexth->allTexts[7];
-	const std::string & messageLostOther = VLC->generaltexth->allTexts[8];
+	const MetaString messageWonSelf = MetaString::createFromTextID("core.genrltxt.659");
+	const MetaString messageWonOther = MetaString::createFromTextID("core.genrltxt.5");
+	const MetaString messageLostSelf = MetaString::createFromTextID("core.genrltxt.7");
+	const MetaString messageLostOther = MetaString::createFromTextID("core.genrltxt.8");
 
 	auto evaluateEvent = [=](const EventCondition & condition)
 	{

+ 6 - 5
lib/CGameStateFwd.h

@@ -10,6 +10,7 @@
 #pragma once
 
 #include "CCreatureSet.h"
+#include "MetaString.h"
 #include "int3.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
@@ -101,12 +102,12 @@ struct DLL_LINKAGE InfoAboutTown : public InfoAboutArmy
 class DLL_LINKAGE EVictoryLossCheckResult
 {
 public:
-	static EVictoryLossCheckResult victory(std::string toSelf, std::string toOthers)
+	static EVictoryLossCheckResult victory(MetaString toSelf, MetaString toOthers)
 	{
 		return EVictoryLossCheckResult(VICTORY, toSelf, toOthers);
 	}
 
-	static EVictoryLossCheckResult defeat(std::string toSelf, std::string toOthers)
+	static EVictoryLossCheckResult defeat(MetaString toSelf, MetaString toOthers)
 	{
 		return EVictoryLossCheckResult(DEFEAT, toSelf, toOthers);
 	}
@@ -140,8 +141,8 @@ public:
 		return EVictoryLossCheckResult(-intValue, messageToOthers, messageToSelf);
 	}
 
-	std::string messageToSelf;
-	std::string messageToOthers;
+	MetaString messageToSelf;
+	MetaString messageToOthers;
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
@@ -157,7 +158,7 @@ private:
 		VICTORY= +1
 	};
 
-	EVictoryLossCheckResult(si32 intValue, std::string toSelf, std::string toOthers):
+	EVictoryLossCheckResult(si32 intValue, MetaString toSelf, MetaString toOthers):
 		messageToSelf(toSelf),
 		messageToOthers(toOthers),
 		intValue(intValue)

+ 77 - 49
lib/MetaString.cpp

@@ -22,18 +22,38 @@
 
 VCMI_LIB_NAMESPACE_BEGIN
 
+MetaString MetaString::createFromRawString(const std::string & value)
+{
+	MetaString result;
+	result.appendRawString(value);
+	return result;
+}
+
+MetaString MetaString::createFromTextID(const std::string & value)
+{
+	MetaString result;
+	result.appendTextID(value);
+	return result;
+}
+
 void MetaString::appendLocalString(EMetaText type, ui32 serial)
 {
 	message.push_back(EMessage::APPEND_LOCAL_STRING);
 	localStrings.emplace_back(type, serial);
 }
 
-void MetaString::appendRawString(std::string value)
+void MetaString::appendRawString(const std::string & value)
 {
 	message.push_back(EMessage::APPEND_RAW_STRING);
 	exactStrings.push_back(value);
 }
 
+void MetaString::appendTextID(const std::string & value)
+{
+	message.push_back(EMessage::APPEND_TEXTID_STRING);
+	stringsTextID.push_back(value);
+}
+
 void MetaString::appendNumber(int64_t value)
 {
 	message.push_back(EMessage::APPEND_NUMBER);
@@ -52,6 +72,12 @@ void MetaString::replaceRawString(const std::string &txt)
 	exactStrings.push_back(txt);
 }
 
+void MetaString::replaceTextID(const std::string & value)
+{
+	message.push_back(EMessage::REPLACE_TEXTID_STRING);
+	stringsTextID.push_back(value);
+}
+
 void MetaString::replaceNumber(int64_t txt)
 {
 	message.push_back(EMessage::REPLACE_NUMBER);
@@ -68,6 +94,7 @@ void MetaString::clear()
 {
 	exactStrings.clear();
 	localStrings.clear();
+	stringsTextID.clear();
 	message.clear();
 	numbers.clear();
 }
@@ -167,93 +194,94 @@ DLL_LINKAGE std::string MetaString::toString() const
 	size_t exSt = 0;
 	size_t loSt = 0;
 	size_t nums = 0;
+	size_t textID = 0;
 	dst.clear();
 
 	for(const auto & elem : message)
 	{
 		switch(elem)
 		{
-		case EMessage::APPEND_RAW_STRING:
-			dst += exactStrings[exSt++];
-			break;
-		case EMessage::APPEND_LOCAL_STRING:
-			{
-				std::string hlp = getLocalString(localStrings[loSt++]);
-				dst += hlp;
-			}
-			break;
-		case EMessage::APPEND_NUMBER:
-			dst += std::to_string(numbers[nums++]);
-			break;
-		case EMessage::REPLACE_RAW_STRING:
-			boost::replace_first(dst, "%s", exactStrings[exSt++]);
-			break;
-		case EMessage::REPLACE_LOCAL_STRING:
-			{
-				std::string hlp = getLocalString(localStrings[loSt++]);
-				boost::replace_first(dst, "%s", hlp);
-			}
-			break;
-		case EMessage::REPLACE_NUMBER:
-			boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
-			break;
-		case EMessage::REPLACE_POSITIVE_NUMBER:
-			boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++]));
-			break;
-		default:
-			logGlobal->error("MetaString processing error! Received message of type %d", static_cast<int>(elem));
-			assert(0);
-			break;
+			case EMessage::APPEND_RAW_STRING:
+				dst += exactStrings[exSt++];
+				break;
+			case EMessage::APPEND_LOCAL_STRING:
+				dst += getLocalString(localStrings[loSt++]);
+				break;
+			case EMessage::APPEND_TEXTID_STRING:
+				dst += VLC->generaltexth->translate(stringsTextID[textID++]);
+				break;
+			case EMessage::APPEND_NUMBER:
+				dst += std::to_string(numbers[nums++]);
+				break;
+			case EMessage::REPLACE_RAW_STRING:
+				boost::replace_first(dst, "%s", exactStrings[exSt++]);
+				break;
+			case EMessage::REPLACE_LOCAL_STRING:
+				boost::replace_first(dst, "%s", getLocalString(localStrings[loSt++]));
+				break;
+			case EMessage::REPLACE_TEXTID_STRING:
+				boost::replace_first(dst, "%s", VLC->generaltexth->translate(stringsTextID[textID++]));
+				break;
+			case EMessage::REPLACE_NUMBER:
+				boost::replace_first(dst, "%d", std::to_string(numbers[nums++]));
+				break;
+			case EMessage::REPLACE_POSITIVE_NUMBER:
+				boost::replace_first(dst, "%+d", '+' + std::to_string(numbers[nums++]));
+				break;
+			default:
+				logGlobal->error("MetaString processing error! Received message of type %d", static_cast<int>(elem));
+				assert(0);
+				break;
 		}
 	}
 	return dst;
 }
 
-DLL_LINKAGE std::string MetaString::buildList () const
+DLL_LINKAGE std::string MetaString::buildList() const
 {
 	size_t exSt = 0;
 	size_t loSt = 0;
 	size_t nums = 0;
+	size_t textID = 0;
 	std::string lista;
-	for (int i = 0; i < message.size(); ++i)
+	for(int i = 0; i < message.size(); ++i)
 	{
-		if (i > 0 && (message[i] == EMessage::APPEND_RAW_STRING || message[i] == EMessage::APPEND_LOCAL_STRING))
+		if(i > 0 && (message[i] == EMessage::APPEND_RAW_STRING || message[i] == EMessage::APPEND_LOCAL_STRING))
 		{
-			if (exSt == exactStrings.size() - 1)
+			if(exSt == exactStrings.size() - 1)
 				lista += VLC->generaltexth->allTexts[141]; //" and "
 			else
 				lista += ", ";
 		}
-		switch (message[i])
+		switch(message[i])
 		{
 			case EMessage::APPEND_RAW_STRING:
 				lista += exactStrings[exSt++];
 				break;
 			case EMessage::APPEND_LOCAL_STRING:
-			{
-				std::string hlp = getLocalString (localStrings[loSt++]);
-				lista += hlp;
-			}
+				lista += getLocalString(localStrings[loSt++]);
+				break;
+			case EMessage::APPEND_TEXTID_STRING:
+				lista += VLC->generaltexth->translate(stringsTextID[textID++]);
 				break;
 			case EMessage::APPEND_NUMBER:
 				lista += std::to_string(numbers[nums++]);
 				break;
 			case EMessage::REPLACE_RAW_STRING:
-				lista.replace (lista.find("%s"), 2, exactStrings[exSt++]);
+				lista.replace(lista.find("%s"), 2, exactStrings[exSt++]);
 				break;
 			case EMessage::REPLACE_LOCAL_STRING:
-			{
-				std::string hlp = getLocalString (localStrings[loSt++]);
-				lista.replace (lista.find("%s"), 2, hlp);
-			}
+				lista.replace(lista.find("%s"), 2, getLocalString(localStrings[loSt++]));
+				break;
+			case EMessage::REPLACE_TEXTID_STRING:
+				lista.replace(lista.find("%s"), 2, VLC->generaltexth->translate(stringsTextID[textID++]));
 				break;
 			case EMessage::REPLACE_NUMBER:
-				lista.replace (lista.find("%d"), 2, std::to_string(numbers[nums++]));
+				lista.replace(lista.find("%d"), 2, std::to_string(numbers[nums++]));
 				break;
 			default:
-				logGlobal->error("MetaString processing error! Received message of type %d",int(message[i]));
+				logGlobal->error("MetaString processing error! Received message of type %d", int(message[i]));
 		}
-
 	}
 	return lista;
 }

+ 20 - 4
lib/MetaString.h

@@ -15,6 +15,7 @@ class CreatureID;
 class CStackBasicDescriptor;
 using TQuantity = si32;
 
+/// Strings classes that can be used as replacement in MetaString
 enum class EMetaText : uint8_t
 {
 	GENERAL_TXT = 1,
@@ -37,6 +38,8 @@ enum class EMetaText : uint8_t
 	JK_TXT
 };
 
+/// Class for string formatting tools that also support transfer over network with localization using language of local install
+/// Can be used to compose resulting text from multiple line segments and with placeholder replacement
 class DLL_LINKAGE MetaString
 {
 private:
@@ -44,9 +47,11 @@ private:
 	{
 		APPEND_RAW_STRING,
 		APPEND_LOCAL_STRING,
+		APPEND_TEXTID_STRING,
 		APPEND_NUMBER,
 		REPLACE_RAW_STRING,
 		REPLACE_LOCAL_STRING,
+		REPLACE_TEXTID_STRING,
 		REPLACE_NUMBER,
 		REPLACE_POSITIVE_NUMBER
 	};
@@ -55,22 +60,32 @@ private:
 
 	std::vector<std::pair<EMetaText,ui32> > localStrings;
 	std::vector<std::string> exactStrings;
+	std::vector<std::string> stringsTextID;
 	std::vector<int64_t> numbers;
 
 	std::string getLocalString(const std::pair<EMetaText, ui32> & txt) const;
 
 public:
+	/// Creates MetaString and appends provided raw string to it
+	static MetaString createFromRawString(const std::string & value);
+	/// Creates MetaString and appends provided text ID string to it
+	static MetaString createFromTextID(const std::string & value);
+
 	/// Appends local string to resulting string
 	void appendLocalString(EMetaText type, ui32 serial);
 	/// Appends raw string, without translation to resulting string
-	void appendRawString(std::string value);
+	void appendRawString(const std::string & value);
+	/// Appends text ID that will be translated in output
+	void appendTextID(const std::string & value);
 	/// Appends specified number to resulting string
 	void appendNumber(int64_t value);
 
 	/// Replaces first '%s' placeholder in string with specified local string
 	void replaceLocalString(EMetaText type, ui32 serial);
 	/// Replaces first '%s' placeholder in string with specified fixed, untranslated string
-	void replaceRawString(const std::string &txt);
+	void replaceRawString(const std::string & txt);
+	/// Repalces first '%s' placeholder with string ID that will be translated in output
+	void replaceTextID(const std::string & value);
 	/// Replaces first '%d' placeholder in string with specified number
 	void replaceNumber(int64_t txt);
 	/// Replaces first '%+d' placeholder in string with specified number using '+' sign as prefix
@@ -79,13 +94,13 @@ public:
 	/// Replaces first '%s' placeholder with singular or plural name depending on creatures count
 	void replaceCreatureName(const CreatureID & id, TQuantity count);
 	/// Replaces first '%s' placeholder with singular or plural name depending on creatures count
-	void replaceCreatureName(const CStackBasicDescriptor &stack);
+	void replaceCreatureName(const CStackBasicDescriptor & stack);
 
 	/// erases any existing content in the string
 	void clear();
 
 	///used to handle loot from creature bank
-	std::string buildList () const;
+	std::string buildList() const;
 
 	/// Convert all stored values into a single, user-readable string
 	std::string toString() const;
@@ -97,6 +112,7 @@ public:
 	{
 		h & exactStrings;
 		h & localStrings;
+		h & stringsTextID;
 		h & message;
 		h & numbers;
 	}

+ 8 - 9
lib/mapping/CMap.cpp

@@ -398,17 +398,17 @@ void CMap::checkForObjectives()
 			switch (cond.condition)
 			{
 				case EventCondition::HAVE_ARTIFACT:
-					boost::algorithm::replace_first(event.onFulfill, "%s", VLC->arth->objects[cond.objectType]->getNameTranslated());
+					event.onFulfill.replaceTextID(VLC->artifacts()->getByIndex(cond.objectType)->getNameTextID());
 					break;
 
 				case EventCondition::HAVE_CREATURES:
-					boost::algorithm::replace_first(event.onFulfill, "%s", VLC->creh->objects[cond.objectType]->getNameSingularTranslated());
-					boost::algorithm::replace_first(event.onFulfill, "%d", std::to_string(cond.value));
+					event.onFulfill.replaceTextID(VLC->creatures()->getByIndex(cond.objectType)->getNameSingularTextID());
+					event.onFulfill.replaceNumber(cond.value);
 					break;
 
 				case EventCondition::HAVE_RESOURCES:
-					boost::algorithm::replace_first(event.onFulfill, "%s", VLC->generaltexth->restypes[cond.objectType]);
-					boost::algorithm::replace_first(event.onFulfill, "%d", std::to_string(cond.value));
+					event.onFulfill.replaceLocalString(EMetaText::RES_NAMES, cond.objectType);
+					event.onFulfill.replaceNumber(cond.value);
 					break;
 
 				case EventCondition::HAVE_BUILDING:
@@ -424,10 +424,10 @@ void CMap::checkForObjectives()
 					{
 						const auto * town = dynamic_cast<const CGTownInstance *>(cond.object);
 						if (town)
-							boost::algorithm::replace_first(event.onFulfill, "%s", town->getNameTranslated());
+							event.onFulfill.replaceRawString(town->getNameTranslated());
 						const auto * hero = dynamic_cast<const CGHeroInstance *>(cond.object);
 						if (hero)
-							boost::algorithm::replace_first(event.onFulfill, "%s", hero->getNameTranslated());
+							event.onFulfill.replaceRawString(hero->getNameTranslated());
 					}
 					break;
 
@@ -439,7 +439,7 @@ void CMap::checkForObjectives()
 					{
 						const auto * hero = dynamic_cast<const CGHeroInstance *>(cond.object);
 						if (hero)
-							boost::algorithm::replace_first(event.onFulfill, "%s", hero->getNameTranslated());
+							event.onFulfill.replaceRawString(hero->getNameTranslated());
 					}
 					break;
 				case EventCondition::TRANSPORT:
@@ -499,7 +499,6 @@ void CMap::removeQuestInstance(CQuest * quest)
 }
 
 void CMap::setUniqueInstanceName(CGObjectInstance * obj)
-
 {
 	//this gives object unique name even if objects are removed later
 

+ 6 - 6
lib/mapping/CMapHeader.cpp

@@ -96,29 +96,29 @@ void CMapHeader::setupEvents()
 	//Victory condition - defeat all
 	TriggeredEvent standardVictory;
 	standardVictory.effect.type = EventEffect::VICTORY;
-	standardVictory.effect.toOtherMessage = "core.genrltxt.5";
+	standardVictory.effect.toOtherMessage.appendTextID("core.genrltxt.5");
 	standardVictory.identifier = "standardVictory";
 	standardVictory.description.clear(); // TODO: display in quest window
-	standardVictory.onFulfill = "core.genrltxt.659";
+	standardVictory.onFulfill.appendTextID("core.genrltxt.659");
 	standardVictory.trigger = EventExpression(victoryCondition);
 
 	//Loss condition - 7 days without town
 	TriggeredEvent standardDefeat;
 	standardDefeat.effect.type = EventEffect::DEFEAT;
-	standardDefeat.effect.toOtherMessage = "core.genrltxt.8";
+	standardDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.8");
 	standardDefeat.identifier = "standardDefeat";
 	standardDefeat.description.clear(); // TODO: display in quest window
-	standardDefeat.onFulfill = "core.genrltxt.7";
+	standardDefeat.onFulfill.appendTextID("core.genrltxt.7");
 	standardDefeat.trigger = EventExpression(defeatCondition);
 
 	triggeredEvents.push_back(standardVictory);
 	triggeredEvents.push_back(standardDefeat);
 
 	victoryIconIndex = 11;
-	victoryMessage = VLC->generaltexth->victoryConditions[0];
+	victoryMessage.appendTextID("core.vcdesc.0");
 
 	defeatIconIndex = 3;
-	defeatMessage = VLC->generaltexth->lossCondtions[0];
+	defeatMessage.appendTextID("core.lcdesc.0");
 }
 
 CMapHeader::CMapHeader() : version(EMapFormat::VCMI), height(72), width(72),

+ 6 - 5
lib/mapping/CMapHeader.h

@@ -13,6 +13,7 @@
 #include "../CModVersion.h"
 #include "../LogicalExpression.h"
 #include "../int3.h"
+#include "../MetaString.h"
 
 VCMI_LIB_NAMESPACE_BEGIN
 
@@ -159,7 +160,7 @@ struct DLL_LINKAGE EventEffect
 	si8 type;
 
 	/// message that will be sent to other players
-	std::string toOtherMessage;
+	MetaString toOtherMessage;
 
 	template <typename Handler>
 	void serialize(Handler & h, const int version)
@@ -178,10 +179,10 @@ struct DLL_LINKAGE TriggeredEvent
 	std::string identifier;
 
 	/// string-description, for use in UI (capture town to win)
-	std::string description;
+	MetaString description;
 
 	/// Message that will be displayed when this event is triggered (You captured town. You won!)
-	std::string onFulfill;
+	MetaString onFulfill;
 
 	/// Effect of this event. TODO: refactor into something more flexible
 	EventEffect effect;
@@ -229,8 +230,8 @@ public:
 	///	maximum level for heroes. This is the default value.
 	ui8 levelLimit;
 
-	std::string victoryMessage;
-	std::string defeatMessage;
+	MetaString victoryMessage;
+	MetaString defeatMessage;
 	ui16 victoryIconIndex;
 	ui16 defeatIconIndex;
 

+ 47 - 40
lib/mapping/MapFormatH3M.cpp

@@ -300,6 +300,8 @@ enum class ELossConditionType : uint8_t
 void CMapLoaderH3M::readVictoryLossConditions()
 {
 	mapHeader->triggeredEvents.clear();
+	mapHeader->victoryMessage.clear();
+	mapHeader->defeatMessage.clear();
 
 	auto vicCondition = static_cast<EVictoryConditionType>(reader->readUInt8());
 
@@ -309,18 +311,18 @@ void CMapLoaderH3M::readVictoryLossConditions()
 
 	TriggeredEvent standardVictory;
 	standardVictory.effect.type = EventEffect::VICTORY;
-	standardVictory.effect.toOtherMessage = "core.genrltxt.5";
+	standardVictory.effect.toOtherMessage.appendTextID("core.genrltxt.5");
 	standardVictory.identifier = "standardVictory";
 	standardVictory.description.clear(); // TODO: display in quest window
-	standardVictory.onFulfill = "core.genrltxt.659";
+	standardVictory.onFulfill.appendTextID("core.genrltxt.659");
 	standardVictory.trigger = EventExpression(victoryCondition);
 
 	TriggeredEvent standardDefeat;
 	standardDefeat.effect.type = EventEffect::DEFEAT;
-	standardDefeat.effect.toOtherMessage = "core.genrltxt.8";
+	standardDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.8");
 	standardDefeat.identifier = "standardDefeat";
 	standardDefeat.description.clear(); // TODO: display in quest window
-	standardDefeat.onFulfill = "core.genrltxt.7";
+	standardDefeat.onFulfill.appendTextID("core.genrltxt.7");
 	standardDefeat.trigger = EventExpression(defeatCondition);
 
 	// Specific victory conditions
@@ -329,7 +331,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 		// create normal condition
 		mapHeader->triggeredEvents.push_back(standardVictory);
 		mapHeader->victoryIconIndex = 11;
-		mapHeader->victoryMessage = VLC->generaltexth->victoryConditions[0];
+		mapHeader->victoryMessage.appendTextID("core.vcdesc.0");
 	}
 	else
 	{
@@ -339,7 +341,7 @@ void CMapLoaderH3M::readVictoryLossConditions()
 		specialVictory.description.clear(); // TODO: display in quest window
 
 		mapHeader->victoryIconIndex = static_cast<ui16>(vicCondition);
-		mapHeader->victoryMessage = VLC->generaltexth->victoryConditions[static_cast<size_t>(vicCondition) + 1];
+		mapHeader->victoryMessage.appendTextID(TextIdentifier("core.vcdesc", static_cast<size_t>(vicCondition) + 1).get());
 
 		bool allowNormalVictory = reader->readBool();
 		bool appliesToAI = reader->readBool();
@@ -368,8 +370,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				EventCondition cond(EventCondition::HAVE_ARTIFACT);
 				cond.objectType = reader->readArtifact();
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.281";
-				specialVictory.onFulfill = "core.genrltxt.280";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.281");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.280");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -379,8 +381,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.objectType = reader->readCreature();
 				cond.value = reader->readInt32();
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.277";
-				specialVictory.onFulfill = "core.genrltxt.6";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.277");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.6");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -390,8 +392,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.objectType = reader->readUInt8();
 				cond.value = reader->readInt32();
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.279";
-				specialVictory.onFulfill = "core.genrltxt.278";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.279");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.278");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -405,8 +407,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.objectType = BuildingID::FORT + reader->readUInt8();
 				oper.expressions.emplace_back(cond);
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.283";
-				specialVictory.onFulfill = "core.genrltxt.282";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.283");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.282");
 				specialVictory.trigger = EventExpression(oper);
 				break;
 			}
@@ -418,8 +420,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				if(cond.position.z > 2)
 					cond.position = int3(-1, -1, -1);
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.285";
-				specialVictory.onFulfill = "core.genrltxt.284";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.285");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.284");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -429,8 +431,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.objectType = Obj::HERO;
 				cond.position = reader->readInt3();
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.253";
-				specialVictory.onFulfill = "core.genrltxt.252";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.253");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.252");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -440,8 +442,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.objectType = Obj::TOWN;
 				cond.position = reader->readInt3();
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.250";
-				specialVictory.onFulfill = "core.genrltxt.249";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.250");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.249");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -451,8 +453,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.objectType = Obj::MONSTER;
 				cond.position = reader->readInt3();
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.287";
-				specialVictory.onFulfill = "core.genrltxt.286";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.287");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.286");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -462,8 +464,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR1));
 				oper.expressions.emplace_back(EventCondition(EventCondition::CONTROL, 0, Obj::CREATURE_GENERATOR4));
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.289";
-				specialVictory.onFulfill = "core.genrltxt.288";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.289");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.288");
 				specialVictory.trigger = EventExpression(oper);
 				break;
 			}
@@ -472,8 +474,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				EventCondition cond(EventCondition::CONTROL);
 				cond.objectType = Obj::MINE;
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.291";
-				specialVictory.onFulfill = "core.genrltxt.290";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.291");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.290");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -483,8 +485,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.objectType = reader->readUInt8();
 				cond.position = reader->readInt3();
 
-				specialVictory.effect.toOtherMessage = "core.genrltxt.293";
-				specialVictory.onFulfill = "core.genrltxt.292";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.293");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.292");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -493,8 +495,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				EventCondition cond(EventCondition::DESTROY);
 				cond.objectType = Obj::MONSTER;
 
-				specialVictory.effect.toOtherMessage = "Defeat all monsters"; // Eliminate all monsters on the map
-				specialVictory.onFulfill = "You have defeated all of the monsters plaguing this land!";
+				specialVictory.effect.toOtherMessage.appendRawString("Defeat all monsters"); // Eliminate all monsters on the map
+				specialVictory.onFulfill.appendRawString("You have defeated all of the monsters plaguing this land!");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -503,8 +505,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				EventCondition cond(EventCondition::DAYS_PASSED);
 				cond.value = reader->readUInt32();
 
-				specialVictory.effect.toOtherMessage = "Survive beyond a time limit"; // Survive by month %d, week %d, day %d.
-				specialVictory.onFulfill = "You have managed to survive!";
+				specialVictory.effect.toOtherMessage.appendRawString("Survive beyond a time limit"); // Survive by month %d, week %d, day %d.
+				specialVictory.onFulfill.appendRawString("You have managed to survive!");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -526,8 +528,8 @@ void CMapLoaderH3M::readVictoryLossConditions()
 		// if normal victory allowed - add one more quest
 		if(allowNormalVictory)
 		{
-			mapHeader->victoryMessage += " / ";
-			mapHeader->victoryMessage += VLC->generaltexth->victoryConditions[0];
+			mapHeader->victoryMessage.appendRawString(" / ");
+			mapHeader->victoryMessage.appendTextID("core.vcdesc.0");
 			mapHeader->triggeredEvents.push_back(standardVictory);
 		}
 		mapHeader->triggeredEvents.push_back(specialVictory);
@@ -538,18 +540,17 @@ void CMapLoaderH3M::readVictoryLossConditions()
 	if(lossCond == ELossConditionType::LOSSSTANDARD)
 	{
 		mapHeader->defeatIconIndex = 3;
-		mapHeader->defeatMessage = VLC->generaltexth->lossCondtions[0];
+		mapHeader->defeatMessage.appendTextID("core.lcdesc.0");
 	}
 	else
 	{
 		TriggeredEvent specialDefeat;
 		specialDefeat.effect.type = EventEffect::DEFEAT;
-		specialDefeat.effect.toOtherMessage = "core.genrltxt.5";
+		specialDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.5");
 		specialDefeat.identifier = "specialDefeat";
 		specialDefeat.description.clear(); // TODO: display in quest window
 
 		mapHeader->defeatIconIndex = static_cast<ui16>(lossCond);
-		mapHeader->defeatMessage = VLC->generaltexth->lossCondtions[static_cast<size_t>(lossCond) + 1];
 
 		switch(lossCond)
 		{
@@ -561,8 +562,10 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.position = reader->readInt3();
 
 				noneOf.expressions.emplace_back(cond);
-				specialDefeat.onFulfill = "core.genrltxt.251";
+				specialDefeat.onFulfill.appendTextID("core.genrltxt.251");
 				specialDefeat.trigger = EventExpression(noneOf);
+
+				mapHeader->defeatMessage.appendTextID("core.lcdesc.1");
 				break;
 			}
 			case ELossConditionType::LOSSHERO:
@@ -573,8 +576,10 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				cond.position = reader->readInt3();
 
 				noneOf.expressions.emplace_back(cond);
-				specialDefeat.onFulfill = "core.genrltxt.253";
+				specialDefeat.onFulfill.appendTextID("core.genrltxt.253");
 				specialDefeat.trigger = EventExpression(noneOf);
+
+				mapHeader->defeatMessage.appendTextID("core.lcdesc.2");
 				break;
 			}
 			case ELossConditionType::TIMEEXPIRES:
@@ -582,8 +587,10 @@ void CMapLoaderH3M::readVictoryLossConditions()
 				EventCondition cond(EventCondition::DAYS_PASSED);
 				cond.value = reader->readUInt16();
 
-				specialDefeat.onFulfill = "core.genrltxt.254";
+				specialDefeat.onFulfill.appendTextID("core.genrltxt.254");
 				specialDefeat.trigger = EventExpression(cond);
+
+				mapHeader->defeatMessage.appendTextID("core.lcdesc.3");
 				break;
 			}
 		}

+ 11 - 11
lib/mapping/MapFormatJson.cpp

@@ -423,10 +423,10 @@ void CMapFormatJson::serializeHeader(JsonSerializeFormat & handler)
 
 	handler.serializeLIC("allowedHeroes", &HeroTypeID::decode, &HeroTypeID::encode, VLC->heroh->getDefaultAllowed(), mapHeader->allowedHeroes);
 
-	handler.serializeString("victoryString", mapHeader->victoryMessage);
+//	handler.serializeString("victoryString", mapHeader->victoryMessage);
 	handler.serializeInt("victoryIconIndex", mapHeader->victoryIconIndex);
 
-	handler.serializeString("defeatString", mapHeader->defeatMessage);
+//	handler.serializeString("defeatString", mapHeader->defeatMessage);
 	handler.serializeInt("defeatIconIndex", mapHeader->defeatIconIndex);
 }
 
@@ -683,10 +683,10 @@ void CMapFormatJson::readTriggeredEvent(TriggeredEvent & event, const JsonNode &
 {
 	using namespace TriggeredEventsDetail;
 
-	event.onFulfill = source["message"].String();
-	event.description = source["description"].String();
+//	event.onFulfill = source["message"].String();
+//	event.description = source["description"].String();
 	event.effect.type = vstd::find_pos(typeNames, source["effect"]["type"].String());
-	event.effect.toOtherMessage = source["effect"]["messageToSend"].String();
+//	event.effect.toOtherMessage = source["effect"]["messageToSend"].String();
 	event.trigger = EventExpression(source["condition"], JsonToCondition); // logical expression
 }
 
@@ -704,16 +704,16 @@ void CMapFormatJson::writeTriggeredEvent(const TriggeredEvent & event, JsonNode
 {
 	using namespace TriggeredEventsDetail;
 
-	if(!event.onFulfill.empty())
-		dest["message"].String() = event.onFulfill;
+//	if(!event.onFulfill.empty())
+//		dest["message"].String() = event.onFulfill;
 
-	if(!event.description.empty())
-		dest["description"].String() = event.description;
+//	if(!event.description.empty())
+//		dest["description"].String() = event.description;
 
 	dest["effect"]["type"].String() = typeNames.at(static_cast<size_t>(event.effect.type));
 
-	if(!event.effect.toOtherMessage.empty())
-		dest["effect"]["messageToSend"].String() = event.effect.toOtherMessage;
+//	if(!event.effect.toOtherMessage.empty())
+//		dest["effect"]["messageToSend"].String() = event.effect.toOtherMessage;
 
 	dest["condition"] = event.trigger.toJson(ConditionToJson);
 }

+ 34 - 32
mapeditor/mapsettings.cpp

@@ -166,8 +166,8 @@ MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
 	};
 	
 	//victory & loss messages
-	ui->victoryMessageEdit->setText(QString::fromStdString(controller.map()->victoryMessage));
-	ui->defeatMessageEdit->setText(QString::fromStdString(controller.map()->defeatMessage));
+	ui->victoryMessageEdit->setText(QString::fromStdString(controller.map()->victoryMessage.toString()));
+	ui->defeatMessageEdit->setText(QString::fromStdString(controller.map()->defeatMessage.toString()));
 	
 	//victory & loss conditions
 	const std::array<std::string, 8> conditionStringsWin = {
@@ -550,8 +550,8 @@ void MapSettings::on_pushButton_clicked()
 	
 	//victory & loss messages
 	
-	controller.map()->victoryMessage = ui->victoryMessageEdit->text().toStdString();
-	controller.map()->defeatMessage = ui->defeatMessageEdit->text().toStdString();
+	controller.map()->victoryMessage = MetaString::createFromRawString(ui->victoryMessageEdit->text().toStdString());
+	controller.map()->defeatMessage = MetaString::createFromRawString(ui->defeatMessageEdit->text().toStdString());
 	
 	//victory & loss conditions
 	EventCondition victoryCondition(EventCondition::STANDARD_WIN);
@@ -561,19 +561,19 @@ void MapSettings::on_pushButton_clicked()
 	//Victory condition - defeat all
 	TriggeredEvent standardVictory;
 	standardVictory.effect.type = EventEffect::VICTORY;
-	standardVictory.effect.toOtherMessage = "core.genrltxt.5";
+	standardVictory.effect.toOtherMessage.appendTextID("core.genrltxt.5");
 	standardVictory.identifier = "standardVictory";
 	standardVictory.description.clear(); // TODO: display in quest window
-	standardVictory.onFulfill = "core.genrltxt.659";
+	standardVictory.onFulfill.appendTextID("core.genrltxt.659");
 	standardVictory.trigger = EventExpression(victoryCondition);
 
 	//Loss condition - 7 days without town
 	TriggeredEvent standardDefeat;
 	standardDefeat.effect.type = EventEffect::DEFEAT;
-	standardDefeat.effect.toOtherMessage = "core.genrltxt.8";
+	standardDefeat.effect.toOtherMessage.appendTextID("core.genrltxt.8");
 	standardDefeat.identifier = "standardDefeat";
 	standardDefeat.description.clear(); // TODO: display in quest window
-	standardDefeat.onFulfill = "core.genrltxt.7";
+	standardDefeat.onFulfill.appendTextID("core.genrltxt.7");
 	standardDefeat.trigger = EventExpression(defeatCondition);
 	
 	controller.map()->triggeredEvents.clear();
@@ -583,7 +583,7 @@ void MapSettings::on_pushButton_clicked()
 	{
 		controller.map()->triggeredEvents.push_back(standardVictory);
 		controller.map()->victoryIconIndex = 11;
-		controller.map()->victoryMessage = VLC->generaltexth->victoryConditions[0];
+		controller.map()->victoryMessage.appendTextID(VLC->generaltexth->victoryConditions[0]);
 	}
 	else
 	{
@@ -595,7 +595,7 @@ void MapSettings::on_pushButton_clicked()
 		specialVictory.description.clear(); // TODO: display in quest window
 		
 		controller.map()->victoryIconIndex = vicCondition;
-		controller.map()->victoryMessage = VLC->generaltexth->victoryConditions[size_t(vicCondition) + 1];
+		controller.map()->victoryMessage.appendTextID(VLC->generaltexth->victoryConditions[size_t(vicCondition) + 1]);
 		
 		switch(vicCondition)
 		{
@@ -603,8 +603,8 @@ void MapSettings::on_pushButton_clicked()
 				EventCondition cond(EventCondition::HAVE_ARTIFACT);
 				assert(victoryTypeWidget);
 				cond.objectType = victoryTypeWidget->currentData().toInt();
-				specialVictory.effect.toOtherMessage = "core.genrltxt.281";
-				specialVictory.onFulfill = "core.genrltxt.280";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.281");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.280");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -614,8 +614,8 @@ void MapSettings::on_pushButton_clicked()
 				assert(victoryTypeWidget);
 				cond.objectType = victoryTypeWidget->currentData().toInt();
 				cond.value = victoryValueWidget->text().toInt();
-				specialVictory.effect.toOtherMessage = "core.genrltxt.277";
-				specialVictory.onFulfill = "core.genrltxt.276";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.277");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.276");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -625,8 +625,8 @@ void MapSettings::on_pushButton_clicked()
 				assert(victoryTypeWidget);
 				cond.objectType = victoryTypeWidget->currentData().toInt();
 				cond.value = victoryValueWidget->text().toInt();
-				specialVictory.effect.toOtherMessage = "core.genrltxt.279";
-				specialVictory.onFulfill = "core.genrltxt.278";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.279");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.278");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -638,8 +638,8 @@ void MapSettings::on_pushButton_clicked()
 				int townIdx = victorySelectWidget->currentData().toInt();
 				if(townIdx > -1)
 					cond.position = controller.map()->objects[townIdx]->pos;
-				specialVictory.effect.toOtherMessage = "core.genrltxt.283";
-				specialVictory.onFulfill = "core.genrltxt.282";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.283");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.282");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -650,8 +650,8 @@ void MapSettings::on_pushButton_clicked()
 				cond.objectType = Obj::TOWN;
 				int townIdx = victoryTypeWidget->currentData().toInt();
 				cond.position = controller.map()->objects[townIdx]->pos;
-				specialVictory.effect.toOtherMessage = "core.genrltxt.250";
-				specialVictory.onFulfill = "core.genrltxt.249";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.250");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.249");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -662,8 +662,8 @@ void MapSettings::on_pushButton_clicked()
 				cond.objectType = Obj::HERO;
 				int heroIdx = victoryTypeWidget->currentData().toInt();
 				cond.position = controller.map()->objects[heroIdx]->pos;
-				specialVictory.effect.toOtherMessage = "core.genrltxt.253";
-				specialVictory.onFulfill = "core.genrltxt.252";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.253");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.252");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -675,8 +675,8 @@ void MapSettings::on_pushButton_clicked()
 				int townIdx = victorySelectWidget->currentData().toInt();
 				if(townIdx > -1)
 					cond.position = controller.map()->objects[townIdx]->pos;
-				specialVictory.effect.toOtherMessage = "core.genrltxt.293";
-				specialVictory.onFulfill = "core.genrltxt.292";
+				specialVictory.effect.toOtherMessage.appendTextID("core.genrltxt.293");
+				specialVictory.onFulfill.appendTextID("core.genrltxt.292");
 				specialVictory.trigger = EventExpression(cond);
 				break;
 			}
@@ -697,8 +697,8 @@ void MapSettings::on_pushButton_clicked()
 		// if normal victory allowed - add one more quest
 		if(ui->standardVictoryCheck->isChecked())
 		{
-			controller.map()->victoryMessage += " / ";
-			controller.map()->victoryMessage += VLC->generaltexth->victoryConditions[0];
+			controller.map()->victoryMessage.appendRawString(" / ");
+			controller.map()->victoryMessage.appendTextID(VLC->generaltexth->victoryConditions[0]);
 			controller.map()->triggeredEvents.push_back(standardVictory);
 		}
 		controller.map()->triggeredEvents.push_back(specialVictory);
@@ -709,7 +709,7 @@ void MapSettings::on_pushButton_clicked()
 	{
 		controller.map()->triggeredEvents.push_back(standardDefeat);
 		controller.map()->defeatIconIndex = 3;
-		controller.map()->defeatMessage = VLC->generaltexth->lossCondtions[0];
+		controller.map()->defeatMessage.appendTextID("core.lcdesc.0");
 	}
 	else
 	{
@@ -721,7 +721,6 @@ void MapSettings::on_pushButton_clicked()
 		specialDefeat.description.clear(); // TODO: display in quest window
 		
 		controller.map()->defeatIconIndex = lossCondition;
-		controller.map()->defeatMessage = VLC->generaltexth->lossCondtions[size_t(lossCondition) + 1];
 		
 		switch(lossCondition)
 		{
@@ -733,8 +732,9 @@ void MapSettings::on_pushButton_clicked()
 				int townIdx = loseTypeWidget->currentData().toInt();
 				cond.position = controller.map()->objects[townIdx]->pos;
 				noneOf.expressions.push_back(cond);
-				specialDefeat.onFulfill = "core.genrltxt.251";
+				specialDefeat.onFulfill.appendTextID("core.genrltxt.251");
 				specialDefeat.trigger = EventExpression(noneOf);
+				controller.map()->defeatMessage.appendTextID("core.lcdesc.1");
 				break;
 			}
 				
@@ -746,8 +746,9 @@ void MapSettings::on_pushButton_clicked()
 				int townIdx = loseTypeWidget->currentData().toInt();
 				cond.position = controller.map()->objects[townIdx]->pos;
 				noneOf.expressions.push_back(cond);
-				specialDefeat.onFulfill = "core.genrltxt.253";
+				specialDefeat.onFulfill.appendTextID("core.genrltxt.253");
 				specialDefeat.trigger = EventExpression(noneOf);
+				controller.map()->defeatMessage.appendTextID("core.lcdesc.2");
 				break;
 			}
 				
@@ -755,8 +756,9 @@ void MapSettings::on_pushButton_clicked()
 				EventCondition cond(EventCondition::DAYS_PASSED);
 				assert(loseValueWidget);
 				cond.value = expiredDate(loseValueWidget->text());
-				specialDefeat.onFulfill = "core.genrltxt.254";
+				specialDefeat.onFulfill.appendTextID("core.genrltxt.254");
 				specialDefeat.trigger = EventExpression(cond);
+				controller.map()->defeatMessage.appendTextID("core.lcdesc.3");
 				break;
 			}
 				
@@ -764,7 +766,7 @@ void MapSettings::on_pushButton_clicked()
 				EventCondition cond(EventCondition::DAYS_WITHOUT_TOWN);
 				assert(loseValueWidget);
 				cond.value = loseValueWidget->text().toInt();
-				specialDefeat.onFulfill = "core.genrltxt.7";
+				specialDefeat.onFulfill.appendTextID("core.genrltxt.7");
 				specialDefeat.trigger = EventExpression(cond);
 				break;
 			}

+ 2 - 6
server/CGameHandler.cpp

@@ -5790,12 +5790,8 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
 void CGameHandler::getVictoryLossMessage(PlayerColor player, const EVictoryLossCheckResult & victoryLossCheckResult, InfoWindow & out) const
 {
 	out.player = player;
-	out.text.clear();
-	out.text.appendRawString(VLC->generaltexth->translate(victoryLossCheckResult.messageToOthers));
-	// hackish, insert one player-specific string, if applicable
-	if (victoryLossCheckResult.messageToOthers.find("%s") != std::string::npos)
-		out.text.replaceLocalString(EMetaText::COLOR, player.getNum());
-
+	out.text = victoryLossCheckResult.messageToOthers;
+	out.text.replaceLocalString(EMetaText::COLOR, player.getNum());
 	out.components.emplace_back(Component::EComponentType::FLAG, player.getNum(), 0, 0);
 }