2
0
Эх сурвалжийг харах

MetaString refactor to eliminate integer usage for identifiers

- entity names are now stored and serialized as text ID's
- added helper methods for convenience to get entities names to
metastring
Ivan Savenko 1 жил өмнө
parent
commit
86a3806bec

+ 2 - 2
client/CPlayerInterface.cpp

@@ -337,13 +337,13 @@ void CPlayerInterface::acceptTurn(QueryID queryID)
 			if (daysWithoutCastle < 6)
 			{
 				text.appendLocalString(EMetaText::ARRAY_TXT,128); //%s, you only have %d days left to capture a town or you will be banished from this land.
-				text.replaceLocalString(EMetaText::COLOR, playerColor.getNum());
+				text.replaceName(playerColor);
 				text.replaceNumber(7 - daysWithoutCastle);
 			}
 			else if (daysWithoutCastle == 6)
 			{
 				text.appendLocalString(EMetaText::ARRAY_TXT,129); //%s, this is your last day to capture a town or you will be banished from this land.
-				text.replaceLocalString(EMetaText::COLOR, playerColor.getNum());
+				text.replaceName(playerColor);
 			}
 
 			showInfoDialogAndWait(components, text);

+ 5 - 5
client/lobby/CBonusSelection.cpp

@@ -151,13 +151,13 @@ void CBonusSelection::createBonusesIcons()
 		{
 		case CampaignBonusType::SPELL:
 			desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
-			desc.replaceLocalString(EMetaText::SPELL_NAME, bonDescs[i].info2);
+			desc.replaceName(SpellID(bonDescs[i].info2));
 			break;
 		case CampaignBonusType::MONSTER:
 			picNumber = bonDescs[i].info2 + 2;
 			desc.appendLocalString(EMetaText::GENERAL_TXT, 717);
 			desc.replaceNumber(bonDescs[i].info3);
-			desc.replaceLocalString(EMetaText::CRE_PL_NAMES, bonDescs[i].info2);
+			desc.replaceNamePlural(bonDescs[i].info2);
 			break;
 		case CampaignBonusType::BUILDING:
 		{
@@ -187,11 +187,11 @@ void CBonusSelection::createBonusesIcons()
 		}
 		case CampaignBonusType::ARTIFACT:
 			desc.appendLocalString(EMetaText::GENERAL_TXT, 715);
-			desc.replaceLocalString(EMetaText::ART_NAMES, bonDescs[i].info2);
+			desc.replaceName(ArtifactID(bonDescs[i].info2));
 			break;
 		case CampaignBonusType::SPELL_SCROLL:
 			desc.appendLocalString(EMetaText::GENERAL_TXT, 716);
-			desc.replaceLocalString(EMetaText::ART_NAMES, bonDescs[i].info2);
+			desc.replaceName(ArtifactID(bonDescs[i].info2));
 			break;
 		case CampaignBonusType::PRIMARY_SKILL:
 		{
@@ -229,7 +229,7 @@ void CBonusSelection::createBonusesIcons()
 		case CampaignBonusType::SECONDARY_SKILL:
 			desc.appendLocalString(EMetaText::GENERAL_TXT, 718);
 			desc.replaceTextID(TextIdentifier("core", "genrltxt", "levels", bonDescs[i].info3 - 1).get());
-			desc.replaceLocalString(EMetaText::SEC_SKILL_NAME, bonDescs[i].info2);
+			desc.replaceName(SecondarySkill(bonDescs[i].info2));
 			picNumber = bonDescs[i].info2 * 3 + bonDescs[i].info3 - 1;
 
 			break;

+ 91 - 74
lib/MetaString.cpp

@@ -112,74 +112,29 @@ std::string MetaString::getLocalString(const std::pair<EMetaText, ui32> & txt) c
 
 	switch(type)
 	{
-		case EMetaText::ART_NAMES:
-		{
-			const auto * art = ArtifactID(ser).toEntity(VLC);
-			if(art)
-				return art->getNameTranslated();
-			return "#!#";
-		}
-		case EMetaText::ART_DESCR:
-		{
-			const auto * art = ArtifactID(ser).toEntity(VLC);
-			if(art)
-				return art->getDescriptionTranslated();
-			return "#!#";
-		}
-		case EMetaText::ART_EVNTS:
-		{
-			const auto * art = ArtifactID(ser).toEntity(VLC);
-			if(art)
-				return art->getEventTranslated();
-			return "#!#";
-		}
-		case EMetaText::CRE_PL_NAMES:
-		{
-			const auto * cre = CreatureID(ser).toEntity(VLC);
-			if(cre)
-				return cre->getNamePluralTranslated();
-			return "#!#";
-		}
-		case EMetaText::CRE_SING_NAMES:
-		{
-			const auto * cre = CreatureID(ser).toEntity(VLC);
-			if(cre)
-				return cre->getNameSingularTranslated();
-			return "#!#";
-		}
-		case EMetaText::MINE_NAMES:
-		{
-			return VLC->generaltexth->translate("core.minename", ser);
-		}
-		case EMetaText::MINE_EVNTS:
-		{
-			return VLC->generaltexth->translate("core.mineevnt", ser);
-		}
-		case EMetaText::SPELL_NAME:
-		{
-			const auto * spell = SpellID(ser).toEntity(VLC);
-			if(spell)
-				return spell->getNameTranslated();
-			return "#!#";
-		}
-		case EMetaText::OBJ_NAMES:
-			return VLC->objtypeh->getObjectName(ser, 0);
-		case EMetaText::SEC_SKILL_NAME:
-			return VLC->skillh->getByIndex(ser)->getNameTranslated();
+//		case EMetaText::ART_DESCR:
+//		{
+//			const auto * art = ArtifactID(ser).toArtifact(VLC->artifacts());
+//			if(art)
+//				return art->getDescriptionTranslated();
+//			return "#!#";
+//		}
+//		case EMetaText::MINE_NAMES:
+//		{
+//			return VLC->generaltexth->translate("core.minename", ser);
+//		}
+//		case EMetaText::MINE_EVNTS:
+//		{
+//			return VLC->generaltexth->translate(, ser);
+//		}
 		case EMetaText::GENERAL_TXT:
 			return VLC->generaltexth->translate("core.genrltxt", ser);
 		case EMetaText::RES_NAMES:
 			return VLC->generaltexth->translate("core.restypes", ser);
 		case EMetaText::ARRAY_TXT:
 			return VLC->generaltexth->translate("core.arraytxt", ser);
-		case EMetaText::CREGENS:
-			return VLC->objtypeh->getObjectName(Obj::CREATURE_GENERATOR1, ser);
-		case EMetaText::CREGENS4:
-			return VLC->objtypeh->getObjectName(Obj::CREATURE_GENERATOR4, ser);
 		case EMetaText::ADVOB_TXT:
 			return VLC->generaltexth->translate("core.advevent", ser);
-		case EMetaText::COLOR:
-			return VLC->generaltexth->translate("vcmi.capitalColors", ser);
 		case EMetaText::JK_TXT:
 			return VLC->generaltexth->translate("core.jktext", ser);
 		default:
@@ -287,20 +242,6 @@ DLL_LINKAGE std::string MetaString::buildList() const
 	return lista;
 }
 
-void MetaString::replaceCreatureName(const CreatureID & id, TQuantity count) //adds sing or plural name;
-{
-	if (count == 1)
-		replaceLocalString (EMetaText::CRE_SING_NAMES, id);
-	else
-		replaceLocalString (EMetaText::CRE_PL_NAMES, id);
-}
-
-void MetaString::replaceCreatureName(const CStackBasicDescriptor & stack)
-{
-	assert(stack.type); //valid type
-	replaceCreatureName(stack.type->getId(), stack.count);
-}
-
 bool MetaString::operator == (const MetaString & other) const
 {
 	return message == other.message && localStrings == other.localStrings && exactStrings == other.exactStrings && stringsTextID == other.stringsTextID && numbers == other.numbers;
@@ -395,4 +336,80 @@ void MetaString::serializeJson(JsonSerializeFormat & handler)
 		jsonDeserialize(handler.getCurrent());
 }
 
+void MetaString::appendName(const SpellID & id)
+{
+	appendTextID(id.toSpell(VLC->spells())->getNameTextID());
+}
+
+void MetaString::appendName(const PlayerColor & id)
+{
+	appendTextID(TextIdentifier("vcmi.capitalColors", id.getNum()).get());
+}
+
+void MetaString::appendName(const CreatureID & id, TQuantity count)
+{
+	if(count == 1)
+		appendNameSingular(id);
+	else
+		appendNamePlural(id);
+}
+
+void MetaString::appendNameSingular(const CreatureID & id)
+{
+	appendTextID(id.toCreature(VLC->creatures())->getNameSingularTextID());
+}
+
+void MetaString::appendNamePlural(const CreatureID & id)
+{
+	appendTextID(id.toCreature(VLC->creatures())->getNamePluralTextID());
+}
+
+void MetaString::replaceName(const ArtifactID & id)
+{
+	replaceTextID(id.toArtifact(VLC->artifacts())->getNameTextID());
+}
+
+void MetaString::replaceName(const MapObjectID& id)
+{
+	replaceTextID(VLC->objtypeh->getObjectName(id, 0));
+}
+
+void MetaString::replaceName(const PlayerColor & id)
+{
+	replaceTextID(TextIdentifier("vcmi.capitalColors", id.getNum()).get());
+}
+
+void MetaString::replaceName(const SecondarySkill & id)
+{
+	replaceTextID(VLC->skillh->getById(id)->getNameTextID());
+}
+
+void MetaString::replaceName(const SpellID & id)
+{
+	replaceTextID(id.toSpell(VLC->spells())->getNameTextID());
+}
+
+void MetaString::replaceNameSingular(const CreatureID & id)
+{
+	replaceTextID(id.toCreature(VLC->creatures())->getNameSingularTextID());
+}
+
+void MetaString::replaceNamePlural(const CreatureID & id)
+{
+	replaceTextID(id.toCreature(VLC->creatures())->getNamePluralTextID());
+}
+
+void MetaString::replaceName(const CreatureID & id, TQuantity count) //adds sing or plural name;
+{
+	if(count == 1)
+		replaceNameSingular(id);
+	else
+		replaceNamePlural(id);
+}
+
+void MetaString::replaceName(const CStackBasicDescriptor & stack)
+{
+	replaceName(stack.type->getId(), stack.count);
+}
+
 VCMI_LIB_NAMESPACE_END

+ 22 - 15
lib/MetaString.h

@@ -12,31 +12,24 @@
 VCMI_LIB_NAMESPACE_BEGIN
 
 class JsonNode;
+class ArtifactID;
 class CreatureID;
 class CStackBasicDescriptor;
 class JsonSerializeFormat;
+class MapObjectID;
+class MapObjectSubID;
+class PlayerColor;
+class SecondarySkill;
+class SpellID;
 using TQuantity = si32;
 
 /// Strings classes that can be used as replacement in MetaString
 enum class EMetaText : uint8_t
 {
 	GENERAL_TXT = 1,
-	OBJ_NAMES,
 	RES_NAMES,
-	ART_NAMES,
 	ARRAY_TXT,
-	CRE_PL_NAMES,
-	CREGENS,
-	MINE_NAMES,
-	MINE_EVNTS,
 	ADVOB_TXT,
-	ART_EVNTS,
-	SPELL_NAME,
-	SEC_SKILL_NAME,
-	CRE_SING_NAMES,
-	CREGENS4,
-	COLOR,
-	ART_DESCR,
 	JK_TXT
 };
 
@@ -82,6 +75,12 @@ public:
 	/// Appends specified number to resulting string
 	void appendNumber(int64_t value);
 
+	void appendName(const SpellID& id);
+	void appendName(const PlayerColor& id);
+	void appendName(const CreatureID & id, TQuantity count);
+	void appendNameSingular(const CreatureID & id);
+	void appendNamePlural(const CreatureID & id);
+
 	/// 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
@@ -93,10 +92,18 @@ public:
 	/// Replaces first '%+d' placeholder in string with specified number using '+' sign as prefix
 	void replacePositiveNumber(int64_t txt);
 
+	void replaceName(const ArtifactID & id);
+	void replaceName(const MapObjectID& id);
+	void replaceName(const PlayerColor& id);
+	void replaceName(const SecondarySkill& id);
+	void replaceName(const SpellID& id);
+
 	/// Replaces first '%s' placeholder with singular or plural name depending on creatures count
-	void replaceCreatureName(const CreatureID & id, TQuantity count);
+	void replaceName(const CreatureID & id, TQuantity count);
+	void replaceNameSingular(const CreatureID & id);
+	void replaceNamePlural(const CreatureID & id);
 	/// Replaces first '%s' placeholder with singular or plural name depending on creatures count
-	void replaceCreatureName(const CStackBasicDescriptor & stack);
+	void replaceName(const CStackBasicDescriptor & stack);
 
 	/// erases any existing content in the string
 	void clear();

+ 1 - 1
lib/battle/CUnitState.cpp

@@ -485,7 +485,7 @@ void CUnitState::getCastDescription(const spells::Spell * spell, const std::vect
 	text.appendLocalString(EMetaText::GENERAL_TXT, 565);//The %s casts %s
 	//todo: use text 566 for single creature
 	getCasterName(text);
-	text.replaceLocalString(EMetaText::SPELL_NAME, spell->getIndex());
+	text.replaceName(spell->getId());
 }
 
 int32_t CUnitState::manaLimit() const

+ 4 - 4
lib/battle/Unit.cpp

@@ -187,11 +187,11 @@ void Unit::addText(MetaString & text, EMetaText type, int32_t serial, const boos
 void Unit::addNameReplacement(MetaString & text, const boost::logic::tribool & plural) const
 {
 	if(boost::logic::indeterminate(plural))
-		text.replaceCreatureName(creatureId(), getCount());
+		text.replaceName(creatureId(), getCount());
 	else if(plural)
-		text.replaceLocalString(EMetaText::CRE_PL_NAMES, creatureIndex());
+		text.replaceName(creatureIndex(), 2);
 	else
-		text.replaceLocalString(EMetaText::CRE_SING_NAMES, creatureIndex());
+		text.replaceName(creatureIndex(), 1);
 }
 
 std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
@@ -200,7 +200,7 @@ std::string Unit::formatGeneralMessage(const int32_t baseTextId) const
 
 	MetaString text;
 	text.appendLocalString(EMetaText::GENERAL_TXT, textId);
-	text.replaceCreatureName(creatureId(), getCount());
+	text.replaceName(creatureId(), getCount());
 
 	return text.toString();
 }

+ 1 - 0
lib/constants/EntityIdentifiers.h

@@ -704,6 +704,7 @@ public:
 		NONE = -1,
 		ARCHER = 2, // for debug / fallback
 		IMP = 42, // for Deity of Fire
+		FAMILIAR = 43, // for Deity of Fire
 		SKELETON = 56, // for Skeleton Transformer
 		BONE_DRAGON = 68, // for Skeleton Transformer
 		TROGLODYTES = 70, // for Abandoned Mine

+ 4 - 4
lib/mapObjects/CBank.cpp

@@ -279,7 +279,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 		{
 			iw.components.emplace_back(ComponentType::ARTIFACT, elem);
 			loot.appendRawString("%s");
-			loot.replaceLocalString(EMetaText::ART_NAMES, elem);
+			loot.replaceName(elem);
 			cb->giveHeroNewArtifact(hero, elem.toArtifact(), ArtifactPosition::FIRST_AVAILABLE);
 		}
 		//display loot
@@ -293,7 +293,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 					return a.type->getFightValue() < b.type->getFightValue();
 				})->type;
 
-				iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, strongest->getId());
+				iw.text.replaceNamePlural(strongest->getId());
 				iw.text.replaceRawString(loot.buildList());
 			}
 			cb->showInfoDialog(&iw);
@@ -315,7 +315,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 			for(const SpellID & spellId : bc->spells)
 			{
 				const auto * spell = spellId.toEntity(VLC);
-				iw.text.appendLocalString(EMetaText::SPELL_NAME, spellId);
+				iw.text.appendName(spellId);
 				if(spell->getLevel() <= hero->maxSpellLevel())
 				{
 					if(hero->canLearnSpell(spell))
@@ -354,7 +354,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 		{
 			iw.components.emplace_back(ComponentType::CREATURE, elem.second->getId(), elem.second->getCount());
 			loot.appendRawString("%s");
-			loot.replaceCreatureName(*elem.second);
+			loot.replaceName(*elem.second);
 		}
 
 		if(ourArmy.stacksCount())

+ 4 - 4
lib/mapObjects/CGCreature.cpp

@@ -40,7 +40,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const
 	else
 		ms.appendLocalString(EMetaText::ARRAY_TXT, quantityTextIndex);
 	ms.appendRawString(" ");
-	ms.appendLocalString(EMetaText::CRE_PL_NAMES, getCreature());
+	ms.appendNamePlural(getCreature());
 
 	return ms.toString();
 }
@@ -52,7 +52,7 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
 		MetaString ms;
 		ms.appendNumber(stacks.begin()->second->count);
 		ms.appendRawString(" ");
-		ms.appendLocalString(EMetaText::CRE_PL_NAMES, getCreature());
+		ms.appendName(getCreature(), stacks.begin()->second->count);
 		return ms.toString();
 	}
 	else
@@ -159,7 +159,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 			BlockingDialog ynd(true,false);
 			ynd.player = h->tempOwner;
 			ynd.text.appendLocalString(EMetaText::ADVOB_TXT, 86);
-			ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, getCreature());
+			ynd.text.replaceName(getCreature(), getStackCount(SlotID(0)));
 			cb->showBlockingDialog(&ynd);
 			break;
 		}
@@ -468,7 +468,7 @@ void CGCreature::flee( const CGHeroInstance * h ) const
 	BlockingDialog ynd(true,false);
 	ynd.player = h->tempOwner;
 	ynd.text.appendLocalString(EMetaText::ADVOB_TXT,91);
-	ynd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, getCreature());
+	ynd.text.replaceName(getCreature(), getStackCount(SlotID(0)));
 	cb->showBlockingDialog(&ynd);
 }
 

+ 10 - 10
lib/mapObjects/CGDwelling.cpp

@@ -234,7 +234,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
 		iw.type = EInfoWindowMode::AUTO;
 		iw.player = h->tempOwner;
 		iw.text.appendLocalString(EMetaText::ADVOB_TXT, 44); //{%s} \n\n The camp is deserted.  Perhaps you should try next week.
-		iw.text.replaceLocalString(EMetaText::OBJ_NAMES, ID);
+		iw.text.replaceName(ID);
 		cb->sendAndApply(&iw);
 		return;
 	}
@@ -249,12 +249,12 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
 		BlockingDialog bd(true,false);
 		bd.player = h->tempOwner;
 		bd.text.appendLocalString(EMetaText::GENERAL_TXT, 421); //Much to your dismay, the %s is guarded by %s %s. Do you wish to fight the guards?
-		bd.text.replaceLocalString(ID == Obj::CREATURE_GENERATOR1 ? EMetaText::CREGENS : EMetaText::CREGENS4, subID);
+		bd.text.replaceTextID(getObjectHandler()->getNameTextID());
 		if(settings["gameTweaks"]["numericCreaturesQuantities"].Bool())
 			bd.text.replaceRawString(CCreature::getQuantityRangeStringForId(Slots().begin()->second->getQuantityID()));
 		else
 			bd.text.replaceLocalString(EMetaText::ARRAY_TXT, 173 + (int)Slots().begin()->second->getQuantityID()*3);
-		bd.text.replaceCreatureName(*Slots().begin()->second);
+		bd.text.replaceName(*Slots().begin()->second);
 		cb->showBlockingDialog(&bd);
 		return;
 	}
@@ -270,16 +270,16 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const
 	if(ID == Obj::CREATURE_GENERATOR1 || ID == Obj::CREATURE_GENERATOR4)
 	{
 		bd.text.appendLocalString(EMetaText::ADVOB_TXT, ID == Obj::CREATURE_GENERATOR1 ? 35 : 36); //{%s} Would you like to recruit %s? / {%s} Would you like to recruit %s, %s, %s, or %s?
-		bd.text.replaceLocalString(ID == Obj::CREATURE_GENERATOR1 ? EMetaText::CREGENS : EMetaText::CREGENS4, subID);
+		bd.text.replaceTextID(getObjectHandler()->getNameTextID());
 		for(const auto & elem : creatures)
-			bd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, elem.second[0]);
+			bd.text.replaceNamePlural(elem.second[0]);
 	}
 	else if(ID == Obj::REFUGEE_CAMP)
 	{
 		bd.text.appendLocalString(EMetaText::ADVOB_TXT, 35); //{%s} Would you like to recruit %s?
-		bd.text.replaceLocalString(EMetaText::OBJ_NAMES, ID);
+		bd.text.replaceName(ID);
 		for(const auto & elem : creatures)
-			bd.text.replaceLocalString(EMetaText::CRE_PL_NAMES, elem.second[0]);
+			bd.text.replaceNamePlural(elem.second[0]);
 	}
 	else if(ID == Obj::WAR_MACHINE_FACTORY)
 		bd.text.appendLocalString(EMetaText::ADVOB_TXT, 157); //{War Machine Factory} Would you like to purchase War Machines?
@@ -436,7 +436,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 				iw.type = EInfoWindowMode::AUTO;
 				iw.player = h->tempOwner;
 				iw.text.appendLocalString(EMetaText::GENERAL_TXT, 425);//The %s would join your hero, but there aren't enough provisions to support them.
-				iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, crid);
+				iw.text.replaceNamePlural(crid);
 				cb->showInfoDialog(&iw);
 			}
 			else //give creatures
@@ -452,7 +452,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 				iw.player = h->tempOwner;
 				iw.text.appendLocalString(EMetaText::GENERAL_TXT, 423); //%d %s join your army.
 				iw.text.replaceNumber(count);
-				iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, crid);
+				iw.text.replaceNamePlural(crid);
 
 				cb->showInfoDialog(&iw);
 				cb->sendAndApply(&sac);
@@ -464,7 +464,7 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 			InfoWindow iw;
 			iw.type = EInfoWindowMode::AUTO;
 			iw.text.appendLocalString(EMetaText::GENERAL_TXT, 422); //There are no %s here to recruit.
-			iw.text.replaceLocalString(EMetaText::CRE_PL_NAMES, crid);
+			iw.text.replaceNamePlural(crid);
 			iw.player = h->tempOwner;
 			cb->sendAndApply(&iw);
 		}

+ 2 - 2
lib/mapObjects/CGHeroInstance.cpp

@@ -752,7 +752,7 @@ void CGHeroInstance::getCastDescription(const spells::Spell * spell, const std::
 
 	text.appendLocalString(EMetaText::GENERAL_TXT, textIndex);
 	getCasterName(text);
-	text.replaceLocalString(EMetaText::SPELL_NAME, spell->getIndex());
+	text.replaceName(spell->getId());
 	if(singleTarget)
 		attacked.at(0)->addNameReplacement(text, true);
 }
@@ -958,7 +958,7 @@ void CGHeroInstance::showNecromancyDialog(const CStackBasicDescriptor &raisedSta
 	{
 		iw.text.appendLocalString(EMetaText::GENERAL_TXT, 146);
 	}
-	iw.text.replaceCreatureName(raisedStack);
+	iw.text.replaceName(raisedStack);
 
 	cb->showInfoDialog(&iw);
 }

+ 1 - 1
lib/mapObjects/CGPandoraBox.cpp

@@ -135,7 +135,7 @@ void CGPandoraBox::grantRewardWithMessage(const CGHeroInstance * h, int index, b
 		for(auto c : vi.reward.creatures)
 		{
 			loot.appendRawString("%s");
-			loot.replaceCreatureName(c);
+			loot.replaceName(c);
 		}
 		
 		if(vi.reward.creatures.size() == 1 && vi.reward.creatures[0].count == 1)

+ 4 - 4
lib/mapObjects/CQuest.cpp

@@ -223,7 +223,7 @@ void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & com
 		for(const auto & elem : mission.artifacts)
 		{
 			loot.appendRawString("%s");
-			loot.replaceLocalString(EMetaText::ART_NAMES, elem);
+			loot.replaceName(elem);
 		}
 		text.replaceRawString(loot.buildList());
 	}
@@ -234,7 +234,7 @@ void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & com
 		for(const auto & elem : mission.creatures)
 		{
 			loot.appendRawString("%s");
-			loot.replaceCreatureName(elem);
+			loot.replaceName(elem);
 		}
 		text.replaceRawString(loot.buildList());
 	}
@@ -258,7 +258,7 @@ void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & com
 	{
 		MetaString loot;
 		for(auto & p : mission.players)
-			loot.appendLocalString(EMetaText::COLOR, p);
+			loot.appendName(p);
 		
 		text.replaceRawString(loot.buildList());
 	}
@@ -329,7 +329,7 @@ void CQuest::addKillTargetReplacements(MetaString &out) const
 		out.replaceTextID(heroName);
 	if(stackToKill.type)
 	{
-		out.replaceCreatureName(stackToKill);
+		out.replaceName(stackToKill);
 		out.replaceRawString(VLC->generaltexth->arraytxt[147+stackDirection]);
 	}
 }

+ 3 - 3
lib/mapObjects/MiscObjects.cpp

@@ -169,7 +169,7 @@ void CGMine::flagMine(const PlayerColor & player) const
 	InfoWindow iw;
 	iw.type = EInfoWindowMode::AUTO;
 	iw.soundID = soundBase::FLAGMINE;
-	iw.text.appendLocalString(EMetaText::MINE_EVNTS, producedResource); //not use subID, abandoned mines uses default mine texts
+	iw.text.appendTextID(TextIdentifier("core.mineevnt", producedResource.getNum()).get()); //not use subID, abandoned mines uses default mine texts
 	iw.player = player;
 	iw.components.emplace_back(ComponentType::RESOURCE_PER_DAY, producedResource, producedQuantity);
 	cb->showInfoDialog(&iw);
@@ -820,7 +820,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
 				if(!message.empty())
 					iw.text = message;
 				else
-					iw.text.appendLocalString(EMetaText::ART_EVNTS, getArtifact());
+					iw.text.appendTextID(getArtifact().toArtifact()->getEventTextID());
 			}
 			break;
 			case Obj::SPELL_SCROLL:
@@ -832,7 +832,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
 				else
 				{
 					iw.text.appendLocalString(EMetaText::ADVOB_TXT,135);
-					iw.text.replaceLocalString(EMetaText::SPELL_NAME, spell.getNum());
+					iw.text.replaceName(spell);
 				}
 			}
 			break;

+ 6 - 6
lib/rewardable/Info.cpp

@@ -249,23 +249,23 @@ void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variab
 	for (const auto & variable : variables.values )
 	{
 		if( boost::algorithm::starts_with(variable.first, "spell"))
-			target.replaceLocalString(EMetaText::SPELL_NAME, variable.second);
+			target.replaceName(SpellID(variable.second));
 
 		if( boost::algorithm::starts_with(variable.first, "secondarySkill"))
-			target.replaceLocalString(EMetaText::SEC_SKILL_NAME, variable.second);
+			target.replaceName(SecondarySkill(variable.second));
 	}
 }
 
 void Rewardable::Info::replaceTextPlaceholders(MetaString & target, const Variables & variables, const VisitInfo & info) const
 {
 	for (const auto & artifact : info.reward.artifacts )
-		target.replaceLocalString(EMetaText::ART_NAMES, artifact.getNum());
+		target.replaceName(artifact);
 
-	for (const auto & artifact : info.reward.spells )
-		target.replaceLocalString(EMetaText::SPELL_NAME, artifact.getNum());
+	for (const auto & spell : info.reward.spells )
+		target.replaceName(spell);
 
 	for (const auto & secondary : info.reward.secondary )
-		target.replaceLocalString(EMetaText::SEC_SKILL_NAME, secondary.first.getNum());
+		target.replaceName(secondary.first);
 
 	replaceTextPlaceholders(target, variables);
 }

+ 1 - 1
lib/spells/BonusCaster.cpp

@@ -45,7 +45,7 @@ void BonusCaster::getCastDescription(const Spell * spell, const std::vector<cons
 
 	text.appendLocalString(EMetaText::GENERAL_TXT, textIndex);
 	getCasterName(text);
-	text.replaceLocalString(EMetaText::SPELL_NAME, spell->getIndex());
+	text.replaceName(spell->getId());
 	if(singleTarget)
 		attacked.at(0)->addNameReplacement(text, true);
 }

+ 1 - 1
lib/spells/ISpellMechanics.cpp

@@ -489,7 +489,7 @@ bool BaseMechanics::adaptProblem(ESpellCastProblem source, Problem & target) con
 			{
 				//The %s prevents %s from casting 3rd level or higher spells.
 				text.appendLocalString(EMetaText::GENERAL_TXT, 536);
-				text.replaceLocalString(EMetaText::ART_NAMES, b->sid.as<ArtifactID>());
+				text.replaceName(b->sid.as<ArtifactID>());
 				caster->getCasterName(text);
 				target.add(std::move(text), spells::Problem::NORMAL);
 			}

+ 1 - 1
lib/spells/effects/Damage.cpp

@@ -176,7 +176,7 @@ void Damage::describeEffect(std::vector<MetaString> & log, const Mechanics * m,
 		{
 			MetaString line;
 			line.appendLocalString(EMetaText::GENERAL_TXT, 376); // Spell %s does %d damage
-			line.replaceLocalString(EMetaText::SPELL_NAME, m->getSpellIndex());
+			line.replaceName(m->getSpellId());
 			line.replaceNumber(static_cast<int>(damage));
 
 			log.push_back(line);

+ 1 - 1
lib/spells/effects/Summon.cpp

@@ -66,7 +66,7 @@ bool Summon::applicable(Problem & problem, const Mechanics * m) const
 			{
 				text.replaceRawString(caster->getNameTranslated());
 
-				text.replaceLocalString(EMetaText::CRE_PL_NAMES, elemental->creatureIndex());
+				text.replaceNamePlural(elemental->creatureId());
 
 				if(caster->type->gender == EHeroGender::FEMALE)
 					text.replaceLocalString(EMetaText::GENERAL_TXT, 540);

+ 19 - 17
server/CGameHandler.cpp

@@ -908,24 +908,24 @@ void CGameHandler::onNewTurn()
 			{
 				case NewTurn::DOUBLE_GROWTH:
 					iw.text.appendLocalString(EMetaText::ARRAY_TXT, 131);
-					iw.text.replaceLocalString(EMetaText::CRE_SING_NAMES, n.creatureid);
-					iw.text.replaceLocalString(EMetaText::CRE_SING_NAMES, n.creatureid);
+					iw.text.replaceNameSingular(n.creatureid);
+					iw.text.replaceNameSingular(n.creatureid);
 					break;
 				case NewTurn::PLAGUE:
 					iw.text.appendLocalString(EMetaText::ARRAY_TXT, 132);
 					break;
 				case NewTurn::BONUS_GROWTH:
 					iw.text.appendLocalString(EMetaText::ARRAY_TXT, 134);
-					iw.text.replaceLocalString(EMetaText::CRE_SING_NAMES, n.creatureid);
-					iw.text.replaceLocalString(EMetaText::CRE_SING_NAMES, n.creatureid);
+					iw.text.replaceNameSingular(n.creatureid);
+					iw.text.replaceNameSingular(n.creatureid);
 					break;
 				case NewTurn::DEITYOFFIRE:
 					iw.text.appendLocalString(EMetaText::ARRAY_TXT, 135);
-					iw.text.replaceLocalString(EMetaText::CRE_SING_NAMES, 42); //%s imp
-					iw.text.replaceLocalString(EMetaText::CRE_SING_NAMES, 42); //%s imp
-					iw.text.replacePositiveNumber(15);							//%+d 15
-					iw.text.replaceLocalString(EMetaText::CRE_SING_NAMES, 43); //%s familiar
-					iw.text.replacePositiveNumber(15);							//%+d 15
+					iw.text.replaceNameSingular(CreatureID::IMP); //%s imp
+					iw.text.replaceNameSingular(CreatureID::IMP); //%s imp
+					iw.text.replacePositiveNumber(15);//%+d 15
+					iw.text.replaceNameSingular(CreatureID::FAMILIAR); //%s familiar
+					iw.text.replacePositiveNumber(15);//%+d 15
 					break;
 				default:
 					if (newMonth)
@@ -1350,7 +1350,7 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owne
 				InfoWindow iw;
 				iw.player = oldOwner;
 				iw.text.appendLocalString(EMetaText::GENERAL_TXT, 6); //%s, you have lost your last town. If you do not conquer another town in the next week, you will be eliminated.
-				iw.text.replaceLocalString(EMetaText::COLOR, oldOwner.getNum());
+				iw.text.replaceName(oldOwner);
 				sendAndApply(&iw);
 			}
 		}
@@ -1588,7 +1588,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
 			for (auto it : cs2.spells)
 			{
 				iw.components.emplace_back(ComponentType::SPELL, it);
-				iw.text.appendLocalString(EMetaText::SPELL_NAME, it.toEnum());
+				iw.text.appendName(it);
 				switch (size--)
 				{
 					case 2:
@@ -1616,7 +1616,7 @@ void CGameHandler::useScholarSkill(ObjectInstanceID fromHero, ObjectInstanceID t
 			for (auto it : cs1.spells)
 			{
 				iw.components.emplace_back(ComponentType::SPELL, it);
-				iw.text.appendLocalString(EMetaText::SPELL_NAME, it.toEnum());
+				iw.text.appendName(it);
 				switch (size--)
 				{
 					case 2:
@@ -3639,7 +3639,7 @@ void CGameHandler::getVictoryLossMessage(PlayerColor player, const EVictoryLossC
 {
 	out.player = player;
 	out.text = victoryLossCheckResult.messageToSelf;
-	out.text.replaceLocalString(EMetaText::COLOR, player.getNum());
+	out.text.replaceName(player);
 	out.components.emplace_back(ComponentType::FLAG, player);
 }
 
@@ -3661,16 +3661,18 @@ bool CGameHandler::dig(const CGHeroInstance *h)
 	iw.player = h->tempOwner;
 	if (gs->map->grailPos == h->visitablePos())
 	{
+		ArtifactID grail = ArtifactID::GRAIL;
+
 		iw.text.appendLocalString(EMetaText::GENERAL_TXT, 58); //"Congratulations! After spending many hours digging here, your hero has uncovered the "
-		iw.text.appendLocalString(EMetaText::ART_NAMES, ArtifactID::GRAIL);
+		iw.text.replaceName(grail);
 		iw.soundID = soundBase::ULTIMATEARTIFACT;
-		giveHeroNewArtifact(h, VLC->arth->objects[ArtifactID::GRAIL], ArtifactPosition::FIRST_AVAILABLE); //give grail
+		giveHeroNewArtifact(h, grail.toArtifact(), ArtifactPosition::FIRST_AVAILABLE); //give grail
 		sendAndApply(&iw);
 
 		iw.soundID = soundBase::invalid;
-		iw.components.emplace_back(ComponentType::ARTIFACT, ArtifactID(ArtifactID::GRAIL));
+		iw.components.emplace_back(ComponentType::ARTIFACT, grail);
 		iw.text.clear();
-		iw.text.appendLocalString(EMetaText::ART_DESCR, ArtifactID::GRAIL);
+		iw.text.appendTextID(grail.toArtifact()->getDescriptionTextID());
 		sendAndApply(&iw);
 	}
 	else

+ 1 - 1
server/battles/BattleActionProcessor.cpp

@@ -1067,7 +1067,7 @@ void BattleActionProcessor::makeAttack(const CBattleInfoCallback & battle, const
 			{
 				MetaString text;
 				text.appendLocalString(EMetaText::GENERAL_TXT, 376);
-				text.replaceLocalString(EMetaText::SPELL_NAME, SpellID::FIRE_SHIELD);
+				text.replaceName(SpellID(SpellID::FIRE_SHIELD));
 				text.replaceNumber(totalDamage);
 				blm.lines.push_back(std::move(text));
 			}

+ 1 - 1
server/battles/BattleResultProcessor.cpp

@@ -462,7 +462,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
 		auto it = cs.spells.begin();
 		for (int i = 0; i < cs.spells.size(); i++, it++)
 		{
-			iw.text.replaceLocalString(EMetaText::SPELL_NAME, it->toEnum());
+			iw.text.replaceName(*it);
 			if (i == cs.spells.size() - 2) //we just added pre-last name
 				iw.text.replaceLocalString(EMetaText::GENERAL_TXT, 141); // " and "
 			iw.components.emplace_back(ComponentType::SPELL, *it);