diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index e450d698d..49f24ed35 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1171,7 +1171,6 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta sendAndApply(&vc); } visitCastleObjects(obj, hero); - giveSpells (obj, hero); if (obj->visitingHero && obj->garrisonHero) useScholarSkill(obj->visitingHero->id, obj->garrisonHero->id); @@ -1180,10 +1179,27 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta void CGameHandler::visitCastleObjects(const CGTownInstance * t, const CGHeroInstance * h) { + std::vector visitors; + visitors.push_back(h); + visitCastleObjects(t, visitors); +} + +void CGameHandler::visitCastleObjects(const CGTownInstance * t, std::vector visitors) +{ + std::vector buildingsToVisit; + for (auto const & hero : visitors) + giveSpells (t, hero); + for (auto & building : t->rewardableBuildings) { if (!t->town->buildings.at(building.first)->manualHeroVisit) - building.second->onHeroVisit(h); + buildingsToVisit.push_back(building.first); + } + + if (!buildingsToVisit.empty()) + { + auto visitQuery = std::make_shared(this, t, visitors, buildingsToVisit); + queries->addQuery(visitQuery); } } @@ -2144,10 +2160,15 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, if (!force) { - if(t->garrisonHero) //garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order - objectVisited(t, t->garrisonHero); - if(t->visitingHero) - objectVisited(t, t->visitingHero); + //garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order + std::vector visitors; + if (t->garrisonHero) + visitors.push_back(t->garrisonHero); + if (t->visitingHero) + visitors.push_back(t->visitingHero); + + if (!visitors.empty()) + visitCastleObjects(t, visitors); } checkVictoryLossConditionsForPlayer(t->tempOwner); @@ -2173,19 +2194,15 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid) return true; } - if (t->rewardableBuildings.count(bid)) + if (t->rewardableBuildings.count(bid) && t->visitingHero && t->town->buildings.at(bid)->manualHeroVisit) { - auto & hero = t->visitingHero; - auto * building = t->rewardableBuildings.at(bid); - - if (hero && t->town->buildings.at(bid)->manualHeroVisit) - { - auto visitQuery = std::make_shared(this, t, hero, bid); - queries->addQuery(visitQuery); - building->onHeroVisit(hero); - queries->popIfTop(visitQuery); - return true; - } + std::vector buildingsToVisit; + std::vector visitors; + buildingsToVisit.push_back(bid); + visitors.push_back(t->visitingHero); + auto visitQuery = std::make_shared(this, t, visitors, buildingsToVisit); + queries->addQuery(visitQuery); + return true; } return true; diff --git a/server/CGameHandler.h b/server/CGameHandler.h index b7df98e05..cdb194377 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -182,6 +182,7 @@ public: void visitObjectOnTile(const TerrainTile &t, const CGHeroInstance * h); bool teleportHero(ObjectInstanceID hid, ObjectInstanceID dstid, ui8 source, PlayerColor asker = PlayerColor::NEUTRAL); void visitCastleObjects(const CGTownInstance * obj, const CGHeroInstance * hero) override; + void visitCastleObjects(const CGTownInstance * obj, std::vector visitors); void levelUpHero(const CGHeroInstance * hero, SecondarySkill skill);//handle client respond and send one more request if needed void levelUpHero(const CGHeroInstance * hero);//initial call - check if hero have remaining levelups & handle them void levelUpCommander (const CCommanderInstance * c, int skill); //secondary skill 1 to 6, special skill : skill - 100 diff --git a/server/queries/VisitQueries.cpp b/server/queries/VisitQueries.cpp index 38beccbe6..cac6b873b 100644 --- a/server/queries/VisitQueries.cpp +++ b/server/queries/VisitQueries.cpp @@ -11,6 +11,8 @@ #include "VisitQueries.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapObjects/CGTownInstance.h" +#include "../../lib/mapObjects/TownBuildingInstance.h" #include "../CGameHandler.h" #include "QueriesProcessor.h" @@ -29,7 +31,7 @@ bool VisitQuery::blocksPack(const CPack * pack) const return true; } -void VisitQuery::onExposure(QueryPtr topQuery) +void MapObjectVisitQuery::onExposure(QueryPtr topQuery) { //Object may have been removed and deleted. if(gh->isValidObject(visitedObject)) @@ -54,13 +56,31 @@ void MapObjectVisitQuery::onRemoval(PlayerColor color) gh->removeObject(visitedObject, color); } -TownBuildingVisitQuery::TownBuildingVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero, BuildingID buildingToVisit) - : VisitQuery(owner, Obj, Hero) - , visitedBuilding(buildingToVisit) +TownBuildingVisitQuery::TownBuildingVisitQuery(CGameHandler * owner, const CGTownInstance * Obj, std::vector heroes, std::vector buildingToVisit) + : VisitQuery(owner, Obj, heroes.front()) + , visitedTown(Obj) { + // generate in reverse order - first building-hero pair to handle must be in the end of vector + for (auto const * hero : boost::adaptors::reverse(heroes)) + for (auto const & building : boost::adaptors::reverse(buildingToVisit)) + visitedBuilding.push_back({ hero, building}); } -void TownBuildingVisitQuery::onRemoval(PlayerColor color) +void TownBuildingVisitQuery::onExposure(QueryPtr topQuery) { - + onAdded(players.front()); +} + +void TownBuildingVisitQuery::onAdded(PlayerColor color) +{ + while (!visitedBuilding.empty() && owner->topQuery(color).get() == this) + { + visitingHero = visitedBuilding.back().hero; + auto * building = visitedTown->rewardableBuildings.at(visitedBuilding.back().building); + building->onHeroVisit(visitingHero); + visitedBuilding.pop_back(); + } + + if (visitedBuilding.empty() && owner->topQuery(color).get() == this) + owner->popIfTop(*this); } diff --git a/server/queries/VisitQueries.h b/server/queries/VisitQueries.h index 0d7ce5cca..d2f554a49 100644 --- a/server/queries/VisitQueries.h +++ b/server/queries/VisitQueries.h @@ -11,19 +11,22 @@ #include "CQuery.h" +VCMI_LIB_NAMESPACE_BEGIN +class CGTownInstance; +VCMI_LIB_NAMESPACE_END + //Created when hero visits object. //Removed when query above is resolved (or immediately after visit if no queries were created) class VisitQuery : public CQuery { protected: - VisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero); + VisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero); public: - const CGObjectInstance *visitedObject; - const CGHeroInstance *visitingHero; + const CGObjectInstance * visitedObject; + const CGHeroInstance * visitingHero; - bool blocksPack(const CPack *pack) const final; - void onExposure(QueryPtr topQuery) final; + bool blocksPack(const CPack * pack) const final; }; class MapObjectVisitQuery final : public VisitQuery @@ -31,17 +34,26 @@ class MapObjectVisitQuery final : public VisitQuery public: bool removeObjectAfterVisit; - MapObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero); + MapObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero); void onRemoval(PlayerColor color) final; + void onExposure(QueryPtr topQuery) final; }; class TownBuildingVisitQuery final : public VisitQuery { + struct BuildingVisit + { + const CGHeroInstance * hero; + BuildingID building; + }; + + const CGTownInstance * visitedTown; + std::vector visitedBuilding; + public: - BuildingID visitedBuilding; + TownBuildingVisitQuery(CGameHandler * owner, const CGTownInstance * Obj, std::vector heroes, std::vector buildingToVisit); - TownBuildingVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero, BuildingID buildingToVisit); - - void onRemoval(PlayerColor color) final; + void onAdded(PlayerColor color) final; + void onExposure(QueryPtr topQuery) final; };