diff --git a/AI/Nullkiller/AIGateway.cpp b/AI/Nullkiller/AIGateway.cpp index 74f101883..6eeef4e08 100644 --- a/AI/Nullkiller/AIGateway.cpp +++ b/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 & objectPositions, bool showTerrain) diff --git a/AI/Nullkiller/AIGateway.h b/AI/Nullkiller/AIGateway.h index 261a368b5..424739082 100644 --- a/AI/Nullkiller/AIGateway.h +++ b/AI/Nullkiller/AIGateway.h @@ -130,7 +130,7 @@ public: void tileHidden(const std::unordered_set & 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 & objectPositions, bool showTerrain) override; std::optional makeSurrenderRetreatDecision(const BattleID & battleID, const BattleStateInfoForRetreat & battleState) override; diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index a4fea7d24..2098cfa70 100644 --- a/AI/VCAI/VCAI.cpp +++ b/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 & objectPositions, bool showTerrain) diff --git a/AI/VCAI/VCAI.h b/AI/VCAI/VCAI.h index 4abdbcd62..f31a22e0d 100644 --- a/AI/VCAI/VCAI.h +++ b/AI/VCAI/VCAI.h @@ -163,7 +163,7 @@ public: void tileHidden(const std::unordered_set & 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 & 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; diff --git a/client/CPlayerInterface.cpp b/client/CPlayerInterface.cpp index be2ded812..d91621c32 100644 --- a/client/CPlayerInterface.cpp +++ b/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(market, visitor, EMarketMode::ARTIFACT_EXP); + GH.windows().createAndPushWindow(market, visitor, onWindowClosed, EMarketMode::ARTIFACT_EXP); else if(market->allowsTrade(EMarketMode::CREATURE_EXP) && visitor->getAlignment() != EAlignment::GOOD) - GH.windows().createAndPushWindow(market, visitor, EMarketMode::CREATURE_EXP); + GH.windows().createAndPushWindow(market, visitor, onWindowClosed, EMarketMode::CREATURE_EXP); else if(market->allowsTrade(EMarketMode::CREATURE_UNDEAD)) - GH.windows().createAndPushWindow(market, visitor); + GH.windows().createAndPushWindow(market, visitor, onWindowClosed); else if(!market->availableModes().empty()) - GH.windows().createAndPushWindow(market, visitor, market->availableModes().front()); + GH.windows().createAndPushWindow(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(visitor, market); + auto onWindowClosed = [this, queryID](){ + cb->selectionMade(0, queryID); + }; + GH.windows().createAndPushWindow(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(townOrTavern); + auto onWindowClosed = [this, queryID](){ + cb->selectionMade(0, queryID); + }; + GH.windows().createAndPushWindow(object, onWindowClosed); } void CPlayerInterface::showThievesGuildWindow (const CGObjectInstance * obj) diff --git a/client/CPlayerInterface.h b/client/CPlayerInterface.h index fdcad9ff2..175a109b5 100644 --- a/client/CPlayerInterface.h +++ b/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 & 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 &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); diff --git a/client/Client.h b/client/Client.h index 5952404a4..b9c98768b 100644 --- a/client/Client.h +++ b/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 {}; diff --git a/client/NetPacksClient.cpp b/client/NetPacksClient.cpp index b26c07dbc..c0282a450 100644 --- a/client/NetPacksClient.cpp +++ b/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(cl.getObj(ObjectInstanceID(pack.id1))); - const CArmedInstance *dst = dynamic_cast(cl.getObj(ObjectInstanceID(pack.id2))); + assert(pack.queryID == QueryID::NONE); + const CGDwelling *dw = dynamic_cast(cl.getObj(ObjectInstanceID(pack.object))); + const CArmedInstance *dst = dynamic_cast(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; } } diff --git a/client/adventureMap/AdventureMapShortcuts.cpp b/client/adventureMap/AdventureMapShortcuts.cpp index 30edda987..abdb52cd1 100644 --- a/client/adventureMap/AdventureMapShortcuts.cpp +++ b/client/adventureMap/AdventureMapShortcuts.cpp @@ -342,7 +342,7 @@ void AdventureMapShortcuts::showMarketplace() } if(townWithMarket) //if any town has marketplace, open window - GH.windows().createAndPushWindow(townWithMarket); + GH.windows().createAndPushWindow(townWithMarket, nullptr, nullptr, EMarketMode::RESOURCE_RESOURCE); else //if not - complain LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithMarket")); } diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 471a79c2b..5b54b8fe5 100644 --- a/client/windows/CCastleInterface.cpp +++ b/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(town, town->visitingHero); + GH.windows().createAndPushWindow(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(town, town->visitingHero, EMarketMode::RESOURCE_ARTIFACT); + GH.windows().createAndPushWindow(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(town, getHero(), EMarketMode::CREATURE_RESOURCE); + GH.windows().createAndPushWindow(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(getHero(), town); + GH.windows().createAndPushWindow(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(town, getHero()); + GH.windows().createAndPushWindow(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; diff --git a/client/windows/CTradeWindow.cpp b/client/windows/CTradeWindow.cpp index 17dc3d2bf..dfa72c231 100644 --- a/client/windows/CTradeWindow.cpp +++ b/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 & 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> & 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(m, h, Mode); + GH.windows().createAndPushWindow(m, h, functor, Mode); break; default: - GH.windows().createAndPushWindow(m, h, Mode); + GH.windows().createAndPushWindow(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 & 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 & onWindowClosed, EMarketMode Mode) + : CTradeWindow(ImagePath::builtin(Mode == EMarketMode::CREATURE_EXP ? "ALTARMON.bmp" : "ALTRART2.bmp"), Market, Hero, onWindowClosed, Mode) { OBJECT_CONSTRUCTION_CAPTURING(255-DISPOSE); diff --git a/client/windows/CTradeWindow.h b/client/windows/CTradeWindow.h index bdf253b89..b097ebf76 100644 --- a/client/windows/CTradeWindow.h +++ b/client/windows/CTradeWindow.h @@ -83,9 +83,10 @@ public: std::shared_ptr 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 & 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 onWindowClosed; std::shared_ptr statusBar; std::vector> labels; std::vector> 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 & onWindowClosed, EMarketMode Mode); ~CMarketplaceWindow(); Point selectionOffset(bool Left) const override; @@ -157,7 +159,7 @@ public: std::shared_ptr expOnAltar; std::shared_ptr arts; - CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, EMarketMode Mode); + CAltarWindow(const IMarket * Market, const CGHeroInstance * Hero, const std::function & onWindowClosed, EMarketMode Mode); ~CAltarWindow(); void getExpValues(); diff --git a/client/windows/GUIClasses.cpp b/client/windows/GUIClasses.cpp index 8bf9052c1..f21aef79d 100644 --- a/client/windows/GUIClasses.cpp +++ b/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 & 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(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 & 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(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 & 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(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); diff --git a/client/windows/GUIClasses.h b/client/windows/GUIClasses.h index a73669570..cc31ac83c 100644 --- a/client/windows/GUIClasses.h +++ b/client/windows/GUIClasses.h @@ -193,6 +193,8 @@ public: class CTavernWindow : public CStatusbarWindow { + std::function onWindowClosed; + public: class HeroPortrait : public CIntObject { @@ -233,9 +235,10 @@ public: std::shared_ptr rumor; - CTavernWindow(const CGObjectInstance * TavernObj); + CTavernWindow(const CGObjectInstance * TavernObj, const std::function & 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 all; std::shared_ptr convert; std::shared_ptr cancel; + + std::function 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 & onWindowClosed); }; class CUniversityWindow : public CStatusbarWindow @@ -390,10 +396,13 @@ class CUniversityWindow : public CStatusbarWindow std::shared_ptr title; std::shared_ptr clerkSpeech; + std::function onWindowClosed; + public: - CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market); + CUniversityWindow(const CGHeroInstance * _hero, const IMarket * _market, const std::function & onWindowClosed); void makeDeal(int skill); + void close(); }; /// Confirmation window for University diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index cdbb40209..03e96dbeb 100644 --- a/lib/IGameCallback.h +++ b/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; diff --git a/lib/IGameEventsReceiver.h b/lib/IGameEventsReceiver.h index 065bd7f2a..fa72fc247 100644 --- a/lib/IGameEventsReceiver.h +++ b/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 &pos){}; diff --git a/lib/NetPacks.h b/lib/NetPacks.h index f26733bb0..d34a0f6c2 100644 --- a/lib/NetPacks.h +++ b/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 void serialize(Handler & h, const int version) { + h & queryID; h & window; - h & id1; - h & id2; + h & object; + h & visitor; } }; diff --git a/lib/mapObjects/CGDwelling.cpp b/lib/mapObjects/CGDwelling.cpp index 8f4b2d982..becd3c336 100644 --- a/lib/mapObjects/CGDwelling.cpp +++ b/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); } } diff --git a/lib/mapObjects/CGMarket.cpp b/lib/mapObjects/CGMarket.cpp index 747c925f4..595c130ba 100644 --- a/lib/mapObjects/CGMarket.cpp +++ b/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 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 diff --git a/lib/mapObjects/CGObjectInstance.cpp b/lib/mapObjects/CGObjectInstance.cpp index 6b5a0b274..1b2f0f92a 100644 --- a/lib/mapObjects/CGObjectInstance.cpp +++ b/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; } diff --git a/lib/mapObjects/IObjectInterface.cpp b/lib/mapObjects/IObjectInterface.cpp index b76106232..867c02456 100644 --- a/lib/mapObjects/IObjectInterface.cpp +++ b/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; diff --git a/lib/mapObjects/IObjectInterface.h b/lib/mapObjects/IObjectInterface.h index b27a66f00..b0992a868 100644 --- a/lib/mapObjects/IObjectInterface.h +++ b/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; diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index 818521339..771f84186 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/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 diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 6238d9057..273ea0514 100644 --- a/server/CGameHandler.cpp +++ b/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(this, visitor, window); + pack.queryID = windowQuery->queryID; + queries->addQuery(windowQuery); + } + sendAndApply(&pack); } bool CGameHandler::isAllowedExchange(ObjectInstanceID id1, ObjectInstanceID id2) diff --git a/server/CGameHandler.h b/server/CGameHandler.h index e17da3d3c..2784aa6a7 100644 --- a/server/CGameHandler.h +++ b/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; diff --git a/server/queries/MapQueries.cpp b/server/queries/MapQueries.cpp index c19d510fd..21b27910e 100644 --- a/server/queries/MapQueries.cpp +++ b/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(pack) != nullptr) + return false; + } + + if (mode == EOpenWindowMode::MARKET_WINDOW) + { + if(dynamic_ptr_cast(pack) != nullptr) + return false; + + if(dynamic_ptr_cast(pack)) + return false; + + if(dynamic_ptr_cast(pack)) + return false; + + if(dynamic_ptr_cast(pack) != nullptr) + return false; + } + + return CDialogQuery::blocksPack(pack); +} + void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const { // do not change to dynamic_ptr_cast - SIGSEGV! diff --git a/server/queries/MapQueries.h b/server/queries/MapQueries.h index e2b21cae0..5b7b5420a 100644 --- a/server/queries/MapQueries.h +++ b/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: