Bläddra i källkod

All text for factions/towns/building are passed through translator

Ivan Savenko 2 år sedan
förälder
incheckning
05a1d7c6e3

+ 2 - 2
AI/Nullkiller/AIGateway.cpp

@@ -1351,8 +1351,8 @@ bool AIGateway::moveHeroToTile(int3 dst, HeroPtr h)
 
 void AIGateway::buildStructure(const CGTownInstance * t, BuildingID building)
 {
-	auto name = t->town->buildings.at(building)->Name();
-	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->name, t->pos.toString());
+	auto name = t->town->buildings.at(building)->getNameTranslated();
+	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->pos.toString());
 	cb->buildBuilding(t, building); //just do this;
 }
 

+ 2 - 2
AI/Nullkiller/Analyzers/BuildAnalyzer.cpp

@@ -130,7 +130,7 @@ void BuildAnalyzer::update()
 
 	for(const CGTownInstance* town : towns)
 	{
-		logAi->trace("Checking town %s", town->name);
+		logAi->trace("Checking town %s", town->getNameTranslated());
 
 		developmentInfos.push_back(TownDevelopmentInfo(town));
 		TownDevelopmentInfo & developmentInfo = developmentInfos.back();
@@ -351,7 +351,7 @@ BuildingInfo::BuildingInfo(
 	dailyIncome = building->produce;
 	exists = town->hasBuilt(id);
 	prerequisitesCount = 1;
-	name = building->Name();
+	name = building->getNameTranslated();
 
 	if(creature)
 	{

+ 6 - 6
AI/Nullkiller/Behaviors/DefenceBehavior.cpp

@@ -50,14 +50,14 @@ Goals::TGoalVec DefenceBehavior::decompose() const
 
 void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInstance * town) const
 {
-	logAi->trace("Evaluating defence for %s", town->name);
+	logAi->trace("Evaluating defence for %s", town->getNameTranslated());
 
 	auto treatNode = ai->nullkiller->dangerHitMap->getObjectTreat(town);
 	auto treats = { treatNode.fastestDanger, treatNode.maximumDanger };
 
 	if(!treatNode.fastestDanger.hero)
 	{
-		logAi->trace("No treat found for town %s", town->name);
+		logAi->trace("No treat found for town %s", town->getNameTranslated());
 
 		return;
 	}
@@ -79,7 +79,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 		logAi->trace(
 			"Hero %s in garrison of town %s is suposed to defend the town",
 			town->garrisonHero->getNameTranslated(),
-			town->name);
+			town->getNameTranslated());
 
 		return;
 	}
@@ -88,7 +88,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 	if(reinforcement)
 	{
-		logAi->trace("Town %s can buy defence army %lld", town->name, reinforcement);
+		logAi->trace("Town %s can buy defence army %lld", town->getNameTranslated(), reinforcement);
 		tasks.push_back(Goals::sptr(Goals::BuyArmy(town, reinforcement).setpriority(0.5f)));
 	}
 
@@ -98,7 +98,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 	{
 		logAi->trace(
 			"Town %s has treat %lld in %s turns, hero: %s",
-			town->name,
+			town->getNameTranslated(),
 			treat.danger,
 			std::to_string(treat.turn),
 			treat.hero->getNameTranslated());
@@ -187,7 +187,7 @@ void DefenceBehavior::evaluateDefence(Goals::TGoalVec & tasks, const CGTownInsta
 
 		if(paths.empty())
 		{
-			logAi->trace("No ways to defend town %s", town->name);
+			logAi->trace("No ways to defend town %s", town->getNameTranslated());
 
 			continue;
 		}

+ 2 - 2
AI/Nullkiller/Goals/BuildThis.cpp

@@ -46,7 +46,7 @@ bool BuildThis::operator==(const BuildThis & other) const
 
 std::string BuildThis::toString() const
 {
-	return "Build " + buildingInfo.name + " in " + town->name;
+	return "Build " + buildingInfo.name + " in " + town->getNameTranslated();
 }
 
 void BuildThis::accept(AIGateway * ai)
@@ -58,7 +58,7 @@ void BuildThis::accept(AIGateway * ai)
 		if(cb->canBuildStructure(town, b) == EBuildingState::ALLOWED)
 		{
 			logAi->debug("Player %d will build %s in town of %s at %s",
-				ai->playerID, town->town->buildings.at(b)->Name(), town->name, town->pos.toString());
+				ai->playerID, town->town->buildings.at(b)->getNameTranslated(), town->getNameTranslated(), town->pos.toString());
 			cb->buildBuilding(town, b);
 
 			return;

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

@@ -29,7 +29,7 @@ bool BuyArmy::operator==(const BuyArmy & other) const
 
 std::string BuyArmy::toString() const
 {
-	return "Buy army at " + town->name;
+	return "Buy army at " + town->getNameTranslated();
 }
 
 void BuyArmy::accept(AIGateway * ai)

+ 3 - 3
AI/Nullkiller/Goals/ExchangeSwapTownHeroes.cpp

@@ -33,7 +33,7 @@ ExchangeSwapTownHeroes::ExchangeSwapTownHeroes(
 
 std::string ExchangeSwapTownHeroes::toString() const
 {
-	return "Exchange and swap heroes of " + town->name;
+	return "Exchange and swap heroes of " + town->getNameTranslated();
 }
 
 bool ExchangeSwapTownHeroes::operator==(const ExchangeSwapTownHeroes & other) const
@@ -60,7 +60,7 @@ void ExchangeSwapTownHeroes::accept(AIGateway * ai)
 
 		ai->buildArmyIn(town);
 		ai->nullkiller->unlockHero(currentGarrisonHero.get());
-		logAi->debug("Extracted hero %s from garrison of %s", currentGarrisonHero->getNameTranslated(), town->name);
+		logAi->debug("Extracted hero %s from garrison of %s", currentGarrisonHero->getNameTranslated(), town->getNameTranslated());
 
 		return;
 	}
@@ -91,7 +91,7 @@ void ExchangeSwapTownHeroes::accept(AIGateway * ai)
 		ai->makePossibleUpgrades(town->visitingHero);
 	}
 
-	logAi->debug("Put hero %s to garrison of %s", garrisonHero->getNameTranslated(), town->name);
+	logAi->debug("Put hero %s to garrison of %s", garrisonHero->getNameTranslated(), town->getNameTranslated());
 }
 
 }

+ 3 - 3
AI/Nullkiller/Goals/RecruitHero.cpp

@@ -26,7 +26,7 @@ using namespace Goals;
 
 std::string RecruitHero::toString() const
 {
-	return "Recruit hero at " + town->name;
+	return "Recruit hero at " + town->getNameTranslated();
 }
 
 void RecruitHero::accept(AIGateway * ai)
@@ -40,7 +40,7 @@ void RecruitHero::accept(AIGateway * ai)
 		throw cannotFulfillGoalException("No town to recruit hero!");
 	}
 
-	logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
+	logAi->debug("Trying to recruit a hero in %s at %s", t->getNameTranslated(), t->visitablePos().toString());
 
 	auto heroes = cb->getAvailableHeroes(t);
 
@@ -78,4 +78,4 @@ void RecruitHero::accept(AIGateway * ai)
 		ai->moveHeroToTile(t->visitablePos(), t->visitingHero.get());
 }
 
-}
+}

+ 1 - 1
AI/Nullkiller/Pathfinding/Actions/TownPortalAction.cpp

@@ -34,7 +34,7 @@ void TownPortalAction::execute(const CGHeroInstance * hero) const
 
 std::string TownPortalAction::toString() const
 {
-	return "Town Portal to " + target->name;
+	return "Town Portal to " + target->getNameTranslated();
 }
 /*
 bool TownPortalAction::canAct(const CGHeroInstance * hero, const AIPathNode * source) const

+ 1 - 1
AI/Nullkiller/Pathfinding/Actors.cpp

@@ -463,5 +463,5 @@ TownGarrisonActor::TownGarrisonActor(const CGTownInstance * town, uint64_t chain
 
 std::string TownGarrisonActor::toString() const
 {
-	return town->name;
+	return town->getNameTranslated();
 }

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

@@ -41,5 +41,5 @@ TSubgoal BuyArmy::whatToDoToAchieve()
 
 std::string BuyArmy::completeMessage() const
 {
-	return boost::format("Bought army of value %d in town of %s") % value, town->name;
+	return boost::format("Bought army of value %d in town of %s") % value, town->getNameTranslated();
 }

+ 5 - 5
AI/VCAI/VCAI.cpp

@@ -1427,7 +1427,7 @@ void VCAI::wander(HeroPtr h)
 			{
 				//TODO pick the truly best
 				const CGTownInstance * t = *boost::max_element(townsNotReachable, compareReinforcements);
-				logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->getNameTranslated(), t->name, t->visitablePos().toString());
+				logAi->debug("%s can't reach any town, we'll try to make our way to %s at %s", h->getNameTranslated(), t->getNameTranslated(), t->visitablePos().toString());
 				int3 pos1 = h->pos;
 				striveToGoal(sptr(Goals::ClearWayTo(t->visitablePos()).sethero(h))); //TODO: drop "strive", add to mainLoop
 				//if out hero is stuck, we may need to request another hero to clear the way we see
@@ -1997,8 +1997,8 @@ bool VCAI::moveHeroToTile(int3 dst, HeroPtr h)
 
 void VCAI::buildStructure(const CGTownInstance * t, BuildingID building)
 {
-	auto name = t->town->buildings.at(building)->Name();
-	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->name, t->pos.toString());
+	auto name = t->town->buildings.at(building)->getNameTranslated();
+	logAi->debug("Player %d will build %s in town of %s at %s", ai->playerID, name, t->getNameTranslated(), t->pos.toString());
 	cb->buildBuilding(t, building); //just do this;
 }
 
@@ -2081,7 +2081,7 @@ void VCAI::tryRealize(Goals::BuildThis & g)
 		if (cb->canBuildStructure(t, b) == EBuildingState::ALLOWED)
 		{
 			logAi->debug("Player %d will build %s in town of %s at %s",
-				playerID, t->town->buildings.at(b)->Name(), t->name, t->pos.toString());
+				playerID, t->town->buildings.at(b)->getNameTranslated(), t->getNameTranslated(), t->pos.toString());
 			cb->buildBuilding(t, b);
 			throw goalFulfilledException(sptr(g));
 		}
@@ -2439,7 +2439,7 @@ void VCAI::checkHeroArmy(HeroPtr h)
 
 void VCAI::recruitHero(const CGTownInstance * t, bool throwing)
 {
-	logAi->debug("Trying to recruit a hero in %s at %s", t->name, t->visitablePos().toString());
+	logAi->debug("Trying to recruit a hero in %s at %s", t->getNameTranslated(), t->visitablePos().toString());
 
 	auto heroes = cb->getAvailableHeroes(t);
 	if(heroes.size())

+ 1 - 1
client/battle/BattleInterfaceClasses.cpp

@@ -804,7 +804,7 @@ void StackQueue::update()
 
 int32_t StackQueue::getSiegeShooterIconID()
 {
-	return owner.siegeController->getSiegedTown()->town->faction->index;
+	return owner.siegeController->getSiegedTown()->town->faction->getIndex();
 }
 
 StackQueue::StackBox::StackBox(StackQueue * owner):

+ 3 - 3
client/battle/BattleSiegeController.cpp

@@ -65,7 +65,7 @@ std::string BattleSiegeController::getWallPieceImageName(EWallVisual::EWallVisua
 	{
 	case EWallVisual::BACKGROUND_WALL:
 		{
-			switch(town->town->faction->index)
+			switch(town->town->faction->getIndex())
 			{
 			case ETownType::RAMPART:
 			case ETownType::NECROPOLIS:
@@ -135,8 +135,8 @@ bool BattleSiegeController::getWallPieceExistance(EWallVisual::EWallVisual what)
 
 	switch (what)
 	{
-	case EWallVisual::MOAT:              return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->index != ETownType::TOWER;
-	case EWallVisual::MOAT_BANK:         return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->index != ETownType::TOWER && town->town->faction->index != ETownType::NECROPOLIS;
+	case EWallVisual::MOAT:              return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->getIndex() != ETownType::TOWER;
+	case EWallVisual::MOAT_BANK:         return town->hasBuilt(BuildingID::CITADEL) && town->town->faction->getIndex() != ETownType::TOWER && town->town->faction->getIndex() != ETownType::NECROPOLIS;
 	case EWallVisual::KEEP_BATTLEMENT:   return town->hasBuilt(BuildingID::CITADEL) && owner.curInt->cb->battleGetWallState(EWallPart::KEEP) != EWallState::DESTROYED;
 	case EWallVisual::UPPER_BATTLEMENT:  return town->hasBuilt(BuildingID::CASTLE) && owner.curInt->cb->battleGetWallState(EWallPart::UPPER_TOWER) != EWallState::DESTROYED;
 	case EWallVisual::BOTTOM_BATTLEMENT: return town->hasBuilt(BuildingID::CASTLE) && owner.curInt->cb->battleGetWallState(EWallPart::BOTTOM_TOWER) != EWallState::DESTROYED;

+ 1 - 1
client/lobby/CBonusSelection.cpp

@@ -203,7 +203,7 @@ void CBonusSelection::createBonusesIcons()
 			picNumber = -1;
 
 			if(vstd::contains((*CGI->townh)[faction]->town->buildings, buildID))
-				desc = (*CGI->townh)[faction]->town->buildings.find(buildID)->second->Name();
+				desc = (*CGI->townh)[faction]->town->buildings.find(buildID)->second->getNameTranslated();
 
 			break;
 		}

+ 1 - 1
client/lobby/OptionsTab.cpp

@@ -184,7 +184,7 @@ std::string OptionsTab::CPlayerSettingsHelper::getName()
 		default:
 		{
 			auto factionIndex = settings.castle >= CGI->townh->size() ? 0 : settings.castle;
-			return (*CGI->townh)[factionIndex]->name;
+			return (*CGI->townh)[factionIndex]->getNameTranslated();
 		}
 	}
 	}

+ 3 - 3
client/widgets/CComponent.cpp

@@ -172,7 +172,7 @@ std::string CComponent::getDescription()
 	case spell:      return (*CGI->spellh)[subtype]->getDescriptionTranslated(val);
 	case morale:     return CGI->generaltexth->heroscrn[ 4 - (val>0) + (val<0)];
 	case luck:       return CGI->generaltexth->heroscrn[ 7 - (val>0) + (val<0)];
-	case building:   return (*CGI->townh)[subtype]->town->buildings[BuildingID(val)]->Description();
+	case building:   return (*CGI->townh)[subtype]->town->buildings[BuildingID(val)]->getDescriptionTranslated();
 	case hero:       return "";
 	case flag:       return "";
 	}
@@ -228,10 +228,10 @@ std::string CComponent::getSubtitleInternal()
 			auto building = (*CGI->townh)[subtype]->town->buildings[BuildingID(val)];
 			if(!building)
 			{
-				logGlobal->error("Town of faction %s has no building #%d", (*CGI->townh)[subtype]->town->faction->name, val);
+				logGlobal->error("Town of faction %s has no building #%d", (*CGI->townh)[subtype]->town->faction->getNameTranslated(), val);
 				return (boost::format("Missing building #%d") % val).str();
 			}
-			return building->Name();
+			return building->getNameTranslated();
 		}
 	case hero:       return "";
 	case flag:       return CGI->generaltexth->capColors[subtype];

+ 29 - 29
client/windows/CCastleInterface.cpp

@@ -145,8 +145,8 @@ void CBuildingRect::clickRight(tribool down, bool previousState)
 		const CBuilding *bld = town->town->buildings.at(bid);
 		if (bid < BuildingID::DWELL_FIRST)
 		{
-			CRClickPopup::createAndPush(CInfoWindow::genText(bld->Name(), bld->Description()),
-				std::make_shared<CComponent>(CComponent::building, bld->town->faction->index, bld->bid));
+			CRClickPopup::createAndPush(CInfoWindow::genText(bld->getNameTranslated(), bld->getDescriptionTranslated()),
+				std::make_shared<CComponent>(CComponent::building, bld->town->faction->getIndex(), bld->bid));
 		}
 		else
 		{
@@ -235,7 +235,7 @@ std::string CBuildingRect::getSubtitle()//hover text for building
 	int bid = getBuilding()->bid;
 
 	if (bid<30)//non-dwellings - only buiding name
-		return town->town->buildings.at(getBuilding()->bid)->Name();
+		return town->town->buildings.at(getBuilding()->bid)->getNameTranslated();
 	else//dwellings - recruit %creature%
 	{
 		auto & availableCreatures = town->creatures[(bid-30)%GameConstants::CREATURES_PER_TOWN].second;
@@ -736,7 +736,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 						if(town->visitingHero)
 							GH.pushIntT<CMarketplaceWindow>(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT);
 						else
-							LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
+							LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
 						break;
 
 				case BuildingSubID::FOUNTAIN_OF_FORTUNE:
@@ -747,7 +747,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 						if(getHero())
 							GH.pushIntT<CMarketplaceWindow>(town, getHero(), EMarketMode::CREATURE_RESOURCE);
 						else
-							LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->Name())); //Only visiting heroes may use the %s.
+							LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
 						break;
 
 				case BuildingSubID::MAGIC_UNIVERSITY:
@@ -801,7 +801,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
 	const CGHeroInstance *hero = town->visitingHero;
 	if(!hero)
 	{
-		LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->town->buildings.find(BuildingID::BLACKSMITH)->second->Name()));
+		LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % town->town->buildings.find(BuildingID::BLACKSMITH)->second->getNameTranslated()));
 		return;
 	}
 	auto art = artifactID.toArtifact(CGI->artifacts());
@@ -815,7 +815,7 @@ void CCastleBuildings::enterBlacksmith(ArtifactID artifactID)
 void CCastleBuildings::enterBuilding(BuildingID building)
 {
 	std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(CComponent::building, town->subID, building));
-	LOCPLINT->showInfoDialog( town->town->buildings.find(building)->second->Description(), comps);
+	LOCPLINT->showInfoDialog( town->town->buildings.find(building)->second->getDescriptionTranslated(), comps);
 }
 
 void CCastleBuildings::enterCastleGate()
@@ -831,7 +831,7 @@ void CCastleBuildings::enterCastleGate()
 	{
 		const CGTownInstance *t = Town;
 		if (t->id != this->town->id && t->visitingHero == nullptr && //another town, empty and this is
-			t->town->faction->index == town->town->faction->index && //the town of the same faction
+			t->town->faction->getId() == town->town->faction->getId() && //the town of the same faction
 			t->hasBuilt(BuildingSubID::CASTLE_GATE)) //and the town has a castle gate
 		{
 			availableTowns.push_back(t->id.getNum());//add to the list
@@ -866,18 +866,18 @@ void CCastleBuildings::enterToTheQuickRecruitmentWindow()
 void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID::EBuildingSubID subID, BuildingID::EBuildingID upgrades)
 {
 	std::vector<std::shared_ptr<CComponent>> comps(1, std::make_shared<CComponent>(CComponent::building,town->subID,building));
-	std::string descr = town->town->buildings.find(building)->second->Description();
+	std::string descr = town->town->buildings.find(building)->second->getDescriptionTranslated();
 	std::string hasNotProduced;
 	std::string hasProduced;
 
-	if(this->town->town->faction->index == (TFaction)ETownType::RAMPART)
+	if(this->town->town->faction->getIndex() == (TFaction)ETownType::RAMPART)
 	{
 		hasNotProduced = CGI->generaltexth->allTexts[677];
 		hasProduced = CGI->generaltexth->allTexts[678];
 	}
 	else
 	{
-		auto buildingName = town->town->getSpecialBuilding(subID)->Name();
+		auto buildingName = town->town->getSpecialBuilding(subID)->getNameTranslated();
 
 		hasNotProduced = std::string(CGI->generaltexth->translate("vcmi.townHall.hasNotProduced"));
 		hasProduced = std::string(CGI->generaltexth->translate("vcmi.townHall.hasProduced"));
@@ -890,7 +890,7 @@ void CCastleBuildings::enterFountain(const BuildingID & building, BuildingSubID:
 			&& town->town->buildings.find(BuildingID(upgrades))->second->subId == BuildingSubID::MYSTIC_POND);
 
 	if(upgrades != BuildingID::NONE)
-		descr += "\n\n"+town->town->buildings.find(BuildingID(upgrades))->second->Description();
+		descr += "\n\n"+town->town->buildings.find(BuildingID(upgrades))->second->getDescriptionTranslated();
 
 	if(isMysticPondOrItsUpgrade) //for vanila Rampart like towns
 	{
@@ -1114,7 +1114,7 @@ void CTownInfo::hover(bool on)
 	if(on)
 	{
 		if(building )
-			GH.statusbar->write(building->Name());
+			GH.statusbar->write(building->getNameTranslated());
 	}
 	else
 	{
@@ -1126,8 +1126,8 @@ void CTownInfo::clickRight(tribool down, bool previousState)
 {
 	if(building && down)
 	{
-		auto c =  std::make_shared<CComponent>(CComponent::building, building->town->faction->index, building->bid);
-		CRClickPopup::createAndPush(CInfoWindow::genText(building->Name(), building->Description()), c);
+		auto c =  std::make_shared<CComponent>(CComponent::building, building->town->faction->getIndex(), building->bid);
+		CRClickPopup::createAndPush(CInfoWindow::genText(building->getNameTranslated(), building->getDescriptionTranslated()), c);
 	}
 }
 
@@ -1152,7 +1152,7 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
 	garr->type |= REDRAW_PARENT;
 
 	heroes = std::make_shared<HeroSlots>(town, Point(241, 387), Point(241, 483), garr, true);
-	title = std::make_shared<CLabel>(85, 387, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, town->name);
+	title = std::make_shared<CLabel>(85, 387, FONT_MEDIUM, ETextAlignment::TOPLEFT, Colors::WHITE, town->getNameTranslated());
 	income = std::make_shared<CLabel>(195, 443, FONT_SMALL, ETextAlignment::CENTER);
 	icon = std::make_shared<CAnimImage>("ITPT", 0, 0, 15, 387);
 
@@ -1316,7 +1316,7 @@ CHallInterface::CBuildingBox::CBuildingBox(int x, int y, const CGTownInstance *
 	header = std::make_shared<CAnimImage>("TPTHBAR", panelIndex[state], 0, 1, 73);
 	if(iconIndex[state] >=0)
 		mark = std::make_shared<CAnimImage>("TPTHCHK", iconIndex[state], 0, 136, 56);
-	name = std::make_shared<CLabel>(75, 81, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, building->Name());
+	name = std::make_shared<CLabel>(75, 81, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, building->getNameTranslated());
 
 	//todo: add support for all possible states
 	if(state >= EBuildingState::BUILDING_ERROR)
@@ -1334,7 +1334,7 @@ void CHallInterface::CBuildingBox::hover(bool on)
 			toPrint = CGI->generaltexth->allTexts[223];
 		else
 			toPrint = CGI->generaltexth->hcommands[state];
-		boost::algorithm::replace_first(toPrint,"%s",building->Name());
+		boost::algorithm::replace_first(toPrint,"%s",building->getNameTranslated());
 		GH.statusbar->write(toPrint);
 	}
 	else
@@ -1368,7 +1368,7 @@ CHallInterface::CHallInterface(const CGTownInstance * Town):
 	auto statusbarBackground = std::make_shared<CPicture>(background->getSurface(), barRect, 5, 556, false);
 	statusbar = CGStatusBar::create(statusbarBackground);
 
-	title = std::make_shared<CLabel>(399, 12, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, town->town->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->Name());
+	title = std::make_shared<CLabel>(399, 12, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, town->town->buildings.at(BuildingID(town->hallLevel()+BuildingID::VILLAGE_HALL))->getNameTranslated());
 	exit = std::make_shared<CButton>(Point(748, 556), "TPMAGE1.DEF", CButton::tooltip(CGI->generaltexth->hcommands[8]), [&](){close();}, SDLK_RETURN);
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
@@ -1415,8 +1415,8 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 	auto statusbarBackground = std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26);
 	statusbar = CGStatusBar::create(statusbarBackground);
 
-	name = std::make_shared<CLabel>(197, 30, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->hcommands[7]) % building->Name()));
-	description = std::make_shared<CTextBox>(building->Description(), Rect(33, 135, 329, 67), 0, FONT_MEDIUM, ETextAlignment::CENTER);
+	name = std::make_shared<CLabel>(197, 30, FONT_MEDIUM, ETextAlignment::CENTER, Colors::WHITE, boost::str(boost::format(CGI->generaltexth->hcommands[7]) % building->getNameTranslated()));
+	description = std::make_shared<CTextBox>(building->getDescriptionTranslated(), Rect(33, 135, 329, 67), 0, FONT_MEDIUM, ETextAlignment::CENTER);
 	stateText = std::make_shared<CTextBox>(getTextForState(state), Rect(33, 216, 329, 67), 0, FONT_SMALL, ETextAlignment::CENTER);
 
 	//Create components for all required resources
@@ -1432,8 +1432,8 @@ CBuildWindow::CBuildWindow(const CGTownInstance *Town, const CBuilding * Buildin
 
 	if(!rightClick)
 	{	//normal window
-		std::string tooltipYes = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->Name());
-		std::string tooltipNo  = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->Name());
+		std::string tooltipYes = boost::str(boost::format(CGI->generaltexth->allTexts[595]) % building->getNameTranslated());
+		std::string tooltipNo  = boost::str(boost::format(CGI->generaltexth->allTexts[596]) % building->getNameTranslated());
 
 		buy = std::make_shared<CButton>(Point(45, 446), "IBUY30", CButton::tooltip(tooltipYes), [&](){ buyFunc(); }, SDLK_RETURN);
 		buy->setBorderColor(Colors::METALLIC_GOLD);
@@ -1460,7 +1460,7 @@ std::string CBuildWindow::getTextForState(int state)
 	case EBuildingState::ALREADY_PRESENT:
 	case EBuildingState::CANT_BUILD_TODAY:
 	case EBuildingState::NO_RESOURCES:
-		ret.replace(ret.find_first_of("%s"), 2, building->Name());
+		ret.replace(ret.find_first_of("%s"), 2, building->getNameTranslated());
 		break;
 	case EBuildingState::ALLOWED:
 		return CGI->generaltexth->allTexts[219]; //all prereq. are met
@@ -1468,7 +1468,7 @@ std::string CBuildWindow::getTextForState(int state)
 		{
 			auto toStr = [&](const BuildingID build) -> std::string
 			{
-				return town->town->buildings.at(build)->Name();
+				return town->town->buildings.at(build)->getNameTranslated();
 			};
 
 			ret = CGI->generaltexth->allTexts[52];
@@ -1478,7 +1478,7 @@ std::string CBuildWindow::getTextForState(int state)
 	case EBuildingState::MISSING_BASE:
 		{
 			std::string msg = CGI->generaltexth->translate("vcmi.townHall.missingBase");
-			ret = boost::str(boost::format(msg) % town->town->buildings.at(building->upgrade)->Name());
+			ret = boost::str(boost::format(msg) % town->town->buildings.at(building->upgrade)->getNameTranslated());
 			break;
 		}
 	}
@@ -1542,9 +1542,9 @@ CFortScreen::CFortScreen(const CGTownInstance * town):
 		fortSize--;
 
 	const CBuilding * fortBuilding = town->town->buildings.at(BuildingID(town->fortLevel()+6));
-	title = std::make_shared<CLabel>(400, 12, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, fortBuilding->Name());
+	title = std::make_shared<CLabel>(400, 12, FONT_BIG, ETextAlignment::CENTER, Colors::WHITE, fortBuilding->getNameTranslated());
 
-	std::string text = boost::str(boost::format(CGI->generaltexth->fcommands[6]) % fortBuilding->Name());
+	std::string text = boost::str(boost::format(CGI->generaltexth->fcommands[6]) % fortBuilding->getNameTranslated());
 	exit = std::make_shared<CButton>(Point(748, 556), "TPMAGE1", CButton::tooltip(text), [&](){ close(); }, SDLK_RETURN);
 	exit->assignedKeys.insert(SDLK_ESCAPE);
 
@@ -1629,7 +1629,7 @@ CFortScreen::RecruitArea::RecruitArea(int posX, int posY, const CGTownInstance *
 	if(getMyBuilding() != nullptr)
 	{
 		buildingIcon = std::make_shared<CAnimImage>(town->town->clientInfo.buildingsIcons, getMyBuilding()->bid, 0, 4, 21);
-		buildingName = std::make_shared<CLabel>(78, 101, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, getMyBuilding()->Name());
+		buildingName = std::make_shared<CLabel>(78, 101, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, getMyBuilding()->getNameTranslated());
 
 		if(vstd::contains(town->builtBuildings, getMyBuilding()->bid))
 		{

+ 1 - 1
client/windows/CKingdomInterface.cpp

@@ -767,7 +767,7 @@ CTownItem::CTownItem(const CGTownInstance * Town)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 	background = std::make_shared<CAnimImage>("OVSLOT", 6);
-	name = std::make_shared<CLabel>(74, 8, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, town->name);
+	name = std::make_shared<CLabel>(74, 8, FONT_SMALL, ETextAlignment::TOPLEFT, Colors::WHITE, town->getNameTranslated());
 
 	income = std::make_shared<CLabel>( 190, 60, FONT_SMALL, ETextAlignment::CENTER, Colors::WHITE, boost::lexical_cast<std::string>(town->dailyIncome()[Res::GOLD]));
 	hall = std::make_shared<CTownInfo>( 69, 31, town, true);

+ 3 - 3
client/windows/CTradeWindow.cpp

@@ -668,14 +668,14 @@ CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInsta
 		switch (mode)
 		{
 		case EMarketMode::CREATURE_RESOURCE:
-			title = (*CGI->townh)[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->Name();
+			title = (*CGI->townh)[ETownType::STRONGHOLD]->town->buildings[BuildingID::FREELANCERS_GUILD]->getNameTranslated();
 			break;
 		case EMarketMode::RESOURCE_ARTIFACT:
-			title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name();
+			title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
 			sliderNeeded = false;
 			break;
 		case EMarketMode::ARTIFACT_RESOURCE:
-			title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->Name();
+			title = (*CGI->townh)[market->o->subID]->town->buildings[BuildingID::ARTIFACT_MERCHANT]->getNameTranslated();
 
 			// create image that copies part of background containing slot MISC_1 into position of slot MISC_5
 			// this is workaround for bug in H3 files where this slot for ragdoll on this screen is missing

+ 1 - 1
client/windows/GUIClasses.cpp

@@ -1586,7 +1586,7 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket
 
 		if(town)
 		{
-			auto faction = town->town->faction->index;
+			auto faction = town->town->faction->getId();
 			auto bid = town->town->getSpecialBuilding(BuildingSubID::MAGIC_UNIVERSITY)->bid;
 			titlePic = std::make_shared<CAnimImage>((*CGI->townh)[faction]->town->clientInfo.buildingsIcons, bid);
 		}

+ 4 - 0
include/vcmi/Faction.h

@@ -18,8 +18,12 @@ class FactionID;
 
 class DLL_LINKAGE Faction : public EntityT<FactionID>
 {
+	using EntityT<FactionID>::getName;
 public:
 	virtual bool hasTown() const = 0;
+
+	virtual std::string getNameTranslated() const = 0;
+	virtual std::string getNameTextID() const = 0;
 };
 
 VCMI_LIB_NAMESPACE_END

+ 9 - 8
lib/CGameState.cpp

@@ -328,7 +328,7 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, PlayerColor pl
 
 	if(player>=PlayerColor::PLAYER_LIMIT)
 	{
-		logGlobal->error("Cannot pick hero for faction %d. Wrong owner!", town->faction->index);
+		logGlobal->error("Cannot pick hero for faction %s. Wrong owner!", town->faction->getJsonKey());
 		return nullptr;
 	}
 
@@ -339,7 +339,7 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, PlayerColor pl
 		for(auto & elem : available)
 		{
 			if(pavailable.find(elem.first)->second & 1<<player.getNum()
-				&& elem.second->type->heroClass->faction == town->faction->index)
+				&& elem.second->type->heroClass->faction == town->faction->getIndex())
 			{
 				pool.push_back(elem.second); //get all available heroes
 			}
@@ -364,7 +364,7 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, PlayerColor pl
 			    ( !bannedClass || elem.second->type->heroClass != bannedClass) ) // and his class is not same as other hero
 			{
 				pool.push_back(elem.second);
-				sum += elem.second->type->heroClass->selectionProbability[town->faction->index]; //total weight
+				sum += elem.second->type->heroClass->selectionProbability[town->faction->getIndex()]; //total weight
 			}
 		}
 		if(!pool.size() || sum == 0)
@@ -376,7 +376,7 @@ CGHeroInstance * CGameState::HeroesPool::pickHeroFor(bool native, PlayerColor pl
 		r = rand.nextInt(sum - 1);
 		for (auto & elem : pool)
 		{
-			r -= elem->type->heroClass->selectionProbability[town->faction->index];
+			r -= elem->type->heroClass->selectionProbability[town->faction->getIndex()];
 			if(r < 0)
 			{
 				ret = elem;
@@ -623,7 +623,7 @@ std::pair<Obj,int> CGameState::pickObject (CGObjectInstance *obj)
 
 			if (result.first == Obj::NO_OBJ)
 			{
-				logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->name, int(level));
+				logGlobal->error("Error: failed to find dwelling for %s of level %d", (*VLC->townh)[faction]->getNameTranslated(), int(level));
 				result = std::make_pair(Obj::CREATURE_GENERATOR1, *RandomGeneratorUtil::nextItem(VLC->objtypeh->knownSubObjects(Obj::CREATURE_GENERATOR1), getRandomGenerator()));
 			}
 
@@ -1711,9 +1711,10 @@ void CGameState::initTowns()
 		{
 			vti->town = (*VLC->townh)[vti->subID]->town;
 		}
-		if(vti->name.empty())
+		if(vti->getNameTranslated().empty())
 		{
-			vti->name = *RandomGeneratorUtil::nextItem(vti->town->names, getRandomGenerator());
+			size_t nameID = getRandomGenerator().nextInt(vti->town->getRandomNamesCount());
+			vti->setNameTranslated(vti->town->getRandomNameTranslated(nameID));
 		}
 
 		//init buildings
@@ -3076,7 +3077,7 @@ void InfoAboutTown::initFromTown(const CGTownInstance *t, bool detailed)
 	army = ArmyDescriptor(t->getUpperArmy(), detailed);
 	built = t->builded;
 	fortLevel = t->fortLevel();
-	name = t->name;
+	name = t->getNameTranslated();
 	tType = t->town;
 
 	vstd::clear_pointer(details);

+ 2 - 2
lib/CHeroHandler.cpp

@@ -364,11 +364,11 @@ void CHeroClassHandler::afterLoadFinalization()
 		{
 			if (!faction->town)
 				continue;
-			if (heroClass->selectionProbability.count(faction->index))
+			if (heroClass->selectionProbability.count(faction->getIndex()))
 				continue;
 
 			float chance = static_cast<float>(heroClass->defaultTavernChance * faction->town->defaultTavernChance);
-			heroClass->selectionProbability[faction->index] = static_cast<int>(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it
+			heroClass->selectionProbability[faction->getIndex()] = static_cast<int>(sqrt(chance) + 0.5); //FIXME: replace with std::round once MVS supports it
 		}
 		// set default probabilities for gaining secondary skills where not loaded previously
 		heroClass->secSkillProbability.resize(VLC->skillh->size(), -1);

+ 76 - 29
lib/CTownHandler.cpp

@@ -44,14 +44,29 @@ const std::map<std::string, CBuilding::ETowerHeight> CBuilding::TOWER_TYPES =
 	{ "skyship", CBuilding::HEIGHT_SKYSHIP }
 };
 
-const std::string & CBuilding::Name() const
+std::string CBuilding::getJsonKey() const
 {
-	return name;
+	return identifier;
+}
+
+std::string CBuilding::getNameTranslated() const
+{
+	return VLC->generaltexth->translate(getNameTextID());
+}
+
+std::string CBuilding::getDescriptionTranslated() const
+{
+	return VLC->generaltexth->translate(getDescriptionTextID());
+}
+
+std::string CBuilding::getNameTextID() const
+{
+	return TextIdentifier("building", town->faction->getJsonKey(), modScope, identifier, "name").get();
 }
 
-const std::string & CBuilding::Description() const
+std::string CBuilding::getDescriptionTextID() const
 {
-	return description;
+	return TextIdentifier("building", town->faction->getJsonKey(), modScope, identifier, "description").get();
 }
 
 BuildingID CBuilding::getBase() const
@@ -109,7 +124,7 @@ int32_t CFaction::getIconIndex() const
 
 const std::string & CFaction::getName() const
 {
-	return name;
+	return identifier;
 }
 
 const std::string & CFaction::getJsonKey() const
@@ -138,6 +153,16 @@ void CFaction::registerIcons(const IconRegistar & cb) const
 	}
 }
 
+std::string CFaction::getNameTranslated() const
+{
+	return VLC->generaltexth->translate(getNameTextID());
+}
+
+std::string CFaction::getNameTextID() const
+{
+	return TextIdentifier("faction", modScope, identifier, "name").get();
+}
+
 FactionID CFaction::getId() const
 {
 	return FactionID(index);
@@ -173,12 +198,19 @@ CTown::~CTown()
 		str.dellNull();
 }
 
-std::string CTown::getLocalizedFactionName() const
+std::string CTown::getRandomNameTranslated(size_t index) const
 {
-	if(faction == nullptr)
-		return "Random";
-	else
-		return faction->name;
+	return VLC->generaltexth->translate(getRandomNameTextID(index));
+}
+
+std::string CTown::getRandomNameTextID(size_t index) const
+{
+	return TextIdentifier("faction", faction->getJsonKey(), "randomName", index).get();
+}
+
+size_t CTown::getRandomNamesCount() const
+{
+	return namesCount;
 }
 
 std::string CTown::getBuildingScope() const
@@ -187,7 +219,7 @@ std::string CTown::getBuildingScope() const
 		//no faction == random faction
 		return "building";
 	else
-		return "building." + faction->identifier;
+		return "building." + faction->getJsonKey();
 }
 
 std::set<si32> CTown::getAllBuildings() const
@@ -231,6 +263,11 @@ void CTown::setGreeting(BuildingSubID::EBuildingSubID subID, const std::string m
 CTownHandler::CTownHandler()
 {
 	randomTown = new CTown();
+	randomFaction = new CFaction();
+	randomFaction->town = randomTown;
+	randomTown->faction = randomFaction;
+	randomFaction->identifier = "random";
+	randomFaction->modScope = "core";
 }
 
 CTownHandler::~CTownHandler()
@@ -460,10 +497,10 @@ void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building)
 		}
 		else if(building->bid == BuildingID::GRAIL
 			&& building->town->faction != nullptr
-			&& boost::algorithm::ends_with(building->town->faction->identifier, ":cove"))
+			&& boost::algorithm::ends_with(building->town->faction->getJsonKey(), ":cove"))
 		{
 			static TPropagatorPtr allCreaturesPropagator(new CPropagatorNodeType(CBonusSystemNode::ENodeTypes::ALL_CREATURES));
-			static auto factionLimiter = std::make_shared<CreatureFactionLimiter>(building->town->faction->index);
+			static auto factionLimiter = std::make_shared<CreatureFactionLimiter>(building->town->faction->getIndex());
 			b = createBonus(building, Bonus::NO_TERRAIN_PENALTY, 0, allCreaturesPropagator);
 			b->addLimiter(factionLimiter);
 		}
@@ -505,7 +542,7 @@ std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::Bonus
 std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype)
 {
 	std::ostringstream descr;
-	descr << build->name;
+	descr << build->getNameTranslated();
 	return createBonusImpl(build->bid, type, val, prop, descr.str(), subtype);
 }
 
@@ -523,7 +560,7 @@ void CTownHandler::loadSpecialBuildingBonuses(const JsonNode & source, BonusList
 {
 	for(auto b : source.Vector())
 	{
-		auto bonus = JsonUtils::parseBuildingBonus(b, building->bid, building->name);
+		auto bonus = JsonUtils::parseBuildingBonus(b, building->bid, building->getNameTranslated());
 
 		if(bonus == nullptr)
 			continue;
@@ -533,7 +570,7 @@ void CTownHandler::loadSpecialBuildingBonuses(const JsonNode & source, BonusList
 			auto limPtr = dynamic_cast<CreatureFactionLimiter*>(bonus->limiter.get());
 
 			if(limPtr != nullptr && limPtr->faction == (TFaction)-1)
-				limPtr->faction = building->town->faction->index;
+				limPtr->faction = building->town->faction->getIndex();
 		}
 		//JsonUtils::parseBuildingBonus produces UNKNOWN type propagator instead of empty.
 		if(bonus->propagator != nullptr
@@ -567,8 +604,10 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 
 	ret->identifier = stringID;
 	ret->town = town;
-	ret->name = source["name"].String();
-	ret->description = source["description"].String();
+
+	VLC->generaltexth->registerString(ret->getNameTextID(), source["name"].String());
+	VLC->generaltexth->registerString(ret->getDescriptionTextID(), source["description"].String());
+
 	ret->resources = TResources(source["cost"]);
 	ret->produce =   TResources(source["produce"]);
 
@@ -589,7 +628,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 				ret->subId = BuildingSubID::CUSTOM_VISITING_BONUS;
 
 			for(auto & bonus : ret->onVisitBonuses)
-				bonus->sid = Bonus::getSid32(ret->town->faction->index, ret->bid);
+				bonus->sid = Bonus::getSid32(ret->town->faction->getIndex(), ret->bid);
 		}
 	}
 	//MODS COMPATIBILITY FOR 0.96
@@ -630,7 +669,7 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 		if(stringID == source["upgrades"].String())
 		{
 			throw std::runtime_error(boost::str(boost::format("Building with ID '%s' of town '%s' can't be an upgrade of the same building.") %
-												stringID % ret->town->getLocalizedFactionName()));
+												stringID % ret->town->faction->getNameTranslated()));
 		}
 
 		VLC->modh->identifiers.requestIdentifier(ret->town->getBuildingScope(), source["upgrades"], [=](si32 identifier)
@@ -664,21 +703,21 @@ void CTownHandler::loadStructure(CTown &town, const std::string & stringID, cons
 	ret->building = nullptr;
 	ret->buildable = nullptr;
 
-	VLC->modh->identifiers.tryRequestIdentifier( source.meta, "building." + town.faction->identifier, stringID, [=, &town](si32 identifier) mutable
+	VLC->modh->identifiers.tryRequestIdentifier( source.meta, "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
 	{
 		ret->building = town.buildings[BuildingID(identifier)];
 	});
 
 	if (source["builds"].isNull())
 	{
-		VLC->modh->identifiers.tryRequestIdentifier( source.meta, "building." + town.faction->identifier, stringID, [=, &town](si32 identifier) mutable
+		VLC->modh->identifiers.tryRequestIdentifier( source.meta, "building." + town.faction->getJsonKey(), stringID, [=, &town](si32 identifier) mutable
 		{
 			ret->building = town.buildings[BuildingID(identifier)];
 		});
 	}
 	else
 	{
-		VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, source["builds"], [=, &town](si32 identifier) mutable
+		VLC->modh->identifiers.requestIdentifier("building." + town.faction->getJsonKey(), source["builds"], [=, &town](si32 identifier) mutable
 		{
 			ret->buildable = town.buildings[BuildingID(identifier)];
 		});
@@ -729,7 +768,7 @@ void CTownHandler::loadTownHall(CTown &town, const JsonNode & source)
 				auto & dst = dstBox[k];
 				auto & src = srcBox[k];
 
-				VLC->modh->identifiers.requestIdentifier("building." + town.faction->identifier, src, [&](si32 identifier)
+				VLC->modh->identifiers.requestIdentifier("building." + town.faction->getJsonKey(), src, [&](si32 identifier)
 				{
 					dst = BuildingID(identifier);
 				});
@@ -757,7 +796,7 @@ void CTownHandler::loadSiegeScreen(CTown &town, const JsonNode & source)
 		auto crId = CreatureID(creature);
 		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
+				, town.faction->getNameTranslated()
 				, (*VLC->creh)[crId]->getNameSingularTranslated());
 
 		town.clientInfo.siegeShooter = crId;
@@ -848,7 +887,13 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 	town->moatHexes = source["moatHexes"].convertTo<std::vector<BattleHex> >();
 
 	town->mageLevel = static_cast<ui32>(source["mageGuild"].Float());
-	town->names = source["names"].convertTo<std::vector<std::string> >();
+
+	town->namesCount = 0;
+	for (auto const & name : source["names"].Vector())
+	{
+		VLC->generaltexth->registerString(town->getRandomNameTextID(town->namesCount), name.String());
+		town->namesCount += 1;
+	}
 
 	//  Horde building creature level
 	for(const JsonNode &node : source["horde"].Vector())
@@ -886,7 +931,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 
 		VLC->modh->identifiers.requestIdentifier(node.second.meta, "heroClass",node.first, [=](si32 classID)
 		{
-			VLC->heroh->classes[HeroClassID(classID)]->selectionProbability[town->faction->index] = chance;
+			VLC->heroh->classes[HeroClassID(classID)]->selectionProbability[town->faction->getIndex()] = chance;
 		});
 	}
 
@@ -896,7 +941,7 @@ void CTownHandler::loadTown(CTown * town, const JsonNode & source)
 
 		VLC->modh->identifiers.requestIdentifier(node.second.meta, "spell", node.first, [=](si32 spellID)
 		{
-			VLC->spellh->objects.at(spellID)->probabilities[town->faction->index] = chance;
+			VLC->spellh->objects.at(spellID)->probabilities[town->faction->getIndex()] = chance;
 		});
 	}
 
@@ -941,9 +986,11 @@ CFaction * CTownHandler::loadFromJson(const std::string & scope, const JsonNode
 	auto faction = new CFaction();
 
 	faction->index = static_cast<TFaction>(index);
-	faction->name = source["name"].String();
+	faction->modScope = scope;
 	faction->identifier = identifier;
 
+	VLC->generaltexth->registerString(faction->getNameTextID(), source["name"].String());
+
 	faction->creatureBg120 = source["creatureBackground"]["120px"].String();
 	faction->creatureBg130 = source["creatureBackground"]["130px"].String();
 

+ 28 - 16
lib/CTownHandler.h

@@ -38,9 +38,8 @@ class JsonSerializeFormat;
 
 class DLL_LINKAGE CBuilding
 {
-
-	std::string name;
-	std::string description;
+	std::string modScope;
+	std::string identifier;
 
 public:
 	typedef LogicalExpression<BuildingID> TRequired;
@@ -49,7 +48,6 @@ public:
 	TResources resources;
 	TResources produce;
 	TRequired requirements;
-	std::string identifier;
 
 	BuildingID bid; //structure ID
 	BuildingID upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty
@@ -80,8 +78,13 @@ public:
 
 	CBuilding() : town(nullptr), mode(BUILD_NORMAL) {};
 
-	const std::string &Name() const;
-	const std::string &Description() const;
+	std::string getJsonKey() const;
+
+	std::string getNameTranslated() const;
+	std::string getDescriptionTranslated() const;
+
+	std::string getNameTextID() const;
+	std::string getDescriptionTextID() const;
 
 	//return base of upgrade(s) or this
 	BuildingID getBase() const;
@@ -116,13 +119,12 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
+		h & modScope;
 		h & identifier;
 		h & town;
 		h & bid;
 		h & resources;
 		h & produce;
-		h & name;
-		h & description;
 		h & requirements;
 		h & upgrade;
 		h & mode;
@@ -180,12 +182,15 @@ struct DLL_LINKAGE SPuzzleInfo
 
 class DLL_LINKAGE CFaction : public Faction
 {
-public:
-	std::string name; //town name, by default - from TownName.txt
+	friend class CTownHandler;
+	std::string modScope; //town name, by default - from TownName.txt
 	std::string identifier;
 
 	TFaction index;
 
+	const std::string & getName() const override;
+
+public:
 	TerrainId nativeTerrain;
 	EAlignment::EAlignment alignment;
 	bool preferUndergroundPlacement;
@@ -202,11 +207,13 @@ public:
 
 	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;
 	FactionID getId() const override;
 
+	std::string getNameTranslated() const override;
+	std::string getNameTextID() const override;
+
 	bool hasTown() const override;
 
 	void updateFrom(const JsonNode & data);
@@ -214,7 +221,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & name;
+		h & modScope;
 		h & identifier;
 		h & index;
 		h & nativeTerrain;
@@ -228,11 +235,13 @@ public:
 
 class DLL_LINKAGE CTown
 {
+	friend class CTownHandler;
+	size_t namesCount;
+
 public:
 	CTown();
 	~CTown();
 
-	std::string getLocalizedFactionName() const;
 	std::string getBuildingScope() const;
 	std::set<si32> getAllBuildings() const;
 	const CBuilding * getSpecialBuilding(BuildingSubID::EBuildingSubID subID) const;
@@ -240,9 +249,11 @@ public:
 	void setGreeting(BuildingSubID::EBuildingSubID subID, const std::string message) const; //may affect only mutable field
 	BuildingID::EBuildingID getBuildingType(BuildingSubID::EBuildingSubID subID) const;
 
-	CFaction * faction;
+	std::string getRandomNameTranslated(size_t index) const;
+	std::string getRandomNameTextID(size_t index) const;
+	size_t getRandomNamesCount() const;
 
-	std::vector<std::string> names; //names of the town instances
+	CFaction * faction;
 
 	/// level -> list of creatures on this tier
 	// TODO: replace with pointers to CCreature
@@ -327,7 +338,7 @@ public:
 
 	template <typename Handler> void serialize(Handler &h, const int version)
 	{
-		h & names;
+		h & namesCount;
 		h & faction;
 		h & creatures;
 		h & dwellings;
@@ -400,6 +411,7 @@ public:
 	static R getMappedValue(const JsonNode & node, const R defval, const std::map<std::string, R> & map, bool required = true);
 
 	CTown * randomTown;
+	CFaction * randomFaction;
 
 	CTownHandler();
 	~CTownHandler();

+ 20 - 10
lib/mapObjects/CGTownInstance.cpp

@@ -738,7 +738,7 @@ void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
 
 std::string CGTownInstance::getObjectName() const
 {
-	return name + ", " + town->faction->name;
+	return name + ", " + town->faction->getNameTranslated();
 }
 
 bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId) const
@@ -894,7 +894,7 @@ void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structu
 
 bool CGTownInstance::hasBuiltInOldWay(ETownType::ETownType type, BuildingID bid) const
 {
-	return (this->town->faction != nullptr && this->town->faction->index == type && hasBuilt(bid));
+	return (this->town->faction != nullptr && this->town->faction->getIndex() == type && hasBuilt(bid));
 }
 
 void CGTownInstance::newTurn(CRandomGenerator & rand) const
@@ -1178,7 +1178,7 @@ void CGTownInstance::updateAppearance()
 
 std::string CGTownInstance::nodeName() const
 {
-	return "Town (" + (town ? town->faction->name : "unknown") + ") of " +  name;
+	return "Town (" + (town ? town->faction->getNameTranslated() : "unknown") + ") of " +  name;
 }
 
 void CGTownInstance::deserializationFix()
@@ -1332,6 +1332,16 @@ CBonusSystemNode & CGTownInstance::whatShouldBeAttached()
 	return townAndVis;
 }
 
+std::string CGTownInstance::getNameTranslated() const
+{
+	return name;
+}
+
+void CGTownInstance::setNameTranslated( const std::string & newName )
+{
+	name = newName;
+}
+
 const CArmedInstance * CGTownInstance::getUpperArmy() const
 {
 	if(garrisonHero)
@@ -1377,7 +1387,7 @@ bool CGTownInstance::hasBuilt(BuildingID buildingID) const
 
 bool CGTownInstance::hasBuilt(BuildingID buildingID, int townID) const
 {
-	if (townID == town->faction->index || townID == ETownType::ANY)
+	if (townID == town->faction->getIndex() || townID == ETownType::ANY)
 		return hasBuilt(buildingID);
 	return false;
 }
@@ -1513,7 +1523,7 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
 
 		auto encodeBuilding = [this](si32 index) -> std::string
 		{
-			return getTown()->buildings.at(BuildingID(index))->identifier;
+			return getTown()->buildings.at(BuildingID(index))->getJsonKey();
 		};
 
 		const std::set<si32> standard = getTown()->getAllBuildings();//by default all buildings are allowed
@@ -1760,7 +1770,7 @@ void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
 
 		case BuildingSubID::CUSTOM_VISITING_BONUS:
 			const auto building = town->town->buildings.at(bID);
-			if(!h->hasBonusFrom(Bonus::TOWN_STRUCTURE, Bonus::getSid32(building->town->faction->index, building->bid)))
+			if(!h->hasBonusFrom(Bonus::TOWN_STRUCTURE, Bonus::getSid32(building->town->faction->getIndex(), building->bid)))
 			{
 				const auto & bonuses = building->onVisitBonuses;
 				applyBonuses(const_cast<CGHeroInstance *>(h), bonuses);
@@ -1819,7 +1829,7 @@ GrowthInfo::Entry::Entry(const std::string &format, int _count)
 GrowthInfo::Entry::Entry(int subID, BuildingID building, int _count)
 	: count(_count)
 {
-	description = boost::str(boost::format("%s %+d") % (*VLC->townh)[subID]->town->buildings.at(building)->Name() % count);
+	description = boost::str(boost::format("%s %+d") % (*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated() % count);
 }
 
 GrowthInfo::Entry::Entry(int _count, const std::string &fullDescription)
@@ -1870,12 +1880,12 @@ const std::string CGTownBuilding::getVisitingBonusGreeting() const
 		bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingDefence"));
 		break;
 	}
-	auto buildingName = town->town->getSpecialBuilding(bType)->Name();
+	auto buildingName = town->town->getSpecialBuilding(bType)->getNameTranslated();
 
 	if(bonusGreeting.empty())
 	{
 		bonusGreeting = "Error: Bonus greeting for '%s' is not localized.";
-		logGlobal->error("'%s' building of '%s' faction has not localized bonus greeting.", buildingName, town->town->getLocalizedFactionName());
+		logGlobal->error("'%s' building of '%s' faction has not localized bonus greeting.", buildingName, town->town->faction->getNameTranslated());
 	}
 	boost::algorithm::replace_first(bonusGreeting, "%s", buildingName);
 	town->town->setGreeting(bType, bonusGreeting);
@@ -1887,7 +1897,7 @@ const std::string CGTownBuilding::getCustomBonusGreeting(const Bonus & bonus) co
 	if(bonus.type == Bonus::TOWN_MAGIC_WELL)
 	{
 		auto bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingInTownMagicWell"));
-		auto buildingName = town->town->getSpecialBuilding(bType)->Name();
+		auto buildingName = town->town->getSpecialBuilding(bType)->getNameTranslated();
 		boost::algorithm::replace_first(bonusGreeting, "%s", buildingName);
 		return bonusGreeting;
 	}

+ 4 - 1
lib/mapObjects/CGTownInstance.h

@@ -205,12 +205,12 @@ struct DLL_LINKAGE GrowthInfo
 
 class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket
 {
+	std::string name; // name of town
 public:
 	enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
 
 	CTownAndVisitingHero townAndVis;
 	const CTown * town;
-	std::string name; // name of town
 	si32 builded; //how many buildings has been built this turn
 	si32 destroyed; //how many buildings has been destroyed this turn
 	ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
@@ -283,6 +283,9 @@ public:
 	void setGarrisonedHero(CGHeroInstance *h);
 	const CArmedInstance *getUpperArmy() const; //garrisoned hero if present or the town itself
 
+	std::string getNameTranslated() const;
+	void setNameTranslated( std::string const & newName );
+
 	//////////////////////////////////////////////////////////////////////////
 
 	bool passableFor(PlayerColor color) const override;

+ 1 - 1
lib/mapObjects/CommonConstructors.cpp

@@ -58,7 +58,7 @@ void CTownInstanceConstructor::afterLoadFinalization()
 	{
 		filters[entry.first] = LogicalExpression<BuildingID>(entry.second, [this](const JsonNode & node)
 		{
-			return BuildingID(VLC->modh->identifiers.getIdentifier("building." + faction->identifier, node.Vector()[0]).get());
+			return BuildingID(VLC->modh->identifiers.getIdentifier("building." + faction->getJsonKey(), node.Vector()[0]).get());
 		});
 	}
 }

+ 1 - 1
lib/mapping/CMap.cpp

@@ -565,7 +565,7 @@ void CMap::checkForObjectives()
 					{
 						const CGTownInstance *town = dynamic_cast<const CGTownInstance*>(cond.object);
 						if (town)
-							boost::algorithm::replace_first(event.onFulfill, "%s", town->name);
+							boost::algorithm::replace_first(event.onFulfill, "%s", town->getNameTranslated());
 						const CGHeroInstance *hero = dynamic_cast<const CGHeroInstance*>(cond.object);
 						if (hero)
 							boost::algorithm::replace_first(event.onFulfill, "%s", hero->getNameTranslated());

+ 1 - 1
lib/mapping/MapFormatH3M.cpp

@@ -1942,7 +1942,7 @@ CGTownInstance * CMapLoaderH3M::readTown(int castleID)
 	bool hasName = reader.readBool();
 	if(hasName)
 	{
-		nt->name = reader.readString();
+		nt->setNameTranslated( reader.readString());
 	}
 
 	bool hasGarrison = reader.readBool();

+ 3 - 3
lib/mapping/MapFormatJson.cpp

@@ -388,11 +388,11 @@ void CMapFormatJson::serializeAllowedFactions(JsonSerializeFormat & handler, std
 	temp.resize(VLC->townh->size(), false);
 	auto standard = VLC->townh->getDefaultAllowed();
 
-    if(handler.saving)
+	if(handler.saving)
 	{
 		for(auto faction : VLC->townh->objects)
-			if(faction->town && vstd::contains(value, faction->index))
-				temp[std::size_t(faction->index)] = true;
+			if(faction->town && vstd::contains(value, faction->getIndex()))
+				temp[std::size_t(faction->getIndex())] = true;
 	}
 
 	handler.serializeLIC("allowedFactions", &FactionID::decode, &FactionID::encode, standard, temp);

+ 1 - 1
lib/rmg/CMapGenerator.cpp

@@ -175,7 +175,7 @@ std::string CMapGenerator::getMapDescription() const
 		if(pSettings.getStartingTown() != CMapGenOptions::CPlayerSettings::RANDOM_TOWN)
 		{
 			ss << ", " << GameConstants::PLAYER_COLOR_NAMES[pSettings.getColor().getNum()]
-			   << " town choice is " << (*VLC->townh)[pSettings.getStartingTown()]->name;
+			   << " town choice is " << (*VLC->townh)[pSettings.getStartingTown()]->getNameTranslated();
 		}
 	}
 

+ 2 - 2
mapeditor/inspector/inspector.cpp

@@ -264,7 +264,7 @@ void Inspector::updateProperties(CGTownInstance * o)
 {
 	if(!o) return;
 	
-	addProperty("Town name", o->name, false);
+	addProperty("Town name", o->getNameTranslated(), false);
 	
 	auto * delegate = new TownBuildingsDelegate(*o);
 	addProperty("Buildings", PropertyEditorPlaceholder(), delegate, false);
@@ -492,7 +492,7 @@ void Inspector::setProperty(CGTownInstance * o, const QString & key, const QVari
 	if(!o) return;
 	
 	if(key == "Town name")
-		o->name = value.toString().toStdString();
+		o->setNameTranslated(value.toString().toStdString());
 }
 
 void Inspector::setProperty(CGSignBottle * o, const QString & key, const QVariant & value)

+ 1 - 1
mapeditor/inspector/townbulidingswidget.cpp

@@ -95,7 +95,7 @@ QStandardItem * TownBulidingsWidget::addBuilding(const CTown & ctown, int bId, s
 		return nullptr;
 	}
 	
-	QString name = tr(building->Name().c_str());
+	QString name = tr(building->getNameTranslated().c_str());
 	
 	if(name.isEmpty())
 		name = QString::fromStdString(defaultBuildingIdConversion(buildingId));

+ 2 - 2
mapeditor/playerparams.cpp

@@ -28,7 +28,7 @@ PlayerParams::PlayerParams(MapController & ctrl, int playerId, QWidget *parent)
 	for(auto idx : VLC->townh->getAllowedFactions())
 	{
 		CFaction * faction = VLC->townh->objects.at(idx);
-		auto * item = new QListWidgetItem(QString::fromStdString(faction->name));
+		auto * item = new QListWidgetItem(QString::fromStdString(faction->getNameTranslated()));
 		item->setData(Qt::UserRole, QVariant::fromValue(idx));
 		item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
 		ui->allowedFactions->addItem(item);
@@ -64,7 +64,7 @@ PlayerParams::PlayerParams(MapController & ctrl, int playerId, QWidget *parent)
 			{
 				if(playerInfo.hasMainTown && playerInfo.posOfMainTown == town->pos)
 					foundMainTown = townIndex;
-				const auto name = ctown->faction ? town->getObjectName() : town->name + ", (random)";
+				const auto name = ctown->faction ? town->getObjectName() : town->getNameTranslated() + ", (random)";
 				ui->mainTown->addItem(tr(name.c_str()), QVariant::fromValue(i));
 				++townIndex;
 			}

+ 5 - 5
server/CGameHandler.cpp

@@ -1704,7 +1704,7 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa
 	const PlayerState * p = getPlayerState(town->tempOwner);
 	if (!p)
 	{
-		logGlobal->warn("There is no player owner of town %s at %s", town->name, town->pos.toString());
+		logGlobal->warn("There is no player owner of town %s at %s", town->getNameTranslated(), town->pos.toString());
 		return;
 	}
 
@@ -2512,7 +2512,7 @@ bool CGameHandler::teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui
 	if (((h->getOwner() != t->getOwner())
 		&& complain("Cannot teleport hero to another player"))
 
-	|| (from->town->faction->index != t->town->faction->index
+	|| (from->town->faction->getId() != t->town->faction->getId()
 		&& complain("Source town and destination town should belong to the same faction"))
 
 	|| ((!from || !from->hasBuilt(BuildingSubID::CASTLE_GATE))
@@ -3437,9 +3437,9 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID,
 	if(!t)
 		COMPLAIN_RETF("No such town (ID=%s)!", tid);
 	if(!t->town->buildings.count(requestedID))
-		COMPLAIN_RETF("Town of faction %s does not have info about building ID=%s!", t->town->faction->name % requestedID);
+		COMPLAIN_RETF("Town of faction %s does not have info about building ID=%s!", t->town->faction->getNameTranslated() % requestedID);
 	if(t->hasBuilt(requestedID))
-		COMPLAIN_RETF("Building %s is already built in %s", t->town->buildings.at(requestedID)->Name() % t->name);
+		COMPLAIN_RETF("Building %s is already built in %s", t->town->buildings.at(requestedID)->getNameTranslated() % t->getNameTranslated());
 
 	const CBuilding * requestedBuilding = t->town->buildings.at(requestedID);
 
@@ -7066,7 +7066,7 @@ void CGameHandler::handleCheatCode(std::string & cheat, PlayerColor player, cons
 		for (auto & build : town->town->buildings)
 		{
 			if (!town->hasBuilt(build.first)
-				&& !build.second->Name().empty()
+				&& !build.second->getNameTranslated().empty()
 				&& build.first != BuildingID::SHIP)
 			{
 				buildStructure(town->id, build.first, true);