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

Greatly simplify town buildings logic

Ivan Savenko 1 жил өмнө
parent
commit
f1e63792f0

+ 4 - 8
client/windows/CCastleInterface.cpp

@@ -781,13 +781,6 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 							enterBuilding(building);
 						break;
 
-				case BuildingSubID::BROTHERHOOD_OF_SWORD:
-						if(upgrades == BuildingID::TAVERN)
-							LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
-						else
-							enterBuilding(building);
-						break;
-
 				case BuildingSubID::CASTLE_GATE:
 						if (LOCPLINT->makingTurn)
 							enterCastleGate();
@@ -819,8 +812,11 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 						break;
 
 				default:
+					if(upgrades == BuildingID::TAVERN)
+						LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
+					else
 						enterBuilding(building);
-						break;
+					break;
 				}
 				break;
 

+ 164 - 2
config/buildingsLibrary.json

@@ -4,7 +4,15 @@
 	"mageGuild3":     { "id" : 2,  "upgrades" : "mageGuild2" },
 	"mageGuild4":     { "id" : 3,  "upgrades" : "mageGuild3" },
 	"mageGuild5":     { "id" : 4,  "upgrades" : "mageGuild4" },
-	"tavern":         { "id" : 5 },
+	"tavern": {
+		"id" : 5,
+		"bonuses": [
+			{
+				"type": "MORALE",
+				"val": 1
+			}
+		]
+	},
 	"shipyard":       { "id" : 6 },
 	"fort":           { "id" : 7 },
 	"citadel":        { "id" : 8,  "upgrades" : "fort" },
@@ -37,5 +45,159 @@
 
 	"marketplace":    { "id" : 14 },
 	"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ] },
-	"blacksmith":     { "id" : 16 }
+	"blacksmith":     { "id" : 16 },
+
+	// Previously hardcoded buildings that might be used by mods
+	// Section 1 - building with bonuses during sieges
+	"brotherhoodOfSword" : {
+		"bonuses": [
+			{
+				"type": "MORALE",
+				"val": 2
+			}
+		]
+	},
+	
+	"fountainOfFortune" : {
+		"bonuses": [
+			{
+				"type": "LUCK",
+				"subtype": "primarySkill.knowledge",
+				"val": 2
+			}
+		]
+	},
+	
+	"spellPowerGarrisonBonus" : {
+		"bonuses": [
+			{
+				"type": "PRIMARY_SKILL",
+				"subtype": "primarySkill.knowledge",
+				"val": 2
+			}
+		]
+	},
+	
+	"attackGarrisonBonus" : {
+		"bonuses": [
+			{
+				"type": "PRIMARY_SKILL",
+				"subtype": "primarySkill.knowledge",
+				"val": 2
+			}
+		]
+	},
+	
+	"defenseGarrisonBonus" : {
+		"bonuses": [
+			{
+				"type": "PRIMARY_SKILL",
+				"subtype": "primarySkill.knowledge",
+				"val": 2
+			}
+		]
+	},
+	
+	"lighthouse" : {
+		"bonuses": [
+			{
+				"propagator": "PLAYER_PROPAGATOR",
+				"type": "MOVEMENT",
+				"subtype": "heroMovementSea",
+				"val": 500
+			}
+		]
+	},
+
+	// Section 2 - buildings that are visitable by hero
+	"stables": {
+		"configuration" : {
+			"visitMode" : "bonus",
+			"rewards" : [
+				{
+					"message" : "@core.genrltxt.580",
+					"movePoints" : 400,
+					"bonuses" : [ { "type" : "MOVEMENT", "subtype" : "heroMovementLand",  "val" : 400, "valueType" : "ADDITIVE_VALUE", "duration" : "ONE_WEEK"} ]
+				}
+			]
+		}
+	},
+	"manaVortex": {
+		"configuration" : {
+			"resetParameters" : {
+				"period" : 7,
+				"visitors" : true
+			},
+			"visitMode" : "hero", // Should be 'once' to match (somewhat buggy) H3 logic
+			"rewards" : [
+				{
+					"limiter" : {
+						"noneOf" : [ { "manaPercentage" : 200 } ]
+					},
+					"message" : "@core.genrltxt.579",
+					"manaPercentage" : 200
+				}
+			]
+		}
+	},
+
+	"attackVisitingBonus": { 
+		"configuration" : {
+			"visitMode" : "hero",
+			"rewards" : [
+				{
+					"message" : "@core.genrltxt.584",
+					"primary" : { "attack" : 1 }
+				}
+			]
+		}
+	},
+	
+	"defenceVisitingBonus": {
+		"configuration" : {
+			"visitMode" : "hero",
+			"rewards" : [
+				{
+					"message" : "@core.genrltxt.585",
+					"primary" : { "defence" : 1 }
+				}
+			]
+		}
+	},
+	
+	"spellPowerVisitingBonus": {
+		"configuration" : {
+			"visitMode" : "hero",
+			"rewards" : [
+				{
+					"message" : "@core.genrltxt.582",
+					"primary" : { "spellpower" : 1 }
+				}
+			]
+		}
+	},
+
+	"knowledgeVisitingBonus": {
+		"configuration" : {
+			"visitMode" : "hero",
+			"rewards" : [
+				{
+					"message" : "@core.genrltxt.581",
+					"primary" : { "knowledge" : 1 }
+				}
+			]
+		}
+	},
+
+	"experienceVisitingBonus": {
+		"configuration" : {
+			"visitMode" : "hero",
+			"rewards" : [
+				{
+					"message" : "@core.genrltxt.583",
+					"heroExperience" : 1000
+				}
+			]
+		}
+	}
 }

+ 1 - 13
config/factions/castle.json

@@ -174,19 +174,7 @@
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl3" },
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde1" ], "mode" : "auto" },
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },
-				"special2":       {
-					"type" : "configurable",
-					"requires" : [ "dwellingLvl4" ],
-					"configuration" : {
-						"visitMode" : "bonus",
-						"rewards" : [
-							{
-								"message" : "@core.genrltxt.580",
-								"movePoints" : 400,
-								"bonuses" : [ { "type" : "MOVEMENT", "subtype" : "heroMovementLand",  "val" : 400, "valueType" : "ADDITIVE_VALUE", "duration" : "ONE_WEEK"} ]
-							}
-						]
-					}
+				"special2":       { "type" : "stables", "requires" : [ "dwellingLvl4" ],
 				},
 				"special3":       { "type" : "brotherhoodOfSword", "upgrades" : "tavern" },
 				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }, "bonuses": [ { "type": "MORALE", "val": 2, "propagator": "PLAYER_PROPAGATOR" } ] },

+ 2 - 32
config/factions/dungeon.json

@@ -174,39 +174,9 @@
 				"special1":       { "type" : "artifactMerchant", "requires" : [ "marketplace" ] },
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl1" },
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
-				"special2":       {
-					"type" : "configurable",
-					"requires" : [ "mageGuild1" ],
-					"configuration" : {
-						"resetParameters" : {
-							"period" : 7,
-							"visitors" : true
-						},
-						"visitMode" : "hero", // Should be 'once' to match (somewhat buggy) H3 logic
-						"rewards" : [
-							{
-								"limiter" : {
-									"noneOf" : [ { "manaPercentage" : 200 } ]
-								},
-								"message" : "@core.genrltxt.579",
-								"manaPercentage" : 200
-							}
-						]
-					}
-				},
+				"special2":       { "type" : "manaVortex", "requires" : [ "mageGuild1" ] },
 				"special3":       { "type" : "portalOfSummoning" },
-				"special4":       {
-					"type" : "configurable",
-					"configuration" : {
-						"visitMode" : "hero",
-						"rewards" : [
-							{
-								"message" : "@core.genrltxt.583",
-								"heroExperience" : 1000
-							}
-						]
-					}
-				},
+				"special4":       { "type" : "experienceVisitingBonus" },
 				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 },
 					"bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primarySkill.spellpower", "val": 12 } ] },
 

+ 1 - 13
config/factions/fortress.json

@@ -169,19 +169,7 @@
 				"resourceSilo":   { "produce": { "wood": 1, "ore": 1 } },
 				"blacksmith":     { },
 
-				"special1":       {
-					"type" : "configurable",
-					"requires" : [ "allOf", [ "townHall" ], [ "special2" ] ],
-					"configuration" : {
-						"visitMode" : "hero",
-						"rewards" : [
-							{
-								"message" : "@core.genrltxt.585",
-								"primary" : { "defence" : 1 }
-							}
-						]
-					}
-				},
+				"special1":       { "type" : "defenceVisitingBonus", "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] },
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl1" },
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },

+ 1 - 13
config/factions/inferno.json

@@ -175,19 +175,7 @@
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
 				"special2":       { "type" : "spellPowerGarrisonBonus", "requires" : [ "fort" ] },
 				"special3":       { "type" : "castleGate", "requires" : [ "citadel" ] },
-				"special4":       {
-					"type" : "configurable",
-					"requires" : [ "mageGuild1" ],
-					"configuration" : {
-						"visitMode" : "hero",
-						"rewards" : [
-							{
-								"message" : "@core.genrltxt.582",
-								"primary" : { "spellpower" : 1 }
-							}
-						]
-					}
-				},
+				"special4":       { "type" : "spellPowerVisitingBonus", "requires" : [ "mageGuild1" ] },
 				"horde2":         { "id" : 24, "upgrades" : "dwellingLvl3" },
 				"horde2Upgr":     { "id" : 25, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde2" ], "mode" : "auto" },
 				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},

+ 1 - 13
config/factions/tower.json

@@ -174,19 +174,7 @@
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
 				"special2":       { "type" : "lookoutTower", "height" : "high", "requires" : [ "fort" ] },
 				"special3":       { "type" : "library", "requires" : [ "mageGuild1" ] },
-				"special4":       {
-					"type" : "configurable",
-					"requires" : [ "mageGuild1" ],
-					"configuration" : {
-						"visitMode" : "hero",
-						"rewards" : [
-							{
-								"message" : "@core.genrltxt.581",
-								"primary" : { "knowledge" : 1 }
-							}
-						]
-					}
-				},
+				"special4":       { "type" : "knowledgeVisitingBonus", "requires" : [ "mageGuild1" ] },
 				"grail":          { "height" : "skyship",  "produce" : { "gold": 5000 }, "bonuses": [ { "type": "PRIMARY_SKILL", "subtype": "primarySkill.knowledge", "val": 15 } ] },
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },

+ 1 - 1
config/schemas/townBuilding.json

@@ -36,7 +36,7 @@
 		},
 		"type" : {
 			"type" : "string",
-			"enum" : [ "mysticPond", "artifactMerchant", "freelancersGuild", "magicUniversity", "castleGate", "creatureTransformer", "portalOfSummoning", "ballistaYard", "lookoutTower", "library", "brotherhoodOfSword", "fountainOfFortune", "spellPowerGarrisonBonus", "attackGarrisonBonus", "defenseGarrisonBonus", "escapeTunnel", "lighthouse", "treasury", "thievesGuild", "bank", "configurable" ],
+			"enum" : [ "mysticPond", "artifactMerchant", "freelancersGuild", "magicUniversity", "castleGate", "creatureTransformer", "portalOfSummoning", "ballistaYard", "lookoutTower", "library", "brotherhoodOfSword", "fountainOfFortune", "spellPowerGarrisonBonus", "attackGarrisonBonus", "defenseGarrisonBonus", "escapeTunnel", "lighthouse", "treasury", "thievesGuild", "bank", "configurable", "stables", "manaVortex", "attackVisitingBonus", "defenceVisitingBonus", "spellPowerVisitingBonus", "knowledgeVisitingBonus", "experienceVisitingBonus" ],
 			"description" : "Subtype for some special buildings"
 		},
 		"mode" : {

+ 1 - 14
lib/constants/Enumerations.h

@@ -25,8 +25,6 @@ namespace BuildingSubID
 	{
 		DEFAULT = -50,
 		NONE = -1,
-		STABLES,
-		BROTHERHOOD_OF_SWORD,
 		CASTLE_GATE,
 		CREATURE_TRANSFORMER,
 		MYSTIC_POND,
@@ -34,26 +32,15 @@ namespace BuildingSubID
 		ARTIFACT_MERCHANT,
 		LOOKOUT_TOWER,
 		LIBRARY,
-		MANA_VORTEX,
 		PORTAL_OF_SUMMONING,
 		ESCAPE_TUNNEL,
 		FREELANCERS_GUILD,
 		BALLISTA_YARD,
-		ATTACK_VISITING_BONUS,
 		MAGIC_UNIVERSITY,
-		SPELL_POWER_GARRISON_BONUS,
-		ATTACK_GARRISON_BONUS,
-		DEFENSE_GARRISON_BONUS,
-		DEFENSE_VISITING_BONUS,
-		SPELL_POWER_VISITING_BONUS,
-		KNOWLEDGE_VISITING_BONUS,
-		EXPERIENCE_VISITING_BONUS,
-		LIGHTHOUSE,
 		TREASURY,
 		THIEVES_GUILD,
 		BANK,
-		CUSTOM_VISITING_BONUS,
-		CUSTOM_VISITING_REWARD
+		CUSTOM_VISITING_BONUS
 	};
 }
 

+ 1 - 14
lib/constants/StringConstants.h

@@ -185,26 +185,13 @@ namespace MappedKeys
 		{ "creatureTransformer", BuildingSubID::CREATURE_TRANSFORMER },//only skeleton transformer yet
 		{ "portalOfSummoning", BuildingSubID::PORTAL_OF_SUMMONING },
 		{ "ballistaYard", BuildingSubID::BALLISTA_YARD },
-		{ "stables", BuildingSubID::STABLES },
-		{ "manaVortex", BuildingSubID::MANA_VORTEX },
 		{ "lookoutTower", BuildingSubID::LOOKOUT_TOWER },
 		{ "library", BuildingSubID::LIBRARY },
-		{ "brotherhoodOfSword", BuildingSubID::BROTHERHOOD_OF_SWORD },//morale garrison bonus
 		{ "fountainOfFortune", BuildingSubID::FOUNTAIN_OF_FORTUNE },//luck garrison bonus
-		{ "spellPowerGarrisonBonus", BuildingSubID::SPELL_POWER_GARRISON_BONUS },//such as 'stormclouds', but this name is not ok for good towns
-		{ "attackGarrisonBonus", BuildingSubID::ATTACK_GARRISON_BONUS },
-		{ "defenseGarrisonBonus", BuildingSubID::DEFENSE_GARRISON_BONUS },
 		{ "escapeTunnel", BuildingSubID::ESCAPE_TUNNEL },
-		{ "attackVisitingBonus", BuildingSubID::ATTACK_VISITING_BONUS },
-		{ "defenceVisitingBonus", BuildingSubID::DEFENSE_VISITING_BONUS },
-		{ "spellPowerVisitingBonus", BuildingSubID::SPELL_POWER_VISITING_BONUS },
-		{ "knowledgeVisitingBonus", BuildingSubID::KNOWLEDGE_VISITING_BONUS },
-		{ "experienceVisitingBonus", BuildingSubID::EXPERIENCE_VISITING_BONUS },
-		{ "lighthouse", BuildingSubID::LIGHTHOUSE },
 		{ "treasury", BuildingSubID::TREASURY },
 		{ "thievesGuild", BuildingSubID::THIEVES_GUILD },
-		{ "bank", BuildingSubID::BANK },
-		{ "configurable", BuildingSubID::CUSTOM_VISITING_REWARD}
+		{ "bank", BuildingSubID::BANK }
 	};
 
 	static const std::map<std::string, EMarketMode> MARKET_NAMES_TO_TYPES =

+ 0 - 17
lib/entities/building/CBuilding.h

@@ -89,23 +89,6 @@ public:
 		return bid == BuildingID::MARKETPLACE || subId == BuildingSubID::ARTIFACT_MERCHANT || subId == BuildingSubID::FREELANCERS_GUILD;
 	}
 
-	STRONG_INLINE
-		bool IsWeekBonus() const
-	{
-		return subId == BuildingSubID::STABLES || subId == BuildingSubID::MANA_VORTEX;
-	}
-
-	STRONG_INLINE
-		bool IsVisitingBonus() const
-	{
-		return subId == BuildingSubID::ATTACK_VISITING_BONUS ||
-			   subId == BuildingSubID::DEFENSE_VISITING_BONUS ||
-			   subId == BuildingSubID::SPELL_POWER_VISITING_BONUS ||
-			   subId == BuildingSubID::KNOWLEDGE_VISITING_BONUS ||
-			   subId == BuildingSubID::EXPERIENCE_VISITING_BONUS ||
-			   subId == BuildingSubID::CUSTOM_VISITING_BONUS;
-	}
-
 	void addNewBonus(const std::shared_ptr<Bonus> & b, BonusList & bonusList) const;
 
 	friend class CTownHandler;

+ 0 - 10
lib/entities/faction/CTown.cpp

@@ -78,14 +78,4 @@ BuildingID CTown::getBuildingType(BuildingSubID::EBuildingSubID subID) const
 	return building == nullptr ? BuildingID::NONE : building->bid.num;
 }
 
-std::string CTown::getGreeting(BuildingSubID::EBuildingSubID subID) const
-{
-	return vstd::find_or(specialMessages, subID, std::string());
-}
-
-void CTown::setGreeting(BuildingSubID::EBuildingSubID subID, const std::string & message) const
-{
-	specialMessages.insert(std::pair<BuildingSubID::EBuildingSubID, const std::string>(subID, message));
-}
-
 VCMI_LIB_NAMESPACE_END

+ 0 - 6
lib/entities/faction/CTown.h

@@ -49,8 +49,6 @@ public:
 	std::string getBuildingScope() const;
 	std::set<si32> getAllBuildings() const;
 	const CBuilding * getSpecialBuilding(BuildingSubID::EBuildingSubID subID) const;
-	std::string getGreeting(BuildingSubID::EBuildingSubID subID) const;
-	void setGreeting(BuildingSubID::EBuildingSubID subID, const std::string & message) const; //may affect only mutable field
 	BuildingID getBuildingType(BuildingSubID::EBuildingSubID subID) const;
 
 	std::string getRandomNameTextID(size_t index) const;
@@ -106,10 +104,6 @@ public:
 		std::string towerIconLarge;
 
 	} clientInfo;
-
-private:
-	///generated bonusing buildings messages for all towns of this type.
-	mutable std::map<BuildingSubID::EBuildingSubID, const std::string> specialMessages; //may be changed by CGTownBuilding::getVisitingBonusGreeting() const
 };
 
 VCMI_LIB_NAMESPACE_END

+ 16 - 68
lib/entities/faction/CTownHandler.cpp

@@ -242,43 +242,6 @@ void CTownHandler::loadBuildingRequirements(CBuilding * building, const JsonNode
 	bidsToLoad.push_back(hlp);
 }
 
-void CTownHandler::addBonusesForVanilaBuilding(CBuilding * building) const
-{
-	std::shared_ptr<Bonus> b;
-	static const TPropagatorPtr playerPropagator = std::make_shared<CPropagatorNodeType>(CBonusSystemNode::ENodeTypes::PLAYER);
-
-	if(building->bid == BuildingID::TAVERN)
-	{
-		b = createBonus(building, BonusType::MORALE, +1);
-	}
-
-	switch(building->subId)
-	{
-	case BuildingSubID::BROTHERHOOD_OF_SWORD:
-		b = createBonus(building, BonusType::MORALE, +2);
-		building->overrideBids.insert(BuildingID::TAVERN);
-		break;
-	case BuildingSubID::FOUNTAIN_OF_FORTUNE:
-		b = createBonus(building, BonusType::LUCK, +2);
-		break;
-	case BuildingSubID::SPELL_POWER_GARRISON_BONUS:
-		b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::SPELL_POWER));
-		break;
-	case BuildingSubID::ATTACK_GARRISON_BONUS:
-		b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::ATTACK));
-		break;
-	case BuildingSubID::DEFENSE_GARRISON_BONUS:
-		b = createBonus(building, BonusType::PRIMARY_SKILL, +2, BonusSubtypeID(PrimarySkill::DEFENSE));
-		break;
-	case BuildingSubID::LIGHTHOUSE:
-		b = createBonus(building, BonusType::MOVEMENT, +500, BonusCustomSubtype::heroMovementSea, playerPropagator);
-		break;
-	}
-
-	if(b)
-		building->addNewBonus(b, building->buildingBonuses);
-}
-
 std::shared_ptr<Bonus> CTownHandler::createBonus(CBuilding * build, BonusType type, int val) const
 {
 	return createBonus(build, type, val, BonusSubtypeID(), emptyPropagator());
@@ -355,17 +318,12 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 	ret->resources = TResources(source["cost"]);
 	ret->produce =   TResources(source["produce"]);
 
-	if(ret->bid == BuildingID::TAVERN)
-		addBonusesForVanilaBuilding(ret);
-	else if(ret->bid.IsSpecialOrGrail())
+	if(ret->bid.IsSpecialOrGrail())
 	{
 		loadSpecialBuildingBonuses(source["bonuses"], ret->buildingBonuses, ret);
 
 		if(ret->buildingBonuses.empty())
-		{
 			ret->subId = vstd::find_or(MappedKeys::SPECIAL_BUILDINGS, source["type"].String(), BuildingSubID::NONE);
-			addBonusesForVanilaBuilding(ret);
-		}
 
 		loadSpecialBuildingBonuses(source["onVisitBonuses"], ret->onVisitBonuses, ret);
 
@@ -378,34 +336,24 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons
 				bonus->sid = BonusSourceID(ret->getUniqueTypeID());
 		}
 		
-		if(ret->subId == BuildingSubID::CUSTOM_VISITING_REWARD)
+		if(!source["configuration"].isNull())
 			ret->rewardableObjectInfo.init(source["configuration"], ret->getBaseTextID());
 	}
 	//MODS COMPATIBILITY FOR 0.96
-	if(!ret->produce.nonZero())
-	{
-		switch (ret->bid.toEnum()) {
-			break; case BuildingID::VILLAGE_HALL: ret->produce[EGameResID::GOLD] = 500;
-			break; case BuildingID::TOWN_HALL :   ret->produce[EGameResID::GOLD] = 1000;
-			break; case BuildingID::CITY_HALL :   ret->produce[EGameResID::GOLD] = 2000;
-			break; case BuildingID::CAPITOL :     ret->produce[EGameResID::GOLD] = 4000;
-			break; case BuildingID::GRAIL :       ret->produce[EGameResID::GOLD] = 5000;
-			break; case BuildingID::RESOURCE_SILO :
-			{
-				switch (ret->town->primaryRes.toEnum())
-				{
-					case EGameResID::GOLD:
-						ret->produce[ret->town->primaryRes] = 500;
-						break;
-					case EGameResID::WOOD_AND_ORE:
-						ret->produce[EGameResID::WOOD] = 1;
-						ret->produce[EGameResID::ORE] = 1;
-						break;
-					default:
-						ret->produce[ret->town->primaryRes] = 1;
-						break;
-				}
-			}
+	if(!ret->produce.nonZero() && ret->bid == BuildingID::RESOURCE_SILO)
+	{
+		switch (ret->town->primaryRes.toEnum())
+		{
+			case EGameResID::GOLD:
+				ret->produce[ret->town->primaryRes] = 500;
+				break;
+			case EGameResID::WOOD_AND_ORE:
+				ret->produce[EGameResID::WOOD] = 1;
+				ret->produce[EGameResID::ORE] = 1;
+				break;
+			default:
+				ret->produce[ret->town->primaryRes] = 1;
+				break;
 		}
 	}
 	loadBuildingRequirements(ret, source["requires"], requirementsToLoad);

+ 0 - 1
lib/entities/faction/CTownHandler.h

@@ -80,7 +80,6 @@ public:
 
 	void loadObject(std::string scope, std::string name, const JsonNode & data) override;
 	void loadObject(std::string scope, std::string name, const JsonNode & data, size_t index) override;
-	void addBonusesForVanilaBuilding(CBuilding * building) const;
 
 	void loadCustom() override;
 	void afterLoadFinalization() override;

+ 12 - 269
lib/mapObjects/CGTownBuilding.cpp

@@ -28,9 +28,10 @@ CGTownBuilding::CGTownBuilding(IGameCallback * cb)
 	, town(nullptr)
 {}
 
-CGTownBuilding::CGTownBuilding(CGTownInstance * town)
+CGTownBuilding::CGTownBuilding(CGTownInstance * town, const BuildingID & index)
 	: IObjectInterface(town->cb)
 	, town(town)
+	, bID(index)
 {}
 
 PlayerColor CGTownBuilding::getOwner() const
@@ -58,272 +59,13 @@ int3 CGTownBuilding::getPosition() const
 	return town->getPosition();
 }
 
-std::string CGTownBuilding::getVisitingBonusGreeting() const
-{
-	auto bonusGreeting = town->getTown()->getGreeting(bType);
-
-	if(!bonusGreeting.empty())
-		return bonusGreeting;
-
-	switch(bType)
-	{
-	case BuildingSubID::MANA_VORTEX:
-		bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingManaVortex"));
-		break;
-	case BuildingSubID::KNOWLEDGE_VISITING_BONUS:
-		bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingKnowledge"));
-		break;
-	case BuildingSubID::SPELL_POWER_VISITING_BONUS:
-		bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingSpellPower"));
-		break;
-	case BuildingSubID::ATTACK_VISITING_BONUS:
-		bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingAttack"));
-		break;
-	case BuildingSubID::EXPERIENCE_VISITING_BONUS:
-		bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingExperience"));
-		break;
-	case BuildingSubID::DEFENSE_VISITING_BONUS:
-		bonusGreeting = std::string(VLC->generaltexth->translate("vcmi.townHall.greetingDefence"));
-		break;
-	}
-	auto buildingName = town->getTown()->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->getTown()->faction->getNameTranslated());
-	}
-	boost::algorithm::replace_first(bonusGreeting, "%s", buildingName);
-	town->getTown()->setGreeting(bType, bonusGreeting);
-	return bonusGreeting;
-}
-
-std::string CGTownBuilding::getCustomBonusGreeting(const Bonus & bonus) const
-{
-	if(bonus.type == BonusType::TOWN_MAGIC_WELL)
-	{
-		MetaString wellGreeting = MetaString::createFromTextID("vcmi.townHall.greetingInTownMagicWell");
-
-		wellGreeting.replaceTextID(town->getTown()->getSpecialBuilding(bType)->getNameTextID());
-		return wellGreeting.toString();
-	}
-
-	MetaString greeting = MetaString::createFromTextID("vcmi.townHall.greetingCustomBonus");
-
-	std::string paramTextID;
-	std::string until;
-
-	if(bonus.type == BonusType::MORALE)
-		paramTextID = "core.genrltxt.384"; // Morale
-
-	if(bonus.type == BonusType::LUCK)
-		paramTextID = "core.genrltxt.385"; // Luck
-
-	greeting.replaceTextID(town->getTown()->getSpecialBuilding(bType)->getNameTextID());
-	greeting.replaceNumber(bonus.val);
-	greeting.replaceTextID(paramTextID);
-
-	if (bonus.duration == BonusDuration::ONE_BATTLE)
-		greeting.replaceTextID("vcmi.townHall.greetingCustomUntil");
-	else
-		greeting.replaceRawString(".");
-
-	return greeting.toString();
-}
-
-COPWBonus::COPWBonus(IGameCallback *cb)
-	: CGTownBuilding(cb)
-{}
-
-COPWBonus::COPWBonus(const BuildingID & bid, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
-	: CGTownBuilding(cgTown)
-{
-	bID = bid;
-	bType = subId;
-	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
-}
-
-void COPWBonus::setProperty(ObjProperty what, ObjPropertyID identifier)
-{
-	switch (what)
-	{
-		case ObjProperty::VISITORS:
-			visitors.insert(identifier.as<ObjectInstanceID>());
-			break;
-		case ObjProperty::STRUCTURE_CLEAR_VISITORS:
-			visitors.clear();
-			break;
-	}
-}
-
-void COPWBonus::onHeroVisit (const CGHeroInstance * h) const
-{
-	ObjectInstanceID heroID = h->id;
-	if(town->hasBuilt(bID))
-	{
-		InfoWindow iw;
-		iw.player = h->tempOwner;
-
-		switch (this->bType)
-		{
-		case BuildingSubID::STABLES:
-			if(!h->hasBonusFrom(BonusSource::OBJECT_TYPE, BonusSourceID(Obj(Obj::STABLES)))) //does not stack with advMap Stables
-			{
-				GiveBonus gb;
-				gb.bonus = Bonus(BonusDuration::ONE_WEEK, BonusType::MOVEMENT, BonusSource::OBJECT_TYPE, 600, BonusSourceID(Obj(Obj::STABLES)), BonusCustomSubtype::heroMovementLand);
-				gb.id = heroID;
-				cb->giveHeroBonus(&gb);
-
-				cb->setMovePoints(heroID, 600, false);
-
-				iw.text.appendRawString(VLC->generaltexth->allTexts[580]);
-				cb->showInfoDialog(&iw);
-			}
-			break;
-
-		case BuildingSubID::MANA_VORTEX:
-			if(visitors.empty())
-			{
-				if(h->mana < h->manaLimit() * 2)
-				{
-					cb->setManaPoints (heroID, 2 * h->manaLimit());
-					//TODO: investigate line below
-					//cb->setObjProperty (town->id, ObjProperty::VISITED, true);
-					iw.text.appendRawString(getVisitingBonusGreeting());
-					cb->showInfoDialog(&iw);
-					town->addHeroToStructureVisitors(h, indexOnTV);
-				}
-			}
-			break;
-		}
-	}
-}
-
-CTownBonus::CTownBonus(IGameCallback *cb)
-	: CGTownBuilding(cb)
-{}
-
-CTownBonus::CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown)
-	: CGTownBuilding(cgTown)
-{
-	bID = index;
-	bType = subId;
-	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
-}
-
-void CTownBonus::setProperty(ObjProperty what, ObjPropertyID identifier)
-{
-	if(what == ObjProperty::VISITORS)
-		visitors.insert(identifier.as<ObjectInstanceID>());
-}
-
-void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
-{
-	ObjectInstanceID heroID = h->id;
-	if(town->hasBuilt(bID) && visitors.find(heroID) == visitors.end())
-	{
-		si64 val = 0;
-		InfoWindow iw;
-		PrimarySkill what = PrimarySkill::NONE;
-
-		switch(bType)
-		{
-		case BuildingSubID::KNOWLEDGE_VISITING_BONUS: //wall of knowledge
-			what = PrimarySkill::KNOWLEDGE;
-			val = 1;
-			iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::KNOWLEDGE, 1);
-			break;
-
-		case BuildingSubID::SPELL_POWER_VISITING_BONUS: //order of fire
-			what = PrimarySkill::SPELL_POWER;
-			val = 1;
-			iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::SPELL_POWER, 1);
-			break;
-
-		case BuildingSubID::ATTACK_VISITING_BONUS: //hall of Valhalla
-			what = PrimarySkill::ATTACK;
-			val = 1;
-			iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::ATTACK, 1);
-			break;
-
-		case BuildingSubID::EXPERIENCE_VISITING_BONUS: //academy of battle scholars
-			what = PrimarySkill::EXPERIENCE;
-			val = static_cast<int>(h->calculateXp(1000));
-			iw.components.emplace_back(ComponentType::EXPERIENCE, val);
-			break;
-
-		case BuildingSubID::DEFENSE_VISITING_BONUS: //cage of warlords
-			what = PrimarySkill::DEFENSE;
-			val = 1;
-			iw.components.emplace_back(ComponentType::PRIM_SKILL, PrimarySkill::DEFENSE, 1);
-			break;
-
-		case BuildingSubID::CUSTOM_VISITING_BONUS:
-			const auto building = town->getTown()->buildings.at(bID);
-			if(!h->hasBonusFrom(BonusSource::TOWN_STRUCTURE, BonusSourceID(building->getUniqueTypeID())))
-			{
-				const auto & bonuses = building->onVisitBonuses;
-				applyBonuses(const_cast<CGHeroInstance *>(h), bonuses);
-			}
-			break;
-		}
-
-		if(what != PrimarySkill::NONE)
-		{
-			iw.player = cb->getOwner(heroID);
-				iw.text.appendRawString(getVisitingBonusGreeting());
-			cb->showInfoDialog(&iw);
-			if (what == PrimarySkill::EXPERIENCE)
-				cb->giveExperience(cb->getHero(heroID), val);
-			else
-				cb->changePrimSkill(cb->getHero(heroID), what, val);
-
-			town->addHeroToStructureVisitors(h, indexOnTV);
-		}
-	}
-}
-
-void CTownBonus::applyBonuses(CGHeroInstance * h, const BonusList & bonuses) const
-{
-	auto addToVisitors = false;
-
-	for(const auto & bonus : bonuses)
-	{
-		GiveBonus gb;
-		InfoWindow iw;
-
-		if(bonus->type == BonusType::TOWN_MAGIC_WELL)
-		{
-			if(h->mana >= h->manaLimit())
-				return;
-			cb->setManaPoints(h->id, h->manaLimit());
-			bonus->duration = BonusDuration::ONE_DAY;
-		}
-		gb.bonus = * bonus;
-		gb.id = h->id;
-		cb->giveHeroBonus(&gb);
-
-		if(bonus->duration == BonusDuration::PERMANENT)
-			addToVisitors = true;
-
-		iw.player = cb->getOwner(h->id);
-		iw.text.appendRawString(getCustomBonusGreeting(gb.bonus));
-		cb->showInfoDialog(&iw);
-	}
-	if(addToVisitors)
-		town->addHeroToStructureVisitors(h, indexOnTV);
-}
-
 CTownRewardableBuilding::CTownRewardableBuilding(IGameCallback *cb)
 	: CGTownBuilding(cb)
 {}
 
-CTownRewardableBuilding::CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * cgTown, vstd::RNG & rand)
-	: CGTownBuilding(cgTown)
+CTownRewardableBuilding::CTownRewardableBuilding(CGTownInstance * town, const BuildingID & index, vstd::RNG & rand)
+	: CGTownBuilding(town, index)
 {
-	bID = index;
-	bType = subId;
-	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 	initObj(rand);
 }
 
@@ -336,7 +78,7 @@ void CTownRewardableBuilding::initObj(vstd::RNG & rand)
 Rewardable::Configuration CTownRewardableBuilding::generateConfiguration(vstd::RNG & rand) const
 {
 	Rewardable::Configuration result;
-	auto building = town->town->buildings.at(bID);
+	auto building = town->town->buildings.at(getBuildingType());
 
 	building->rewardableObjectInfo.configureObject(result, rand, cb);
 	for(auto & rewardInfo : result.info)
@@ -355,11 +97,11 @@ void CTownRewardableBuilding::newTurn(vstd::RNG & rand) const
 	if (configuration.resetParameters.period != 0 && cb->getDate(Date::DAY) > 1 && ((cb->getDate(Date::DAY)-1) % configuration.resetParameters.period) == 0)
 	{
 		auto newConfiguration = generateConfiguration(rand);
-		cb->setRewardableObjectConfiguration(town->id, bID, newConfiguration);
+		cb->setRewardableObjectConfiguration(town->id, getBuildingType(), newConfiguration);
 
 		if(configuration.resetParameters.visitors)
 		{
-			cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, indexOnTV);
+			cb->setObjPropertyValue(town->id, ObjProperty::STRUCTURE_CLEAR_VISITORS, getBuildingType());
 		}
 	}
 }
@@ -406,7 +148,7 @@ void CTownRewardableBuilding::blockingDialogAnswered(const CGHeroInstance *hero,
 
 void CTownRewardableBuilding::grantReward(ui32 rewardID, const CGHeroInstance * hero) const
 {
-	town->addHeroToStructureVisitors(hero, indexOnTV);
+	town->addHeroToStructureVisitors(hero, getBuildingType());
 	
 	grantRewardBeforeLevelup(cb, configuration.info.at(rewardID), hero);
 	
@@ -429,7 +171,7 @@ bool CTownRewardableBuilding::wasVisitedBefore(const CGHeroInstance * contextHer
 			return false; //not supported
 		case Rewardable::VISIT_BONUS:
 		{
-			const auto building = town->getTown()->buildings.at(bID);
+			const auto building = town->getTown()->buildings.at(getBuildingType());
 			return contextHero->hasBonusFrom(BonusSource::TOWN_STRUCTURE, BonusSourceID(building->getUniqueTypeID()));
 		}
 		case Rewardable::VISIT_HERO:
@@ -448,7 +190,7 @@ void CTownRewardableBuilding::onHeroVisit(const CGHeroInstance *h) const
 		auto vi = configuration.info.at(index);
 		logGlobal->debug("Granting reward %d. Message says: %s", index, vi.message.toString());
 		
-		town->addHeroToStructureVisitors(h, indexOnTV); //adding to visitors
+		town->addHeroToStructureVisitors(h, getBuildingType()); //adding to visitors
 
 		InfoWindow iw;
 		iw.player = h->tempOwner;
@@ -476,7 +218,8 @@ void CTownRewardableBuilding::onHeroVisit(const CGHeroInstance *h) const
 		cb->showBlockingDialog(&sd);
 	};
 	
-	if(!town->hasBuilt(bID))
+	assert(town->hasBuilt(getBuildingType()));
+	if(!town->hasBuilt(getBuildingType()))
 		return;
 
 	if(!wasVisitedBefore(h))

+ 58 - 61
lib/mapObjects/CGTownBuilding.h

@@ -22,31 +22,18 @@ class DLL_LINKAGE CGTownBuilding : public IObjectInterface
 {
 ///basic class for town structures handled as map objects
 public:
-	CGTownBuilding(CGTownInstance * town);
+	CGTownBuilding(CGTownInstance * town, const BuildingID & index);
 	CGTownBuilding(IGameCallback *cb);
 
-	si32 indexOnTV = 0; //identifies its index on towns vector
 	
 	CGTownInstance * town;
 
-	STRONG_INLINE
-	BuildingSubID::EBuildingSubID getBuildingSubtype() const
-	{
-		return bType;
-	}
-
 	STRONG_INLINE
 	const BuildingID & getBuildingType() const
 	{
 		return bID;
 	}
 
-	STRONG_INLINE
-	void setBuildingSubtype(BuildingSubID::EBuildingSubID subId)
-	{
-		bType = subId;
-	}
-
 	PlayerColor getOwner() const override;
 	MapObjectID getObjGroupIndex() const override;
 	MapObjectSubID getObjTypeIndex() const override;
@@ -57,55 +44,23 @@ public:
 	template <typename Handler> void serialize(Handler &h)
 	{
 		h & bID;
-		h & indexOnTV;
-		h & bType;
-	}
-
-protected:
-	BuildingID bID; //from buildig list
-	BuildingSubID::EBuildingSubID bType = BuildingSubID::NONE;
-
-	std::string getVisitingBonusGreeting() const;
-	std::string getCustomBonusGreeting(const Bonus & bonus) const;
-};
-
-class DLL_LINKAGE COPWBonus : public CGTownBuilding
-{///used for OPW bonusing structures
-public:
-	std::set<ObjectInstanceID> visitors;
-	void setProperty(ObjProperty what, ObjPropertyID identifier) override;
-	void onHeroVisit (const CGHeroInstance * h) const override;
-
-	COPWBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
-	COPWBonus(IGameCallback *cb);
-
-	template <typename Handler> void serialize(Handler &h)
-	{
-		h & static_cast<CGTownBuilding&>(*this);
-		h & visitors;
-	}
-};
-
-class DLL_LINKAGE CTownBonus : public CGTownBuilding
-{
-///used for one-time bonusing structures
-///feel free to merge inheritance tree
-public:
-	std::set<ObjectInstanceID> visitors;
-	void setProperty(ObjProperty what, ObjPropertyID identifier) override;
-	void onHeroVisit (const CGHeroInstance * h) const override;
-
-	CTownBonus(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * TOWN);
-	CTownBonus(IGameCallback *cb);
-
-	template <typename Handler> void serialize(Handler &h)
-	{
-		h & static_cast<CGTownBuilding&>(*this);
-		h & visitors;
+		if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
+		{
+			// no-op
+		}
+		else
+		{
+			si32 indexOnTV = 0; //identifies its index on towns vector
+			BuildingSubID::EBuildingSubID bType = BuildingSubID::NONE;
+
+			h & indexOnTV;
+			h & bType;
+
+		}
 	}
 
 private:
-	void applyBonuses(CGHeroInstance * h, const BonusList & bonuses) const;
+	BuildingID bID; //from building list
 };
 
 class DLL_LINKAGE CTownRewardableBuilding : public CGTownBuilding, public Rewardable::Interface
@@ -135,7 +90,7 @@ public:
 	/// applies player selection of reward
 	void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override;
 	
-	CTownRewardableBuilding(const BuildingID & index, BuildingSubID::EBuildingSubID subId, CGTownInstance * town, vstd::RNG & rand);
+	CTownRewardableBuilding(CGTownInstance * town, const BuildingID & index, vstd::RNG & rand);
 	CTownRewardableBuilding(IGameCallback *cb);
 	
 	template <typename Handler> void serialize(Handler &h)
@@ -146,4 +101,46 @@ public:
 	}
 };
 
+/// Compatibility for old code
+class DLL_LINKAGE CTownCompatBuilding1 : public CTownRewardableBuilding
+{
+public:
+	using CTownRewardableBuilding::CTownRewardableBuilding;
+
+	template <typename Handler> void serialize(Handler &h)
+	{
+		if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
+		{
+			h & static_cast<CTownRewardableBuilding&>(*this);
+		}
+		else
+		{
+			h & static_cast<CGTownBuilding&>(*this);
+			std::set<ObjectInstanceID> visitors;
+			h & visitors;
+		}
+	}
+};
+
+/// Compatibility for old code
+class DLL_LINKAGE CTownCompatBuilding2 : public CTownRewardableBuilding
+{
+public:
+	using CTownRewardableBuilding::CTownRewardableBuilding;
+
+	template <typename Handler> void serialize(Handler &h)
+	{
+		if (h.version >= Handler::Version::NEW_TOWN_BUILDINGS)
+		{
+			h & static_cast<CTownRewardableBuilding&>(*this);
+		}
+		else
+		{
+			h & static_cast<CGTownBuilding&>(*this);
+			std::set<ObjectInstanceID> visitors;
+			h & visitors;
+		}
+	}
+};
+
 VCMI_LIB_NAMESPACE_END

+ 5 - 47
lib/mapObjects/CGTownInstance.cpp

@@ -380,31 +380,12 @@ void CGTownInstance::initOverriddenBids()
 	}
 }
 
-bool CGTownInstance::isBonusingBuildingAdded(BuildingID bid) const
-{
-	auto present = std::find_if(bonusingBuildings.begin(), bonusingBuildings.end(), [&](CGTownBuilding* building)
-		{
-			return building->getBuildingType() == bid;
-		});
-
-	return present != bonusingBuildings.end();
-}
-
-void CGTownInstance::addTownBonuses(vstd::RNG & rand)
+void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
 {
 	for(const auto & kvp : town->buildings)
 	{
-		if(vstd::contains(overriddenBuildings, kvp.first))
-			continue;
-
-		if(kvp.second->IsVisitingBonus())
-			bonusingBuildings.push_back(new CTownBonus(kvp.second->bid, kvp.second->subId, this));
-
-		if(kvp.second->IsWeekBonus())
-			bonusingBuildings.push_back(new COPWBonus(kvp.second->bid, kvp.second->subId, this));
-		
-		if(kvp.second->subId == BuildingSubID::CUSTOM_VISITING_REWARD)
-			bonusingBuildings.push_back(new CTownRewardableBuilding(kvp.second->bid, kvp.second->subId, this, rand));
+		if(!kvp.second->rewardableObjectInfo.getParameters().isNull())
+			bonusingBuildings.push_back(new CTownRewardableBuilding(this, kvp.second->bid, rand));
 	}
 }
 
@@ -460,15 +441,7 @@ void CGTownInstance::deleteTownBonus(BuildingID bid)
 	if(freeIt == nullptr)
 		return;
 
-	auto building = town->buildings.at(bid);
-	auto isVisitingBonus = building->IsVisitingBonus();
-	auto isWeekBonus = building->IsWeekBonus();
-
-	if(!isVisitingBonus && !isWeekBonus)
-		return;
-
 	bonusingBuildings.erase(bonusingBuildings.begin() + i);
-
 	delete freeIt;
 }
 
@@ -527,7 +500,7 @@ void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
 		}
 	}
 	initOverriddenBids();
-	addTownBonuses(rand); //add special bonuses from buildings to the bonusingBuildings vector.
+	initializeConfigurableBuildings(rand);
 	recreateBuildingsBonuses();
 	updateAppearance();
 }
@@ -549,9 +522,6 @@ void CGTownInstance::newTurn(vstd::RNG & rand) const
 			cb->setObjPropertyValue(id, ObjProperty::BONUS_VALUE_FIRST, resID);
 			cb->setObjPropertyValue(id, ObjProperty::BONUS_VALUE_SECOND, resVal);
 		}
-		
-		for(const auto * manaVortex : getBonusingBuildings(BuildingSubID::MANA_VORTEX))
-			cb->setObjPropertyValue(id, ObjProperty::STRUCTURE_CLEAR_VISITORS, manaVortex->indexOnTV); //reset visitors for Mana Vortex
 
 		if (tempOwner == PlayerColor::NEUTRAL) //garrison growth for neutral towns
 		{
@@ -606,7 +576,7 @@ void CGTownInstance::newTurn(vstd::RNG & rand) const
 		}
 	}
 	
-	for(const auto * rewardableBuilding : getBonusingBuildings(BuildingSubID::CUSTOM_VISITING_REWARD))
+	for(const auto * rewardableBuilding : bonusingBuildings)
 		rewardableBuilding->newTurn(rand);
 		
 	if(hasBuilt(BuildingSubID::BANK) && bonusValue.second > 0)
@@ -979,18 +949,6 @@ const CArmedInstance * CGTownInstance::getUpperArmy() const
 	return this;
 }
 
-std::vector<const CGTownBuilding *> CGTownInstance::getBonusingBuildings(BuildingSubID::EBuildingSubID subId) const
-{
-	std::vector<const CGTownBuilding *> ret;
-	for(auto * const building : bonusingBuildings)
-	{
-		if(building->getBuildingSubtype() == subId)
-			ret.push_back(building);
-	}
-	return ret;
-}
-
-
 bool CGTownInstance::hasBuiltSomeTradeBuilding() const
 {
 	for(const auto & bid : builtBuildings)

+ 1 - 3
lib/mapObjects/CGTownInstance.h

@@ -165,7 +165,6 @@ public:
 	GrowthInfo getGrowthInfo(int level) const;
 	bool hasFort() const;
 	bool hasCapitol() const;
-	std::vector<const CGTownBuilding *> getBonusingBuildings(BuildingSubID::EBuildingSubID subId) const;
 	bool hasBuiltSomeTradeBuilding() const;
 	//checks if special building with type buildingID is constructed
 	bool hasBuilt(BuildingSubID::EBuildingSubID buildingID) const;
@@ -231,9 +230,8 @@ private:
 	void onTownCaptured(const PlayerColor & winner) const;
 	int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const;
 	bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
-	bool isBonusingBuildingAdded(BuildingID bid) const;
 	void initOverriddenBids();
-	void addTownBonuses(vstd::RNG & rand);
+	void initializeConfigurableBuildings(vstd::RNG & rand);
 };
 
 VCMI_LIB_NAMESPACE_END

+ 0 - 1
lib/mapObjects/CRewardableObject.h

@@ -95,7 +95,6 @@ public:
 // class DLL_LINKAGE CGPyramid : public CBank
 
 // EXTRA
-// class DLL_LINKAGE COPWBonus : public CGTownBuilding
 // class DLL_LINKAGE CTownBonus : public CGTownBuilding
 // class DLL_LINKAGE CGKeys : public CGObjectInstance //Base class for Keymaster and guards
 // class DLL_LINKAGE CGKeymasterTent : public CGKeys

+ 2 - 2
lib/registerTypes/RegisterTypesMapObjects.h

@@ -87,8 +87,8 @@ void registerTypesMapObjects(Serializer &s)
 
 	//Other object-related
 	s.template registerType<IObjectInterface, CGTownBuilding>();
-		s.template registerType<CGTownBuilding, CTownBonus>();
-		s.template registerType<CGTownBuilding, COPWBonus>();
+		s.template registerType<CGTownBuilding, CTownCompatBuilding1>();
+		s.template registerType<CGTownBuilding, CTownCompatBuilding2>();
 		s.template registerType<CGTownBuilding, CTownRewardableBuilding>();
 
 	s.template registerType<CGObjectInstance, CRewardableObject>();

+ 2 - 1
lib/serializer/ESerializationVersion.h

@@ -63,6 +63,7 @@ enum class ESerializationVersion : int32_t
 	STATISTICS, // 852 - removed random number generators from library classes
 	CAMPAIGN_REGIONS, // 853 - configurable campaign regions
 	EVENTS_PLAYER_SET, // 854 - map & town events use std::set instead of bitmask to store player list
+	NEW_TOWN_BUILDINGS, // 855 - old bonusing buildings have been removed
 
-	CURRENT = EVENTS_PLAYER_SET
+	CURRENT = NEW_TOWN_BUILDINGS
 };