浏览代码

Merge pull request #659 from ShubusCorporation/shc_special_buildings_2

Mod system improvement: Special buildings should work in modders towns. Part II.
Alexander Shishkin 5 年之前
父节点
当前提交
c0a5d1ccf0

+ 1 - 1
config/factions/castle.json

@@ -170,7 +170,7 @@
 				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "ore": 1, "wood": 1 } },
 				"blacksmith":     { "id" : 16 },
 
-				"special1":       { "requires" : [ "shipyard" ] },
+				"special1":       { "type" : "lighthouse", "requires" : [ "shipyard" ] },
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl3" },
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl3", "requires" : [ "horde1" ], "mode" : "auto" },
 				"ship":           { "id" : 20, "upgrades" : "shipyard" },

+ 1 - 1
config/factions/dungeon.json

@@ -174,7 +174,7 @@
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
 				"special2":       { "type" : "manaVortex", "requires" : [ "mageGuild1" ] },
 				"special3":       { "type" : "portalOfSummoning" },
-				"special4":       { },
+				"special4":       { "type" : "experienceVisitingBonus" },
 				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },

+ 1 - 1
config/factions/fortress.json

@@ -169,7 +169,7 @@
 				"resourceSilo":   { "id" : 15, "requires" : [ "marketplace" ], "produce": { "wood": 1, "ore": 1 } },
 				"blacksmith":     { "id" : 16 },
 
-				"special1":       { "requires" : [ "allOf", [ "townHall" ], [ "special2" ] ] },
+				"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 - 1
config/factions/inferno.json

@@ -174,7 +174,7 @@
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
 				"special2":       { "type" : "spellPowerGarrisonBonus", "requires" : [ "fort" ] },
 				"special3":       { "type" : "castleGate", "requires" : [ "citadel" ] },
-				"special4":       { "requires" : [ "mageGuild1" ] },
+				"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 - 1
config/factions/rampart.json

@@ -178,7 +178,7 @@
 				"horde1":         { "id" : 18, "upgrades" : "dwellingLvl2" },
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
 				"special2":       { "type" : "fountainOfFortune", "upgrades" : "special1" },
-				"special3":       { "requires" : [ "horde1" ] },
+				"special3":       { "type" : "treasury", "requires" : [ "horde1" ] },
 				"horde2":         { "id" : 24, "upgrades" : "dwellingLvl5" },
 				"horde2Upgr":     { "id" : 25, "upgrades" : "dwellingUpLvl5", "requires" : [ "horde2" ], "mode" : "auto" },
 				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},

+ 1 - 1
config/factions/stronghold.json

@@ -171,7 +171,7 @@
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl1", "requires" : [ "horde1" ], "mode" : "auto" },
 				"special2":       { "type" : "freelancersGuild", "requires" : [ "marketplace" ] },
 				"special3":       { "type" : "ballistaYard", "requires" : [ "blacksmith" ] },
-				"special4":       { "requires" : [ "fort" ] },
+				"special4":       { "type" : "attackVisitingBonus", "requires" : [ "fort" ] },
 				"grail":          { "id" : 26, "mode" : "grail", "produce": { "gold": 5000 }},
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },

+ 1 - 1
config/factions/tower.json

@@ -174,7 +174,7 @@
 				"horde1Upgr":     { "id" : 19, "upgrades" : "dwellingUpLvl2", "requires" : [ "horde1" ], "mode" : "auto" },
 				"special2":       { "type" : "lookoutTower", "height" : "high", "requires" : [ "fort" ] },
 				"special3":       { "type" : "library", "requires" : [ "mageGuild1" ] },
-				"special4":       { "requires" : [ "mageGuild1" ] },
+				"special4":       { "type" : "knowledgeVisitingBonus", "requires" : [ "mageGuild1" ] },
 				"grail":          { "height" : "skyship",  "produce" : { "gold": 5000 } },
 
 				"dwellingLvl1":   { "id" : 30, "requires" : [ "fort" ] },

+ 7 - 0
lib/CHeroHandler.h

@@ -195,6 +195,13 @@ public:
 		h & imageBattleFemale;
 		h & imageMapMale;
 		h & imageMapFemale;
+
+		if(!h.saving)
+		{
+			for(auto i = 0; i < secSkillProbability.size(); i++)
+				if(secSkillProbability[i] < 0)
+					secSkillProbability[i] = 0;
+		}
 	}
 	EAlignment::EAlignment getAlignment() const;
 };

+ 1 - 0
lib/CSkillHandler.cpp

@@ -34,6 +34,7 @@ CSkill::LevelInfo::~LevelInfo()
 CSkill::CSkill(SecondarySkill id, std::string identifier)
 	: id(id), identifier(identifier)
 {
+	gainChance[0] = gainChance[1] = 0; //affects CHeroClassHandler::afterLoadFinalization()
 	levels.resize(NSecondarySkill::levels.size() - 1);
 }
 

+ 16 - 0
lib/CTownHandler.h

@@ -85,6 +85,22 @@ 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;
+	}
+
 	/// input: faction, bid; output: subId, height;
 	void update792(const BuildingID & bid, BuildingSubID::EBuildingSubID & subId, ETowerHeight & height);
 

+ 16 - 3
lib/GameConstants.h

@@ -442,11 +442,17 @@ namespace BuildingSubID
 		ESCAPE_TUNNEL,
 		FREELANCERS_GUILD,
 		BALLISTA_YARD,
-		HALL_OF_VALHALLA,
+		ATTACK_VISITING_BONUS,
 		MAGIC_UNIVERSITY,
 		SPELL_POWER_GARRISON_BONUS,
 		ATTACK_GARRISON_BONUS,
-		DEFENSE_GARRISON_BONUS
+		DEFENSE_GARRISON_BONUS,
+		DEFENSE_VISITING_BONUS,
+		SPELL_POWER_VISITING_BONUS,
+		KNOWLEDGE_VISITING_BONUS,
+		EXPERIENCE_VISITING_BONUS,
+		LIGHTHOUSE,
+		TREASURY
 	};
 }
 
@@ -490,7 +496,14 @@ namespace MappedKeys
 		{ "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 }
+		{ "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 }
 	};
 }
 

+ 111 - 92
lib/mapObjects/CGTownInstance.cpp

@@ -744,18 +744,36 @@ bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId)
 	return town->getBuildingType(subId) != BuildingID::NONE;
 }
 
-//it does not check hasBuilt(...) because this check is in the OnHeroVisit handler
-bool CGTownInstance::tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID)
+//it does not check hasBuilt because this check is in the OnHeroVisit handler
+void CGTownInstance::tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID)
 {
 	auto bid = town->getBuildingType(subID);
 
-	if(bid == BuildingID::NONE)
-		return false;
+	if(bid != BuildingID::NONE)
+		bonusingBuildings.push_back(new COPWBonus(bid, subID, this));
+}
 
-	bonusingBuildings.push_back(new COPWBonus(bid, subID, this));
-	return true;
+void CGTownInstance::tryAddVisitingBonus(BuildingSubID::EBuildingSubID subID)
+{
+	auto bid = town->getBuildingType(subID);
+
+	if(bid != BuildingID::NONE)
+		bonusingBuildings.push_back(new CTownBonus(bid, subID, this));
+}
+
+void CGTownInstance::addTownBonuses()
+{
+	for(const auto & kvp : town->buildings)
+	{
+		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));
+	}
 }
 
+
 void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structures
 {
 	blockVisit = true;
@@ -776,24 +794,7 @@ void CGTownInstance::initObj(CRandomGenerator & rand) ///initialize town structu
 				creatures[level].second.push_back(town->creatures[level][upgradeNum]);
 		}
 	}
-	tryAddOnePerWeekBonus(BuildingSubID::STABLES);
-	tryAddOnePerWeekBonus(BuildingSubID::MANA_VORTEX);
-
-	switch (subID)
-	{
-		//add new visitable objects
-		case ETownType::DUNGEON:
-		case ETownType::TOWER: 
-		case ETownType::INFERNO: 
-		case ETownType::STRONGHOLD:
-			bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_4, this));
-			break;
-		case ETownType::FORTRESS:
-			bonusingBuildings.push_back (new CTownBonus(BuildingID::SPECIAL_1, this));
-			break;
-	}
-	//add special bonuses from buildings
-
+	addTownBonuses(); //add special bonuses from buildings to the bonusingBuildings vector.
 	recreateBuildingsBonuses();
 	updateAppearance();
 }
@@ -803,18 +804,35 @@ void CGTownInstance::updateBonusingBuildings()
 	if (this->town->faction != nullptr)
 	{
 		//firstly, update subtype for the Bonusing objects, which are already stored in the bonusing list
-		for (auto building : bonusingBuildings)
+		for (auto building : bonusingBuildings) //no garrison bonuses here, only week and visiting bonuses
 		{
 			switch (this->town->faction->index)
 			{
 			case ETownType::CASTLE:
-				if (building->getBuildingType() == BuildingID::SPECIAL_2)
-					building->setBuildingSubtype(BuildingSubID::STABLES);
+				building->setBuildingSubtype(BuildingSubID::STABLES);
 				break;
 
 			case ETownType::DUNGEON:
 				if(building->getBuildingType() == BuildingID::SPECIAL_2)
 					building->setBuildingSubtype(BuildingSubID::MANA_VORTEX);
+				else if(building->getBuildingType() == BuildingID::SPECIAL_4)
+					building->setBuildingSubtype(BuildingSubID::EXPERIENCE_VISITING_BONUS);
+				break;
+
+			case ETownType::TOWER:
+				building->setBuildingSubtype(BuildingSubID::KNOWLEDGE_VISITING_BONUS);
+				break;
+
+			case ETownType::STRONGHOLD:
+				building->setBuildingSubtype(BuildingSubID::ATTACK_VISITING_BONUS);
+				break;
+
+			case ETownType::INFERNO:
+				building->setBuildingSubtype(BuildingSubID::SPELL_POWER_VISITING_BONUS);
+				break;
+
+			case ETownType::FORTRESS:
+				building->setBuildingSubtype(BuildingSubID::DEFENSE_VISITING_BONUS);
 				break;
 			}
 		}
@@ -824,23 +842,24 @@ void CGTownInstance::updateBonusingBuildings()
 	{
 		auto & building = kvp.second;
 
-		switch (building->subId)
+		if(building->subId == BuildingSubID::PORTAL_OF_SUMMONING)
 		{
-		case BuildingSubID::PORTAL_OF_SUMMONING:
 			if(!hasBuiltInOldWay(ETownType::DUNGEON, BuildingID::PORTAL_OF_SUMMON))
 				creatures.resize(GameConstants::CREATURES_PER_TOWN + 1);
-			break;
-		///'hasBuilt' checking for COPW bonuses is in the COPWBonus::onHeroVisit
-		case BuildingSubID::STABLES:
-			if(getBonusingBuilding(building->subId) == nullptr)
-				tryAddOnePerWeekBonus(BuildingSubID::STABLES);
-			break;
-
-		case BuildingSubID::MANA_VORTEX:
-			if(getBonusingBuilding(building->subId) == nullptr)
-				tryAddOnePerWeekBonus(BuildingSubID::MANA_VORTEX);
-			break;
+			continue;
 		}
+		if(!building->IsVisitingBonus() && !building->IsWeekBonus()) //it's not bonusing => nothing to handle
+			continue;
+
+		if(getBonusingBuilding(building->subId) != nullptr) //it's already added => already handled
+			continue;
+
+		///'hasBuilt' checking for bonuses is in the onHeroVisit handler
+		if(building->IsWeekBonus())
+			tryAddOnePerWeekBonus(building->subId);
+
+		if(building->IsVisitingBonus())
+			tryAddVisitingBonus(building->subId);
 	}
 	recreateBuildingsBonuses(); ///Clear all bonuses and recreate
 }
@@ -1180,11 +1199,11 @@ void CGTownInstance::recreateBuildingsBonuses()
 	addBonusIfBuilt(BuildingSubID::SPELL_POWER_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::SPELL_POWER);//works as Brimstone Clouds
 	addBonusIfBuilt(BuildingSubID::ATTACK_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::ATTACK);//works as Blood Obelisk
 	addBonusIfBuilt(BuildingSubID::DEFENSE_GARRISON_BONUS, Bonus::PRIMARY_SKILL, +2, PrimarySkill::DEFENSE);//works as Glyphs of Fear
+	addBonusIfBuilt(BuildingSubID::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, playerProp);
 
 	if(subID == ETownType::CASTLE) //castle
 	{
-		addBonusIfBuilt(BuildingID::LIGHTHOUSE, Bonus::SEA_MOVEMENT, +500, playerProp);
-		addBonusIfBuilt(BuildingID::GRAIL,      Bonus::MORALE, +2, playerProp); //colossus
+		addBonusIfBuilt(BuildingID::GRAIL, Bonus::MORALE, +2, playerProp); //colossus
 	}
 	else if(subID == ETownType::RAMPART) //rampart
 	{
@@ -1215,14 +1234,14 @@ void CGTownInstance::recreateBuildingsBonuses()
 	}
 }
 
-bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, int subtype)
+bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype)
 {
 	BuildingID currentBid = BuildingID::NONE;
 	std::ostringstream descr;
 
 	for(const auto & bid : builtBuildings)
 	{
-		if (town->buildings.at(bid)->subId == subId)
+		if(town->buildings.at(bid)->subId == subId)
 		{
 			descr << town->buildings.at(bid)->Name();
 			currentBid = bid;
@@ -1230,12 +1249,12 @@ bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus:
 		}
 	}
 	return currentBid == BuildingID::NONE ? false
-		: addBonusImpl(currentBid, type, val, emptyPropagator, descr.str(), subtype);
+		: addBonusImpl(currentBid, type, val, prop, descr.str(), subtype);
 }
 
-bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype)
+bool CGTownInstance::addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, int subtype)
 {
-	return addBonusIfBuilt(building, type, val, emptyPropagator, subtype);
+	return addBonusIfBuilt(subId, type, val, emptyPropagator, subtype);
 }
 
 bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype)
@@ -1248,6 +1267,11 @@ bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type,
 	return addBonusImpl(building, type, val, prop, descr.str(), subtype);
 }
 
+bool CGTownInstance::addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype)
+{
+	return addBonusIfBuilt(building, type, val, emptyPropagator, subtype);
+}
+
 bool CGTownInstance::addBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype)
 {
 	auto b = std::make_shared<Bonus>(Bonus::PERMANENT, type, Bonus::TOWN_STRUCTURE, val, building, description, subtype);
@@ -1686,9 +1710,10 @@ void COPWBonus::onHeroVisit(const CGHeroInstance * h) const
 		}
 	}
 }
-CTownBonus::CTownBonus (BuildingID index, CGTownInstance *TOWN)
+CTownBonus::CTownBonus (BuildingID index, BuildingSubID::EBuildingSubID subId, CGTownInstance *TOWN)
 {
 	bID = index;
+	bType = subId;
 	town = TOWN;
 	indexOnTV = static_cast<si32>(town->bonusingBuildings.size());
 }
@@ -1699,64 +1724,58 @@ void CTownBonus::setProperty (ui8 what, ui32 val)
 		visitors.insert(ObjectInstanceID(val));
 }
 
-void CTownBonus::onHeroVisit (const CGHeroInstance * h) const
+void CTownBonus::onHeroVisit(const CGHeroInstance * h) const
 {
 	ObjectInstanceID heroID = h->id;
 	if (town->hasBuilt(bID) && visitors.find(heroID) == visitors.end())
 	{
-		si32 mid=0;
+		si32 mid = 0;
 		si64 val = 0;
 		InfoWindow iw;
 		PrimarySkill::PrimarySkill what = PrimarySkill::ATTACK;
 
-		switch (bID)
+		switch (bType)
 		{
-			case BuildingID::SPECIAL_4:
-				switch(town->subID)
-				{
-					case ETownType::TOWER: //wall
-						what = PrimarySkill::KNOWLEDGE;
-						val = 1;
-						mid = 581;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 3, 1, 0));
-						break;
-					case ETownType::INFERNO: //order of fire
-						what = PrimarySkill::SPELL_POWER;
-						val = 1;
-						mid = 582;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 2, 1, 0));
-						break;
-					case ETownType::STRONGHOLD://hall of Valhalla
-						what = PrimarySkill::ATTACK;
-						val = 1;
-						mid = 584;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 0, 1, 0));
-						break;
-					case ETownType::DUNGEON://academy of battle scholars
-						what = PrimarySkill::EXPERIENCE;
-						val = static_cast<int>(h->calculateXp(1000));
-						mid = 583;
-						iw.components.push_back (Component(Component::EXPERIENCE, 0, val, 0));
-						break;
-				}
-				break;
-			case BuildingID::SPECIAL_1:
-				switch(town->subID)
-				{
-					case ETownType::FORTRESS: //cage of warlords
-						what = PrimarySkill::DEFENSE;
-						val = 1;
-						mid = 585;
-						iw.components.push_back (Component(Component::PRIM_SKILL, 1, 1, 0));
-						break;
-				}
-				break;
+		case BuildingSubID::KNOWLEDGE_VISITING_BONUS: //wall of knowledge
+			what = PrimarySkill::KNOWLEDGE;
+			val = 1;
+			mid = 581;
+			iw.components.push_back(Component(Component::PRIM_SKILL, 3, 1, 0));
+			break;
+
+		case BuildingSubID::SPELL_POWER_VISITING_BONUS: //order of fire
+			what = PrimarySkill::SPELL_POWER;
+			val = 1;
+			mid = 582;
+			iw.components.push_back(Component(Component::PRIM_SKILL, 2, 1, 0));
+			break;
+
+		case BuildingSubID::ATTACK_VISITING_BONUS: //hall of Valhalla
+			what = PrimarySkill::ATTACK;
+			val = 1;
+			mid = 584;
+			iw.components.push_back(Component(Component::PRIM_SKILL, 0, 1, 0));
+			break;
+
+		case BuildingSubID::EXPERIENCE_VISITING_BONUS: //academy of battle scholars
+			what = PrimarySkill::EXPERIENCE;
+			val = static_cast<int>(h->calculateXp(1000));
+			mid = 583;
+			iw.components.push_back(Component(Component::EXPERIENCE, 0, val, 0));
+			break;
+
+		case BuildingSubID::DEFENSE_VISITING_BONUS: //cage of warlords
+			what = PrimarySkill::DEFENSE;
+			val = 1;
+			mid = 585;
+			iw.components.push_back(Component(Component::PRIM_SKILL, 1, 1, 0));
+			break;
 		}
 		assert(mid);
 		iw.player = cb->getOwner(heroID);
 		iw.text << VLC->generaltexth->allTexts[mid];
 		cb->showInfoDialog(&iw);
-		cb->changePrimSkill (cb->getHero(heroID), what, val);
+		cb->changePrimSkill(cb->getHero(heroID), what, val);
 		town->addHeroToStructureVisitors(h, indexOnTV);
 	}
 }

+ 5 - 2
lib/mapObjects/CGTownInstance.h

@@ -158,7 +158,7 @@ public:
 	void setProperty(ui8 what, ui32 val) override;
 	void onHeroVisit (const CGHeroInstance * h) const override;
 
-	CTownBonus (BuildingID index, CGTownInstance *TOWN);
+	CTownBonus (BuildingID index, BuildingSubID::EBuildingSubID subId, CGTownInstance *TOWN);
 	CTownBonus () {};
 
 	template <typename Handler> void serialize(Handler &h, const int version)
@@ -262,6 +262,7 @@ public:
 	void deserializationFix();
 	void recreateBuildingsBonuses();
 	///bid: param to bind a building with a bonus, subId: param to check if already built
+	bool addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, TPropagatorPtr & prop, int subtype = -1);
 	bool addBonusIfBuilt(BuildingSubID::EBuildingSubID subId, Bonus::BonusType type, int val, int subtype = -1);
 	bool addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr &prop, int subtype = -1); //returns true if building is built and bonus has been added
 	bool addBonusIfBuilt(BuildingID building, Bonus::BonusType type, int val, int subtype = -1); //convienence version of above
@@ -342,5 +343,7 @@ private:
 	bool hasBuiltInOldWay(ETownType::ETownType type, BuildingID bid) const;
 	bool addBonusImpl(BuildingID building, Bonus::BonusType type, int val, TPropagatorPtr & prop, const std::string & description, int subtype = -1);
 	bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
-	bool tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID);
+	void tryAddOnePerWeekBonus(BuildingSubID::EBuildingSubID subID);
+	void tryAddVisitingBonus(BuildingSubID::EBuildingSubID subID);
+	void addTownBonuses();
 };

+ 12 - 2
server/CGameHandler.cpp

@@ -1812,7 +1812,7 @@ void CGameHandler::newTurn()
 				setPortalDwelling(t, true, (n.specialWeek == NewTurn::PLAGUE ? true : false)); //set creatures for Portal of Summoning
 
 			if (!firstTurn)
-				if (t->hasBuilt(BuildingID::TREASURY, ETownType::RAMPART) && player < PlayerColor::PLAYER_LIMIT)
+				if (t->hasBuilt(BuildingSubID::TREASURY) && player < PlayerColor::PLAYER_LIMIT)
 						n.res[player][Res::GOLD] += hadGold.at(player)/10; //give 10% of starting gold
 
 			if (!vstd::contains(n.cres, t->id))
@@ -3848,8 +3848,18 @@ bool CGameHandler::queryReply(QueryID qid, const JsonNode & answer, PlayerColor
 	logGlobal->trace(answer.toJson());
 
 	auto topQuery = queries.topQuery(player);
+
 	COMPLAIN_RET_FALSE_IF(!topQuery, "This player doesn't have any queries!");
-	COMPLAIN_RET_FALSE_IF(topQuery->queryID != qid, "This player top query has different ID!");
+
+	if(topQuery->queryID != qid)
+	{
+		auto currentQuery = queries.getQuery(qid);
+
+		if(currentQuery != nullptr && currentQuery->endsByPlayerAnswer())
+			currentQuery->setReply(answer);
+
+		COMPLAIN_RET("This player top query has different ID!"); //topQuery->queryID != qid
+	}
 	COMPLAIN_RET_FALSE_IF(!topQuery->endsByPlayerAnswer(), "This query cannot be ended by player's answer!");
 
 	topQuery->setReply(answer);

+ 11 - 2
server/CQuery.cpp

@@ -248,10 +248,10 @@ std::vector<std::shared_ptr<const CQuery>> Queries::allQueries() const
 	return ret;
 }
 
-std::vector<std::shared_ptr<CQuery>> Queries::allQueries()
+std::vector<QueryPtr> Queries::allQueries()
 {
 	//TODO code duplication with const function :(
-	std::vector<std::shared_ptr<CQuery>> ret;
+	std::vector<QueryPtr> ret;
 	for(auto & playerQueries : queries)
 		for(auto & query : playerQueries.second)
 			ret.push_back(query);
@@ -259,6 +259,15 @@ std::vector<std::shared_ptr<CQuery>> Queries::allQueries()
 	return ret;
 }
 
+QueryPtr Queries::getQuery(QueryID queryID)
+{
+	for(auto & playerQueries : queries)
+		for(auto & query : playerQueries.second)
+			if(query->queryID == queryID)
+				return query;
+	return nullptr;
+}
+
 void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
 {
 	assert(result);

+ 2 - 2
server/CQuery.h

@@ -219,7 +219,7 @@ public:
 	QueryPtr topQuery(PlayerColor player);
 
 	std::vector<std::shared_ptr<const CQuery>> allQueries() const;
-	std::vector<std::shared_ptr<CQuery>> allQueries();
+	std::vector<QueryPtr> allQueries();
+	QueryPtr getQuery(QueryID queryID);
 	//void removeQuery
-
 };