From 327ff0147159cfa0b1046904657e2b4a285d180a Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 3 Sep 2024 16:31:07 +0000 Subject: [PATCH 1/5] Implemented explicitly visitable town buildings, e.g. hota mana vortex Added flag `manualHeroVisit` flag to town building. If this flag is set, then building will only be activated on click and will not give its effect on hero recrutiment, hero visit, or new day. This allows implementing changes to Mana Vortex from HotA --- CCallback.cpp | 4 +- CCallback.h | 4 +- client/windows/CCastleInterface.cpp | 17 ++++- client/windows/CCastleInterface.h | 3 +- config/factions/dungeon.json | 1 + config/schemas/townBuilding.json | 6 +- .../Entities_Format/Town_Building_Format.md | 64 ++++++++++--------- lib/entities/building/CBuilding.h | 1 + lib/entities/faction/CTownHandler.cpp | 3 + lib/networkPacks/NetPackVisitor.h | 2 +- lib/networkPacks/NetPacksLib.cpp | 4 +- lib/networkPacks/PacksForServer.h | 12 ++-- lib/serializer/RegisterTypes.h | 2 +- server/CGameHandler.cpp | 30 +++++++-- server/CGameHandler.h | 2 +- server/NetPacksServer.cpp | 4 +- server/ServerNetPackVisitors.h | 2 +- 17 files changed, 104 insertions(+), 57 deletions(-) diff --git a/CCallback.cpp b/CCallback.cpp index d8a8fac99..8d2709f3d 100644 --- a/CCallback.cpp +++ b/CCallback.cpp @@ -217,12 +217,12 @@ bool CCallback::buildBuilding(const CGTownInstance *town, BuildingID buildingID) return true; } -bool CCallback::triggerTownSpecialBuildingAction(const CGTownInstance *town, BuildingSubID::EBuildingSubID subBuildingID) +bool CCallback::visitTownBuilding(const CGTownInstance *town, BuildingID buildingID) { if(town->tempOwner!=player) return false; - TriggerTownSpecialBuildingAction pack(town->id, subBuildingID); + VisitTownBuilding pack(town->id, buildingID); sendRequest(&pack); return true; } diff --git a/CCallback.h b/CCallback.h index 0f406dc2c..a934113ce 100644 --- a/CCallback.h +++ b/CCallback.h @@ -75,7 +75,7 @@ public: //town virtual void recruitHero(const CGObjectInstance *townOrTavern, const CGHeroInstance *hero, const HeroTypeID & nextHero=HeroTypeID::NONE)=0; virtual bool buildBuilding(const CGTownInstance *town, BuildingID buildingID)=0; - virtual bool triggerTownSpecialBuildingAction(const CGTownInstance *town, BuildingSubID::EBuildingSubID subBuildingID)=0; + virtual bool visitTownBuilding(const CGTownInstance *town, BuildingID buildingID)=0; virtual void recruitCreatures(const CGDwelling *obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1)=0; virtual bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE)=0; //if newID==-1 then best possible upgrade will be made virtual void swapGarrisonHero(const CGTownInstance *town)=0; @@ -182,7 +182,7 @@ public: void manageHeroCostume(ObjectInstanceID hero, size_t costumeIdx, bool saveCostume) override; void eraseArtifactByClient(const ArtifactLocation & al) override; bool buildBuilding(const CGTownInstance *town, BuildingID buildingID) override; - bool triggerTownSpecialBuildingAction(const CGTownInstance *town, BuildingSubID::EBuildingSubID subBuildingID) override; + bool visitTownBuilding(const CGTownInstance *town, BuildingID buildingID) override; void recruitCreatures(const CGDwelling * obj, const CArmedInstance * dst, CreatureID ID, ui32 amount, si32 level=-1) override; bool dismissCreature(const CArmedInstance *obj, SlotID stackPos) override; bool upgradeCreature(const CArmedInstance *obj, SlotID stackPos, CreatureID newID=CreatureID::NONE) override; diff --git a/client/windows/CCastleInterface.cpp b/client/windows/CCastleInterface.cpp index 79bb69330..bd582296f 100644 --- a/client/windows/CCastleInterface.cpp +++ b/client/windows/CCastleInterface.cpp @@ -743,6 +743,12 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu } } + if (town->rewardableBuildings.count(buildingToTest) && town->town->buildings.at(buildingToTest)->manualHeroVisit) + { + enterRewardable(buildingToTest); + return true; + } + if (buildingToTest >= BuildingID::DWELL_FIRST) { enterDwelling((BuildingID::getLevelFromDwelling(buildingToTest))); @@ -820,7 +826,7 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu return true; case BuildingSubID::BANK: - enterBank(); + enterBank(buildingTarget); return true; } } @@ -837,6 +843,11 @@ bool CCastleBuildings::buildingTryActivateCustomUI(BuildingID buildingToTest, Bu return false; } +void CCastleBuildings::enterRewardable(BuildingID building) +{ + LOCPLINT->cb->visitTownBuilding(town, building); +} + void CCastleBuildings::enterBlacksmith(BuildingID building, ArtifactID artifactID) { const CGHeroInstance *hero = town->visitingHero; @@ -1053,7 +1064,7 @@ void CCastleBuildings::enterAnyThievesGuild() LOCPLINT->showInfoDialog(CGI->generaltexth->translate("vcmi.adventureMap.noTownWithTavern")); } -void CCastleBuildings::enterBank() +void CCastleBuildings::enterBank(BuildingID building) { std::vector> components; if(town->bonusValue.second > 0) @@ -1064,7 +1075,7 @@ void CCastleBuildings::enterBank() else{ components.push_back(std::make_shared(ComponentType::RESOURCE, GameResID(GameResID::GOLD), 2500)); - LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.townStructure.bank.borrow"), [this](){ LOCPLINT->cb->triggerTownSpecialBuildingAction(town, BuildingSubID::BANK); }, nullptr, components); + LOCPLINT->showYesNoDialog(CGI->generaltexth->translate("vcmi.townStructure.bank.borrow"), [this, building](){ LOCPLINT->cb->visitTownBuilding(town, building); }, nullptr, components); } } diff --git a/client/windows/CCastleInterface.h b/client/windows/CCastleInterface.h index c756c1236..3b8fb6037 100644 --- a/client/windows/CCastleInterface.h +++ b/client/windows/CCastleInterface.h @@ -167,10 +167,11 @@ public: void enterDwelling(int level); void enterTownHall(); + void enterRewardable(BuildingID building); void enterMagesGuild(); void enterAnyMarket(); void enterAnyThievesGuild(); - void enterBank(); + void enterBank(BuildingID building); void enterToTheQuickRecruitmentWindow(); bool buildingTryActivateCustomUI(BuildingID buildingToTest, BuildingID buildingTarget); diff --git a/config/factions/dungeon.json b/config/factions/dungeon.json index ef8d1b7e5..9002b44a3 100644 --- a/config/factions/dungeon.json +++ b/config/factions/dungeon.json @@ -180,6 +180,7 @@ "visitors" : true }, "visitMode" : "hero", // Should be 'once' to match (somewhat buggy) H3 logic + "visitMode" : "once", "rewards" : [ { "limiter" : { diff --git a/config/schemas/townBuilding.json b/config/schemas/townBuilding.json index 9fdf00e66..8a7635c55 100644 --- a/config/schemas/townBuilding.json +++ b/config/schemas/townBuilding.json @@ -61,11 +61,15 @@ "description" : "If set to true, this building will replace all bonuses from base building, leaving only bonuses defined by this building", "type" : "boolean" }, + "manualHeroVisit" : { + "description" : "If set to true, this building will not automatically activate on new day or on entering town and needs to be activated manually on click", + "type" : "boolean" + }, "configuration" : { "description" : "Optional, configuration of building that can be activated by visiting hero", "$ref" : "rewardable.json" }, - "firtufications" : { + "fortifications" : { "type" : "object", "additionalProperties" : false, "description" : "Fortifications provided by this buildings, if any", diff --git a/docs/modders/Entities_Format/Town_Building_Format.md b/docs/modders/Entities_Format/Town_Building_Format.md index 3f8339fab..9cc14dd7d 100644 --- a/docs/modders/Entities_Format/Town_Building_Format.md +++ b/docs/modders/Entities_Format/Town_Building_Format.md @@ -16,18 +16,18 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config ##### Order of Fire from Inferno: ```jsonc "special4": { - "requires" : [ "mageGuild1" ], + "requires" : [ "mageGuild1" ], "name" : "Order of Fire", "description" : "Increases spellpower of visiting hero", "cost" : { - "mercury" : 5, + "mercury" : 5, "gold" : 1000 }, "configuration" : { - "visitMode" : "hero", + "visitMode" : "hero", "rewards" : [ - { - // NOTE: this forces vcmi to load string from H3 text file. In order to define own string simply write your own message without '@' symbol + { + // NOTE: this forces vcmi to load string from H3 text file. In order to define own string simply write your own message without '@' symbol "message" : "@core.genrltxt.582", "primary" : { "spellpower" : 1 } } @@ -39,22 +39,22 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config ##### Mana Vortex from Dungeon ```jsonc "special2": { - "requires" : [ "mageGuild1" ], + "requires" : [ "mageGuild1" ], "name" : "Mana Vortex", "description" : "Doubles mana points of the first visiting hero each week", "cost" : { - "gold" : 5000 + "gold" : 5000 }, "configuration" : { - "resetParameters" : { - "period" : 7, + "resetParameters" : { + "period" : 7, "visitors" : true }, "visitMode" : "once", "rewards" : [ - { - "limiter" : { - "noneOf" : [ { "manaPercentage" : 200 } ] + { + "limiter" : { + "noneOf" : [ { "manaPercentage" : 200 } ] }, "message" : "As you near the mana vortex your body is filled with new energy. You have doubled your normal spell points.", "manaPercentage" : 200 @@ -67,14 +67,14 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config #### Resource Silo with custom production ```jsonc "resourceSilo": { - "name" : "Wood Resource Silo", + "name" : "Wood Resource Silo", "description" : "Produces 2 wood every day", "cost" : { - "wood" : 10, + "wood" : 10, "gold" : 5000 }, "produce" : { - "wood": 2 + "wood": 2 } }, ``` @@ -193,11 +193,15 @@ These are just a couple of examples of what can be done in VCMI. See vcmi config // Buildings which bonuses should be overridden with bonuses of the current building "overrides" : [ "anotherBuilding ] - // Bonuses provided by this special building if this building or any of its upgrades are constructed in town + // Bonuses provided by this special building if this building or any of its upgrades are constructed in town + "bonuses" : [ BONUS_FORMAT ] + + // If set to true, this building will not automatically activate on new day or on entering town and needs to be activated manually on click + "manualHeroVisit" : false, + + // Bonuses provided by this special building if this building or any of its upgrades are constructed in town "bonuses" : [ BONUS_FORMAT ] - // If set to true, this building will replace all bonuses from base building, leaving only bonuses defined by this building" - "upgradeReplacesBonuses" : false, // If the building is a market, it requires market mode. "marketModes" : [ "resource-resource", "resource-player" ], @@ -209,18 +213,18 @@ Building requirements can be described using logical expressions: ```jsonc "requires" : [ - "allOf", // Normal H3 "build all" mode - [ "mageGuild1" ], - [ - "noneOf", // available only when none of these building are built - [ "dwelling5A" ], - [ "dwelling5AUpgrade" ] - ], - [ - "anyOf", // any non-zero number of these buildings must be built - [ "tavern" ], - [ "blacksmith" ] - ] + "allOf", // Normal H3 "build all" mode + [ "mageGuild1" ], + [ + "noneOf", // available only when none of these building are built + [ "dwelling5A" ], + [ "dwelling5AUpgrade" ] + ], + [ + "anyOf", // any non-zero number of these buildings must be built + [ "tavern" ], + [ "blacksmith" ] + ] ] ``` ### List of unique town buildings diff --git a/lib/entities/building/CBuilding.h b/lib/entities/building/CBuilding.h index 4f4531a85..9c35fd14c 100644 --- a/lib/entities/building/CBuilding.h +++ b/lib/entities/building/CBuilding.h @@ -44,6 +44,7 @@ public: BuildingID upgrade; /// indicates that building "upgrade" can be improved by this, -1 = empty BuildingSubID::EBuildingSubID subId; /// subtype for special buildings, -1 = the building is not special bool upgradeReplacesBonuses = false; + bool manualHeroVisit = false; BonusList buildingBonuses; Rewardable::Info rewardableObjectInfo; ///configurable rewards for special buildings diff --git a/lib/entities/faction/CTownHandler.cpp b/lib/entities/faction/CTownHandler.cpp index 82f1311b3..355de1f2b 100644 --- a/lib/entities/faction/CTownHandler.cpp +++ b/lib/entities/faction/CTownHandler.cpp @@ -299,6 +299,9 @@ void CTownHandler::loadBuilding(CTown * town, const std::string & stringID, cons ret->resources = TResources(source["cost"]); ret->produce = TResources(source["produce"]); + ret->manualHeroVisit = source["manualHeroVisit"].Bool(); + ret->upgradeReplacesBonuses = source["upgradeReplacesBonuses"].Bool(); + const JsonNode & fortifications = source["fortifications"]; if (!fortifications.isNull()) { diff --git a/lib/networkPacks/NetPackVisitor.h b/lib/networkPacks/NetPackVisitor.h index 9060bb494..475b9b5db 100644 --- a/lib/networkPacks/NetPackVisitor.h +++ b/lib/networkPacks/NetPackVisitor.h @@ -126,7 +126,7 @@ public: virtual void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) {} virtual void visitDisbandCreature(DisbandCreature & pack) {} virtual void visitBuildStructure(BuildStructure & pack) {} - virtual void visitTriggerTownSpecialBuildingAction(TriggerTownSpecialBuildingAction & pack) {} + virtual void visitVisitTownBuilding(VisitTownBuilding & pack) {} virtual void visitRazeStructure(RazeStructure & pack) {} virtual void visitRecruitCreatures(RecruitCreatures & pack) {} virtual void visitUpgradeCreature(UpgradeCreature & pack) {} diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 4ce58097d..a9cbd36f8 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -582,9 +582,9 @@ void BuildStructure::visitTyped(ICPackVisitor & visitor) visitor.visitBuildStructure(*this); } -void TriggerTownSpecialBuildingAction::visitTyped(ICPackVisitor & visitor) +void VisitTownBuilding::visitTyped(ICPackVisitor & visitor) { - visitor.visitTriggerTownSpecialBuildingAction(*this); + visitor.visitVisitTownBuilding(*this); } void RazeStructure::visitTyped(ICPackVisitor & visitor) diff --git a/lib/networkPacks/PacksForServer.h b/lib/networkPacks/PacksForServer.h index c0e40b4c0..a909cf652 100644 --- a/lib/networkPacks/PacksForServer.h +++ b/lib/networkPacks/PacksForServer.h @@ -280,16 +280,16 @@ struct DLL_LINKAGE BuildStructure : public CPackForServer } }; -struct DLL_LINKAGE TriggerTownSpecialBuildingAction : public CPackForServer +struct DLL_LINKAGE VisitTownBuilding : public CPackForServer { - TriggerTownSpecialBuildingAction() = default; - TriggerTownSpecialBuildingAction(const ObjectInstanceID & TID, const BuildingSubID::EBuildingSubID SID) + VisitTownBuilding() = default; + VisitTownBuilding(const ObjectInstanceID & TID, const BuildingID BID) : tid(TID) - , sid(SID) + , bid(BID) { } ObjectInstanceID tid; - BuildingSubID::EBuildingSubID sid; + BuildingID bid; void visitTyped(ICPackVisitor & visitor) override; @@ -297,7 +297,7 @@ struct DLL_LINKAGE TriggerTownSpecialBuildingAction : public CPackForServer { h & static_cast(*this); h & tid; - h & sid; + h & bid; } }; diff --git a/lib/serializer/RegisterTypes.h b/lib/serializer/RegisterTypes.h index 070c76f8e..463e5f75f 100644 --- a/lib/serializer/RegisterTypes.h +++ b/lib/serializer/RegisterTypes.h @@ -239,7 +239,7 @@ void registerTypes(Serializer &s) s.template registerType(183); s.template registerType(184); s.template registerType(185); - s.template registerType(186); + s.template registerType(186); s.template registerType(187); s.template registerType(188); s.template registerType(189); diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 58fe4d444..eab97a9db 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1181,7 +1181,10 @@ void CGameHandler::heroVisitCastle(const CGTownInstance * obj, const CGHeroInsta void CGameHandler::visitCastleObjects(const CGTownInstance * t, const CGHeroInstance * h) { for (auto & building : t->rewardableBuildings) - building.second->onHeroVisit(h); + { + if (!t->town->buildings.at(building.first)->manualHeroVisit) + building.second->onHeroVisit(h); + } } void CGameHandler::stopHeroVisitCastle(const CGTownInstance * obj, const CGHeroInstance * hero) @@ -2148,20 +2151,39 @@ bool CGameHandler::buildStructure(ObjectInstanceID tid, BuildingID requestedID, return true; } -bool CGameHandler::triggerTownSpecialBuildingAction(ObjectInstanceID tid, BuildingSubID::EBuildingSubID sid) +bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid) { const CGTownInstance * t = getTown(tid); - if(t->town->getBuildingType(sid) == BuildingID::NONE) + if(!t->hasBuilt(bid)) return false; - if(sid == BuildingSubID::EBuildingSubID::BANK) + auto subID = t->town->buildings.at(bid)->subId; + + if(subID == BuildingSubID::EBuildingSubID::BANK) { TResources res; res[EGameResID::GOLD] = 2500; giveResources(t->getOwner(), res); setObjPropertyValue(t->id, ObjProperty::BONUS_VALUE_SECOND, 2500); + return true; + } + + if (t->rewardableBuildings.count(bid)) + { + auto & hero = t->garrisonHero ? t->garrisonHero : t->visitingHero; + auto * building = t->rewardableBuildings.at(bid); + + if (hero && t->town->buildings.at(bid)->manualHeroVisit) + { + // FIXME: query might produce unintended side effects, double check + auto visitQuery = std::make_shared(this, t, hero, t->visitablePos()); + queries->addQuery(visitQuery); + building->onHeroVisit(hero); + queries->popIfTop(visitQuery); + return true; + } } return true; diff --git a/server/CGameHandler.h b/server/CGameHandler.h index 889339e82..e2ceecd85 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -214,7 +214,7 @@ public: bool upgradeCreature( ObjectInstanceID objid, SlotID pos, CreatureID upgID ); bool recruitCreatures(ObjectInstanceID objid, ObjectInstanceID dst, CreatureID crid, ui32 cram, si32 level, PlayerColor player); bool buildStructure(ObjectInstanceID tid, BuildingID bid, bool force=false);//force - for events: no cost, no checkings - bool triggerTownSpecialBuildingAction(ObjectInstanceID tid, BuildingSubID::EBuildingSubID sid); + bool visitTownBuilding(ObjectInstanceID tid, BuildingID bid); bool razeStructure(ObjectInstanceID tid, BuildingID bid); bool disbandCreature( ObjectInstanceID id, SlotID pos ); bool arrangeStacks( ObjectInstanceID id1, ObjectInstanceID id2, ui8 what, SlotID p1, SlotID p2, si32 val, PlayerColor player); diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index d1ebbf895..d3938deeb 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -139,12 +139,12 @@ void ApplyGhNetPackVisitor::visitBuildStructure(BuildStructure & pack) result = gh.buildStructure(pack.tid, pack.bid); } -void ApplyGhNetPackVisitor::visitTriggerTownSpecialBuildingAction(TriggerTownSpecialBuildingAction & pack) +void ApplyGhNetPackVisitor::visitVisitTownBuilding(VisitTownBuilding & pack) { gh.throwIfWrongOwner(&pack, pack.tid); gh.throwIfPlayerNotActive(&pack); - result = gh.triggerTownSpecialBuildingAction(pack.tid, pack.sid); + result = gh.visitTownBuilding(pack.tid, pack.bid); } void ApplyGhNetPackVisitor::visitRecruitCreatures(RecruitCreatures & pack) diff --git a/server/ServerNetPackVisitors.h b/server/ServerNetPackVisitors.h index 68601e929..34593c1da 100644 --- a/server/ServerNetPackVisitors.h +++ b/server/ServerNetPackVisitors.h @@ -41,7 +41,7 @@ public: void visitBulkSmartSplitStack(BulkSmartSplitStack & pack) override; void visitDisbandCreature(DisbandCreature & pack) override; void visitBuildStructure(BuildStructure & pack) override; - void visitTriggerTownSpecialBuildingAction(TriggerTownSpecialBuildingAction & pack) override; + void visitVisitTownBuilding(VisitTownBuilding & pack) override; void visitRecruitCreatures(RecruitCreatures & pack) override; void visitUpgradeCreature(UpgradeCreature & pack) override; void visitGarrisonHeroSwap(GarrisonHeroSwap & pack) override; From 29f87d64071ac1135a9b03e5bba9c7ec4ed25df2 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 4 Sep 2024 14:17:22 +0000 Subject: [PATCH 2/5] pass object/hero parameters directly instead of passing query --- server/CGameHandler.cpp | 2 +- server/queries/BattleQueries.cpp | 4 ++-- server/queries/BattleQueries.h | 2 +- server/queries/CQuery.cpp | 2 +- server/queries/CQuery.h | 29 +++++++++++++++++++++-------- server/queries/MapQueries.cpp | 24 ++++++++++++------------ server/queries/MapQueries.h | 10 +++++----- 7 files changed, 43 insertions(+), 30 deletions(-) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index eab97a9db..5f2b8fd3d 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3851,7 +3851,7 @@ bool CGameHandler::isValidObject(const CGObjectInstance *obj) const bool CGameHandler::isBlockedByQueries(const CPack *pack, PlayerColor player) { - if (!strcmp(typeid(*pack).name(), typeid(PlayerMessage).name())) + if (dynamic_cast(pack) != nullptr) return false; auto query = queries->topQuery(player); diff --git a/server/queries/BattleQueries.cpp b/server/queries/BattleQueries.cpp index 4fc0c25f0..a1fa45158 100644 --- a/server/queries/BattleQueries.cpp +++ b/server/queries/BattleQueries.cpp @@ -23,12 +23,12 @@ #include "../../lib/networkPacks/PacksForServer.h" #include "../../lib/serializer/Cast.h" -void CBattleQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const +void CBattleQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { assert(result); if(result) - objectVisit.visitedObject->battleFinished(objectVisit.visitingHero, *result); + visitedObject->battleFinished(visitingHero, *result); } CBattleQuery::CBattleQuery(CGameHandler * owner, const IBattleInfo * bi): diff --git a/server/queries/BattleQueries.h b/server/queries/BattleQueries.h index 6916cb7f5..ae32b08b3 100644 --- a/server/queries/BattleQueries.h +++ b/server/queries/BattleQueries.h @@ -29,7 +29,7 @@ public: CBattleQuery(CGameHandler * owner); CBattleQuery(CGameHandler * owner, const IBattleInfo * Bi); //TODO - void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; + void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const override; bool blocksPack(const CPack *pack) const override; void onRemoval(PlayerColor color) override; void onExposure(QueryPtr topQuery) override; diff --git a/server/queries/CQuery.cpp b/server/queries/CQuery.cpp index 02eb14762..f4f4508da 100644 --- a/server/queries/CQuery.cpp +++ b/server/queries/CQuery.cpp @@ -87,7 +87,7 @@ bool CQuery::blocksPack(const CPack * pack) const return false; } -void CQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const +void CQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { } diff --git a/server/queries/CQuery.h b/server/queries/CQuery.h index 5773ecadd..2fde16295 100644 --- a/server/queries/CQuery.h +++ b/server/queries/CQuery.h @@ -14,6 +14,8 @@ VCMI_LIB_NAMESPACE_BEGIN struct CPack; +class CGObjectInstance; +class CGHeroInstance; VCMI_LIB_NAMESPACE_END @@ -40,18 +42,29 @@ public: CQuery(CGameHandler * gh); - virtual bool blocksPack(const CPack *pack) const; //query can block attempting actions by player. Eg. he can't move hero during the battle. + /// query can block attempting actions by player. Eg. he can't move hero during the battle. + virtual bool blocksPack(const CPack *pack) const; - virtual bool endsByPlayerAnswer() const; //query is removed after player gives answer (like dialogs) - virtual void onAdding(PlayerColor color); //called just before query is pushed on stack - virtual void onAdded(PlayerColor color); //called right after query is pushed on stack - virtual void onRemoval(PlayerColor color); //called after query is removed from stack - virtual void onExposure(QueryPtr topQuery);//called when query immediately above is removed and this is exposed (becomes top) - virtual std::string toString() const; + /// query is removed after player gives answer (like dialogs) + virtual bool endsByPlayerAnswer() const; - virtual void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const; + /// called just before query is pushed on stack + virtual void onAdding(PlayerColor color); + + /// called right after query is pushed on stack + virtual void onAdded(PlayerColor color); + + /// called after query is removed from stack + virtual void onRemoval(PlayerColor color); + + /// called when query immediately above is removed and this is exposed (becomes top) + virtual void onExposure(QueryPtr topQuery); + + /// called when this query is being removed and must report its result to currently visited object + virtual void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const; virtual void setReply(std::optional reply); + virtual std::string toString() const; virtual ~CQuery(); protected: diff --git a/server/queries/MapQueries.cpp b/server/queries/MapQueries.cpp index 72f9bfbce..60942c88b 100644 --- a/server/queries/MapQueries.cpp +++ b/server/queries/MapQueries.cpp @@ -71,14 +71,14 @@ void CObjectVisitQuery::onExposure(QueryPtr topQuery) { //Object may have been removed and deleted. if(gh->isValidObject(visitedObject)) - topQuery->notifyObjectAboutRemoval(*this); + topQuery->notifyObjectAboutRemoval(visitedObject, visitingHero); owner->popIfTop(*this); } -void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const +void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { - objectVisit.visitedObject->garrisonDialogClosed(objectVisit.visitingHero); + visitedObject->garrisonDialogClosed(visitingHero); } CGarrisonDialogQuery::CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance * up, const CArmedInstance * down): @@ -150,10 +150,10 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const return CDialogQuery::blocksPack(pack); } -void CBlockingDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const +void CBlockingDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { assert(answer); - objectVisit.visitedObject->blockingDialogAnswered(objectVisit.visitingHero, *answer); + visitedObject->blockingDialogAnswered(visitingHero, *answer); } CBlockingDialogQuery::CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog & bd): @@ -223,12 +223,12 @@ bool OpenWindowQuery::blocksPack(const CPack *pack) const return CDialogQuery::blocksPack(pack); } -void CTeleportDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const +void CTeleportDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { // do not change to dynamic_ptr_cast - SIGSEGV! - auto obj = dynamic_cast(objectVisit.visitedObject); + auto obj = dynamic_cast(visitedObject); if(obj) - obj->teleportDialogAnswered(objectVisit.visitingHero, *answer, td.exits); + obj->teleportDialogAnswered(visitingHero, *answer, td.exits); else logGlobal->error("Invalid instance in teleport query"); } @@ -254,9 +254,9 @@ void CHeroLevelUpDialogQuery::onRemoval(PlayerColor color) gh->levelUpHero(hero, hlu.skills[*answer]); } -void CHeroLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const +void CHeroLevelUpDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { - objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero); + visitedObject->heroLevelUpDone(visitingHero); } CCommanderLevelUpDialogQuery::CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp & Clu, const CGHeroInstance * Hero): @@ -273,9 +273,9 @@ void CCommanderLevelUpDialogQuery::onRemoval(PlayerColor color) gh->levelUpCommander(hero->commander, clu.skills[*answer]); } -void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CObjectVisitQuery & objectVisit) const +void CCommanderLevelUpDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { - objectVisit.visitedObject->heroLevelUpDone(objectVisit.visitingHero); + visitedObject->heroLevelUpDone(visitingHero); } CHeroMovementQuery::CHeroMovementQuery(CGameHandler * owner, const TryMoveHero & Tmh, const CGHeroInstance * Hero, bool VisitDestAfterVictory): diff --git a/server/queries/MapQueries.h b/server/queries/MapQueries.h index 7a6cabf3c..e81c8a72b 100644 --- a/server/queries/MapQueries.h +++ b/server/queries/MapQueries.h @@ -73,7 +73,7 @@ public: std::array exchangingArmies; CGarrisonDialogQuery(CGameHandler * owner, const CArmedInstance *up, const CArmedInstance *down); - void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; + void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const override; bool blocksPack(const CPack *pack) const override; }; @@ -85,7 +85,7 @@ public: CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog &bd); - void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; + void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const override; }; class OpenWindowQuery : public CDialogQuery @@ -105,7 +105,7 @@ public: CTeleportDialogQuery(CGameHandler * owner, const TeleportDialog &td); - void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; + void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const override; }; class CHeroLevelUpDialogQuery : public CDialogQuery @@ -114,7 +114,7 @@ public: CHeroLevelUpDialogQuery(CGameHandler * owner, const HeroLevelUp &Hlu, const CGHeroInstance * Hero); void onRemoval(PlayerColor color) override; - void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; + void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const override; HeroLevelUp hlu; const CGHeroInstance * hero; @@ -126,7 +126,7 @@ public: CCommanderLevelUpDialogQuery(CGameHandler * owner, const CommanderLevelUp &Clu, const CGHeroInstance * Hero); void onRemoval(PlayerColor color) override; - void notifyObjectAboutRemoval(const CObjectVisitQuery &objectVisit) const override; + void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const override; CommanderLevelUp clu; const CGHeroInstance * hero; From 81af66d35bf62b11ee5e3675b0aa22074dd1673b Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 4 Sep 2024 14:32:36 +0000 Subject: [PATCH 3/5] Move visit query to a separate class, remove no longer needed Cast.h --- client/CServerHandler.cpp | 1 - lib/CMakeLists.txt | 1 - lib/serializer/Cast.h | 26 -------- server/CGameHandler.cpp | 5 +- server/CMakeLists.txt | 2 + server/NetPacksServer.cpp | 1 - server/battles/BattleResultProcessor.cpp | 3 +- server/queries/BattleQueries.cpp | 5 +- server/queries/CQuery.cpp | 3 +- server/queries/MapQueries.cpp | 82 +++++++----------------- server/queries/MapQueries.h | 21 ------ server/queries/VisitQueries.cpp | 47 ++++++++++++++ server/queries/VisitQueries.h | 29 +++++++++ 13 files changed, 109 insertions(+), 117 deletions(-) delete mode 100644 lib/serializer/Cast.h create mode 100644 server/queries/VisitQueries.cpp create mode 100644 server/queries/VisitQueries.h diff --git a/client/CServerHandler.cpp b/client/CServerHandler.cpp index 5d19ab960..d11bafa22 100644 --- a/client/CServerHandler.cpp +++ b/client/CServerHandler.cpp @@ -51,7 +51,6 @@ #include #include #include -#include "../lib/serializer/Cast.h" #include "LobbyClientNetPackVisitors.h" #include diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e5b9f8892..f859bb058 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -620,7 +620,6 @@ set(lib_MAIN_HEADERS serializer/JsonSerializeFormat.h serializer/JsonSerializer.h serializer/JsonUpdater.h - serializer/Cast.h serializer/ESerializationVersion.h serializer/RegisterTypes.h serializer/Serializeable.h diff --git a/lib/serializer/Cast.h b/lib/serializer/Cast.h deleted file mode 100644 index 7525f6d85..000000000 --- a/lib/serializer/Cast.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Cast.h, part of VCMI engine - * - * Authors: listed in file AUTHORS in main folder - * - * License: GNU General Public License v2.0 or later - * Full text of license available in license.txt file, in main folder - * - */ -#pragma once - -VCMI_LIB_NAMESPACE_BEGIN - -template -inline const T * dynamic_ptr_cast(const F * ptr) -{ - return dynamic_cast(ptr); -} - -template -inline T * dynamic_ptr_cast(F * ptr) -{ - return dynamic_cast(ptr); -} - -VCMI_LIB_NAMESPACE_END diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 5f2b8fd3d..c5dd4ad6f 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -21,6 +21,7 @@ #include "processors/TurnOrderProcessor.h" #include "queries/QueriesProcessor.h" #include "queries/MapQueries.h" +#include "queries/VisitQueries.h" #include "../lib/ArtifactUtils.h" #include "../lib/CArtHandler.h" @@ -2178,7 +2179,7 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid) if (hero && t->town->buildings.at(bid)->manualHeroVisit) { // FIXME: query might produce unintended side effects, double check - auto visitQuery = std::make_shared(this, t, hero, t->visitablePos()); + auto visitQuery = std::make_shared(this, t, hero); queries->addQuery(visitQuery); building->onHeroVisit(hero); queries->popIfTop(visitQuery); @@ -3226,7 +3227,7 @@ void CGameHandler::objectVisited(const CGObjectInstance * obj, const CGHeroInsta visitedObject = visitedTown; } } - visitQuery = std::make_shared(this, visitedObject, h, visitedObject->visitablePos()); + visitQuery = std::make_shared(this, visitedObject, h); queries->addQuery(visitQuery); //TODO real visit pos HeroVisit hv; diff --git a/server/CMakeLists.txt b/server/CMakeLists.txt index bd98d94ac..a65c3ee0f 100644 --- a/server/CMakeLists.txt +++ b/server/CMakeLists.txt @@ -9,6 +9,7 @@ set(vcmiservercommon_SRCS queries/BattleQueries.cpp queries/CQuery.cpp queries/MapQueries.cpp + queries/VisitQueries.cpp queries/QueriesProcessor.cpp processors/HeroPoolProcessor.cpp @@ -36,6 +37,7 @@ set(vcmiservercommon_HEADERS queries/BattleQueries.h queries/CQuery.h queries/MapQueries.h + queries/VisitQueries.h queries/QueriesProcessor.h processors/HeroPoolProcessor.h diff --git a/server/NetPacksServer.cpp b/server/NetPacksServer.cpp index d3938deeb..2a7b493d4 100644 --- a/server/NetPacksServer.cpp +++ b/server/NetPacksServer.cpp @@ -27,7 +27,6 @@ #include "../lib/battle/Unit.h" #include "../lib/spells/CSpellHandler.h" #include "../lib/spells/ISpellMechanics.h" -#include "../lib/serializer/Cast.h" void ApplyGhNetPackVisitor::visitSaveGame(SaveGame & pack) { diff --git a/server/battles/BattleResultProcessor.cpp b/server/battles/BattleResultProcessor.cpp index 45e08c59b..614f9e59b 100644 --- a/server/battles/BattleResultProcessor.cpp +++ b/server/battles/BattleResultProcessor.cpp @@ -27,7 +27,6 @@ #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/networkPacks/PacksForClientBattle.h" #include "../../lib/networkPacks/PacksForClient.h" -#include "../../lib/serializer/Cast.h" #include "../../lib/spells/CSpellHandler.h" #include @@ -81,7 +80,7 @@ CasualtiesAfterBattle::CasualtiesAfterBattle(const CBattleInfoCallback & battle, else if(warMachine != ArtifactID::CATAPULT && st->getCount() <= 0) { logGlobal->debug("War machine has been destroyed"); - auto hero = dynamic_ptr_cast (army); + auto hero = dynamic_cast (army); if (hero) removedWarMachines.push_back (ArtifactLocation(hero->id, hero->getArtPos(warMachine, true))); else diff --git a/server/queries/BattleQueries.cpp b/server/queries/BattleQueries.cpp index a1fa45158..80ac9029c 100644 --- a/server/queries/BattleQueries.cpp +++ b/server/queries/BattleQueries.cpp @@ -21,7 +21,6 @@ #include "../../lib/mapObjects/CGObjectInstance.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/networkPacks/PacksForServer.h" -#include "../../lib/serializer/Cast.h" void CBattleQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { @@ -51,10 +50,10 @@ CBattleQuery::CBattleQuery(CGameHandler * owner): bool CBattleQuery::blocksPack(const CPack * pack) const { - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; return true; diff --git a/server/queries/CQuery.cpp b/server/queries/CQuery.cpp index f4f4508da..3b9d8dd84 100644 --- a/server/queries/CQuery.cpp +++ b/server/queries/CQuery.cpp @@ -14,7 +14,6 @@ #include "../CGameHandler.h" -#include "../../lib/serializer/Cast.h" #include "../../lib/networkPacks/PacksForServer.h" std::ostream & operator<<(std::ostream & out, const CQuery & query) @@ -116,7 +115,7 @@ void CQuery::setReply(std::optional reply) bool CQuery::blockAllButReply(const CPack * pack) const { //We accept only query replies from correct player - if(auto reply = dynamic_ptr_cast(pack)) + if(auto reply = dynamic_cast(pack)) return !vstd::contains(players, reply->player); return true; diff --git a/server/queries/MapQueries.cpp b/server/queries/MapQueries.cpp index 60942c88b..858154215 100644 --- a/server/queries/MapQueries.cpp +++ b/server/queries/MapQueries.cpp @@ -13,10 +13,9 @@ #include "QueriesProcessor.h" #include "../CGameHandler.h" #include "../TurnTimerHandler.h" -#include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/mapObjects/MiscObjects.h" #include "../../lib/networkPacks/PacksForServer.h" -#include "../../lib/serializer/Cast.h" TimerPauseQuery::TimerPauseQuery(CGameHandler * owner, PlayerColor player): CQuery(owner) @@ -44,38 +43,6 @@ bool TimerPauseQuery::endsByPlayerAnswer() const return true; } -CObjectVisitQuery::CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero, int3 Tile): - CQuery(owner), visitedObject(Obj), visitingHero(Hero), tile(Tile), removeObjectAfterVisit(false) -{ - addPlayer(Hero->tempOwner); -} - -bool CObjectVisitQuery::blocksPack(const CPack *pack) const -{ - //During the visit itself ALL actions are blocked. - //(However, the visit may trigger a query above that'll pass some.) - return true; -} - -void CObjectVisitQuery::onRemoval(PlayerColor color) -{ - gh->objectVisitEnded(*this); - - //TODO or should it be destructor? - //Can object visit affect 2 players and what would be desired behavior? - if(removeObjectAfterVisit) - gh->removeObject(visitedObject, color); -} - -void CObjectVisitQuery::onExposure(QueryPtr topQuery) -{ - //Object may have been removed and deleted. - if(gh->isValidObject(visitedObject)) - topQuery->notifyObjectAboutRemoval(visitedObject, visitingHero); - - owner->popIfTop(*this); -} - void CGarrisonDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { visitedObject->garrisonDialogClosed(visitingHero); @@ -97,22 +64,22 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const ourIds.insert(this->exchangingArmies[0]->id); ourIds.insert(this->exchangingArmies[1]->id); - if(auto stacks = dynamic_ptr_cast(pack)) + if(auto stacks = dynamic_cast(pack)) return !vstd::contains(ourIds, stacks->id1) || !vstd::contains(ourIds, stacks->id2); - if(auto stacks = dynamic_ptr_cast(pack)) + if(auto stacks = dynamic_cast(pack)) return !vstd::contains(ourIds, stacks->srcOwner); - if(auto stacks = dynamic_ptr_cast(pack)) + if(auto stacks = dynamic_cast(pack)) return !vstd::contains(ourIds, stacks->srcOwner); - if(auto stacks = dynamic_ptr_cast(pack)) + if(auto stacks = dynamic_cast(pack)) return !vstd::contains(ourIds, stacks->srcOwner); - if(auto stacks = dynamic_ptr_cast(pack)) + if(auto stacks = dynamic_cast(pack)) return !vstd::contains(ourIds, stacks->srcArmy) || !vstd::contains(ourIds, stacks->destArmy); - if(auto arts = dynamic_ptr_cast(pack)) + if(auto arts = dynamic_cast(pack)) { if(auto id1 = arts->src.artHolder) if(!vstd::contains(ourIds, id1)) @@ -123,28 +90,28 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const return true; return false; } - if(auto dismiss = dynamic_ptr_cast(pack)) + if(auto dismiss = dynamic_cast(pack)) return !vstd::contains(ourIds, dismiss->id); - if(auto arts = dynamic_ptr_cast(pack)) + if(auto arts = dynamic_cast(pack)) return !vstd::contains(ourIds, arts->srcHero) || !vstd::contains(ourIds, arts->dstHero); - if(auto arts = dynamic_ptr_cast(pack)) + if(auto arts = dynamic_cast(pack)) return !vstd::contains(ourIds, arts->artHolder); - if(auto art = dynamic_ptr_cast(pack)) + if(auto art = dynamic_cast(pack)) { if(auto id = art->al.artHolder) return !vstd::contains(ourIds, id); } - if(auto dismiss = dynamic_ptr_cast(pack)) + if(auto dismiss = dynamic_cast(pack)) return !vstd::contains(ourIds, dismiss->heroID); - if(auto upgrade = dynamic_ptr_cast(pack)) + if(auto upgrade = dynamic_cast(pack)) return !vstd::contains(ourIds, upgrade->id); - if(auto formation = dynamic_ptr_cast(pack)) + if(auto formation = dynamic_cast(pack)) return !vstd::contains(ourIds, formation->hid); return CDialogQuery::blocksPack(pack); @@ -179,44 +146,44 @@ bool OpenWindowQuery::blocksPack(const CPack *pack) const { if (mode == EOpenWindowMode::RECRUITMENT_FIRST || mode == EOpenWindowMode::RECRUITMENT_ALL) { - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; // If hero has no free slots, he might get some stacks merged automatically - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; } if (mode == EOpenWindowMode::TAVERN_WINDOW) { - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; } if (mode == EOpenWindowMode::UNIVERSITY_WINDOW) { - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; } if (mode == EOpenWindowMode::MARKET_WINDOW) { - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; - if(dynamic_ptr_cast(pack)) + if(dynamic_cast(pack)) return false; - if(dynamic_ptr_cast(pack)) + if(dynamic_cast(pack)) return false; - if(dynamic_ptr_cast(pack) != nullptr) + if(dynamic_cast(pack) != nullptr) return false; } @@ -225,7 +192,6 @@ bool OpenWindowQuery::blocksPack(const CPack *pack) const void CTeleportDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { - // do not change to dynamic_ptr_cast - SIGSEGV! auto obj = dynamic_cast(visitedObject); if(obj) obj->teleportDialogAnswered(visitingHero, *answer, td.exits); diff --git a/server/queries/MapQueries.h b/server/queries/MapQueries.h index e81c8a72b..cfd152969 100644 --- a/server/queries/MapQueries.h +++ b/server/queries/MapQueries.h @@ -15,12 +15,8 @@ VCMI_LIB_NAMESPACE_BEGIN class CGHeroInstance; class CGObjectInstance; -class int3; VCMI_LIB_NAMESPACE_END - -class TurnTimerHandler; - //Created when player starts turn or when player puts game on [ause //Removed when player accepts a turn or continur play class TimerPauseQuery : public CQuery @@ -34,23 +30,6 @@ public: bool endsByPlayerAnswer() const override; }; -//Created when hero visits object. -//Removed when query above is resolved (or immediately after visit if no queries were created) -class CObjectVisitQuery : public CQuery -{ -public: - const CGObjectInstance *visitedObject; - const CGHeroInstance *visitingHero; - int3 tile; //may be different than hero pos -> eg. visit via teleport - bool removeObjectAfterVisit; - - CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero, int3 Tile); - - bool blocksPack(const CPack *pack) const override; - void onRemoval(PlayerColor color) override; - void onExposure(QueryPtr topQuery) override; -}; - //Created when hero attempts move and something happens //(not necessarily position change, could be just an object interaction). class CHeroMovementQuery : public CQuery diff --git a/server/queries/VisitQueries.cpp b/server/queries/VisitQueries.cpp new file mode 100644 index 000000000..e61b73293 --- /dev/null +++ b/server/queries/VisitQueries.cpp @@ -0,0 +1,47 @@ +/* + * VisitQueries.cpp, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#include "StdInc.h" +#include "VisitQueries.h" + +#include "QueriesProcessor.h" +#include "../CGameHandler.h" +#include "../../lib/mapObjects/CGHeroInstance.h" + +CObjectVisitQuery::CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero): + CQuery(owner), visitedObject(Obj), visitingHero(Hero), removeObjectAfterVisit(false) +{ + addPlayer(Hero->tempOwner); +} + +bool CObjectVisitQuery::blocksPack(const CPack *pack) const +{ + //During the visit itself ALL actions are blocked. + //(However, the visit may trigger a query above that'll pass some.) + return true; +} + +void CObjectVisitQuery::onRemoval(PlayerColor color) +{ + gh->objectVisitEnded(*this); + + //TODO or should it be destructor? + //Can object visit affect 2 players and what would be desired behavior? + if(removeObjectAfterVisit) + gh->removeObject(visitedObject, color); +} + +void CObjectVisitQuery::onExposure(QueryPtr topQuery) +{ + //Object may have been removed and deleted. + if(gh->isValidObject(visitedObject)) + topQuery->notifyObjectAboutRemoval(visitedObject, visitingHero); + + owner->popIfTop(*this); +} diff --git a/server/queries/VisitQueries.h b/server/queries/VisitQueries.h new file mode 100644 index 000000000..733c99d25 --- /dev/null +++ b/server/queries/VisitQueries.h @@ -0,0 +1,29 @@ +/* + * VisitQueries.h, part of VCMI engine + * + * Authors: listed in file AUTHORS in main folder + * + * License: GNU General Public License v2.0 or later + * Full text of license available in license.txt file, in main folder + * + */ +#pragma once + +#include "CQuery.h" + +//Created when hero visits object. +//Removed when query above is resolved (or immediately after visit if no queries were created) +class CObjectVisitQuery : public CQuery +{ +public: + const CGObjectInstance *visitedObject; + const CGHeroInstance *visitingHero; + + bool removeObjectAfterVisit; + + CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero); + + bool blocksPack(const CPack *pack) const override; + void onRemoval(PlayerColor color) override; + void onExposure(QueryPtr topQuery) override; +}; From d34b4a141efa17de72d2833a56cab1b549e1a42e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 4 Sep 2024 14:54:09 +0000 Subject: [PATCH 4/5] Split map object visit from town building visit. Removes side effects from building visit --- config/factions/dungeon.json | 1 - server/CGameHandler.cpp | 21 +++++++------- server/CGameHandler.h | 2 +- server/queries/VisitQueries.cpp | 51 ++++++++++++++++++++++----------- server/queries/VisitQueries.h | 28 ++++++++++++++---- 5 files changed, 69 insertions(+), 34 deletions(-) diff --git a/config/factions/dungeon.json b/config/factions/dungeon.json index 9002b44a3..54cafa27b 100644 --- a/config/factions/dungeon.json +++ b/config/factions/dungeon.json @@ -179,7 +179,6 @@ "period" : 7, "visitors" : true }, - "visitMode" : "hero", // Should be 'once' to match (somewhat buggy) H3 logic "visitMode" : "once", "rewards" : [ { diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index c5dd4ad6f..7b0556a08 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -2178,8 +2178,7 @@ bool CGameHandler::visitTownBuilding(ObjectInstanceID tid, BuildingID bid) if (hero && t->town->buildings.at(bid)->manualHeroVisit) { - // FIXME: query might produce unintended side effects, double check - auto visitQuery = std::make_shared(this, t, hero); + auto visitQuery = std::make_shared(this, t, hero, bid); queries->addQuery(visitQuery); building->onHeroVisit(hero); queries->popIfTop(visitQuery); @@ -3208,7 +3207,7 @@ void CGameHandler::objectVisited(const CGObjectInstance * obj, const CGHeroInsta throw std::runtime_error("Can not visit object that is being visited"); } - std::shared_ptr visitQuery; + std::shared_ptr visitQuery; auto startVisit = [&](ObjectVisitStarted & event) { @@ -3227,7 +3226,7 @@ void CGameHandler::objectVisited(const CGObjectInstance * obj, const CGHeroInsta visitedObject = visitedTown; } } - visitQuery = std::make_shared(this, visitedObject, h); + visitQuery = std::make_shared(this, visitedObject, h); queries->addQuery(visitQuery); //TODO real visit pos HeroVisit hv; @@ -3246,11 +3245,11 @@ void CGameHandler::objectVisited(const CGObjectInstance * obj, const CGHeroInsta queries->popIfTop(visitQuery); //visit ends here if no queries were created } -void CGameHandler::objectVisitEnded(const CObjectVisitQuery & query) +void CGameHandler::objectVisitEnded(const CGHeroInstance *h, PlayerColor player) { using events::ObjectVisitEnded; - logGlobal->debug("%s visit ends.\n", query.visitingHero->nodeName()); + logGlobal->debug("%s visit ends.\n", h->nodeName()); auto endVisit = [&](ObjectVisitEnded & event) { @@ -3263,7 +3262,7 @@ void CGameHandler::objectVisitEnded(const CObjectVisitQuery & query) //TODO: ObjectVisitEnded should also have id of visited object, //but this requires object being deleted only by `removeAfterVisit()` but not `removeObject()` - ObjectVisitEnded::defaultExecute(serverEventBus.get(), endVisit, query.players.front(), query.visitingHero->id); + ObjectVisitEnded::defaultExecute(serverEventBus.get(), endVisit, player, h->id); } bool CGameHandler::buildBoat(ObjectInstanceID objid, PlayerColor playerID) @@ -3874,7 +3873,7 @@ void CGameHandler::removeAfterVisit(const CGObjectInstance *object) //If the object is being visited, there must be a matching query for (const auto &query : queries->allQueries()) { - if (auto someVistQuery = std::dynamic_pointer_cast(query)) + if (auto someVistQuery = std::dynamic_pointer_cast(query)) { if (someVistQuery->visitedObject == object) { @@ -3938,7 +3937,7 @@ const CGHeroInstance * CGameHandler::getVisitingHero(const CGObjectInstance *obj for(const auto & query : queries->allQueries()) { - auto visit = std::dynamic_pointer_cast(query); + auto visit = std::dynamic_pointer_cast(query); if (visit && visit->visitedObject == obj) return visit->visitingHero; } @@ -3951,7 +3950,7 @@ const CGObjectInstance * CGameHandler::getVisitingObject(const CGHeroInstance *h for(const auto & query : queries->allQueries()) { - auto visit = std::dynamic_pointer_cast(query); + auto visit = std::dynamic_pointer_cast(query); if (visit && visit->visitingHero == hero) return visit->visitedObject; } @@ -3968,7 +3967,7 @@ bool CGameHandler::isVisitCoveredByAnotherQuery(const CGObjectInstance *obj, con // visitation query is covered by other query that must be answered first if (auto topQuery = queries->topQuery(hero->getOwner())) - if (auto visit = std::dynamic_pointer_cast(topQuery)) + if (auto visit = std::dynamic_pointer_cast(topQuery)) return !(visit->visitedObject == obj && visit->visitingHero == hero); return true; diff --git a/server/CGameHandler.h b/server/CGameHandler.h index e2ceecd85..c741cd25b 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -232,7 +232,7 @@ public: bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h ); - void objectVisitEnded(const CObjectVisitQuery &query); + void objectVisitEnded(const CGHeroInstance *h, PlayerColor player); bool dig(const CGHeroInstance *h); void moveArmy(const CArmedInstance *src, const CArmedInstance *dst, bool allowMerging); diff --git a/server/queries/VisitQueries.cpp b/server/queries/VisitQueries.cpp index e61b73293..38beccbe6 100644 --- a/server/queries/VisitQueries.cpp +++ b/server/queries/VisitQueries.cpp @@ -10,34 +10,26 @@ #include "StdInc.h" #include "VisitQueries.h" -#include "QueriesProcessor.h" -#include "../CGameHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../CGameHandler.h" +#include "QueriesProcessor.h" -CObjectVisitQuery::CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero): - CQuery(owner), visitedObject(Obj), visitingHero(Hero), removeObjectAfterVisit(false) +VisitQuery::VisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero) + : CQuery(owner) + , visitedObject(Obj) + , visitingHero(Hero) { addPlayer(Hero->tempOwner); } -bool CObjectVisitQuery::blocksPack(const CPack *pack) const +bool VisitQuery::blocksPack(const CPack * pack) const { //During the visit itself ALL actions are blocked. //(However, the visit may trigger a query above that'll pass some.) return true; } -void CObjectVisitQuery::onRemoval(PlayerColor color) -{ - gh->objectVisitEnded(*this); - - //TODO or should it be destructor? - //Can object visit affect 2 players and what would be desired behavior? - if(removeObjectAfterVisit) - gh->removeObject(visitedObject, color); -} - -void CObjectVisitQuery::onExposure(QueryPtr topQuery) +void VisitQuery::onExposure(QueryPtr topQuery) { //Object may have been removed and deleted. if(gh->isValidObject(visitedObject)) @@ -45,3 +37,30 @@ void CObjectVisitQuery::onExposure(QueryPtr topQuery) owner->popIfTop(*this); } + +MapObjectVisitQuery::MapObjectVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero) + : VisitQuery(owner, Obj, Hero) + , removeObjectAfterVisit(false) +{ +} + +void MapObjectVisitQuery::onRemoval(PlayerColor color) +{ + gh->objectVisitEnded(visitingHero, players.front()); + + //TODO or should it be destructor? + //Can object visit affect 2 players and what would be desired behavior? + if(removeObjectAfterVisit) + gh->removeObject(visitedObject, color); +} + +TownBuildingVisitQuery::TownBuildingVisitQuery(CGameHandler * owner, const CGObjectInstance * Obj, const CGHeroInstance * Hero, BuildingID buildingToVisit) + : VisitQuery(owner, Obj, Hero) + , visitedBuilding(buildingToVisit) +{ +} + +void TownBuildingVisitQuery::onRemoval(PlayerColor color) +{ + +} diff --git a/server/queries/VisitQueries.h b/server/queries/VisitQueries.h index 733c99d25..0d7ce5cca 100644 --- a/server/queries/VisitQueries.h +++ b/server/queries/VisitQueries.h @@ -13,17 +13,35 @@ //Created when hero visits object. //Removed when query above is resolved (or immediately after visit if no queries were created) -class CObjectVisitQuery : public CQuery +class VisitQuery : public CQuery { +protected: + VisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero); + public: const CGObjectInstance *visitedObject; const CGHeroInstance *visitingHero; + bool blocksPack(const CPack *pack) const final; + void onExposure(QueryPtr topQuery) final; +}; + +class MapObjectVisitQuery final : public VisitQuery +{ +public: bool removeObjectAfterVisit; - CObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero); + MapObjectVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero); - bool blocksPack(const CPack *pack) const override; - void onRemoval(PlayerColor color) override; - void onExposure(QueryPtr topQuery) override; + void onRemoval(PlayerColor color) final; +}; + +class TownBuildingVisitQuery final : public VisitQuery +{ +public: + BuildingID visitedBuilding; + + TownBuildingVisitQuery(CGameHandler * owner, const CGObjectInstance *Obj, const CGHeroInstance *Hero, BuildingID buildingToVisit); + + void onRemoval(PlayerColor color) final; }; From b09d9d501b011902379fed9136e2e83e06ed1ce5 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Wed, 4 Sep 2024 15:14:56 +0000 Subject: [PATCH 5/5] Pass pointer to actual caller in showBlockingDialog callWith Fixes activation of all rewardable buildings in town on blocking dialog answer --- client/Client.h | 2 +- lib/IGameCallback.h | 3 ++- lib/mapObjects/CBank.cpp | 2 +- lib/mapObjects/CGCreature.cpp | 6 +++--- lib/mapObjects/CGDwelling.cpp | 4 ++-- lib/mapObjects/CGPandoraBox.cpp | 2 +- lib/mapObjects/CGTownInstance.cpp | 6 ------ lib/mapObjects/CGTownInstance.h | 1 - lib/mapObjects/CQuest.cpp | 2 +- lib/mapObjects/CRewardableObject.cpp | 2 +- lib/mapObjects/MiscObjects.cpp | 8 ++++---- lib/mapObjects/TownBuildingInstance.cpp | 2 +- server/CGameHandler.cpp | 4 ++-- server/CGameHandler.h | 2 +- server/queries/MapQueries.cpp | 7 ++++--- server/queries/MapQueries.h | 4 +++- test/mock/mock_IGameCallback.h | 2 +- 17 files changed, 28 insertions(+), 31 deletions(-) diff --git a/client/Client.h b/client/Client.h index 5c1d53ca8..9fe51b4ed 100644 --- a/client/Client.h +++ b/client/Client.h @@ -164,7 +164,7 @@ public: void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs = false) override {}; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs = false) override {}; - void showBlockingDialog(BlockingDialog * iw) override {}; + void showBlockingDialog(const IObjectInterface * caller, BlockingDialog * iw) override {}; void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {}; void showTeleportDialog(TeleportDialog * iw) override {}; void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) override {}; diff --git a/lib/IGameCallback.h b/lib/IGameCallback.h index ec8ffd488..81b845ff9 100644 --- a/lib/IGameCallback.h +++ b/lib/IGameCallback.h @@ -33,6 +33,7 @@ class CStackBasicDescriptor; class CGCreature; class CSaveFile; class CLoadFile; +class IObjectInterface; enum class EOpenWindowMode : uint8_t; namespace spells @@ -98,7 +99,7 @@ public: virtual void giveExperience(const CGHeroInstance * hero, TExpType val) =0; virtual void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false)=0; virtual void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false)=0; - virtual void showBlockingDialog(BlockingDialog *iw) =0; + virtual void showBlockingDialog(const IObjectInterface * caller, 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 showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) = 0; diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index 8177b56af..3333af6a5 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -178,7 +178,7 @@ void CBank::onHeroVisit(const CGHeroInstance * h) const if (banktext == 32) bd.text.replaceRawString(getObjectName()); - cb->showBlockingDialog(&bd); + cb->showBlockingDialog(this, &bd); } void CBank::doVisit(const CGHeroInstance * hero) const diff --git a/lib/mapObjects/CGCreature.cpp b/lib/mapObjects/CGCreature.cpp index 389ce862b..15b7a8221 100644 --- a/lib/mapObjects/CGCreature.cpp +++ b/lib/mapObjects/CGCreature.cpp @@ -164,7 +164,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const ynd.player = h->tempOwner; ynd.text.appendLocalString(EMetaText::ADVOB_TXT, 86); ynd.text.replaceName(getCreature(), getStackCount(SlotID(0))); - cb->showBlockingDialog(&ynd); + cb->showBlockingDialog(this, &ynd); break; } default: //join for gold @@ -180,7 +180,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const boost::algorithm::replace_first(tmp, "%d", std::to_string(action)); boost::algorithm::replace_first(tmp,"%s",VLC->creatures()->getById(getCreature())->getNamePluralTranslated()); ynd.text.appendRawString(tmp); - cb->showBlockingDialog(&ynd); + cb->showBlockingDialog(this, &ynd); break; } } @@ -475,7 +475,7 @@ void CGCreature::flee( const CGHeroInstance * h ) const ynd.player = h->tempOwner; ynd.text.appendLocalString(EMetaText::ADVOB_TXT,91); ynd.text.replaceName(getCreature(), getStackCount(SlotID(0))); - cb->showBlockingDialog(&ynd); + cb->showBlockingDialog(this, &ynd); } void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const diff --git a/lib/mapObjects/CGDwelling.cpp b/lib/mapObjects/CGDwelling.cpp index 4c8e473fb..72805de1b 100644 --- a/lib/mapObjects/CGDwelling.cpp +++ b/lib/mapObjects/CGDwelling.cpp @@ -244,7 +244,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const else bd.text.replaceLocalString(EMetaText::ARRAY_TXT, 173 + (int)Slots().begin()->second->getQuantityID()*3); bd.text.replaceName(*Slots().begin()->second); - cb->showBlockingDialog(&bd); + cb->showBlockingDialog(this, &bd); return; } @@ -280,7 +280,7 @@ void CGDwelling::onHeroVisit( const CGHeroInstance * h ) const bd.flags |= BlockingDialog::SAFE_TO_AUTOACCEPT; } - cb->showBlockingDialog(&bd); + cb->showBlockingDialog(this, &bd); } void CGDwelling::newTurn(vstd::RNG & rand) const diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 8e931cf90..eaf4d8473 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -175,7 +175,7 @@ void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const BlockingDialog bd (true, false); bd.player = h->getOwner(); bd.text.appendLocalString(EMetaText::ADVOB_TXT, 14); - cb->showBlockingDialog(&bd); + cb->showBlockingDialog(this, &bd); } void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const diff --git a/lib/mapObjects/CGTownInstance.cpp b/lib/mapObjects/CGTownInstance.cpp index 7903e5fcd..d7813fba2 100644 --- a/lib/mapObjects/CGTownInstance.cpp +++ b/lib/mapObjects/CGTownInstance.cpp @@ -301,12 +301,6 @@ void CGTownInstance::setOwner(const PlayerColor & player) const cb->setOwner(this, player); } -void CGTownInstance::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const -{ - for (auto building : rewardableBuildings) - building.second->blockingDialogAnswered(hero, answer); // FIXME: why call for every building? -} - void CGTownInstance::onHeroVisit(const CGHeroInstance * h) const { if(cb->gameState()->getPlayerRelations( getOwner(), h->getOwner() ) == PlayerRelations::ENEMIES) diff --git a/lib/mapObjects/CGTownInstance.h b/lib/mapObjects/CGTownInstance.h index f2cb73c5c..fab98714e 100644 --- a/lib/mapObjects/CGTownInstance.h +++ b/lib/mapObjects/CGTownInstance.h @@ -239,7 +239,6 @@ public: protected: void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; void serializeJsonOptions(JsonSerializeFormat & handler) override; - void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override; private: FactionID randomizeFaction(vstd::RNG & rand); diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index ca06e4d49..66b405055 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -851,7 +851,7 @@ void CGBorderGuard::onHeroVisit(const CGHeroInstance * h) const BlockingDialog bd (true, false); bd.player = h->getOwner(); bd.text.appendLocalString (EMetaText::ADVOB_TXT, 17); - cb->showBlockingDialog (&bd); + cb->showBlockingDialog (this, &bd); } else { diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 4fedb7f09..e0ebe2d8f 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -52,7 +52,7 @@ void CRewardableObject::selectRewardWithMessage(const CGHeroInstance * contextHe sd.player = contextHero->tempOwner; sd.text = dialog; sd.components = loadComponents(contextHero, rewardIndices); - cb->showBlockingDialog(&sd); + cb->showBlockingDialog(this, &sd); } diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index a9f18cb55..b83fa8fdc 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -88,7 +88,7 @@ void CGMine::onHeroVisit( const CGHeroInstance * h ) const BlockingDialog ynd(true,false); ynd.player = h->tempOwner; ynd.text.appendLocalString(EMetaText::ADVOB_TXT, isAbandoned() ? 84 : 187); - cb->showBlockingDialog(&ynd); + cb->showBlockingDialog(this, &ynd); return; } @@ -310,7 +310,7 @@ void CGResource::onHeroVisit( const CGHeroInstance * h ) const BlockingDialog ynd(true,false); ynd.player = h->getOwner(); ynd.text = message; - cb->showBlockingDialog(&ynd); + cb->showBlockingDialog(this, &ynd); } else { @@ -879,7 +879,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const ynd.text.replaceRawString(getArmyDescription()); ynd.text.replaceLocalString(EMetaText::GENERAL_TXT, 43); // creatures } - cb->showBlockingDialog(&ynd); + cb->showBlockingDialog(this, &ynd); } break; case Obj::SPELL_SCROLL: @@ -889,7 +889,7 @@ void CGArtifact::onHeroVisit(const CGHeroInstance * h) const BlockingDialog ynd(true,false); ynd.player = h->getOwner(); ynd.text = message; - cb->showBlockingDialog(&ynd); + cb->showBlockingDialog(this, &ynd); } else blockingDialogAnswered(h, true); diff --git a/lib/mapObjects/TownBuildingInstance.cpp b/lib/mapObjects/TownBuildingInstance.cpp index 3bded0bce..dd8b59b6d 100644 --- a/lib/mapObjects/TownBuildingInstance.cpp +++ b/lib/mapObjects/TownBuildingInstance.cpp @@ -221,7 +221,7 @@ void TownRewardableBuildingInstance::onHeroVisit(const CGHeroInstance *h) const if (rewards.size() == 1) configuration.info.at(rewards.front()).reward.loadComponents(sd.components, h); - cb->showBlockingDialog(&sd); + cb->showBlockingDialog(this, &sd); }; if(!town->hasBuilt(getBuildingType())) diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 7b0556a08..a818a4646 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -1078,9 +1078,9 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owne } } -void CGameHandler::showBlockingDialog(BlockingDialog *iw) +void CGameHandler::showBlockingDialog(const IObjectInterface * caller, BlockingDialog *iw) { - auto dialogQuery = std::make_shared(this, *iw); + auto dialogQuery = std::make_shared(this, caller, *iw); queries->addQuery(dialogQuery); iw->queryID = dialogQuery->queryID; sendToAllClients(iw); diff --git a/server/CGameHandler.h b/server/CGameHandler.h index c741cd25b..995b4be99 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -113,7 +113,7 @@ public: void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override; void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override; - void showBlockingDialog(BlockingDialog *iw) override; + void showBlockingDialog(const IObjectInterface * caller, BlockingDialog *iw) override; void showTeleportDialog(TeleportDialog *iw) override; void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override; void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) override; diff --git a/server/queries/MapQueries.cpp b/server/queries/MapQueries.cpp index 858154215..2881368d4 100644 --- a/server/queries/MapQueries.cpp +++ b/server/queries/MapQueries.cpp @@ -120,11 +120,12 @@ bool CGarrisonDialogQuery::blocksPack(const CPack * pack) const void CBlockingDialogQuery::notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const { assert(answer); - visitedObject->blockingDialogAnswered(visitingHero, *answer); + caller->blockingDialogAnswered(visitingHero, *answer); } -CBlockingDialogQuery::CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog & bd): - CDialogQuery(owner) +CBlockingDialogQuery::CBlockingDialogQuery(CGameHandler * owner, const IObjectInterface * caller, const BlockingDialog & bd): + CDialogQuery(owner), + caller(caller) { this->bd = bd; addPlayer(bd.player); diff --git a/server/queries/MapQueries.h b/server/queries/MapQueries.h index cfd152969..41b06d3b7 100644 --- a/server/queries/MapQueries.h +++ b/server/queries/MapQueries.h @@ -15,6 +15,7 @@ VCMI_LIB_NAMESPACE_BEGIN class CGHeroInstance; class CGObjectInstance; +class IObjectInterface; VCMI_LIB_NAMESPACE_END //Created when player starts turn or when player puts game on [ause @@ -60,9 +61,10 @@ public: class CBlockingDialogQuery : public CDialogQuery { public: + const IObjectInterface * caller; BlockingDialog bd; //copy of pack... debug purposes - CBlockingDialogQuery(CGameHandler * owner, const BlockingDialog &bd); + CBlockingDialogQuery(CGameHandler * owner, const IObjectInterface * caller, const BlockingDialog &bd); void notifyObjectAboutRemoval(const CGObjectInstance * visitedObject, const CGHeroInstance * visitingHero) const override; }; diff --git a/test/mock/mock_IGameCallback.h b/test/mock/mock_IGameCallback.h index 61cccfd06..3f3765068 100644 --- a/test/mock/mock_IGameCallback.h +++ b/test/mock/mock_IGameCallback.h @@ -50,7 +50,7 @@ public: void giveExperience(const CGHeroInstance * hero, TExpType val) override {} void changePrimSkill(const CGHeroInstance * hero, PrimarySkill which, si64 val, bool abs=false) override {} void changeSecSkill(const CGHeroInstance * hero, SecondarySkill which, int val, bool abs=false) override {} - void showBlockingDialog(BlockingDialog *iw) override {} + void showBlockingDialog(const IObjectInterface * caller, BlockingDialog *iw) override {} void showGarrisonDialog(ObjectInstanceID upobj, ObjectInstanceID hid, bool removableUnits) override {} //cb will be called when player closes garrison window void showTeleportDialog(TeleportDialog *iw) override {} void showObjectWindow(const CGObjectInstance * object, EOpenWindowMode window, const CGHeroInstance * visitor, bool addQuery) override {};