Jelajahi Sumber

Added Query to track visit duration for Taverns and Markets

Ivan Savenko 2 tahun lalu
induk
melakukan
898733eed7

+ 12 - 3
AI/Nullkiller/AIGateway.cpp

@@ -154,10 +154,13 @@ void AIGateway::artifactAssembled(const ArtifactLocation & al)
 	NET_EVENT_HANDLER;
 }
 
-void AIGateway::showTavernWindow(const CGObjectInstance * townOrTavern)
+void AIGateway::showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
+
+	status.addQuery(queryID, "TavernWindow");
+	requestActionASAP([=](){ answerQuery(queryID, 0); });
 }
 
 void AIGateway::showThievesGuildWindow(const CGObjectInstance * obj)
@@ -425,10 +428,13 @@ void AIGateway::receivedResource()
 	NET_EVENT_HANDLER;
 }
 
-void AIGateway::showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor)
+void AIGateway::showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
+
+	status.addQuery(queryID, "UniversityWindow");
+	requestActionASAP([=](){ answerQuery(queryID, 0); });
 }
 
 void AIGateway::heroManaPointsChanged(const CGHeroInstance * hero)
@@ -498,10 +504,13 @@ void AIGateway::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonu
 	NET_EVENT_HANDLER;
 }
 
-void AIGateway::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor)
+void AIGateway::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
+
+	status.addQuery(queryID, "MarketWindow");
+	requestActionASAP([=](){ answerQuery(queryID, 0); });
 }
 
 void AIGateway::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain)

+ 3 - 3
AI/Nullkiller/AIGateway.h

@@ -130,7 +130,7 @@ public:
 	void tileHidden(const std::unordered_set<int3> & pos) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
-	void showTavernWindow(const CGObjectInstance * townOrTavern) override;
+	void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
 	void showThievesGuildWindow(const CGObjectInstance * obj) override;
 	void playerBlocked(int reason, bool start) override;
 	void showPuzzleMap() override;
@@ -157,7 +157,7 @@ public:
 	void requestRealized(PackageApplied * pa) override;
 	void receivedResource() override;
 	void objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) override;
-	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
+	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
 	void heroManaPointsChanged(const CGHeroInstance * hero) override;
 	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
 	void battleResultsApplied() override;
@@ -165,7 +165,7 @@ public:
 	void objectPropertyChanged(const SetObjectProperty * sop) override;
 	void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
 	void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
-	void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
+	void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
 	void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
 	std::optional<BattleAction> makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override;
 

+ 12 - 3
AI/VCAI/VCAI.cpp

@@ -165,10 +165,13 @@ void VCAI::artifactAssembled(const ArtifactLocation & al)
 	NET_EVENT_HANDLER;
 }
 
-void VCAI::showTavernWindow(const CGObjectInstance * townOrTavern)
+void VCAI::showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
+
+	status.addQuery(queryID, "TavernWindow");
+	requestActionASAP([=](){ answerQuery(queryID, 0); });
 }
 
 void VCAI::showThievesGuildWindow(const CGObjectInstance * obj)
@@ -512,10 +515,13 @@ void VCAI::receivedResource()
 	NET_EVENT_HANDLER;
 }
 
-void VCAI::showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor)
+void VCAI::showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
+
+	status.addQuery(queryID, "UniversityWindow");
+	requestActionASAP([=](){ answerQuery(queryID, 0); });
 }
 
 void VCAI::heroManaPointsChanged(const CGHeroInstance * hero)
@@ -577,10 +583,13 @@ void VCAI::heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bo
 	NET_EVENT_HANDLER;
 }
 
-void VCAI::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor)
+void VCAI::showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID)
 {
 	LOG_TRACE(logAi);
 	NET_EVENT_HANDLER;
+
+	status.addQuery(queryID, "MarketWindow");
+	requestActionASAP([=](){ answerQuery(queryID, 0); });
 }
 
 void VCAI::showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain)

+ 3 - 3
AI/VCAI/VCAI.h

@@ -163,7 +163,7 @@ public:
 	void tileHidden(const std::unordered_set<int3> & pos) override;
 	void artifactMoved(const ArtifactLocation & src, const ArtifactLocation & dst) override;
 	void artifactAssembled(const ArtifactLocation & al) override;
-	void showTavernWindow(const CGObjectInstance * townOrTavern) override;
+	void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
 	void showThievesGuildWindow(const CGObjectInstance * obj) override;
 	void playerBlocked(int reason, bool start) override;
 	void showPuzzleMap() override;
@@ -190,7 +190,7 @@ public:
 	void requestRealized(PackageApplied * pa) override;
 	void receivedResource() override;
 	void objectRemoved(const CGObjectInstance * obj, const PlayerColor & initiator) override;
-	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor) override;
+	void showUniversityWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
 	void heroManaPointsChanged(const CGHeroInstance * hero) override;
 	void heroSecondarySkillChanged(const CGHeroInstance * hero, int which, int val) override;
 	void battleResultsApplied() override;
@@ -198,7 +198,7 @@ public:
 	void objectPropertyChanged(const SetObjectProperty * sop) override;
 	void buildChanged(const CGTownInstance * town, BuildingID buildingID, int what) override;
 	void heroBonusChanged(const CGHeroInstance * hero, const Bonus & bonus, bool gain) override;
-	void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor) override;
+	void showMarketWindow(const IMarket * market, const CGHeroInstance * visitor, QueryID queryID) override;
 	void showWorldViewEx(const std::vector<ObjectPosInfo> & objectPositions, bool showTerrain) override;
 
 	void battleStart(const BattleID & battleID, const CCreatureSet * army1, const CCreatureSet * army2, int3 tile, const CGHeroInstance * hero1, const CGHeroInstance * hero2, bool side, bool replayAllowed) override;

+ 18 - 9
client/CPlayerInterface.cpp

@@ -1621,24 +1621,30 @@ void CPlayerInterface::battleNewRoundFirst(const BattleID & battleID)
 	battleInt->newRoundFirst();
 }
 
-void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor)
+void CPlayerInterface::showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
+	auto onWindowClosed = [this, queryID](){
+		cb->selectionMade(0, queryID);
+	};
 
 	if(market->allowsTrade(EMarketMode::ARTIFACT_EXP) && visitor->getAlignment() != EAlignment::EVIL)
-		GH.windows().createAndPushWindow<CAltarWindow>(market, visitor, EMarketMode::ARTIFACT_EXP);
+		GH.windows().createAndPushWindow<CAltarWindow>(market, visitor, onWindowClosed, EMarketMode::ARTIFACT_EXP);
 	else if(market->allowsTrade(EMarketMode::CREATURE_EXP) && visitor->getAlignment() != EAlignment::GOOD)
-		GH.windows().createAndPushWindow<CAltarWindow>(market, visitor, EMarketMode::CREATURE_EXP);
+		GH.windows().createAndPushWindow<CAltarWindow>(market, visitor, onWindowClosed, EMarketMode::CREATURE_EXP);
 	else if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD))
-		GH.windows().createAndPushWindow<CTransformerWindow>(market, visitor);
+		GH.windows().createAndPushWindow<CTransformerWindow>(market, visitor, onWindowClosed);
 	else if(!market->availableModes().empty())
-		GH.windows().createAndPushWindow<CMarketplaceWindow>(market, visitor, market->availableModes().front());
+		GH.windows().createAndPushWindow<CMarketplaceWindow>(market, visitor, onWindowClosed, market->availableModes().front());
 }
 
-void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor)
+void CPlayerInterface::showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	GH.windows().createAndPushWindow<CUniversityWindow>(visitor, market);
+	auto onWindowClosed = [this, queryID](){
+		cb->selectionMade(0, queryID);
+	};
+	GH.windows().createAndPushWindow<CUniversityWindow>(visitor, market, onWindowClosed);
 }
 
 void CPlayerInterface::showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor)
@@ -1654,10 +1660,13 @@ void CPlayerInterface::availableArtifactsChanged(const CGBlackMarket * bm)
 		cmw->artifactsChanged(false);
 }
 
-void CPlayerInterface::showTavernWindow(const CGObjectInstance *townOrTavern)
+void CPlayerInterface::showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID)
 {
 	EVENT_HANDLER_CALLED_BY_CLIENT;
-	GH.windows().createAndPushWindow<CTavernWindow>(townOrTavern);
+	auto onWindowClosed = [this, queryID](){
+		cb->selectionMade(0, queryID);
+	};
+	GH.windows().createAndPushWindow<CTavernWindow>(object, onWindowClosed);
 }
 
 void CPlayerInterface::showThievesGuildWindow (const CGObjectInstance * obj)

+ 3 - 3
client/CPlayerInterface.h

@@ -122,8 +122,8 @@ protected: // Call-ins from server, should not be called directly, but only via
 	void showTeleportDialog(const CGHeroInstance * hero, TeleportChannelID channel, TTeleportExitsList exits, bool impassable, QueryID askID) override;
 	void showGarrisonDialog(const CArmedInstance *up, const CGHeroInstance *down, bool removableUnits, QueryID queryID) override;
 	void showMapObjectSelectDialog(QueryID askID, const Component & icon, const MetaString & title, const MetaString & description, const std::vector<ObjectInstanceID> & objects) override;
-	void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor) override;
-	void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor) override;
+	void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
+	void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID) override;
 	void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor) override;
 	void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID) override; //called when a hero casts a spell
 	void tileHidden(const std::unordered_set<int3> &pos) override; //called when given tiles become hidden under fog of war
@@ -179,7 +179,7 @@ public: // public interface for use by client via LOCPLINT access
 	void viewWorldMap() override;
 	void showQuestLog() override;
 	void showThievesGuildWindow (const CGObjectInstance * obj) override;
-	void showTavernWindow(const CGObjectInstance *townOrTavern) override;
+	void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) override;
 	void showShipyardDialog(const IShipyard *obj) override; //obj may be town or shipyard;
 
 	void showHeroExchange(ObjectInstanceID hero1, ObjectInstanceID hero2);

+ 1 - 1
client/Client.h

@@ -170,7 +170,7 @@ public:
 	void showBlockingDialog(BlockingDialog * iw) override {};
 	void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {};
 	void showTeleportDialog(TeleportDialog * iw) override {};
-	void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override {};
+	void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) override {};
 	void giveResource(PlayerColor player, GameResID which, int val) override {};
 	virtual void giveResources(PlayerColor player, TResources resources) override {};
 

+ 26 - 17
client/NetPacksClient.cpp

@@ -936,58 +936,67 @@ void ApplyClientNetPackVisitor::visitOpenWindow(OpenWindow & pack)
 	case EOpenWindowMode::RECRUITMENT_FIRST:
 	case EOpenWindowMode::RECRUITMENT_ALL:
 		{
-			const CGDwelling *dw = dynamic_cast<const CGDwelling*>(cl.getObj(ObjectInstanceID(pack.id1)));
-			const CArmedInstance *dst = dynamic_cast<const CArmedInstance*>(cl.getObj(ObjectInstanceID(pack.id2)));
+			assert(pack.queryID == QueryID::NONE);
+			const CGDwelling *dw = dynamic_cast<const CGDwelling*>(cl.getObj(ObjectInstanceID(pack.object)));
+			const CArmedInstance *dst = dynamic_cast<const CArmedInstance*>(cl.getObj(ObjectInstanceID(pack.visitor)));
 			callInterfaceIfPresent(cl, dst->tempOwner, &IGameEventsReceiver::showRecruitmentDialog, dw, dst, pack.window == EOpenWindowMode::RECRUITMENT_FIRST ? 0 : -1);
 		}
 		break;
 	case EOpenWindowMode::SHIPYARD_WINDOW:
 		{
-			const IShipyard *sy = IShipyard::castFrom(cl.getObj(ObjectInstanceID(pack.id1)));
+			assert(pack.queryID == QueryID::NONE);
+			const IShipyard *sy = IShipyard::castFrom(cl.getObj(ObjectInstanceID(pack.object)));
 			callInterfaceIfPresent(cl, sy->getObject()->getOwner(), &IGameEventsReceiver::showShipyardDialog, sy);
 		}
 		break;
 	case EOpenWindowMode::THIEVES_GUILD:
 		{
+			assert(pack.queryID == QueryID::NONE);
 			//displays Thieves' Guild window (when hero enters Den of Thieves)
-			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.id2));
-			callInterfaceIfPresent(cl, PlayerColor(pack.id1), &IGameEventsReceiver::showThievesGuildWindow, obj);
+			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
+			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
+			callInterfaceIfPresent(cl, hero->getOwner(), &IGameEventsReceiver::showThievesGuildWindow, obj);
 		}
 		break;
 	case EOpenWindowMode::UNIVERSITY_WINDOW:
 		{
 			//displays University window (when hero enters University on adventure map)
-			const IMarket *market = IMarket::castFrom(cl.getObj(ObjectInstanceID(pack.id1)));
-			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.id2));
-			callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showUniversityWindow, market, hero);
+			const IMarket *market = IMarket::castFrom(cl.getObj(ObjectInstanceID(pack.object)));
+			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
+			callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showUniversityWindow, market, hero, pack.queryID);
 		}
 		break;
 	case EOpenWindowMode::MARKET_WINDOW:
 		{
 			//displays Thieves' Guild window (when hero enters Den of Thieves)
-			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.id1));
-			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.id2));
+			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
+			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
 			const IMarket *market = IMarket::castFrom(obj);
-			callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero);
+			callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showMarketWindow, market, hero, pack.queryID);
 		}
 		break;
 	case EOpenWindowMode::HILL_FORT_WINDOW:
 		{
+			assert(pack.queryID == QueryID::NONE);
 			//displays Hill fort window
-			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.id1));
-			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.id2));
+			const CGObjectInstance *obj = cl.getObj(ObjectInstanceID(pack.object));
+			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
 			callInterfaceIfPresent(cl, cl.getTile(obj->visitablePos())->visitableObjects.back()->tempOwner, &IGameEventsReceiver::showHillFortWindow, obj, hero);
 		}
 		break;
 	case EOpenWindowMode::PUZZLE_MAP:
 		{
-			callInterfaceIfPresent(cl, PlayerColor(pack.id1), &IGameEventsReceiver::showPuzzleMap);
+			assert(pack.queryID == QueryID::NONE);
+			const CGHeroInstance *hero = cl.getHero(ObjectInstanceID(pack.visitor));
+			callInterfaceIfPresent(cl, hero->getOwner(), &IGameEventsReceiver::showPuzzleMap);
 		}
 		break;
 	case EOpenWindowMode::TAVERN_WINDOW:
-		const CGObjectInstance *obj1 = cl.getObj(ObjectInstanceID(pack.id1)),
-								*obj2 = cl.getObj(ObjectInstanceID(pack.id2));
-		callInterfaceIfPresent(cl, obj1->tempOwner, &IGameEventsReceiver::showTavernWindow, obj2);
+		{
+			const CGObjectInstance *obj1 = cl.getObj(ObjectInstanceID(pack.object));
+			const CGHeroInstance * hero = cl.getHero(ObjectInstanceID(pack.visitor));
+			callInterfaceIfPresent(cl, hero->tempOwner, &IGameEventsReceiver::showTavernWindow, obj1, hero, pack.queryID);
+		}
 		break;
 	}
 }

+ 1 - 1
client/adventureMap/AdventureMapShortcuts.cpp

@@ -342,7 +342,7 @@ void AdventureMapShortcuts::showMarketplace()
 	}
 
 	if(townWithMarket) //if any town has marketplace, open window
-		GH.windows().createAndPushWindow<CMarketplaceWindow>(townWithMarket);
+		GH.windows().createAndPushWindow<CMarketplaceWindow>(townWithMarket, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE);
 	else //if not - complain
 		LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket"));
 }

+ 8 - 8
client/windows/CCastleInterface.cpp

@@ -674,7 +674,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 				break;
 
 		case BuildingID::TAVERN:
-				LOCPLINT->showTavernWindow(town);
+				LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
 				break;
 
 		case BuildingID::SHIPYARD:
@@ -700,7 +700,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 		case BuildingID::MARKETPLACE:
 				// can't use allied marketplace
 				if (town->getOwner() == LOCPLINT->playerID)
-					GH.windows().createAndPushWindow<CMarketplaceWindow>(town, town->visitingHero);
+					GH.windows().createAndPushWindow<CMarketplaceWindow>(town, town->visitingHero, nullptr, EMarketMode::RESOURCE_RESOURCE);
 				else
 					enterBuilding(building);
 				break;
@@ -728,7 +728,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 
 				case BuildingSubID::ARTIFACT_MERCHANT:
 						if(town->visitingHero)
-							GH.windows().createAndPushWindow<CMarketplaceWindow>(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT);
+							GH.windows().createAndPushWindow<CMarketplaceWindow>(town, town->visitingHero, nullptr, EMarketMode::RESOURCE_ARTIFACT);
 						else
 							LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
 						break;
@@ -739,21 +739,21 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 
 				case BuildingSubID::FREELANCERS_GUILD:
 						if(getHero())
-							GH.windows().createAndPushWindow<CMarketplaceWindow>(town, getHero(), EMarketMode::CREATURE_RESOURCE);
+							GH.windows().createAndPushWindow<CMarketplaceWindow>(town, getHero(), nullptr, EMarketMode::CREATURE_RESOURCE);
 						else
 							LOCPLINT->showInfoDialog(boost::str(boost::format(CGI->generaltexth->allTexts[273]) % b->getNameTranslated())); //Only visiting heroes may use the %s.
 						break;
 
 				case BuildingSubID::MAGIC_UNIVERSITY:
 						if (getHero())
-							GH.windows().createAndPushWindow<CUniversityWindow>(getHero(), town);
+							GH.windows().createAndPushWindow<CUniversityWindow>(getHero(), town, nullptr);
 						else
 							enterBuilding(building);
 						break;
 
 				case BuildingSubID::BROTHERHOOD_OF_SWORD:
 						if(upgrades == BuildingID::TAVERN)
-							LOCPLINT->showTavernWindow(town);
+							LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
 						else
 						enterBuilding(building);
 						break;
@@ -763,7 +763,7 @@ void CCastleBuildings::buildingClicked(BuildingID building, BuildingSubID::EBuil
 						break;
 
 				case BuildingSubID::CREATURE_TRANSFORMER: //Skeleton Transformer
-						GH.windows().createAndPushWindow<CTransformerWindow>(town, getHero());
+						GH.windows().createAndPushWindow<CTransformerWindow>(town, getHero(), nullptr);
 						break;
 
 				case BuildingSubID::PORTAL_OF_SUMMONING:
@@ -1319,7 +1319,7 @@ void CCastleInterface::keyPressed(EShortcut key)
 		break;
 	case EShortcut::TOWN_OPEN_TAVERN:
 		if(town->hasBuilt(BuildingID::TAVERN))
-			LOCPLINT->showTavernWindow(town);
+			LOCPLINT->showTavernWindow(town, nullptr, QueryID::NONE);
 		break;
 	default:
 		break;

+ 18 - 7
client/windows/CTradeWindow.cpp

@@ -317,9 +317,10 @@ void CTradeWindow::CTradeableItem::setArtInstance(const CArtifactInstance *art)
 		setID(-1);
 }
 
-CTradeWindow::CTradeWindow(const ImagePath & bgName, const IMarket *Market, const CGHeroInstance *Hero, EMarketMode Mode):
+CTradeWindow::CTradeWindow(const ImagePath & bgName, const IMarket *Market, const CGHeroInstance *Hero, const std::function<void()> & onWindowClosed, EMarketMode Mode):
 	CWindowObject(PLAYER_COLORED, bgName),
 	market(Market),
+	onWindowClosed(onWindowClosed),
 	hero(Hero),
 	readyToTrade(false)
 {
@@ -561,6 +562,14 @@ void CTradeWindow::showAll(Canvas & to)
 	}
 }
 
+void CTradeWindow::close()
+{
+	if (onWindowClosed)
+		onWindowClosed();
+
+	CWindowObject::close();
+}
+
 void CTradeWindow::removeItems(const std::set<std::shared_ptr<CTradeableItem>> & toRemove)
 {
 	for(auto item : toRemove)
@@ -589,17 +598,19 @@ void CTradeWindow::setMode(EMarketMode Mode)
 {
 	const IMarket *m = market;
 	const CGHeroInstance *h = hero;
+	const auto functor = onWindowClosed;
 
+	onWindowClosed = nullptr; // don't call on closing of this window - pass it to next window
 	close();
 
 	switch(Mode)
 	{
 	case EMarketMode::CREATURE_EXP:
 	case EMarketMode::ARTIFACT_EXP:
-		GH.windows().createAndPushWindow<CAltarWindow>(m, h, Mode);
+		GH.windows().createAndPushWindow<CAltarWindow>(m, h, functor, Mode);
 		break;
 	default:
-		GH.windows().createAndPushWindow<CMarketplaceWindow>(m, h, Mode);
+		GH.windows().createAndPushWindow<CMarketplaceWindow>(m, h, functor, Mode);
 		break;
 	}
 }
@@ -635,8 +646,8 @@ ImagePath CMarketplaceWindow::getBackgroundForMode(EMarketMode mode)
 	return {};
 }
 
-CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInstance * Hero, EMarketMode Mode)
-	: CTradeWindow(getBackgroundForMode(Mode), Market, Hero, Mode)
+CMarketplaceWindow::CMarketplaceWindow(const IMarket * Market, const CGHeroInstance * Hero, const std::function<void()> & onWindowClosed, EMarketMode Mode)
+	: CTradeWindow(getBackgroundForMode(Mode), Market, Hero, onWindowClosed, Mode)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 
@@ -1093,8 +1104,8 @@ void CMarketplaceWindow::updateTraderText()
 	traderText->setText(CGI->generaltexth->allTexts[gnrtxtnr]);
 }
 
-CAltarWindow::CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, EMarketMode Mode)
-	: CTradeWindow(ImagePath::builtin(Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, Mode)
+CAltarWindow::CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, const std::function<void()> & onWindowClosed, EMarketMode Mode)
+	: CTradeWindow(ImagePath::builtin(Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, onWindowClosed, Mode)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
 

+ 5 - 3
client/windows/CTradeWindow.h

@@ -83,9 +83,10 @@ public:
 	std::shared_ptr<CSlider> slider; //for choosing amount to be exchanged
 	bool readyToTrade;
 
-	CTradeWindow(const ImagePath & bgName, const IMarket * Market, const CGHeroInstance * Hero, EMarketMode Mode); //c
+	CTradeWindow(const ImagePath & bgName, const IMarket * Market, const CGHeroInstance * Hero, const std::function<void()> & onWindowClosed, EMarketMode Mode); //c
 
 	void showAll(Canvas & to) override;
+	void close();
 
 	void initSubs(bool Left);
 	void initTypes();
@@ -106,6 +107,7 @@ public:
 	virtual void garrisonChanged() = 0;
 	virtual void artifactsChanged(bool left) = 0;
 protected:
+	std::function<void()> onWindowClosed;
 	std::shared_ptr<CGStatusBar> statusBar;
 	std::vector<std::shared_ptr<CLabel>> labels;
 	std::vector<std::shared_ptr<CPicture>> images;
@@ -130,7 +132,7 @@ public:
 	void sliderMoved(int to);
 	void makeDeal();
 	void selectionChanged(bool side) override; //true == left
-	CMarketplaceWindow(const IMarket * Market, const CGHeroInstance * Hero = nullptr, EMarketMode Mode = EMarketMode::RESOURCE_RESOURCE);
+	CMarketplaceWindow(const IMarket * Market, const CGHeroInstance * Hero, const std::function<void()> & onWindowClosed, EMarketMode Mode);
 	~CMarketplaceWindow();
 
 	Point selectionOffset(bool Left) const override;
@@ -157,7 +159,7 @@ public:
 	std::shared_ptr<CLabel> expOnAltar;
 	std::shared_ptr<CArtifactsOfHeroAltar> arts;
 
-	CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, EMarketMode Mode);
+	CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, const std::function<void()> & onWindowClosed, EMarketMode Mode);
 	~CAltarWindow();
 
 	void getExpValues();

+ 32 - 4
client/windows/GUIClasses.cpp

@@ -430,8 +430,9 @@ CLevelWindow::~CLevelWindow()
 	LOCPLINT->showingDialog->setn(false);
 }
 
-CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj)
+CTavernWindow::CTavernWindow(const CGObjectInstance * TavernObj, const std::function<void()> & onWindowClosed)
 	: CStatusbarWindow(PLAYER_COLORED, ImagePath::builtin("TPTAVERN")),
+	onWindowClosed(onWindowClosed),
 	tavernObj(TavernObj)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@@ -500,8 +501,9 @@ void CTavernWindow::recruitb()
 {
 	const CGHeroInstance *toBuy = (selected ? h2 : h1)->h;
 	const CGObjectInstance *obj = tavernObj;
-	close();
+
 	LOCPLINT->cb->recruitHero(obj, toBuy);
+	close();
 }
 
 void CTavernWindow::thievesguildb()
@@ -509,6 +511,14 @@ void CTavernWindow::thievesguildb()
 	GH.windows().createAndPushWindow<CThievesGuildWindow>(tavernObj);
 }
 
+void CTavernWindow::close()
+{
+	if (onWindowClosed)
+		onWindowClosed();
+
+	CStatusbarWindow::close();
+}
+
 CTavernWindow::~CTavernWindow()
 {
 	CCS->videoh->close();
@@ -993,9 +1003,10 @@ void CTransformerWindow::updateGarrisons()
 		item->update();
 }
 
-CTransformerWindow::CTransformerWindow(const IMarket * _market, const CGHeroInstance * _hero)
+CTransformerWindow::CTransformerWindow(const IMarket * _market, const CGHeroInstance * _hero, const std::function<void()> & onWindowClosed)
 	: CStatusbarWindow(PLAYER_COLORED, ImagePath::builtin("SKTRNBK")),
 	hero(_hero),
+	onWindowClosed(onWindowClosed),
 	market(_market)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@@ -1024,6 +1035,14 @@ CTransformerWindow::CTransformerWindow(const IMarket * _market, const CGHeroInst
 	helpRight = std::make_shared<CTextBox>(CGI->generaltexth->allTexts[488], Rect(320, 56, 255, 40), 0, FONT_MEDIUM, ETextAlignment::CENTER, Colors::YELLOW);//creatures here will become skeletons
 }
 
+void CTransformerWindow::close()
+{
+	if (onWindowClosed)
+		onWindowClosed();
+
+	CStatusbarWindow::close();
+}
+
 CUniversityWindow::CItem::CItem(CUniversityWindow * _parent, int _ID, int X, int Y)
 	: CIntObject(LCLICK | SHOW_POPUP | HOVER),
 	ID(_ID),
@@ -1083,9 +1102,10 @@ void CUniversityWindow::CItem::showAll(Canvas & to)
 	CIntObject::showAll(to);
 }
 
-CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market)
+CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function<void()> & onWindowClosed)
 	: CStatusbarWindow(PLAYER_COLORED, ImagePath::builtin("UNIVERS1")),
 	hero(_hero),
+	onWindowClosed(onWindowClosed),
 	market(_market)
 {
 	OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE);
@@ -1130,6 +1150,14 @@ CUniversityWindow::CUniversityWindow(const CGHeroInstance * _hero, const IMarket
 	statusbar = CGStatusBar::create(std::make_shared<CPicture>(background->getSurface(), Rect(8, pos.h - 26, pos.w - 16, 19), 8, pos.h - 26));
 }
 
+void CUniversityWindow::close()
+{
+	if (onWindowClosed)
+		onWindowClosed();
+
+	CStatusbarWindow::close();
+}
+
 void CUniversityWindow::makeDeal(int skill)
 {
 	LOCPLINT->cb->trade(market, EMarketMode::RESOURCE_SKILL, 6, skill, 1, hero);

+ 12 - 3
client/windows/GUIClasses.h

@@ -193,6 +193,8 @@ public:
 
 class CTavernWindow : public CStatusbarWindow
 {
+	std::function<void()> onWindowClosed;
+
 public:
 	class HeroPortrait : public CIntObject
 	{
@@ -233,9 +235,10 @@ public:
 
 	std::shared_ptr<CTextBox> rumor;
 
-	CTavernWindow(const CGObjectInstance * TavernObj);
+	CTavernWindow(const CGObjectInstance * TavernObj, const std::function<void()> & onWindowClosed);
 	~CTavernWindow();
 
+	void close();
 	void recruitb();
 	void thievesguildb();
 	void show(Canvas & to) override;
@@ -349,12 +352,15 @@ class CTransformerWindow : public CStatusbarWindow, public IGarrisonHolder
 	std::shared_ptr<CButton> all;
 	std::shared_ptr<CButton> convert;
 	std::shared_ptr<CButton> cancel;
+
+	std::function<void()> onWindowClosed;
 public:
 
 	void makeDeal();
 	void addAll();
+	void close();
 	void updateGarrisons() override;
-	CTransformerWindow(const IMarket * _market, const CGHeroInstance * _hero);
+	CTransformerWindow(const IMarket * _market, const CGHeroInstance * _hero, const std::function<void()> & onWindowClosed);
 };
 
 class CUniversityWindow : public CStatusbarWindow
@@ -390,10 +396,13 @@ class CUniversityWindow : public CStatusbarWindow
 	std::shared_ptr<CLabel> title;
 	std::shared_ptr<CTextBox> clerkSpeech;
 
+	std::function<void()> onWindowClosed;
+
 public:
-	CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market);
+	CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function<void()> & onWindowClosed);
 
 	void makeDeal(int skill);
+	void close();
 };
 
 /// Confirmation window for University

+ 2 - 1
lib/IGameCallback.h

@@ -25,6 +25,7 @@ struct ArtifactLocation;
 class CCreatureSet;
 class CStackBasicDescriptor;
 class CGCreature;
+enum class EOpenWindowMode : uint8_t;
 
 namespace spells
 {
@@ -96,7 +97,7 @@ public:
 	virtual void showBlockingDialog(BlockingDialog *iw) =0;
 	virtual void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) =0; //cb will be called when player closes garrison window
 	virtual void showTeleportDialog(TeleportDialog *iw) =0;
-	virtual void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) =0;
+	virtual void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) = 0;
 	virtual void giveResource(PlayerColor player, GameResID which, int val)=0;
 	virtual void giveResources(PlayerColor player, TResources resources)=0;
 

+ 3 - 3
lib/IGameEventsReceiver.h

@@ -110,11 +110,11 @@ public:
 
 	virtual void showPuzzleMap(){};
 	virtual void viewWorldMap(){};
-	virtual void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor){};
-	virtual void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor){};
+	virtual void showMarketWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID){};
+	virtual void showUniversityWindow(const IMarket *market, const CGHeroInstance *visitor, QueryID queryID){};
 	virtual void showHillFortWindow(const CGObjectInstance *object, const CGHeroInstance *visitor){};
-	virtual void showTavernWindow(const CGObjectInstance *townOrTavern){};
 	virtual void showThievesGuildWindow (const CGObjectInstance * obj){};
+	virtual void showTavernWindow(const CGObjectInstance * object, const CGHeroInstance * visitor, QueryID queryID) {};
 	virtual void showQuestLog(){};
 	virtual void advmapSpellCast(const CGHeroInstance * caster, SpellID spellID){}; //called when a hero casts a spell
 	virtual void tileHidden(const std::unordered_set<int3> &pos){};

+ 6 - 5
lib/NetPacks.h

@@ -786,19 +786,20 @@ struct DLL_LINKAGE GiveHero : public CPackForClient
 	}
 };
 
-struct DLL_LINKAGE OpenWindow : public CPackForClient
+struct DLL_LINKAGE OpenWindow : public Query
 {
 	EOpenWindowMode window;
-	si32 id1 = -1;
-	si32 id2 = -1;
+	ObjectInstanceID object;
+	ObjectInstanceID visitor;
 
 	virtual void visitTyped(ICPackVisitor & visitor) override;
 
 	template <typename Handler> void serialize(Handler & h, const int version)
 	{
+		h & queryID;
 		h & window;
-		h & id1;
-		h & id2;
+		h & object;
+		h & visitor;
 	}
 };
 

+ 2 - 7
lib/mapObjects/CGDwelling.cpp

@@ -393,13 +393,8 @@ void CGDwelling::heroAcceptsCreatures( const CGHeroInstance *h) const
 			cb->sendAndApply(&sac);
 		}
 
-		OpenWindow ow;
-		ow.id1 = id.getNum();
-		ow.id2 = h->id.getNum();
-		ow.window = (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::REFUGEE_CAMP)
-			? EOpenWindowMode::RECRUITMENT_FIRST
-			: EOpenWindowMode::RECRUITMENT_ALL;
-		cb->sendAndApply(&ow);
+		auto windowMode = (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::REFUGEE_CAMP) ? EOpenWindowMode::RECRUITMENT_FIRST : EOpenWindowMode::RECRUITMENT_ALL;
+		cb->showObjectWindow(this, windowMode, h, false);
 	}
 }
 

+ 2 - 2
lib/mapObjects/CGMarket.cpp

@@ -30,7 +30,7 @@ void CGMarket::initObj(CRandomGenerator & rand)
 
 void CGMarket::onHeroVisit(const CGHeroInstance * h) const
 {
-	openWindow(EOpenWindowMode::MARKET_WINDOW, id.getNum(), h->id.getNum());
+	cb->showObjectWindow(this, EOpenWindowMode::MARKET_WINDOW, h, true);
 }
 
 int CGMarket::getMarketEfficiency() const
@@ -124,7 +124,7 @@ std::vector<int> CGUniversity::availableItemsIds(EMarketMode mode) const
 
 void CGUniversity::onHeroVisit(const CGHeroInstance * h) const
 {
-	openWindow(EOpenWindowMode::UNIVERSITY_WINDOW,id.getNum(),h->id.getNum());
+	cb->showObjectWindow(this, EOpenWindowMode::UNIVERSITY_WINDOW, h, true);
 }
 
 VCMI_LIB_NAMESPACE_END

+ 1 - 1
lib/mapObjects/CGObjectInstance.cpp

@@ -283,7 +283,7 @@ void CGObjectInstance::onHeroVisit( const CGHeroInstance * h ) const
 		break;
 	case Obj::TAVERN:
 		{
-			openWindow(EOpenWindowMode::TAVERN_WINDOW,h->id.getNum(),id.getNum());
+			cb->showObjectWindow(this, EOpenWindowMode::TAVERN_WINDOW, h, true);
 		}
 		break;
 	}

+ 0 - 10
lib/mapObjects/IObjectInterface.cpp

@@ -22,16 +22,6 @@ VCMI_LIB_NAMESPACE_BEGIN
 
 IGameCallback * IObjectInterface::cb = nullptr;
 
-///helpers
-void IObjectInterface::openWindow(const EOpenWindowMode type, const int id1, const int id2)
-{
-	OpenWindow ow;
-	ow.window = type;
-	ow.id1 = id1;
-	ow.id2 = id2;
-	IObjectInterface::cb->sendAndApply(&ow);
-}
-
 void IObjectInterface::showInfoDialog(const ui32 txtID, const ui16 soundID, EInfoWindowMode mode) const
 {
 	InfoWindow iw;

+ 0 - 3
lib/mapObjects/IObjectInterface.h

@@ -52,9 +52,6 @@ public:
 	//unified helper to show info dialog for object owner
 	virtual void showInfoDialog(const ui32 txtID, const ui16 soundID = 0, EInfoWindowMode mode = EInfoWindowMode::AUTO) const;
 
-	//unified helper to show a specific window
-	static void openWindow(const EOpenWindowMode type, const int id1, const int id2 = -1);
-
 	//unified interface, AI helpers
 	virtual bool wasVisited (PlayerColor player) const;
 	virtual bool wasVisited (const CGHeroInstance * h) const;

+ 4 - 5
lib/mapObjects/MiscObjects.cpp

@@ -1399,7 +1399,7 @@ void CGShipyard::onHeroVisit( const CGHeroInstance * h ) const
 	}
 	else
 	{
-		openWindow(EOpenWindowMode::SHIPYARD_WINDOW,id.getNum(),h->id.getNum());
+		cb->showObjectWindow(this, EOpenWindowMode::SHIPYARD_WINDOW, h, false);
 	}
 }
 
@@ -1488,7 +1488,7 @@ void CCartographer::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answ
 
 void CGDenOfthieves::onHeroVisit (const CGHeroInstance * h) const
 {
-	cb->showThievesGuildWindow(h->tempOwner, id);
+	cb->showObjectWindow(this, EOpenWindowMode::THIEVES_GUILD, h, false);
 }
 
 void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
@@ -1507,8 +1507,7 @@ void CGObelisk::onHeroVisit( const CGHeroInstance * h ) const
 
 		// increment general visited obelisks counter
 		cb->setObjProperty(id, CGObelisk::OBJPROP_INC, team.getNum());
-
-		openWindow(EOpenWindowMode::PUZZLE_MAP, h->tempOwner.getNum());
+		cb->showObjectWindow(this, EOpenWindowMode::PUZZLE_MAP, h, false);
 
 		// mark that particular obelisk as visited for all players in the team
 		for(const auto & color : ts->players)
@@ -1619,7 +1618,7 @@ void CGLighthouse::serializeJsonOptions(JsonSerializeFormat& handler)
 
 void HillFort::onHeroVisit(const CGHeroInstance * h) const
 {
-	openWindow(EOpenWindowMode::HILL_FORT_WINDOW,id.getNum(),h->id.getNum());
+	cb->showObjectWindow(this, EOpenWindowMode::HILL_FORT_WINDOW, h, false);
 }
 
 void HillFort::fillUpgradeInfo(UpgradeInfo & info, const CStackInstance &stack) const

+ 13 - 6
server/CGameHandler.cpp

@@ -3364,13 +3364,20 @@ void CGameHandler::showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID h
 	sendAndApply(&gd);
 }
 
-void CGameHandler::showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId)
+void CGameHandler::showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery)
 {
-	OpenWindow ow;
-	ow.window = EOpenWindowMode::THIEVES_GUILD;
-	ow.id1 = player.getNum();
-	ow.id2 = requestingObjId.getNum();
-	sendAndApply(&ow);
+	OpenWindow pack;
+	pack.window = window;
+	pack.object = object->id;
+	pack.visitor = visitor->id;
+
+	if (addQuery)
+	{
+		auto windowQuery = std::make_shared<OpenWindowQuery>(this, visitor, window);
+		pack.queryID = windowQuery->queryID;
+		queries->addQuery(windowQuery);
+	}
+	sendAndApply(&pack);
 }
 
 bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2)

+ 1 - 1
server/CGameHandler.h

@@ -109,7 +109,7 @@ public:
 	void showBlockingDialog(BlockingDialog *iw) override;
 	void showTeleportDialog(TeleportDialog *iw) override;
 	void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override;
-	void showThievesGuildWindow(PlayerColor player, ObjectInstanceID requestingObjId) override;
+	void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) override;
 	void giveResource(PlayerColor player, GameResID which, int val) override;
 	void giveResources(PlayerColor player, TResources resources) override;
 

+ 33 - 0
server/queries/MapQueries.cpp

@@ -157,6 +157,39 @@ CBlockingDialogQuery::CBlockingDialogQuery(CGameHandler * owner, const BlockingD
 	addPlayer(bd.player);
 }
 
+OpenWindowQuery::OpenWindowQuery(CGameHandler * owner, const CGHeroInstance *hero, EOpenWindowMode mode):
+	CDialogQuery(owner),
+	mode(mode)
+{
+	addPlayer(hero->getOwner());
+}
+
+bool OpenWindowQuery::blocksPack(const CPack *pack) const
+{
+	if (mode == EOpenWindowMode::TAVERN_WINDOW)
+	{
+		if(dynamic_ptr_cast<HireHero>(pack) != nullptr)
+			return false;
+	}
+
+	if (mode == EOpenWindowMode::MARKET_WINDOW)
+	{
+		if(dynamic_ptr_cast<ExchangeArtifacts>(pack) != nullptr)
+			return false;
+
+		if(dynamic_ptr_cast<AssembleArtifacts>(pack))
+			return false;
+
+		if(dynamic_ptr_cast<EraseArtifactByClient>(pack))
+			return false;
+
+		if(dynamic_ptr_cast<TradeOnMarketplace>(pack) != nullptr)
+			return false;
+	}
+
+	return CDialogQuery::blocksPack(pack);
+}
+
 void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const
 {
 	// do not change to dynamic_ptr_cast - SIGSEGV!

+ 9 - 1
server/queries/MapQueries.h

@@ -61,7 +61,6 @@ public:
 	virtual void onRemoval(PlayerColor color) override;
 };
 
-
 class CGarrisonDialogQuery : public CDialogQuery //used also for hero exchange dialogs
 {
 public:
@@ -83,6 +82,15 @@ public:
 	virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override;
 };
 
+class OpenWindowQuery : public CDialogQuery
+{
+	EOpenWindowMode mode;
+public:
+	OpenWindowQuery(CGameHandler * owner, const CGHeroInstance *hero, EOpenWindowMode mode);
+
+	virtual bool blocksPack(const CPack *pack) const override;
+};
+
 class CTeleportDialogQuery : public CDialogQuery
 {
 public: