From cb5df096c122c1d28d22f05daf110a57b0ad987e Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 30 Aug 2024 15:24:13 +0000 Subject: [PATCH] Added per-team tracking of scouted state of an object --- lib/CPlayerState.h | 5 +++ lib/mapObjects/CBank.cpp | 2 +- lib/mapObjects/CRewardableObject.cpp | 51 +++++++++----------------- lib/networkPacks/NetPacksLib.cpp | 35 ++++++++---------- lib/networkPacks/PacksForClient.h | 10 ++--- lib/serializer/ESerializationVersion.h | 3 +- 6 files changed, 47 insertions(+), 59 deletions(-) diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index f62b27bef..3da93f139 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -153,6 +153,8 @@ public: //TODO: boost::array, bool if possible boost::multi_array fogOfWarMap; //[z][x][y] true - visible, false - hidden + std::set scoutedObjects; + TeamState(); template void serialize(Handler &h) @@ -173,6 +175,9 @@ public: h & fogOfWarMap; h & static_cast(*this); + + if (h.version >= Handler::Version::TEAM_STATE_SCOUTED_OBJECTS) + h & scoutedObjects; } }; diff --git a/lib/mapObjects/CBank.cpp b/lib/mapObjects/CBank.cpp index beed5a4ae..8ec1baa9c 100644 --- a/lib/mapObjects/CBank.cpp +++ b/lib/mapObjects/CBank.cpp @@ -137,7 +137,7 @@ bool CBank::wasVisited (PlayerColor player) const void CBank::onHeroVisit(const CGHeroInstance * h) const { - ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, h->id); + ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id); cb->sendAndApply(&cov); if(!bankConfig && (ID.toEnum() == Obj::CREATURE_BANK || ID.toEnum() == Obj::DRAGON_UTOPIA)) diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 5f5203740..674edf70b 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -108,24 +108,30 @@ bool CRewardableObject::guardedPresently() const return stacksCount() > 0; } -void CRewardableObject::onHeroVisit(const CGHeroInstance *h) const +void CRewardableObject::onHeroVisit(const CGHeroInstance *hero) const { + if(!wasScouted(hero->getOwner())) + { + ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_SCOUTED, id, hero->id); + cb->sendAndApply(&cov); + } + if (guardedPresently()) { - auto guardedIndexes = getAvailableRewards(h, Rewardable::EEventType::EVENT_GUARDED); + auto guardedIndexes = getAvailableRewards(hero, Rewardable::EEventType::EVENT_GUARDED); auto guardedReward = configuration.info.at(guardedIndexes.at(0)); // ask player to confirm attack BlockingDialog bd(true, false); - bd.player = h->getOwner(); + bd.player = hero->getOwner(); bd.text = guardedReward.message; - bd.components = getPopupComponents(h->getOwner()); + bd.components = getPopupComponents(hero->getOwner()); cb->showBlockingDialog(&bd); } else { - doHeroVisit(h); + doHeroVisit(hero); } } @@ -192,7 +198,7 @@ void CRewardableObject::doHeroVisit(const CGHeroInstance *h) const if(!objectRemovalPossible && getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT).empty()) { - ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, h->id); + ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id); cb->sendAndApply(&cov); } } @@ -202,7 +208,7 @@ void CRewardableObject::doHeroVisit(const CGHeroInstance *h) const if (!wasVisited(h->getOwner())) { - ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, h->id); + ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_PLAYER, id, h->id); cb->sendAndApply(&cov); } @@ -236,27 +242,6 @@ void CRewardableObject::blockingDialogAnswered(const CGHeroInstance * hero, int3 } else { - if(answer == 0) - { - switch(configuration.visitMode) - { - case Rewardable::VISIT_UNLIMITED: - case Rewardable::VISIT_BONUS: - case Rewardable::VISIT_HERO: - case Rewardable::VISIT_LIMITER: - { - // workaround for object with refusable reward not getting marked as visited - // TODO: better solution that would also work for player-visitable objects - if(!wasScouted(hero->getOwner())) - { - ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_TEAM, id, hero->id); - cb->sendAndApply(&cov); - } - } - } - return; // player refused - } - if(answer > 0 && answer - 1 < configuration.info.size()) { auto list = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT); @@ -274,7 +259,7 @@ void CRewardableObject::markAsVisited(const CGHeroInstance * hero) const { cb->setObjPropertyValue(id, ObjProperty::REWARD_CLEARED, true); - ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD, id, hero->id); + ChangeObjectVisitors cov(ChangeObjectVisitors::VISITOR_ADD_HERO, id, hero->id); cb->sendAndApply(&cov); } @@ -330,7 +315,7 @@ bool CRewardableObject::wasVisited(PlayerColor player) const bool CRewardableObject::wasScouted(PlayerColor player) const { - return vstd::contains(cb->getPlayerState(player)->visitedObjects, ObjectInstanceID(id)); + return vstd::contains(cb->getPlayerTeam(player)->scoutedObjects, ObjectInstanceID(id)); } bool CRewardableObject::wasVisited(const CGHeroInstance * h) const @@ -418,9 +403,6 @@ std::vector CRewardableObject::getPopupComponentsImpl(PlayerColor pla if (!wasScouted(player)) return {}; - if (!configuration.showScoutedPreview) - return {}; - if (guardedPresently()) { if (!VLC->settings()->getBoolean(EGameSettings::BANKS_SHOW_GUARDS_COMPOSITION)) @@ -442,6 +424,9 @@ std::vector CRewardableObject::getPopupComponentsImpl(PlayerColor pla } else { + if (!configuration.showScoutedPreview) + return {}; + auto rewardIndices = getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT); if (rewardIndices.empty() && !configuration.info.empty()) { diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 6e2be8e19..bccbbef61 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1035,32 +1035,32 @@ void ChangeObjPos::applyGs(CGameState *gs) void ChangeObjectVisitors::applyGs(CGameState *gs) { switch (mode) { - case VISITOR_ADD: + case VISITOR_ADD_HERO: + gs->getPlayerTeam(gs->getHero(hero)->tempOwner)->scoutedObjects.insert(object); gs->getHero(hero)->visitedObjects.insert(object); gs->getPlayerState(gs->getHero(hero)->tempOwner)->visitedObjects.insert(object); break; - case VISITOR_ADD_TEAM: - { - TeamState *ts = gs->getPlayerTeam(gs->getHero(hero)->tempOwner); - for(const auto & color : ts->players) - { - gs->getPlayerState(color)->visitedObjects.insert(object); - } - } + case VISITOR_ADD_PLAYER: + gs->getPlayerTeam(gs->getHero(hero)->tempOwner)->scoutedObjects.insert(object); + for(const auto & color : gs->getPlayerTeam(gs->getHero(hero)->tempOwner)->players) + gs->getPlayerState(color)->visitedObjects.insert(object); + break; case VISITOR_CLEAR: + // remove visit info from all heroes, including those that are not present on map for (CGHeroInstance * hero : gs->map->allHeroes) - { if (hero) - { - hero->visitedObjects.erase(object); // remove visit info from all heroes, including those that are not present on map - } - } + hero->visitedObjects.erase(object); for(auto &elem : gs->players) - { elem.second.visitedObjects.erase(object); - } + + for(auto &elem : gs->teams) + elem.second.scoutedObjects.erase(object); + + break; + case VISITOR_SCOUTED: + gs->getPlayerTeam(gs->getHero(hero)->tempOwner)->scoutedObjects.insert(object); break; case VISITOR_GLOBAL: @@ -1069,9 +1069,6 @@ void ChangeObjectVisitors::applyGs(CGameState *gs) gs->getPlayerState(gs->getHero(hero)->tempOwner)->visitedObjectsGlobal.insert({objectPtr->ID, objectPtr->subID}); break; } - case VISITOR_REMOVE: - gs->getHero(hero)->visitedObjects.erase(object); - break; } } diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 145bdd19e..5a81ec806 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -1216,11 +1216,11 @@ struct DLL_LINKAGE ChangeObjectVisitors : public CPackForClient { enum VisitMode { - VISITOR_ADD, // mark hero as one that have visited this object - VISITOR_ADD_TEAM, // mark team as one that have visited this object - VISITOR_GLOBAL, // mark player as one that have visited object of this type - VISITOR_REMOVE, // unmark visitor, reversed to ADD - VISITOR_CLEAR // clear all visitors from this object (object reset) + VISITOR_ADD_HERO, // mark hero as one that have visited this object + VISITOR_ADD_PLAYER, // mark player as one that have visited this object instance + VISITOR_GLOBAL, // mark player as one that have visited object of this type + VISITOR_SCOUTED, // marks targeted team as having scouted this object + VISITOR_CLEAR, // clear all visitors from this object (object reset) }; VisitMode mode = VISITOR_CLEAR; // uses VisitMode enum ObjectInstanceID object; diff --git a/lib/serializer/ESerializationVersion.h b/lib/serializer/ESerializationVersion.h index a2f9ebe97..3a835ef1e 100644 --- a/lib/serializer/ESerializationVersion.h +++ b/lib/serializer/ESerializationVersion.h @@ -59,6 +59,7 @@ enum class ESerializationVersion : int32_t CHRONICLES_SUPPORT, // 860 - support for heroes chronicles PER_MAP_GAME_SETTINGS, // 861 - game settings are now stored per-map CAMPAIGN_OUTRO_SUPPORT, // 862 - support for campaign outro video + REWARDABLE_BANKS, // 863 - team state contains list of scouted objects, coast visitable rewardable objects - CURRENT = CAMPAIGN_OUTRO_SUPPORT + CURRENT = REWARDABLE_BANKS };