瀏覽代碼

All artifact strings now pass through translator

Ivan Savenko 2 年之前
父節點
當前提交
388ed88b5d

+ 1 - 1
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -191,7 +191,7 @@ int getDwellingArmyCost(const CGObjectInstance * target)
 
 uint64_t evaluateArtifactArmyValue(CArtifactInstance * art)
 {
-	if(art->artType->id == ArtifactID::SPELL_SCROLL)
+	if(art->artType->getId() == ArtifactID::SPELL_SCROLL)
 		return 1500;
 
 	auto statsValue =

+ 1 - 1
AI/Nullkiller/Goals/AbstractGoal.cpp

@@ -60,7 +60,7 @@ std::string AbstractGoal::toString() const //TODO: virtualize
 		desc = "GATHER TROOPS";
 		break;
 	case GET_ART_TYPE:
-		desc = "GET ARTIFACT OF TYPE " + VLC->arth->objects[aid]->getName();
+		desc = "GET ARTIFACT OF TYPE " + VLC->arth->objects[aid]->getNameTranslated();
 		break;
 	case DIG_AT_TILE:
 		desc = "DIG AT TILE " + tile.toString();

+ 1 - 1
AI/VCAI/Goals/AbstractGoal.cpp

@@ -91,7 +91,7 @@ std::string AbstractGoal::name() const //TODO: virtualize
 	}
 	break;
 	case GET_ART_TYPE:
-		desc = "GET ARTIFACT OF TYPE " + VLC->artifacts()->getByIndex(aid)->getName();
+		desc = "GET ARTIFACT OF TYPE " + VLC->artifacts()->getByIndex(aid)->getNameTranslated();
 		break;
 	case VISIT_TILE:
 		desc = "VISIT TILE " + tile.toString();

+ 2 - 2
client/CPlayerInterface.cpp

@@ -1363,14 +1363,14 @@ void CPlayerInterface::showGarrisonDialog( const CArmedInstance *up, const CGHer
  */
 void CPlayerInterface::showArtifactAssemblyDialog(const Artifact * artifact, const Artifact * assembledArtifact, CFunctionList<bool()> onYes)
 {
-	std::string text = artifact->getDescription();
+	std::string text = artifact->getDescriptionTranslated();
 	text += "\n\n";
 	std::vector<std::shared_ptr<CComponent>> scs;
 
 	if(assembledArtifact)
 	{
 		// You possess all of the components to...
-		text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getName());
+		text += boost::str(boost::format(CGI->generaltexth->allTexts[732]) % assembledArtifact->getNameTranslated());
 
 		// Picture of assembled artifact at bottom.
 		auto sc = std::make_shared<CComponent>(CComponent::artifact, assembledArtifact->getIndex(), 0);

+ 1 - 1
client/battle/BattleWindow.cpp

@@ -420,7 +420,7 @@ void BattleWindow::bSpellf()
 
 			//%s wields the %s, an ancient artifact which creates a p dead to all magic.
 			LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[683])
-										% heroName % CGI->artifacts()->getByIndex(artID)->getName()));
+										% heroName % CGI->artifacts()->getByIndex(artID)->getNameTranslated()));
 		}
 	}
 }

+ 1 - 1
client/lobby/CBonusSelection.cpp

@@ -209,7 +209,7 @@ void CBonusSelection::createBonusesIcons()
 		}
 		case CScenarioTravel::STravelBonus::ARTIFACT:
 			desc = CGI->generaltexth->allTexts[715];
-			boost::algorithm::replace_first(desc, "%s", CGI->artifacts()->getByIndex(bonDescs[i].info2)->getName());
+			boost::algorithm::replace_first(desc, "%s", CGI->artifacts()->getByIndex(bonDescs[i].info2)->getNameTranslated());
 			break;
 		case CScenarioTravel::STravelBonus::SPELL_SCROLL:
 			desc = CGI->generaltexth->allTexts[716];

+ 13 - 13
client/widgets/CArtifactHolder.cpp

@@ -125,13 +125,13 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 	// If clicked on spellbook, open it only if no artifact is held at the moment.
 	if(ourArt && !down && previousState && !ourOwner->commonInfo->src.AOH)
 	{
-		if(ourArt->artType->id == ArtifactID::SPELLBOOK)
-			GH.pushIntT<CSpellWindow>(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt.get());
+		if(ourArt->artType->getId() == ArtifactID::SPELLBOOK)
+				GH.pushIntT<CSpellWindow>(ourOwner->curHero, LOCPLINT, LOCPLINT->battleInt.get());
 	}
 
 	if (!down && previousState)
 	{
-		if(ourArt && ourArt->artType->id == ArtifactID::SPELLBOOK)
+		if(ourArt && ourArt->artType->getId() == ArtifactID::SPELLBOOK)
 			return; //this is handled separately
 
 		if(!ourOwner->commonInfo->src.AOH) //nothing has been clicked
@@ -139,7 +139,7 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 			if(ourArt  //to prevent selecting empty slots (bugfix to what GrayFace reported)
 				&&  ourOwner->curHero->tempOwner == LOCPLINT->playerID)//can't take art from another player
 			{
-				if(ourArt->artType->id == ArtifactID::CATAPULT) //catapult cannot be highlighted
+				if(ourArt->artType->getId() == ArtifactID::CATAPULT) //catapult cannot be highlighted
 				{
 					std::vector<std::shared_ptr<CComponent>> catapult(1, std::make_shared<CComponent>(CComponent::artifact, 3, 0));
 					LOCPLINT->showInfoDialog(CGI->generaltexth->allTexts[312], catapult); //The Catapult must be equipped.
@@ -164,7 +164,7 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 				{
 					const CArtifact * const cur = ourOwner->commonInfo->src.art->artType;
 
-					if(cur->id == ArtifactID::CATAPULT)
+					if(cur->getId() == ArtifactID::CATAPULT)
 					{
 						//should not happen, catapult cannot be selected
 						logGlobal->error("Attempt to move Catapult");
@@ -172,7 +172,7 @@ void CHeroArtPlace::clickLeft(tribool down, bool previousState)
 					else if(cur->isBig())
 					{
 						//war machines cannot go to backpack
-						LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->getName()));
+						LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[153]) % cur->getNameTranslated()));
 					}
 					else
 					{
@@ -217,10 +217,10 @@ bool CHeroArtPlace::askToAssemble(const CArtifactInstance *art, ArtifactPosition
 		LOCPLINT->showArtifactAssemblyDialog(
 			art->artType,
 			combination,
-			std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->id));
+			std::bind(&CCallback::assembleArtifacts, LOCPLINT->cb.get(), hero, slot, true, combination->getId()));
 
 		if(assemblyPossibilities.size() > 2)
-			logGlobal->warn("More than one possibility of assembling on %s... taking only first", art->artType->getName());
+			logGlobal->warn("More than one possibility of assembling on %s... taking only first", art->artType->getNameTranslated());
 		return true;
 	}
 	return false;
@@ -388,7 +388,7 @@ void CHeroArtPlace::setArtifact(const CArtifactInstance *art)
 
 	text = art->getEffectiveDescription(ourOwner->curHero);
 
-	if(art->artType->id == ArtifactID::SPELL_SCROLL)
+	if(art->artType->getId() == ArtifactID::SPELL_SCROLL)
 	{
 		int spellID = art->getGivenSpellID();
 		if(spellID >= 0)
@@ -402,14 +402,14 @@ void CHeroArtPlace::setArtifact(const CArtifactInstance *art)
 	else
 	{
 		baseType = CComponent::artifact;
-		type = art->artType->id;
+		type = art->artType->getId();
 		bonusValue = 0;
 	}
 
 	if (locked) // Locks should appear as empty.
 		hoverText = CGI->generaltexth->allTexts[507];
 	else
-		hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->getName());
+		hoverText = boost::str(boost::format(CGI->generaltexth->heroscrn[1]) % ourArt->artType->getNameTranslated());
 }
 
 void CArtifactsOfHero::SCommonPart::reset()
@@ -1066,7 +1066,7 @@ void CCommanderArtPlace::setArtifact(const CArtifactInstance * art)
 
 	text = art->getEffectiveDescription();
 
-	if (art->artType->id == ArtifactID::SPELL_SCROLL)
+	if (art->artType->getId() == ArtifactID::SPELL_SCROLL)
 	{
 		int spellID = art->getGivenSpellID();
 		if (spellID >= 0)
@@ -1080,7 +1080,7 @@ void CCommanderArtPlace::setArtifact(const CArtifactInstance * art)
 	else
 	{
 		baseType = CComponent::artifact;
-		type = art->artType->id;
+		type = art->artType->getId();
 		bonusValue = 0;
 	}
 }

+ 1 - 1
client/widgets/CComponent.cpp

@@ -206,7 +206,7 @@ std::string CComponent::getSubtitleInternal()
 			else
 				return val > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated();
 		}
-	case artifact:   return CGI->artifacts()->getByIndex(subtype)->getName();
+	case artifact:   return CGI->artifacts()->getByIndex(subtype)->getNameTranslated();
 	case experience:
 		{
 			if(subtype == 1) //+1 level - tree of knowledge

+ 1 - 1
client/windows/CCreatureWindow.cpp

@@ -597,7 +597,7 @@ CStackWindow::MainSection::MainSection(CStackWindow * owner, int yOffset, bool s
 		{
 			parent->stackArtifactIcon = std::make_shared<CAnimImage>("ARTIFACT", art->artType->iconIndex, 0, pos.x, pos.y);
 			parent->stackArtifactHelp = std::make_shared<LRClickableAreaWTextComp>(Rect(pos, Point(44, 44)), CComponent::artifact);
-			parent->stackArtifactHelp->type = art->artType->id;
+			parent->stackArtifactHelp->type = art->artType->getId();
 
 			if(parent->info->owner)
 			{

+ 7 - 7
client/windows/CTradeWindow.cpp

@@ -250,7 +250,7 @@ void CTradeWindow::CTradeableItem::hover(bool on)
 		if(id < 0)
 			GH.statusbar->write(CGI->generaltexth->zelp[582].first);
 		else
-			GH.statusbar->write(CGI->artifacts()->getByIndex(id)->getName());
+			GH.statusbar->write(CGI->artifacts()->getByIndex(id)->getNameTranslated());
 		break;
 	}
 }
@@ -269,7 +269,7 @@ void CTradeWindow::CTradeableItem::clickRight(tribool down, bool previousState)
 		case ARTIFACT_PLACEHOLDER:
 			//TODO: it's would be better for market to contain actual CArtifactInstance and not just ids of certain artifact type so we can use getEffectiveDescription.
 			if(id >= 0)
-				adventureInt->handleRightClick(CGI->artifacts()->getByIndex(id)->getDescription(), down);
+				adventureInt->handleRightClick(CGI->artifacts()->getByIndex(id)->getDescriptionTranslated(), down);
 			break;
 		}
 	}
@@ -290,7 +290,7 @@ std::string CTradeWindow::CTradeableItem::getName(int number) const
 			return CGI->creh->objects[id]->getNamePluralTranslated();
 	case ARTIFACT_TYPE:
 	case ARTIFACT_INSTANCE:
-		return CGI->artifacts()->getByIndex(id)->getName();
+		return CGI->artifacts()->getByIndex(id)->getNameTranslated();
 	}
 	logGlobal->error("Invalid trade item type: %d", (int)type);
 	return "";
@@ -313,7 +313,7 @@ void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art)
 	assert(type == ARTIFACT_PLACEHOLDER || type == ARTIFACT_INSTANCE);
 	hlp = art;
 	if(art)
-		setID(art->artType->id);
+		setID(art->artType->getId());
 	else
 		setID(-1);
 }
@@ -1397,7 +1397,7 @@ void CAltarWindow::calcTotalExp()
 		for(const CArtifactInstance *art : arts->artifactsOnAltar)
 		{
 			int dmp, valOfArt;
-			market->getOffer(art->artType->id, 0, dmp, valOfArt, mode);
+			market->getOffer(art->artType->getId(), 0, dmp, valOfArt, mode);
 			val += valOfArt; //WAS val += valOfArt * arts->artifactsOnAltar.count(*i);
 		}
 	}
@@ -1470,7 +1470,7 @@ void CAltarWindow::showAll(SDL_Surface * to)
 		artIcon->showAll(to);
 
 		int dmp, val;
-		market->getOffer(arts->commonInfo->src.art->artType->id, 0, dmp, val, EMarketMode::ARTIFACT_EXP);
+		market->getOffer(arts->commonInfo->src.art->artType->getId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP);
 		val = static_cast<int>(hero->calculateXp(val));
 		printAtMiddleLoc(boost::lexical_cast<std::string>(val), 304, 498, FONT_SMALL, Colors::WHITE, to);
 	}
@@ -1496,7 +1496,7 @@ bool CAltarWindow::putOnAltar(std::shared_ptr<CTradeableItem> altarSlot, const C
 	}
 
 	int dmp, val;
-	market->getOffer(art->artType->id, 0, dmp, val, EMarketMode::ARTIFACT_EXP);
+	market->getOffer(art->artType->getId(), 0, dmp, val, EMarketMode::ARTIFACT_EXP);
 	val = static_cast<int>(hero->calculateXp(val));
 
 	arts->artifactsOnAltar.insert(art);

+ 9 - 2
include/vcmi/Artifact.h

@@ -19,13 +19,20 @@ class CreatureID;
 
 class DLL_LINKAGE Artifact : public EntityWithBonuses<ArtifactID>
 {
+	using EntityWithBonuses<ArtifactID>::getName;
 public:
 	virtual bool isBig() const = 0;
 	virtual bool isTradable() const = 0;
-	virtual const std::string & getDescription() const = 0;
-	virtual const std::string & getEventText() const = 0;
 	virtual uint32_t getPrice() const = 0;
 	virtual CreatureID getWarMachine() const = 0;
+
+	virtual std::string getDescriptionTranslated() const = 0;
+	virtual std::string getEventTranslated() const = 0;
+	virtual std::string getNameTranslated() const = 0;
+
+	virtual std::string getDescriptionTextID() const = 0;
+	virtual std::string getEventTextID() const = 0;
+	virtual std::string getNameTextID() const = 0;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 50 - 26
lib/CArtHandler.cpp

@@ -62,7 +62,7 @@ int32_t CArtifact::getIconIndex() const
 
 const std::string & CArtifact::getName() const
 {
-	return name;
+	return identifier;
 }
 
 const std::string & CArtifact::getJsonKey() const
@@ -86,13 +86,34 @@ const IBonusBearer * CArtifact::accessBonuses() const
 	return this;
 }
 
-const std::string & CArtifact::getDescription() const
+std::string CArtifact::getDescriptionTranslated() const
+{
+	return VLC->generaltexth->translate(getDescriptionTextID());
+}
+
+std::string CArtifact::getEventTranslated() const
+{
+	return VLC->generaltexth->translate(getEventTextID());
+}
+
+std::string CArtifact::getNameTranslated() const
+{
+	return VLC->generaltexth->translate(getNameTextID());
+}
+
+std::string CArtifact::getDescriptionTextID() const
+{
+	return TextIdentifier("object", modScope, identifier, "description").get();
+}
+
+std::string CArtifact::getEventTextID() const
 {
-	return description;
+	return TextIdentifier("object", modScope, identifier, "event").get();
 }
-const std::string & CArtifact::getEventText() const
+
+std::string CArtifact::getNameTextID() const
 {
-	return eventText;
+	return TextIdentifier("object", modScope, identifier, "name").get();
 }
 
 uint32_t CArtifact::getPrice() const
@@ -167,7 +188,7 @@ void CArtifact::addNewBonus(const std::shared_ptr<Bonus>& b)
 {
 	b->source = Bonus::ARTIFACT;
 	b->duration = Bonus::PERMANENT;
-	b->description = name;
+	b->description = getNameTranslated();
 	CBonusSystemNode::addNewBonus(b);
 }
 
@@ -323,10 +344,13 @@ CArtifact * CArtHandler::loadFromJson(const std::string & scope, const JsonNode
 	}
 	art->id = ArtifactID(index);
 	art->identifier = identifier;
+	art->modScope = scope;
+
 	const JsonNode & text = node["text"];
-	art->name        = text["name"].String();
-	art->description = text["description"].String();
-	art->eventText   = text["event"].String();
+
+	VLC->generaltexth->registerString(art->getNameTextID(), text["name"].String());
+	VLC->generaltexth->registerString(art->getDescriptionTextID(), text["description"].String());
+	VLC->generaltexth->registerString(art->getEventTextID(), text["event"].String());
 
 	const JsonNode & graphics = node["graphics"];
 	art->image = graphics["image"].String();
@@ -734,7 +758,7 @@ void CArtifactInstance::setType( CArtifact *Art )
 
 std::string CArtifactInstance::nodeName() const
 {
-	return "Artifact instance of " + (artType ? artType->getName() : std::string("uninitialized")) + " type";
+	return "Artifact instance of " + (artType ? artType->getJsonKey() : std::string("uninitialized")) + " type";
 }
 
 CArtifactInstance *CArtifactInstance::createScroll(SpellID sid)
@@ -756,9 +780,9 @@ std::string CArtifactInstance::getEffectiveDescription(const CGHeroInstance * he
 {
 	std::string text = artType->getDescription();
 	if (!vstd::contains(text, '{'))
-		text = '{' + artType->getName() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style
+		text = '{' + artType->getNameTranslated() + "}\n\n" + text; //workaround for new artifacts with single name, turns it to H3-style
 
-	if(artType->id == ArtifactID::SPELL_SCROLL)
+	if(artType->getId() == ArtifactID::SPELL_SCROLL)
 	{
 		// we expect scroll description to be like this: This scroll contains the [spell name] spell which is added into your spell book for as long as you carry the scroll.
 		// so we want to replace text in [...] with a spell name
@@ -777,12 +801,12 @@ std::string CArtifactInstance::getEffectiveDescription(const CGHeroInstance * he
 		std::string artList;
 		auto combinedArt = artType->constituentOf[0];
 		text += "\n\n";
-		text += "{" + combinedArt->getName() + "}";
+		text += "{" + combinedArt->getNameTranslated() + "}";
 		int wornArtifacts = 0;
 		for(auto a : *combinedArt->constituents) //TODO: can the artifact be a part of more than one set?
 		{
-			artList += "\n" + a->getName();
-			if (hero->hasArt(a->id, true))
+			artList += "\n" + a->getNameTranslated();
+			if (hero->hasArt(a->getId(), true))
 				wornArtifacts++;
 		}
 		text += " (" + boost::str(boost::format("%d") % wornArtifacts) +  " / " +
@@ -841,7 +865,7 @@ bool CArtifactInstance::canBePutAt(const CArtifactSet *artSet, ArtifactPosition
  	auto possibleSlots = artType->possibleSlots.find(artSet->bearerType());
  	if(possibleSlots == artType->possibleSlots.end())
  	{
-		logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->getName(), artSet->bearerType());
+		logMod->warn("Warning: artifact %s doesn't have defined allowed slots for bearer of type %s", artType->getNameTranslated(), artSet->bearerType());
 		return false;
 	}
 
@@ -889,7 +913,7 @@ std::vector<const CArtifact *> CArtifactInstance::assemblyPossibilities(const CA
 			if(equipped)
 			{
 				// Search for equipped arts
-				if (!h->hasArt(constituent->id, true, false, false))
+				if (!h->hasArt(constituent->getId(), true, false, false))
 				{
 					possible = false;
 					break;
@@ -898,7 +922,7 @@ std::vector<const CArtifact *> CArtifactInstance::assemblyPossibilities(const CA
 			else
 			{
 				// Search in backpack
-				if(!h->hasArtBackpack(constituent->id))
+				if(!h->hasArtBackpack(constituent->getId()))
 				{
 					possible = false;
 					break;
@@ -1061,14 +1085,14 @@ void CCombinedArtifactInstance::createConstituents()
 
 	for(const CArtifact * art : *artType->constituents)
 	{
-		addAsConstituent(CArtifactInstance::createNewArtifactInstance(art->id), ArtifactPosition::PRE_FIRST);
+		addAsConstituent(CArtifactInstance::createNewArtifactInstance(art->getId()), ArtifactPosition::PRE_FIRST);
 	}
 }
 
 void CCombinedArtifactInstance::addAsConstituent(CArtifactInstance *art, ArtifactPosition slot)
 {
 	assert(vstd::contains_if(*artType->constituents, [=](const CArtifact * constituent){
-		return constituent->id == art->artType->id;
+		return constituent->getId() == art->artType->getId();
 	}));
 	assert(art->getParentNodes().size() == 1  &&  art->getParentNodes().front() == art->artType);
 	constituentsInfo.push_back(ConstituentInfo(art, slot));
@@ -1227,7 +1251,7 @@ std::vector<ArtifactPosition> CArtifactSet::getAllArtPositions(ArtifactID aid, b
 {
 	std::vector<ArtifactPosition> result;
 	for(auto & slotInfo : artifactsWorn)
-		if(slotInfo.second.artifact->artType->id == aid && (allowLocked || !slotInfo.second.locked))
+		if(slotInfo.second.artifact->artType->getId() == aid && (allowLocked || !slotInfo.second.locked))
 			result.push_back(slotInfo.first);
 
 	if(onlyWorn)
@@ -1248,7 +1272,7 @@ std::vector<ArtifactPosition> CArtifactSet::getBackpackArtPositions(ArtifactID a
 	for(auto & artInfo : artifactsInBackpack)
 	{
 		auto * art = artInfo.getArt();
-		if(art && art->artType->id == aid)
+		if(art && art->artType->getId() == aid)
 			result.emplace_back(backpackPosition);
 		backpackPosition++;
 	}
@@ -1318,7 +1342,7 @@ CArtifactSet::searchForConstituent(ArtifactID aid) const
 			auto ass = static_cast<CCombinedArtifactInstance *>(art.get());
 			for(auto& ci : ass->constituentsInfo)
 			{
-				if(ci.art->artType->id == aid)
+				if(ci.art->artType->getId() == aid)
 				{
 					return {ass, ci.art};
 				}
@@ -1468,12 +1492,12 @@ void CArtifactSet::serializeJsonHero(JsonSerializeFormat & handler, CMap * map)
 	{
 		backpackTemp.reserve(artifactsInBackpack.size());
 		for(const ArtSlotInfo & info : artifactsInBackpack)
-			backpackTemp.push_back(info.artifact->artType->id);
+			backpackTemp.push_back(info.artifact->artType->getId());
 	}
 	handler.serializeIdArray(NArtifactPosition::backpack, backpackTemp);
 	if(!handler.saving)
 	{
-        for(const ArtifactID & artifactID : backpackTemp)
+		for(const ArtifactID & artifactID : backpackTemp)
 		{
 			auto artifact = CArtifactInstance::createArtifact(map, artifactID.toEnum());
 			auto slot = ArtifactPosition(GameConstants::BACKPACK_START + (si32)artifactsInBackpack.size());
@@ -1503,7 +1527,7 @@ void CArtifactSet::serializeJsonSlot(JsonSerializeFormat & handler, const Artifa
 
 		if(info != nullptr && !info->locked)
 		{
-			artifactID = info->artifact->artType->id;
+			artifactID = info->artifact->artType->getId();
 			handler.serializeId(NArtifactPosition::namesHero[slot.num], artifactID, ArtifactID::NONE);
 		}
 	}

+ 16 - 11
lib/CArtHandler.h

@@ -45,13 +45,16 @@ namespace ArtBearer
 
 class DLL_LINKAGE CArtifact : public Artifact, public CBonusSystemNode //container for artifacts
 {
-protected:
-	std::string name, description; //set if custom
-	std::string eventText; //short story displayed upon picking
+	ArtifactID id;
+
+	std::string modScope;
+	std::string identifier;
+
+	const std::string & getName() const override;
+
 public:
 	enum EartClass {ART_SPECIAL=1, ART_TREASURE=2, ART_MINOR=4, ART_MAJOR=8, ART_RELIC=16}; //artifact classes
 
-	std::string identifier;
 	std::string image;
 	std::string large; // big image for custom artifacts, used in drag & drop
 	std::string advMapDef; //used for adventure map object
@@ -61,19 +64,23 @@ public:
 	std::unique_ptr<std::vector<CArtifact *> > constituents; // Artifacts IDs a combined artifact consists of, or nullptr.
 	std::vector<CArtifact *> constituentOf; // Reverse map of constituents - combined arts that include this art
 	EartClass aClass;
-	ArtifactID id;
 	CreatureID warMachine;
 
 	int32_t getIndex() const override;
 	int32_t getIconIndex() const override;
-	const std::string & getName() const override;
 	const std::string & getJsonKey() const override;
 	void registerIcons(const IconRegistar & cb) const override;
 	ArtifactID getId() const override;
 	virtual const IBonusBearer * accessBonuses() const override;
 
-	const std::string & getEventText() const override;
-	const std::string & getDescription() const override;
+	std::string getDescriptionTranslated() const override;
+	std::string getEventTranslated() const override;
+	std::string getNameTranslated() const override;
+
+	std::string getDescriptionTextID() const override;
+	std::string getEventTextID() const override;
+	std::string getNameTextID() const override;
+
 	uint32_t getPrice() const override;
 	CreatureID getWarMachine() const override;
 	bool isBig() const override;
@@ -91,9 +98,6 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CBonusSystemNode&>(*this);
-		h & name;
-		h & description;
-		h & eventText;
 		h & image;
 		h & large;
 		h & advMapDef;
@@ -104,6 +108,7 @@ public:
 		h & constituentOf;
 		h & aClass;
 		h & id;
+		h & modScope;
 		h & identifier;
 		h & warMachine;
 	}

+ 5 - 5
lib/CGameState.cpp

@@ -75,7 +75,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
 	{
 		auto art = ArtifactID(ser).toArtifact(VLC->artifacts());
 		if(art)
-			dst = art->getName();
+			dst = art->getNameTranslated();
 		else
 			dst = "#!#";
 	}
@@ -83,7 +83,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
 	{
 		auto art = ArtifactID(ser).toArtifact(VLC->artifacts());
 		if(art)
-			dst = art->getDescription();
+			dst = art->getDescriptionTranslated();
 		else
 			dst = "#!#";
 	}
@@ -91,7 +91,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
 	{
 		auto art = ArtifactID(ser).toArtifact(VLC->artifacts());
 		if(art)
-			dst = art->getEventText();
+			dst = art->getEventTranslated();
 		else
 			dst = "#!#";
 	}
@@ -1304,7 +1304,7 @@ void CGameState::prepareCrossoverHeroes(std::vector<CGameState::CampaignHeroRepl
 				if(!art)
 					continue;
 
-				int id  = art->artType->id;
+				int id  = art->artType->getId();
 				assert( 8*18 > id );//number of arts that fits into h3m format
 				bool takeable = travelOptions.artifsKeptByHero[id / 8] & ( 1 << (id%8) );
 
@@ -2891,7 +2891,7 @@ void CGameState::replaceHeroesPlaceholders(const std::vector<CGameState::Campaig
 
 		auto fixArtifact = [&](CArtifactInstance * art)
 		{
-			art->artType = VLC->arth->objects[art->artType->id];
+			art->artType = VLC->arth->objects[art->artType->getId()];
 			gs->map->artInstances.push_back(art);
 			art->id = ArtifactInstanceID((si32)gs->map->artInstances.size() - 1);
 		};

+ 1 - 1
lib/CStack.cpp

@@ -365,7 +365,7 @@ bool CStack::unitHasAmmoCart(const battle::Unit * unit) const
 	auto ownerHero = battle->battleGetOwnerHero(unit);
 	if(ownerHero && ownerHero->artifactsWorn.find(ArtifactPosition::MACH2) != ownerHero->artifactsWorn.end())
 	{
-		if(battle->battleGetOwnerHero(unit)->artifactsWorn.at(ArtifactPosition::MACH2).artifact->artType->id == ArtifactID::AMMO_CART)
+		if(battle->battleGetOwnerHero(unit)->artifactsWorn.at(ArtifactPosition::MACH2).artifact->artType->getId() == ArtifactID::AMMO_CART)
 		{
 			return true;
 		}

+ 1 - 1
lib/HeroBonus.cpp

@@ -1594,7 +1594,7 @@ std::string Bonus::Description() const
 			switch(source)
 			{
 			case ARTIFACT:
-				str << ArtifactID(sid).toArtifact(VLC->artifacts())->getName();
+				str << ArtifactID(sid).toArtifact(VLC->artifacts())->getNameTranslated();
 				break;
 			case SPELL_EFFECT:
 				str << SpellID(sid).toSpell(VLC->spells())->getNameTranslated();

+ 7 - 7
lib/NetPacksLib.cpp

@@ -419,7 +419,7 @@ DLL_LINKAGE void RemoveObject::applyGs(CGameState *gs)
 		beatenHero->tempOwner = PlayerColor::NEUTRAL; //no one owns beaten hero
 		vstd::erase_if(beatenHero->artifactsInBackpack, [](const ArtSlotInfo& asi)
 		{
-			return asi.artifact->artType->id == ArtifactID::GRAIL;
+			return asi.artifact->artType->getId() == ArtifactID::GRAIL;
 		});
 
 		if(beatenHero->visitedTown)
@@ -1061,7 +1061,7 @@ DLL_LINKAGE void EraseArtifact::applyGs(CGameState *gs)
 	auto slot = al.getSlot();
 	if(slot->locked)
 	{
-		logGlobal->debug("Erasing locked artifact: %s", slot->artifact->artType->getName());
+		logGlobal->debug("Erasing locked artifact: %s", slot->artifact->artType->getNameTranslated());
 		DisassembledArtifact dis;
 		dis.al.artHolder = al.artHolder;
 		auto aset = al.getHolderArtSet();
@@ -1081,12 +1081,12 @@ DLL_LINKAGE void EraseArtifact::applyGs(CGameState *gs)
 			}
 		}
 		assert(found && "Failed to determine the assembly this locked artifact belongs to");
-		logGlobal->debug("Found the corresponding assembly: %s", dis.al.getSlot()->artifact->artType->getName());
+		logGlobal->debug("Found the corresponding assembly: %s", dis.al.getSlot()->artifact->artType->getNameTranslated());
 		dis.applyGs(gs);
 	}
 	else
 	{
-		logGlobal->debug("Erasing artifact %s", slot->artifact->artType->getName());
+		logGlobal->debug("Erasing artifact %s", slot->artifact->artType->getNameTranslated());
 	}
 	al.removeArtifact();
 }
@@ -1178,7 +1178,7 @@ DLL_LINKAGE void AssembledArtifact::applyGs(CGameState *gs)
 	bool combineEquipped = !ArtifactUtils::isSlotBackpack(al.slot);
 	assert(vstd::contains_if(transformedArt->assemblyPossibilities(artSet, combineEquipped), [=](const CArtifact * art)->bool
 		{
-			return art->id == builtArt->id;
+			return art->getId() == builtArt->getId();
 		}));
 	MAYBE_UNUSED(transformedArt);
 
@@ -1187,8 +1187,8 @@ DLL_LINKAGE void AssembledArtifact::applyGs(CGameState *gs)
 	// Retrieve all constituents
 	for(const CArtifact * constituent : *builtArt->constituents)
 	{
-		ArtifactPosition pos = combineEquipped ? artSet->getArtPos(constituent->id, true, false) :
-			artSet->getArtBackpackPos(constituent->id);
+		ArtifactPosition pos = combineEquipped ? artSet->getArtPos(constituent->getId(), true, false) :
+			artSet->getArtBackpackPos(constituent->getId());
 		assert(pos >= 0);
 		CArtifactInstance * constituentInstance = artSet->getArt(pos);
 

+ 1 - 1
lib/mapObjects/CGMarket.cpp

@@ -275,7 +275,7 @@ std::vector<int> CGBlackMarket::availableItemsIds(EMarketMode::EMarketMode mode)
 			std::vector<int> ret;
 			for(const CArtifact *a : artifacts)
 				if(a)
-					ret.push_back(a->id);
+					ret.push_back(a->getId());
 				else
 					ret.push_back(-1);
 			return ret;

+ 1 - 1
lib/mapObjects/CGTownInstance.cpp

@@ -1145,7 +1145,7 @@ std::vector<int> CGTownInstance::availableItemsIds(EMarketMode::EMarketMode mode
 		std::vector<int> ret;
 		for(const CArtifact *a : merchantArtifacts)
 			if(a)
-				ret.push_back(a->id);
+				ret.push_back(a->getId());
 			else
 				ret.push_back(-1);
 		return ret;

+ 2 - 2
lib/mapObjects/CRewardableObject.cpp

@@ -861,7 +861,7 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand)
 			info[0].reward.bonuses.push_back(bonus);
 			info[0].limiter.numOfGrants = 1;
 			info[0].message.addTxt(MetaString::ADVOB_TXT, 162);
-			info[0].message.addReplacement(VLC->arth->objects[info[0].reward.artifacts.back()]->getName());
+			info[0].message.addReplacement(VLC->arth->objects[info[0].reward.artifacts.back()]->getNameTranslated());
 		}
 		break;
 	case Obj::WAGON:
@@ -876,7 +876,7 @@ void CGOnceVisitable::initObj(CRandomGenerator & rand)
 				loadRandomArtifact(rand, info[0], 10, 10, 0, 0);
 				info[0].limiter.numOfGrants = 1;
 				info[0].message.addTxt(MetaString::ADVOB_TXT, 155);
-				info[0].message.addReplacement(VLC->arth->objects[info[0].reward.artifacts.back()]->getName());
+				info[0].message.addReplacement(VLC->arth->objects[info[0].reward.artifacts.back()]->getNameTranslated());
 			}
 			else if(hlp < 90) //2 - 5 of non-gold resource
 			{

+ 2 - 14
lib/mapObjects/MiscObjects.cpp

@@ -1320,7 +1320,7 @@ void CGArtifact::initObj(CRandomGenerator & rand)
 
 std::string CGArtifact::getObjectName() const
 {
-	return VLC->artifacts()->getByIndex(subID)->getName();
+	return VLC->artifacts()->getByIndex(subID)->getNameTranslated();
 }
 
 void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
@@ -1337,19 +1337,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const
 				if(message.length())
 					iw.text << message;
 				else
-				{
-					auto artifact = ArtifactID(subID).toArtifact(VLC->artifacts());
-
-					if((artifact != nullptr) && (!artifact->getEventText().empty()))
-					{
-						iw.text.addTxt(MetaString::ART_EVNTS, subID);
-					}
-					else //fix for mod artifacts with no event text
-					{
-						iw.text.addTxt(MetaString::ADVOB_TXT, 183); //% has found treasure
-						iw.text.addReplacement(h->getNameTranslated());
-					}
-				}
+					iw.text.addTxt(MetaString::ART_EVNTS, subID);
 			}
 			break;
 		case Obj::SPELL_SCROLL:

+ 1 - 1
lib/mapping/CMap.cpp

@@ -539,7 +539,7 @@ void CMap::checkForObjectives()
 			switch (cond.condition)
 			{
 				case EventCondition::HAVE_ARTIFACT:
-					boost::algorithm::replace_first(event.onFulfill, "%s", VLC->arth->objects[cond.objectType]->getName());
+					boost::algorithm::replace_first(event.onFulfill, "%s", VLC->arth->objects[cond.objectType]->getNameTranslated());
 					break;
 
 				case EventCondition::HAVE_CREATURES:

+ 1 - 1
lib/mapping/MapFormatH3M.cpp

@@ -679,7 +679,7 @@ void CMapLoaderH3M::readAllowedArtifacts()
 			// combo
 			if (artifact->constituents)
 			{
-				map->allowedArtifact[artifact->id] = false;
+				map->allowedArtifact[artifact->getId()] = false;
 			}
 		}
 		if (map->version == EMapFormat::ROE)

+ 2 - 2
lib/rmg/CMapGenerator.cpp

@@ -112,8 +112,8 @@ void CMapGenerator::initQuestArtsRemaining()
 {
 	for (auto art : VLC->arth->objects)
 	{
-		if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->id) && art->constituentOf.empty()) //don't use parts of combined artifacts
-			questArtifacts.push_back(art->id);
+		if (art->aClass == CArtifact::ART_TREASURE && VLC->arth->legalArtifact(art->getId()) && art->constituentOf.empty()) //don't use parts of combined artifacts
+			questArtifacts.push_back(art->getId());
 	}
 }
 

+ 1 - 1
lib/serializer/CSerializer.cpp

@@ -40,7 +40,7 @@ void CSerializer::addStdVecItems(CGameState *gs, LibClasses *lib)
 	registerVectoredType<CCreature, CreatureID>(&lib->creh->objects,
 		[](const CCreature &cre){ return cre.idNumber; });
 	registerVectoredType<CArtifact, ArtifactID>(&lib->arth->objects,
-		[](const CArtifact &art){ return art.id; });
+		[](const CArtifact &art){ return art.getId(); });
 	registerVectoredType<CArtifactInstance, ArtifactInstanceID>(&gs->map->artInstances,
 		[](const CArtifactInstance &artInst){ return artInst.id; });
 	registerVectoredType<CQuest, si32>(&gs->map->quests,

+ 1 - 1
mapeditor/inspector/questwidget.cpp

@@ -68,7 +68,7 @@ void QuestWidget::obtainData()
 		case CQuest::Emission::MISSION_ART:
 			activeId = true;
 			for(int i = 0; i < map.allowedArtifact.size(); ++i)
-				ui->targetId->addItem(QString::fromStdString(VLC->arth->objects.at(i)->getName()));
+				ui->targetId->addItem(QString::fromStdString(VLC->arth->objects.at(i)->getNameTranslated()));
 			if(!seerhut.quest->m5arts.empty())
 				ui->targetId->setCurrentIndex(seerhut.quest->m5arts.front());
 			//TODO: support multiple artifacts

+ 1 - 1
mapeditor/inspector/rewardswidget.cpp

@@ -82,7 +82,7 @@ QList<QString> RewardsWidget::getListForType(RewardType typeId)
 			for(int i = 0; i < map.allowedArtifact.size(); ++i)
 			{
 				if(map.allowedArtifact[i])
-					result.append(QString::fromStdString(VLC->arth->objects.at(i)->getName()));
+					result.append(QString::fromStdString(VLC->arth->objects.at(i)->getNameTranslated()));
 			}
 			break;
 			

+ 1 - 1
mapeditor/mapsettings.cpp

@@ -50,7 +50,7 @@ MapSettings::MapSettings(MapController & ctrl, QWidget *parent) :
 	}
 	for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
 	{
-		auto * item = new QListWidgetItem(QString::fromStdString(VLC->arth->objects[i]->getName()));
+		auto * item = new QListWidgetItem(QString::fromStdString(VLC->arth->objects[i]->getNameTranslated()));
 		item->setData(Qt::UserRole, QVariant::fromValue(i));
 		item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
 		item->setCheckState(controller.map()->allowedArtifact[i] ? Qt::Checked : Qt::Unchecked);

+ 10 - 10
server/CGameHandler.cpp

@@ -774,7 +774,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
 				ma.src = ArtifactLocation(finishingBattle->loserHero, artSlot.first);
 				const CArtifactInstance * art =  ma.src.getArt();
 				if (art && !art->artType->isBig() &&
-				    art->artType->id != ArtifactID::SPELLBOOK)
+					art->artType->getId() != ArtifactID::SPELLBOOK)
 						// don't move war machines or locked arts (spellbook)
 				{
 					sendMoveArtifact(art, &ma);
@@ -787,7 +787,7 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
 				ma.src = ArtifactLocation(finishingBattle->loserHero,
 					ArtifactPosition(GameConstants::BACKPACK_START)); //backpack automatically shifts arts to beginning
 				const CArtifactInstance * art =  ma.src.getArt();
-				if (art->artType->id != ArtifactID::GRAIL) //grail may not be won
+				if (art->artType->getId() != ArtifactID::GRAIL) //grail may not be won
 				{
 					sendMoveArtifact(art, &ma);
 				}
@@ -834,8 +834,8 @@ void CGameHandler::endBattle(int3 tile, const CGHeroInstance * heroAttacker, con
 		for (auto art : arts) //TODO; separate function to display loot for various ojects?
 		{
 			iw.components.push_back(Component(
-				Component::ARTIFACT, art->artType->id,
-				art->artType->id == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0));
+				Component::ARTIFACT, art->artType->getId(),
+				art->artType->getId() == ArtifactID::SPELL_SCROLL? art->getGivenSpellID() : 0, 0));
 			if (iw.components.size() >= 14)
 			{
 				sendAndApply(&iw);
@@ -3917,7 +3917,7 @@ bool CGameHandler::moveArtifact(const ArtifactLocation &al1, const ArtifactLocat
 		try
 		{
 			auto hero = boost::get<ConstTransitivePtr<CGHeroInstance>>(dst.artHolder);
-			if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->id, dst.slot))
+			if(ArtifactUtils::checkSpellbookIsNeeded(hero, srcArtifact->artType->getId(), dst.slot))
 				giveHeroNewArtifact(hero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
 		}
 		catch (boost::bad_get const &)
@@ -3964,7 +3964,7 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID
 
 				auto art = artifact.second.getArt();
 				assert(art);
-				if(ArtifactUtils::checkSpellbookIsNeeded(dstHero, art->artType->id, artifact.first))
+				if(ArtifactUtils::checkSpellbookIsNeeded(dstHero, art->artType->getId(), artifact.first))
 					giveHeroNewArtifact(dstHero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
 			}
 		};
@@ -4001,7 +4001,7 @@ bool CGameHandler::bulkMoveArtifacts(ObjectInstanceID srcHero, ObjectInstanceID
 			artFittingSet.putArtifact(dstSlot, static_cast<ConstTransitivePtr<CArtifactInstance>>(artifact));
 			slotsSrcDst.push_back(BulkMoveArtifacts::LinkedSlots(srcSlot, dstSlot));
 
-			if(ArtifactUtils::checkSpellbookIsNeeded(pdstHero, artifact->artType->id, dstSlot))
+			if(ArtifactUtils::checkSpellbookIsNeeded(pdstHero, artifact->artType->getId(), dstSlot))
 				giveHeroNewArtifact(pdstHero, VLC->arth->objects[ArtifactID::SPELLBOOK], ArtifactPosition::SPELLBOOK);
 		};
 
@@ -4144,7 +4144,7 @@ bool CGameHandler::buyArtifact(const IMarket *m, const CGHeroInstance *h, Res::E
 	bool found = false;
 	for (const CArtifact *&art : saa.arts)
 	{
-		if (art && art->id == aid)
+		if (art && art->getId() == aid)
 		{
 			art = nullptr;
 			found = true;
@@ -4169,7 +4169,7 @@ bool CGameHandler::sellArtifact(const IMarket *m, const CGHeroInstance *h, Artif
 	COMPLAIN_RET_FALSE_IF((!art->artType->isTradable()), "Cannot sell a war machine or spellbook!");
 
 	int resVal = 0, dump = 1;
-	m->getOffer(art->artType->id, rid, dump, resVal, EMarketMode::ARTIFACT_RESOURCE);
+	m->getOffer(art->artType->getId(), rid, dump, resVal, EMarketMode::ARTIFACT_RESOURCE);
 
 	removeArtifact(ArtifactLocation(h, h->getArtPos(art)));
 	giveResource(h->tempOwner, rid, resVal);
@@ -6327,7 +6327,7 @@ bool CGameHandler::sacrificeArtifact(const IMarket * m, const CGHeroInstance * h
 			COMPLAIN_RET("No artifact at position to sacrifice!");
 		}
 
-		si32 typId = art->artType->id;
+		si32 typId = art->artType->getId();
 		int dmp, expToGive;
 
 		m->getOffer(typId, 0, dmp, expToGive, EMarketMode::ARTIFACT_EXP);