From 4b77140f4a2f870040af0c111d01beeeda14174d Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Tue, 6 May 2025 18:53:43 +0300 Subject: [PATCH] Implemented playerGlobal visit mode for objects like cartographer --- config/objects/cartographer.json | 8 ++++---- lib/mapObjects/CQuest.cpp | 2 +- lib/mapObjects/CRewardableObject.cpp | 10 +++++++--- lib/mapObjects/TownBuildingInstance.cpp | 2 ++ lib/networkPacks/NetPacksLib.cpp | 16 +++++----------- lib/networkPacks/PacksForClient.h | 1 - lib/rewardable/Configuration.h | 12 ++++++------ lib/rewardable/Info.cpp | 4 ++-- lib/rewardable/Info.h | 2 +- lib/rewardable/Limiter.cpp | 2 ++ mapeditor/inspector/rewardswidget.cpp | 4 ++-- 11 files changed, 32 insertions(+), 31 deletions(-) diff --git a/config/objects/cartographer.json b/config/objects/cartographer.json index 4df883c5c..a150ca641 100644 --- a/config/objects/cartographer.json +++ b/config/objects/cartographer.json @@ -18,7 +18,7 @@ "rarity" : 20 }, "compatibilityIdentifiers" : [ "water" ], - "visitMode" : "unlimited", + "visitMode" : "playerGlobal", "canRefuse" : true, "rewards" : [ { @@ -44,7 +44,7 @@ "rarity" : 2 }, "compatibilityIdentifiers" : [ "land" ], - "visitMode" : "unlimited", + "visitMode" : "playerGlobal", "canRefuse" : true, "rewards" : [ { @@ -72,7 +72,7 @@ "rarity" : 20 }, "compatibilityIdentifiers" : [ "subterra" ], - "visitMode" : "unlimited", + "visitMode" : "playerGlobal", "canRefuse" : true, "rewards" : [ { @@ -94,4 +94,4 @@ } } } -} \ No newline at end of file +} diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index b3aeea0d6..e5039dae2 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -815,7 +815,7 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const if (!wasMyColorVisited (h->getOwner()) ) { ChangeObjectVisitors cow; - cow.mode = ChangeObjectVisitors::VISITOR_GLOBAL; + cow.mode = ChangeObjectVisitors::VISITOR_ADD_PLAYER; cow.hero = h->id; cow.object = id; cb->sendAndApply(cow); diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index b38d59c6a..9057ad4dc 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -137,7 +137,9 @@ bool CRewardableObject::wasVisitedBefore(const CGHeroInstance * contextHero) con case Rewardable::VISIT_ONCE: return onceVisitableObjectCleared; case Rewardable::VISIT_PLAYER: - return vstd::contains(cb->getPlayerState(contextHero->getOwner())->visitedObjects, ObjectInstanceID(id)); + return cb->getPlayerState(contextHero->getOwner())->visitedObjects.count(ObjectInstanceID(id)) != 0; + case Rewardable::VISIT_PLAYER_GLOBAL: + return cb->getPlayerState(contextHero->getOwner())->visitedObjectsGlobal.count({ID, subID}) != 0; case Rewardable::VISIT_BONUS: return contextHero->hasBonusFrom(BonusSource::OBJECT_TYPE, BonusSourceID(ID)); case Rewardable::VISIT_HERO: @@ -160,7 +162,9 @@ bool CRewardableObject::wasVisited(PlayerColor player) const return false; case Rewardable::VISIT_ONCE: case Rewardable::VISIT_PLAYER: - return vstd::contains(cb->getPlayerState(player)->visitedObjects, ObjectInstanceID(id)); + return cb->getPlayerState(player)->visitedObjects.count(ObjectInstanceID(id)) != 0; + case Rewardable::VISIT_PLAYER_GLOBAL: + return cb->getPlayerState(player)->visitedObjectsGlobal.count({ID, subID}) != 0; default: return false; } @@ -205,7 +209,7 @@ std::string CRewardableObject::getDisplayTextImpl(PlayerColor player, const CGHe } else { - if(configuration.visitMode == Rewardable::VISIT_PLAYER || configuration.visitMode == Rewardable::VISIT_ONCE) + if(configuration.visitMode == Rewardable::VISIT_PLAYER || configuration.visitMode == Rewardable::VISIT_ONCE || configuration.visitMode == Rewardable::VISIT_PLAYER_GLOBAL) { if (wasVisited(player)) result += "\n" + configuration.visitedTooltip.toString(); diff --git a/lib/mapObjects/TownBuildingInstance.cpp b/lib/mapObjects/TownBuildingInstance.cpp index ba5865b59..22674dc2f 100644 --- a/lib/mapObjects/TownBuildingInstance.cpp +++ b/lib/mapObjects/TownBuildingInstance.cpp @@ -172,6 +172,7 @@ bool TownRewardableBuildingInstance::wasVisitedBefore(const CGHeroInstance * con case Rewardable::VISIT_ONCE: return !visitors.empty(); case Rewardable::VISIT_PLAYER: + case Rewardable::VISIT_PLAYER_GLOBAL: return false; //not supported case Rewardable::VISIT_BONUS: { @@ -211,6 +212,7 @@ bool TownRewardableBuildingInstance::wasVisited(PlayerColor player) const case Rewardable::VISIT_BONUS: case Rewardable::VISIT_HERO: case Rewardable::VISIT_LIMITER: + case Rewardable::VISIT_PLAYER_GLOBAL: return false; case Rewardable::VISIT_ONCE: case Rewardable::VISIT_PLAYER: diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index f4f265973..9cf7621fe 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1045,16 +1045,16 @@ void ChangeObjPos::applyGs(CGameState *gs) void ChangeObjectVisitors::applyGs(CGameState *gs) { + auto objectPtr = gs->getObjInstance(object); + switch (mode) { 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; + [[fallthrough]]; 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); + gs->getPlayerState(gs->getHero(hero)->tempOwner)->visitedObjects.insert(object); + gs->getPlayerState(gs->getHero(hero)->tempOwner)->visitedObjectsGlobal.insert({objectPtr->ID, objectPtr->subID}); break; case VISITOR_CLEAR: @@ -1076,12 +1076,6 @@ void ChangeObjectVisitors::applyGs(CGameState *gs) gs->getPlayerTeam(gs->getHero(hero)->tempOwner)->scoutedObjects.insert(object); break; - case VISITOR_GLOBAL: - { - CGObjectInstance * objectPtr = gs->getObjInstance(object); - gs->getPlayerState(gs->getHero(hero)->tempOwner)->visitedObjectsGlobal.insert({objectPtr->ID, objectPtr->subID}); - break; - } } } diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 57a4bcbc2..9b3a2f992 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -1215,7 +1215,6 @@ struct DLL_LINKAGE ChangeObjectVisitors : public CPackForClient { 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) }; diff --git a/lib/rewardable/Configuration.h b/lib/rewardable/Configuration.h index caedf9132..94424b30c 100644 --- a/lib/rewardable/Configuration.h +++ b/lib/rewardable/Configuration.h @@ -22,7 +22,7 @@ VCMI_LIB_NAMESPACE_BEGIN namespace Rewardable { -enum EVisitMode +enum EVisitMode : uint8_t { VISIT_UNLIMITED, // any number of times. Side effect - object hover text won't contain visited/not visited text VISIT_ONCE, // only once, first to visit get all the rewards @@ -34,7 +34,7 @@ enum EVisitMode }; /// controls selection of reward granted to player -enum ESelectMode +enum ESelectMode : uint8_t { SELECT_FIRST, // first reward that matches limiters SELECT_PLAYER, // player can select from all allowed rewards @@ -42,7 +42,7 @@ enum ESelectMode SELECT_ALL // grant all rewards that match limiters }; -enum class EEventType +enum class EEventType : uint8_t { EVENT_INVALID = 0, EVENT_FIRST_VISIT, @@ -52,7 +52,7 @@ enum class EEventType }; constexpr std::array SelectModeString{"selectFirst", "selectPlayer", "selectRandom", "selectAll"}; -constexpr std::array VisitModeString{"unlimited", "once", "hero", "bonus", "limiter", "player"}; +constexpr std::array VisitModeString{"unlimited", "once", "hero", "bonus", "limiter", "player", "playerGlobal" }; struct DLL_LINKAGE ResetInfo { @@ -150,10 +150,10 @@ struct DLL_LINKAGE Configuration std::vector info; /// how reward will be selected, uses ESelectMode enum - ui8 selectMode = Rewardable::SELECT_FIRST; + ESelectMode selectMode = Rewardable::SELECT_FIRST; /// controls who can visit an object, uses EVisitMode enum - ui8 visitMode = Rewardable::VISIT_UNLIMITED; + EVisitMode visitMode = Rewardable::VISIT_UNLIMITED; /// how and when should the object be reset Rewardable::ResetInfo resetParameters; diff --git a/lib/rewardable/Info.cpp b/lib/rewardable/Info.cpp index e2eb3438f..9fe0c8409 100644 --- a/lib/rewardable/Info.cpp +++ b/lib/rewardable/Info.cpp @@ -471,7 +471,7 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd: { if(Rewardable::VisitModeString[i] == visitMode) { - object.visitMode = i; + object.visitMode = static_cast(i); break; } } @@ -481,7 +481,7 @@ void Rewardable::Info::configureObject(Rewardable::Configuration & object, vstd: { if(Rewardable::SelectModeString[i] == selectMode) { - object.selectMode = i; + object.selectMode = static_cast(i); break; } } diff --git a/lib/rewardable/Info.h b/lib/rewardable/Info.h index ac89bdc8d..12ad5e6d0 100644 --- a/lib/rewardable/Info.h +++ b/lib/rewardable/Info.h @@ -32,7 +32,7 @@ struct Configuration; struct Variables; struct VisitInfo; struct ResetInfo; -enum class EEventType; +enum class EEventType : uint8_t; class DLL_LINKAGE Info : public IObjectInfo { diff --git a/lib/rewardable/Limiter.cpp b/lib/rewardable/Limiter.cpp index 1b72bfa26..e12a1d2c0 100644 --- a/lib/rewardable/Limiter.cpp +++ b/lib/rewardable/Limiter.cpp @@ -30,6 +30,8 @@ Rewardable::Limiter::Limiter() , manaPercentage(0) , manaPoints(0) , canLearnSkills(false) + , commanderAlive(false) + , hasExtraCreatures(false) , primary(GameConstants::PRIMARY_SKILLS, 0) { } diff --git a/mapeditor/inspector/rewardswidget.cpp b/mapeditor/inspector/rewardswidget.cpp index 00d279019..3ad14d6dd 100644 --- a/mapeditor/inspector/rewardswidget.cpp +++ b/mapeditor/inspector/rewardswidget.cpp @@ -249,8 +249,8 @@ void RewardsWidget::obtainData() bool RewardsWidget::commitChanges() { //common parameters - object.configuration.visitMode = ui->visitMode->currentIndex(); - object.configuration.selectMode = ui->selectMode->currentIndex(); + object.configuration.visitMode = static_cast(ui->visitMode->currentIndex()); + object.configuration.selectMode = static_cast(ui->selectMode->currentIndex()); object.configuration.infoWindowType = EInfoWindowMode(ui->windowMode->currentIndex()); if(ui->onSelectText->text().isEmpty()) object.configuration.onSelect.clear();