Răsfoiți Sursa

All creature-related texts go through translator

Ivan Savenko 2 ani în urmă
părinte
comite
d2b837b116

+ 2 - 2
AI/BattleAI/AttackPossibility.cpp

@@ -213,8 +213,8 @@ AttackPossibility AttackPossibility::evaluate(const BattleAttackInfo & attackInf
 	bestAp.shootersBlockedDmg = evaluateBlockedShootersDmg(attackInfo, hex, state);
 
 	logAi->debug("BattleAI best AP: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
-		attackInfo.attacker->unitType()->identifier,
-		attackInfo.defender->unitType()->identifier,
+		attackInfo.attacker->unitType()->getJsonKey(),
+		attackInfo.defender->unitType()->getJsonKey(),
 		(int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
 		bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);
 

+ 2 - 2
AI/BattleAI/BattleAI.cpp

@@ -207,8 +207,8 @@ BattleAction CBattleAI::activeStack( const CStack * stack )
 				}
 
 				logAi->debug("BattleAI: %s -> %s x %d, %s, from %d curpos %d dist %d speed %d: +%lld -%lld = %lld",
-					bestAttack.attackerState->unitType()->identifier,
-					bestAttack.affectedUnits[0]->unitType()->identifier,
+					bestAttack.attackerState->unitType()->getJsonKey(),
+					bestAttack.affectedUnits[0]->unitType()->getJsonKey(),
 					(int)bestAttack.affectedUnits[0]->getCount(), action, (int)bestAttack.from, (int)bestAttack.attack.attacker->getPosition().hex,
 					bestAttack.attack.chargeDistance, bestAttack.attack.attacker->Speed(0, true),
 					bestAttack.defenderDamageReduce, bestAttack.attackerDamageReduce, bestAttack.attackValue()

+ 2 - 2
AI/BattleAI/PotentialTargets.cpp

@@ -90,8 +90,8 @@ PotentialTargets::PotentialTargets(const battle::Unit * attacker, const Hypothet
 		auto & bestAp = possibleAttacks[0];
 
 		logGlobal->info("Battle AI best: %s -> %s at %d from %d, affects %d units: d:%lld a:%lld c:%lld s:%lld",
-			bestAp.attack.attacker->unitType()->identifier,
-			state.battleGetUnitByPos(bestAp.dest)->unitType()->identifier,
+			bestAp.attack.attacker->unitType()->getJsonKey(),
+			state.battleGetUnitByPos(bestAp.dest)->unitType()->getJsonKey(),
 			(int)bestAp.dest, (int)bestAp.from, (int)bestAp.affectedUnits.size(),
 			bestAp.defenderDamageReduce, bestAp.attackerDamageReduce, bestAp.collateralDamageReduce, bestAp.shootersBlockedDmg);
 	}

+ 1 - 1
AI/BattleAI/StackWithBonuses.cpp

@@ -205,7 +205,7 @@ std::string StackWithBonuses::getDescription() const
 	oss << unitOwner().getStr();
 	oss << " battle stack [" << unitId() << "]: " << getCount() << " of ";
 	if(type)
-		oss << type->namePl;
+		oss << type->getJsonKey();
 	else
 		oss << "[UNDEFINED TYPE]";
 

+ 1 - 1
AI/Nullkiller/AIGateway.cpp

@@ -737,7 +737,7 @@ bool AIGateway::makePossibleUpgrades(const CArmedInstance * obj)
 			{
 				myCb->upgradeCreature(obj, SlotID(i), ui.newID[0]);
 				upgraded = true;
-				logAi->debug("Upgraded %d %s to %s", s->count, ui.oldID.toCreature()->namePl, ui.newID[0].toCreature()->namePl);
+				logAi->debug("Upgraded %d %s to %s", s->count, ui.oldID.toCreature()->getNamePluralTranslated(), ui.newID[0].toCreature()->getNamePluralTranslated());
 			}
 		}
 	}

+ 1 - 1
client/battle/BattleInterfaceClasses.cpp

@@ -551,7 +551,7 @@ BattleResultWindow::BattleResultWindow(const BattleResult & br, CPlayerInterface
 			if(best != stacks.end()) //should be always but to be safe...
 			{
 				icons.push_back(std::make_shared<CAnimImage>("TWCRPORT", (*best)->type->getIconIndex(), 0, xs[i], 38));
-				sideNames[i] = (*best)->type->getPluralName();
+				sideNames[i] = (*best)->type->getNamePluralTranslated();
 			}
 		}
 	}

+ 2 - 2
client/battle/BattleProjectileController.cpp

@@ -158,7 +158,7 @@ const CCreature & BattleProjectileController::getShooter(const CStack * stack) c
 
 	if(creature->animation.missleFrameAngles.empty())
 	{
-		logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->nameSing);
+		logAnim->error("Mod error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Trying to use archer's data instead...", creature->getNameSingularTranslated());
 		creature = CGI->creh->objects[CreatureID::ARCHER];
 	}
 
@@ -313,7 +313,7 @@ void BattleProjectileController::createProjectile(const CStack * shooter, Point
 	std::shared_ptr<ProjectileBase> projectile;
 	if (stackUsesRayProjectile(shooter) && stackUsesMissileProjectile(shooter))
 	{
-		logAnim->error("Mod error: Creature '%s' has both missile and ray projectiles configured. Mod should be fixed. Using ray projectile configuration...", shooterInfo.nameSing);
+		logAnim->error("Mod error: Creature '%s' has both missile and ray projectiles configured. Mod should be fixed. Using ray projectile configuration...", shooterInfo.getNameSingularTranslated());
 	}
 
 	if (stackUsesRayProjectile(shooter))

+ 1 - 1
client/lobby/CBonusSelection.cpp

@@ -182,7 +182,7 @@ void CBonusSelection::createBonusesIcons()
 			picNumber = bonDescs[i].info2 + 2;
 			desc = CGI->generaltexth->allTexts[717];
 			boost::algorithm::replace_first(desc, "%d", boost::lexical_cast<std::string>(bonDescs[i].info3));
-			boost::algorithm::replace_first(desc, "%s", CGI->creatures()->getByIndex(bonDescs[i].info2)->getPluralName());
+			boost::algorithm::replace_first(desc, "%s", CGI->creatures()->getByIndex(bonDescs[i].info2)->getNamePluralTranslated());
 			break;
 		case CScenarioTravel::STravelBonus::BUILDING:
 		{

+ 8 - 1
client/widgets/CComponent.cpp

@@ -198,7 +198,14 @@ std::string CComponent::getSubtitleInternal()
 	case primskill:  return boost::str(boost::format("%+d %s") % val % (subtype < 4 ? CGI->generaltexth->primarySkillNames[subtype] : CGI->generaltexth->allTexts[387]));
 	case secskill:   return CGI->generaltexth->levels[val-1] + "\n" + CGI->skillh->getByIndex(subtype)->getNameTranslated();
 	case resource:   return boost::lexical_cast<std::string>(val);
-	case creature:   return (val? boost::lexical_cast<std::string>(val) + " " : "") + CGI->creh->objects[subtype]->*(val != 1 ? &CCreature::namePl : &CCreature::nameSing);
+	case creature:
+		{
+			auto creature = CGI->creh->getByIndex(subtype);
+			if ( val )
+				return boost::lexical_cast<std::string>(val) + " " + (val > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated());
+			else
+				return val > 1 ? creature->getNamePluralTranslated() : creature->getNameSingularTranslated();
+		}
 	case artifact:   return CGI->artifacts()->getByIndex(subtype)->getName();
 	case experience:
 		{

+ 6 - 6
client/widgets/CGarrisonInt.cpp

@@ -53,18 +53,18 @@ void CGarrisonSlot::hover (bool on)
 				if(owner->getSelection() == this)
 				{
 					temp = CGI->generaltexth->tcommands[4]; //View %s
-					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+					boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
 				}
 				else if (owner->getSelection()->creature == creature)
 				{
 					temp = CGI->generaltexth->tcommands[2]; //Combine %s armies
-					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+					boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
 				}
 				else if (owner->getSelection()->creature)
 				{
 					temp = CGI->generaltexth->tcommands[7]; //Exchange %s with %s
-					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
-					boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->getNameSingularTranslated());
+					boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
 				}
 				else
 				{
@@ -92,7 +92,7 @@ void CGarrisonSlot::hover (bool on)
 				{
 					temp = CGI->generaltexth->tcommands[32]; //Select %s (visiting)
 				}
-				boost::algorithm::replace_first(temp,"%s",creature->nameSing);
+				boost::algorithm::replace_first(temp,"%s",creature->getNameSingularTranslated());
 			}
 		}
 		else
@@ -110,7 +110,7 @@ void CGarrisonSlot::hover (bool on)
 				else
 				{
 					temp = CGI->generaltexth->tcommands[6]; //Move %s
-					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->nameSing);
+					boost::algorithm::replace_first(temp,"%s",owner->getSelection()->creature->getNameSingularTranslated());
 				}
 			}
 			else

+ 9 - 9
client/windows/CCastleInterface.cpp

@@ -242,7 +242,7 @@ std::string CBuildingRect::getSubtitle()//hover text for building
 		if(availableCreatures.size())
 		{
 			int creaID = availableCreatures.back();//taking last of available creatures
-			return CGI->generaltexth->allTexts[16] + " " + CGI->creh->objects.at(creaID)->namePl;
+			return CGI->generaltexth->allTexts[16] + " " + CGI->creh->objects.at(creaID)->getNamePluralTranslated();
 		}
 		else
 		{
@@ -284,7 +284,7 @@ CDwellingInfoBox::CDwellingInfoBox(int centerX, int centerY, const CGTownInstanc
 
 	const CCreature * creature = CGI->creh->objects.at(Town->creatures.at(level).second.back());
 
-	title = std::make_shared<CLabel>(80, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, creature->namePl);
+	title = std::make_shared<CLabel>(80, 30, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, creature->getNamePluralTranslated());
 	animation = std::make_shared<CCreaturePic>(30, 44, creature, true, true);
 
 	std::string text = boost::lexical_cast<std::string>(Town->creatures.at(level).first);
@@ -1036,7 +1036,7 @@ void CCreaInfo::update()
 void CCreaInfo::hover(bool on)
 {
 	std::string message = CGI->generaltexth->allTexts[588];
-	boost::algorithm::replace_first(message, "%s", creature->namePl);
+	boost::algorithm::replace_first(message, "%s", creature->getNamePluralTranslated());
 
 	if(on)
 	{
@@ -1064,7 +1064,7 @@ void CCreaInfo::clickLeft(tribool down, bool previousState)
 std::string CCreaInfo::genGrowthText()
 {
 	GrowthInfo gi = town->getGrowthInfo(level);
-	std::string descr = boost::str(boost::format(CGI->generaltexth->allTexts[589]) % creature->nameSing % gi.totalGrowth());
+	std::string descr = boost::str(boost::format(CGI->generaltexth->allTexts[589]) % creature->getNameSingularTranslated() % gi.totalGrowth());
 
 	for(const GrowthInfo::Entry & entry : gi.entries)
 		descr +="\n" + entry.description;
@@ -1641,9 +1641,9 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
 
 	if(getMyCreature() != nullptr)
 	{
-		hoverText = boost::str(boost::format(CGI->generaltexth->tcommands[21]) % getMyCreature()->namePl);
+		hoverText = boost::str(boost::format(CGI->generaltexth->tcommands[21]) % getMyCreature()->getNamePluralTranslated());
 		new CCreaturePic(159, 4, getMyCreature(), false);
-		new CLabel(78,  11, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, getMyCreature()->namePl);
+		new CLabel(78,  11, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, getMyCreature()->getNamePluralTranslated());
 
 		Rect sizes(287, 4, 96, 18);
 		values.push_back(std::make_shared<LabeledValue>(sizes, CGI->generaltexth->allTexts[190], CGI->generaltexth->fcommands[0], getMyCreature()->getAttack(false)));
@@ -1807,15 +1807,15 @@ CBlacksmithDialog::CBlacksmithDialog(bool possible, CreatureID creMachineID, Art
 	anim->clipRect(113,125,200,150);
 
 	title = std::make_shared<CLabel>(165, 28, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW,
-	            boost::str(boost::format(CGI->generaltexth->allTexts[274]) % creature->nameSing));
+				boost::str(boost::format(CGI->generaltexth->allTexts[274]) % creature->getNameSingularTranslated()));
 	costText = std::make_shared<CLabel>(165, 218, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, CGI->generaltexth->jktexts[43]);
 	costValue = std::make_shared<CLabel>(165, 290, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE,
 	                boost::lexical_cast<std::string>(aid.toArtifact(CGI->artifacts())->getPrice()));
 
-	std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->nameSing);
+	std::string text = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % creature->getNameSingularTranslated());
 	buy = std::make_shared<CButton>(Point(42, 312), "IBUY30.DEF", CButton::tooltip(text), [&](){ close(); }, SDLK_RETURN);
 
-	text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->nameSing);
+	text = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % creature->getNameSingularTranslated());
 	cancel = std::make_shared<CButton>(Point(224, 312), "ICANCEL.DEF", CButton::tooltip(text), [&](){ close(); }, SDLK_ESCAPE);
 
 	if(possible)

+ 3 - 3
client/windows/CCreatureWindow.cpp

@@ -86,9 +86,9 @@ public:
 	std::string getName() const
 	{
 		if(commander)
-			return commander->type->nameSing;
+			return commander->type->getNameSingularTranslated();
 		else
-			return creature->namePl;
+			return creature->getNamePluralTranslated();
 	}
 private:
 
@@ -866,7 +866,7 @@ std::string CStackWindow::generateStackExpDescription()
 		tier = 0;
 	int number;
 	std::string expText = CGI->generaltexth->translate("vcmi.stackExperience.description");
-	boost::replace_first(expText, "%s", creature->namePl);
+	boost::replace_first(expText, "%s", creature->getNamePluralTranslated());
 	boost::replace_first(expText, "%s", CGI->generaltexth->translate("vcmi.stackExperience.rank", rank));
 	boost::replace_first(expText, "%i", boost::lexical_cast<std::string>(rank));
 	boost::replace_first(expText, "%i", boost::lexical_cast<std::string>(stack->experience));

+ 3 - 3
client/windows/CTradeWindow.cpp

@@ -244,7 +244,7 @@ void CTradeWindow::CTradeableItem::hover(bool on)
 	{
 	case CREATURE:
 	case CREATURE_PLACEHOLDER:
-		GH.statusbar->write(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->objects[id]->namePl));
+		GH.statusbar->write(boost::str(boost::format(CGI->generaltexth->allTexts[481]) % CGI->creh->objects[id]->getNamePluralTranslated()));
 		break;
 	case ARTIFACT_PLACEHOLDER:
 		if(id < 0)
@@ -285,9 +285,9 @@ std::string CTradeWindow::CTradeableItem::getName(int number) const
 		return CGI->generaltexth->restypes[id];
 	case CREATURE:
 		if(number == 1)
-			return CGI->creh->objects[id]->nameSing;
+			return CGI->creh->objects[id]->getNameSingularTranslated();
 		else
-			return CGI->creh->objects[id]->namePl;
+			return CGI->creh->objects[id]->getNamePluralTranslated();
 	case ARTIFACT_TYPE:
 	case ARTIFACT_INSTANCE:
 		return CGI->artifacts()->getByIndex(id)->getName();

+ 6 - 6
client/windows/GUIClasses.cpp

@@ -140,7 +140,7 @@ void CRecruitmentWindow::select(std::shared_ptr<CCreatureCard> card)
 		totalCostValue->set(card->creature->cost * maxAmount);
 
 		//Recruit %s
-		title->setText(boost::str(boost::format(CGI->generaltexth->tcommands[21]) % card->creature->namePl));
+		title->setText(boost::str(boost::format(CGI->generaltexth->tcommands[21]) % card->creature->getNamePluralTranslated()));
 
 		maxButton->block(maxAmount == 0);
 		slider->block(maxAmount == 0);
@@ -158,7 +158,7 @@ void CRecruitmentWindow::buy()
 		if(dst->ID == Obj::HERO)
 		{
 			txt = CGI->generaltexth->allTexts[425]; //The %s would join your hero, but there aren't enough provisions to support them.
-			boost::algorithm::replace_first(txt, "%s", slider->getValue() > 1 ? CGI->creh->objects[crid]->namePl : CGI->creh->objects[crid]->nameSing);
+			boost::algorithm::replace_first(txt, "%s", slider->getValue() > 1 ? CGI->creh->objects[crid]->getNamePluralTranslated() : CGI->creh->objects[crid]->getNameSingularTranslated());
 		}
 		else
 		{
@@ -332,7 +332,7 @@ CSplitWindow::CSplitWindow(const CCreature * creature, std::function<void(int, i
 	slider = std::make_shared<CSlider>(Point(21, 194), 257, std::bind(&CSplitWindow::sliderMoved, this, _1), 0, sliderPosition, rightAmount - rightMin, true);
 
 	std::string titleStr = CGI->generaltexth->allTexts[256];
-	boost::algorithm::replace_first(titleStr,"%s", creature->namePl);
+	boost::algorithm::replace_first(titleStr,"%s", creature->getNamePluralTranslated());
 	title = std::make_shared<CLabel>(150, 34, FONT_BIG, ETextAlignment::CENTER, Colors::YELLOW, titleStr);
 }
 
@@ -1681,7 +1681,7 @@ CGarrisonWindow::CGarrisonWindow(const CArmedInstance * up, const CGHeroInstance
 		if(up->Slots().size() > 0)
 		{
 			titleText = CGI->generaltexth->allTexts[35];
-			boost::algorithm::replace_first(titleText, "%s", up->Slots().begin()->second->type->namePl);
+			boost::algorithm::replace_first(titleText, "%s", up->Slots().begin()->second->type->getNamePluralTranslated());
 		}
 		else
 		{
@@ -1878,9 +1878,9 @@ std::string CHillFortWindow::getTextForSlot(SlotID slot)
 	std::string str = CGI->generaltexth->allTexts[318];
 	int amount = hero->getStackCount(slot);
 	if(amount == 1)
-		boost::algorithm::replace_first(str,"%s",hero->getCreature(slot)->nameSing);
+		boost::algorithm::replace_first(str,"%s",hero->getCreature(slot)->getNameSingularTranslated());
 	else
-		boost::algorithm::replace_first(str,"%s",hero->getCreature(slot)->namePl);
+		boost::algorithm::replace_first(str,"%s",hero->getCreature(slot)->getNamePluralTranslated());
 
 	return str;
 }

+ 11 - 2
include/vcmi/Creature.h

@@ -18,9 +18,18 @@ class CreatureID;
 
 class DLL_LINKAGE Creature : public EntityWithBonuses<CreatureID>
 {
+protected:
+	using EntityWithBonuses<CreatureID>::getName;
+
+	virtual std::string getNameTranslated() const = 0;
+	virtual std::string getNameTextID() const = 0;
 public:
-	virtual const std::string & getPluralName() const = 0;
-	virtual const std::string & getSingularName() const = 0;
+	virtual std::string getNamePluralTranslated() const = 0;
+	virtual std::string getNameSingularTranslated() const = 0;
+
+	virtual std::string getNamePluralTextID() const = 0;
+	virtual std::string getNameSingularTextID() const = 0;
+
 	virtual uint32_t getMaxHealth() const = 0;
 
 	virtual int32_t getAdvMapAmountMin() const = 0;

+ 1 - 1
lib/CBonusTypeHandler.cpp

@@ -145,7 +145,7 @@ std::string CBonusTypeHandler::bonusToString(const std::shared_ptr<Bonus> & bonu
 		else if(name == "subtype.creature")
 		{
 			 const CreatureID cre(bonus->subtype);
-			 return cre.toCreature()->namePl;
+			 return cre.toCreature()->getNamePluralTranslated();
 		}
 		else if(name == "subtype.spell")
 		{

+ 35 - 18
lib/CCreatureHandler.cpp

@@ -35,7 +35,7 @@ int32_t CCreature::getIconIndex() const
 
 const std::string & CCreature::getName() const
 {
-	return nameSing;//???
+	return identifier;
 }
 
 const std::string & CCreature::getJsonKey() const
@@ -64,16 +64,6 @@ uint32_t CCreature::getMaxHealth() const
 	return CBonusSystemNode::MaxHealth();
 }
 
-const std::string & CCreature::getPluralName() const
-{
-	return namePl;
-}
-
-const std::string & CCreature::getSingularName() const
-{
-	return nameSing;
-}
-
 int32_t CCreature::getAdvMapAmountMin() const
 {
 	return ammMin;
@@ -170,6 +160,36 @@ int32_t CCreature::getCost(int32_t resIndex) const
 		return 0;
 }
 
+std::string CCreature::getNameTranslated() const
+{
+	return getNameSingularTranslated();
+}
+
+std::string CCreature::getNamePluralTranslated() const
+{
+	return VLC->generaltexth->translate(getNamePluralTextID());
+}
+
+std::string CCreature::getNameSingularTranslated() const
+{
+	return VLC->generaltexth->translate(getNameSingularTextID());
+}
+
+std::string CCreature::getNameTextID() const
+{
+	return getNameSingularTextID();
+}
+
+std::string CCreature::getNamePluralTextID() const
+{
+	return TextIdentifier("creatures", modScope, identifier, "name", "plural" ).get();
+}
+
+std::string CCreature::getNameSingularTextID() const
+{
+	return TextIdentifier("creatures", modScope, identifier, "name", "singular" ).get();
+}
+
 int CCreature::getQuantityID(const int & quantity)
 {
 	if (quantity<5)
@@ -281,7 +301,7 @@ bool CCreature::valid() const
 
 std::string CCreature::nodeName() const
 {
-	return "\"" + namePl + "\"";
+	return "\"" + getNamePluralTextID() + "\"";
 }
 
 bool CCreature::isItNativeTerrain(TerrainId terrain) const
@@ -344,12 +364,6 @@ void CCreature::updateFrom(const JsonNode & data)
 
 void CCreature::serializeJson(JsonSerializeFormat & handler)
 {
-	{
-		auto nameNode = handler.enterStruct("name");
-		handler.serializeString("singular", nameSing);
-		handler.serializeString("plural", namePl);
-	}
-
 	handler.serializeInt("fightValue", fightValue);
 	handler.serializeInt("aiValue", AIValue);
 	handler.serializeInt("growth", growth);
@@ -599,6 +613,9 @@ CCreature * CCreatureHandler::loadFromJson(const std::string & scope, const Json
 
 	cre->cost = Res::ResourceSet(node["cost"]);
 
+	VLC->generaltexth->registerString(cre->getNameSingularTextID(), node["name"]["singular"].String());
+	VLC->generaltexth->registerString(cre->getNamePluralTextID(), node["name"]["plural"].String());
+
 	cre->addBonus(node["hitPoints"].Integer(), Bonus::STACK_HEALTH);
 	cre->addBonus(node["speed"].Integer(), Bonus::STACKS_SPEED);
 	cre->addBonus(node["attack"].Integer(), Bonus::PRIMARY_SKILL, PrimarySkill::ATTACK);

+ 17 - 12
lib/CCreatureHandler.h

@@ -29,16 +29,21 @@ class JsonSerializeFormat;
 
 class DLL_LINKAGE CCreature : public Creature, public CBonusSystemNode
 {
-public:
+	friend class CCreatureHandler;
+	std::string modScope;
 	std::string identifier;
 
-	std::string nameRef; // reference name, stringID
-	std::string nameSing;// singular name, e.g. Centaur
-	std::string namePl;  // plural name, e.g. Centaurs
+//	std::string nameRef; // reference name, stringID
+//	std::string nameSing;// singular name, e.g. Centaur
+//	std::string namePl;  // plural name, e.g. Centaurs
 
-	std::string abilityText; //description of abilities
+	const std::string & getName() const override;
+	std::string getNameTranslated() const override;
+	std::string getNameTextID() const override;
 
+public:
 	CreatureID idNumber;
+
 	TFaction faction;
 	ui8 level; // 0 - unknown; 1-7 for "usual" creatures
 
@@ -132,6 +137,12 @@ public:
 
 	ArtifactID warMachine;
 
+	std::string getNamePluralTranslated() const override;
+	std::string getNameSingularTranslated() const override;
+
+	std::string getNamePluralTextID() const override;
+	std::string getNameSingularTextID() const override;
+
 	bool isItNativeTerrain(TerrainId terrain) const;
 	/**
 	Returns creature native terrain considering some terrain bonuses.
@@ -142,13 +153,10 @@ public:
 	TerrainId getNativeTerrain() const;
 	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;
 	CreatureID getId() const override;
 	virtual const IBonusBearer * accessBonuses() const override;
-	const std::string & getPluralName() const override;
-	const std::string & getSingularName() const override;
 	uint32_t getMaxHealth() const override;
 
 	int32_t getAdvMapAmountMin() const override;
@@ -199,9 +207,6 @@ public:
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
 		h & static_cast<CBonusSystemNode&>(*this);
-		h & namePl;
-		h & nameSing;
-		h & nameRef;
 		h & cost;
 		h & upgrades;
 		h & fightValue;
@@ -211,7 +216,6 @@ public:
 		h & ammMin;
 		h & ammMax;
 		h & level;
-		h & abilityText;
 		h & animDefName;
 		h & advMapDef;
 		h & iconIndex;
@@ -226,6 +230,7 @@ public:
 		h & doubleWide;
 		h & special;
 		h & identifier;
+		h & modScope;
 		h & warMachine;
 	}
 

+ 4 - 4
lib/CCreatureSet.cpp

@@ -384,7 +384,7 @@ std::string CCreatureSet::getArmyDescription() const
 	std::vector<std::string> guards;
 	for(auto & elem : stacks)
 	{
-		auto str = boost::str(boost::format("%s %s") % getRoughAmount(elem.first, 2) % getCreature(elem.first)->namePl);
+		auto str = boost::str(boost::format("%s %s") % getRoughAmount(elem.first, 2) % getCreature(elem.first)->getNamePluralTranslated());
 		guards.push_back(str);
 	}
 	if(guards.size())
@@ -843,7 +843,7 @@ std::string CStackInstance::nodeName() const
 	std::ostringstream oss;
 	oss << "Stack of " << count << " of ";
 	if(type)
-		oss << type->namePl;
+		oss << type->getNamePluralTextID();
 	else if(idRand >= 0)
 		oss << "[no type, idRand=" << idRand << "]";
 	else
@@ -875,7 +875,7 @@ CreatureID CStackInstance::getCreatureID() const
 
 std::string CStackInstance::getName() const
 {
-	return (count > 1) ? type->namePl : type->nameSing;
+	return (count > 1) ? type->getNamePluralTranslated() : type->getNameSingularTranslated();
 }
 
 ui64 CStackInstance::getPower() const
@@ -1044,7 +1044,7 @@ void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler)
 	{
 		if(type)
 		{
-			std::string typeName = type->identifier;
+			std::string typeName = type->getJsonKey();
 			handler.serializeString("type", typeName);
 		}
 	}

+ 2 - 2
lib/CGameState.cpp

@@ -99,7 +99,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
 	{
 		auto cre = CreatureID(ser).toCreature(VLC->creatures());
 		if(cre)
-			dst = cre->getPluralName();
+			dst = cre->getNamePluralTranslated();
 		else
 			dst = "#!#";
 	}
@@ -107,7 +107,7 @@ void MetaString::getLocalString(const std::pair<ui8,ui32> &txt, std::string &dst
 	{
 		auto cre = CreatureID(ser).toCreature(VLC->creatures());
 		if(cre)
-			dst = cre->getSingularName();
+			dst = cre->getNameSingularTranslated();
 		else
 			dst = "#!#";
 	}

+ 2 - 2
lib/CStack.cpp

@@ -181,7 +181,7 @@ std::string CStack::nodeName() const
 	oss << owner.getStr();
 	oss << " battle stack [" << ID << "]: " << getCount() << " of ";
 	if(type)
-		oss << type->namePl;
+		oss << type->getNamePluralTextID();
 	else
 		oss << "[UNDEFINED TYPE]";
 
@@ -320,7 +320,7 @@ bool CStack::isMeleeAttackPossible(const battle::Unit * attacker, const battle::
 
 std::string CStack::getName() const
 {
-	return (getCount() == 1) ? type->nameSing : type->namePl; //War machines can't use base
+	return (getCount() == 1) ? type->getNameSingularTranslated() : type->getNamePluralTranslated(); //War machines can't use base
 }
 
 bool CStack::canBeHealed() const

+ 1 - 1
lib/CStack.h

@@ -128,7 +128,7 @@ public:
 			else if(!army || extSlot == SlotID() || !army->hasStackAtSlot(extSlot))
 			{
 				base = nullptr;
-				logGlobal->warn("%s doesn't have a base stack!", type->nameSing);
+				logGlobal->warn("%s doesn't have a base stack!", type->getNameSingularTranslated());
 			}
 			else
 			{

+ 1 - 1
lib/CTownHandler.cpp

@@ -758,7 +758,7 @@ void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source)
 		if(!(*VLC->creh)[crId]->animation.missleFrameAngles.size())
 			logMod->error("Mod '%s' error: Creature '%s' on the Archer's tower is not a shooter. Mod should be fixed. Siege will not work properly!"
 				, town.faction->name
-				, (*VLC->creh)[crId]->nameSing);
+				, (*VLC->creh)[crId]->getNameSingularTranslated());
 
 		town.clientInfo.siegeShooter = crId;
 	});

+ 3 - 3
lib/HeroBonus.cpp

@@ -1600,7 +1600,7 @@ std::string Bonus::Description() const
 				str << SpellID(sid).toSpell(VLC->spells())->getNameTranslated();
 				break;
 			case CREATURE_ABILITY:
-				str << VLC->creh->objects[sid]->namePl;
+				str << VLC->creh->objects[sid]->getNamePluralTranslated();
 				break;
 			case SECONDARY_SKILL:
 				str << VLC->skillh->getByIndex(sid)->getNameTranslated();
@@ -2006,7 +2006,7 @@ void CCreatureTypeLimiter::setCreature (CreatureID id)
 std::string CCreatureTypeLimiter::toString() const
 {
 	boost::format fmt("CCreatureTypeLimiter(creature=%s, includeUpgrades=%s)");
-	fmt % creature->identifier % (includeUpgrades ? "true" : "false");
+	fmt % creature->getJsonKey() % (includeUpgrades ? "true" : "false");
 	return fmt.str();
 }
 
@@ -2015,7 +2015,7 @@ JsonNode CCreatureTypeLimiter::toJsonNode() const
 	JsonNode root(JsonNode::JsonType::DATA_STRUCT);
 
 	root["type"].String() = "CREATURE_TYPE_LIMITER";
-	root["parameters"].Vector().push_back(JsonUtils::stringNode(creature->identifier));
+	root["parameters"].Vector().push_back(JsonUtils::stringNode(creature->getJsonKey()));
 	root["parameters"].Vector().push_back(JsonUtils::boolNode(includeUpgrades));
 
 	return root;

+ 1 - 1
lib/battle/BattleInfo.cpp

@@ -52,7 +52,7 @@ void BattleInfo::calculateCasualties(std::map<ui32,si32> * casualties) const
 		const CStack * const st = elem;
 		si32 killed = st->getKilled();
 		if(killed > 0)
-			casualties[st->side][st->getCreature()->idNumber] += killed;
+			casualties[st->side][st->getCreature()->getId()] += killed;
 	}
 }
 

+ 1 - 1
lib/battle/CBattleInfoCallback.cpp

@@ -701,7 +701,7 @@ bool CBattleInfoCallback::battleCanAttack(const CStack * stack, const CStack * t
 	if(!battleMatchOwner(stack, target))
 		return false;
 
-	auto &id = stack->getCreature()->idNumber;
+	auto id = stack->getCreature()->getId();
 	if (id == CreatureID::FIRST_AID_TENT || id == CreatureID::CATAPULT)
 		return false;
 

+ 1 - 1
lib/battle/CUnitState.cpp

@@ -443,7 +443,7 @@ int32_t CUnitState::creatureIndex() const
 
 CreatureID CUnitState::creatureId() const
 {
-	return unitType()->idNumber;
+	return unitType()->getId();
 }
 
 int32_t CUnitState::creatureLevel() const

+ 3 - 3
lib/mapObjects/CBank.cpp

@@ -59,7 +59,7 @@ void CBank::setConfig(const BankConfig & config)
 	clear(); // remove all stacks, if any
 
 	for (auto & stack : config.guards)
-		setCreature (SlotID(stacksCount()), stack.type->idNumber, stack.count);
+		setCreature (SlotID(stacksCount()), stack.type->getId(), stack.count);
 }
 
 void CBank::setPropertyDer (ui8 what, ui32 val)
@@ -255,7 +255,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 					return a.type->fightValue < b.type->fightValue;
 				})->type;
 
-				iw.text.addReplacement(MetaString::CRE_PL_NAMES, strongest->idNumber);
+				iw.text.addReplacement(MetaString::CRE_PL_NAMES, strongest->getId());
 				iw.text.addReplacement(loot.buildList());
 			}
 			cb->showInfoDialog(&iw);
@@ -309,7 +309,7 @@ void CBank::doVisit(const CGHeroInstance * hero) const
 		CCreatureSet ourArmy;
 		for (auto slot : bc->creatures)
 		{
-			ourArmy.addToSlot(ourArmy.getSlotFor(slot.type->idNumber), slot.type->idNumber, slot.count);
+			ourArmy.addToSlot(ourArmy.getSlotFor(slot.type->idNumber), slot.type->getId(), slot.count);
 		}
 
 		for (auto & elem : ourArmy.Slots())

+ 1 - 1
lib/mapObjects/MiscObjects.cpp

@@ -200,7 +200,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
 			std::string tmp = VLC->generaltexth->advobtxt[90];
 			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(getStackCount(SlotID(0))));
 			boost::algorithm::replace_first(tmp,"%d",boost::lexical_cast<std::string>(action));
-			boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->namePl);
+			boost::algorithm::replace_first(tmp,"%s",VLC->creh->objects[subID]->getNamePluralTranslated());
 			ynd.text << tmp;
 			cb->showBlockingDialog(&ynd);
 			break;

+ 1 - 1
lib/mapping/CMap.cpp

@@ -543,7 +543,7 @@ void CMap::checkForObjectives()
 					break;
 
 				case EventCondition::HAVE_CREATURES:
-					boost::algorithm::replace_first(event.onFulfill, "%s", VLC->creh->objects[cond.objectType]->nameSing);
+					boost::algorithm::replace_first(event.onFulfill, "%s", VLC->creh->objects[cond.objectType]->getNameSingularTranslated());
 					boost::algorithm::replace_first(event.onFulfill, "%d", boost::lexical_cast<std::string>(cond.value));
 					break;
 

+ 2 - 2
mapeditor/inspector/armywidget.cpp

@@ -38,8 +38,8 @@ ArmyWidget::ArmyWidget(CArmedInstance & a, QWidget *parent) :
 		for(int c = 0; c < VLC->creh->objects.size(); ++c)
 		{
 			auto creature = VLC->creh->objects[c];
-			uiSlots[i]->insertItem(c + 1, tr(creature->getPluralName().c_str()));
-			uiSlots[i]->setItemData(c + 1, creature->getId().getNum());
+			uiSlots[i]->insertItem(c + 1, creature->getNamePluralTranslated().c_str());
+			uiSlots[i]->setItemData(c + 1, creature->getIndex());
 		}
 	}
 	

+ 1 - 1
mapeditor/inspector/rewardswidget.cpp

@@ -97,7 +97,7 @@ QList<QString> RewardsWidget::getListForType(RewardType typeId)
 		case RewardType::CREATURE:
 			for(auto creature : VLC->creh->objects)
 			{
-				result.append(QString::fromStdString(creature->getName()));
+				result.append(QString::fromStdString(creature->getNameSingularTranslated()));
 			}
 			break;
 	}

+ 3 - 3
server/CGameHandler.cpp

@@ -1305,11 +1305,11 @@ void CGameHandler::addGenericKilledLog(BattleLogMessage & blm, const CStack * de
 		boost::format txt(formatString);
 		if(killed > 1)
 		{
-			txt % killed % (multiple ? VLC->generaltexth->allTexts[43] : defender->getCreature()->namePl); // creatures perish
+			txt % killed % (multiple ? VLC->generaltexth->allTexts[43] : defender->getCreature()->getNamePluralTranslated()); // creatures perish
 		}
 		else //killed == 1
 		{
-			txt % (multiple ? VLC->generaltexth->allTexts[42] : defender->getCreature()->nameSing); // creature perishes
+			txt % (multiple ? VLC->generaltexth->allTexts[42] : defender->getCreature()->getNameSingularTranslated()); // creature perishes
 		}
 		MetaString line;
 		line << txt.str();
@@ -6423,7 +6423,7 @@ bool CGameHandler::addToSlot(const StackLocation &sl, const CCreature *c, TQuant
 		changeStackCount(sl, count);
 	else
 	{
-		COMPLAIN_RET("Cannot add " + c->namePl + " to slot " + boost::lexical_cast<std::string>(sl.slot) + "!");
+		COMPLAIN_RET("Cannot add " + c->getNamePluralTranslated() + " to slot " + boost::lexical_cast<std::string>(sl.slot) + "!");
 	}
 	return true;
 }