From c872f8418f0fb05cf8059b72d637dc2992a6dea4 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 17 Nov 2023 21:18:34 +0200 Subject: [PATCH] Implemented serialization of MapObjectSubID, refactoring of related code --- lib/CPlayerState.h | 22 ++++++++++++++++++++++ lib/constants/EntityIdentifiers.cpp | 29 +++++++++++++++++++++++++++++ lib/constants/EntityIdentifiers.h | 26 +++++++++++++++++++------- lib/gameState/CGameState.cpp | 5 ----- lib/mapObjects/CGObjectInstance.h | 2 +- lib/mapObjects/CQuest.cpp | 26 +++++++------------------- lib/mapObjects/CQuest.h | 7 ------- lib/mapObjects/MiscObjects.cpp | 24 +++++++++++------------- lib/mapObjects/MiscObjects.h | 4 ---- lib/mapObjects/ObjectTemplate.h | 2 +- lib/mapping/CMap.cpp | 2 -- lib/mapping/CMap.h | 2 -- lib/networkPacks/NetPacksLib.cpp | 10 ++++++++-- lib/networkPacks/ObjProperty.h | 1 - lib/networkPacks/PacksForClient.h | 9 +++++---- 15 files changed, 103 insertions(+), 68 deletions(-) diff --git a/lib/CPlayerState.h b/lib/CPlayerState.h index 7f31ff8ee..175d6ff21 100644 --- a/lib/CPlayerState.h +++ b/lib/CPlayerState.h @@ -27,12 +27,33 @@ struct QuestInfo; struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player { + struct VisitedObjectGlobal + { + MapObjectID id; + MapObjectSubID subID; + + bool operator < (const VisitedObjectGlobal & other) const + { + if (id != other.id) + return id < other.id; + else + return subID < other.subID; + } + + template void serialize(Handler &h, const int version) + { + h & id; + subID.serializeIdentifier(h, id, version); + } + }; + public: PlayerColor color; bool human; //true if human controlled player, false for AI TeamID team; TResources resources; std::set visitedObjects; // as a std::set, since most accesses here will be from visited status checks + std::set visitedObjectsGlobal; std::vector > heroes; std::vector > towns; std::vector > dwellings; //used for town growth @@ -82,6 +103,7 @@ public: h & dwellings; h & quests; h & visitedObjects; + h & visitedObjectsGlobal; h & status; h & daysWithoutCastle; h & cheated; diff --git a/lib/constants/EntityIdentifiers.cpp b/lib/constants/EntityIdentifiers.cpp index acc344022..224bd986a 100644 --- a/lib/constants/EntityIdentifiers.cpp +++ b/lib/constants/EntityIdentifiers.cpp @@ -177,6 +177,35 @@ si32 MapObjectID::decode(const std::string & identifier) return rawId.value(); } +std::string MapObjectSubID::encode(MapObjectID primaryID, int32_t index) +{ + if (index == -1) + return ""; + + if(primaryID == Obj::PRISON || primaryID == Obj::HERO) + return HeroTypeID::encode(index); + + if (primaryID == Obj::SPELL_SCROLL) + return SpellID::encode(index); + + return VLC->objtypeh->getHandlerFor(primaryID, index)->getJsonKey(); +} + +si32 MapObjectSubID::decode(MapObjectID primaryID, const std::string & identifier) +{ + if (identifier.empty()) + return -1; + + if(primaryID == Obj::PRISON || primaryID == Obj::HERO) + return HeroTypeID::decode(identifier); + + if (primaryID == Obj::SPELL_SCROLL) + return SpellID::decode(identifier); + + auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), VLC->objtypeh->getJsonKey(primaryID), identifier); + return rawId.value(); +} + std::string BoatId::encode(int32_t index) { if (index == -1) diff --git a/lib/constants/EntityIdentifiers.h b/lib/constants/EntityIdentifiers.h index f9c7466c8..b678e7fdc 100644 --- a/lib/constants/EntityIdentifiers.h +++ b/lib/constants/EntityIdentifiers.h @@ -490,16 +490,14 @@ public: } }; -class MapObjectSubID : public StaticIdentifier +class DLL_LINKAGE MapObjectSubID : public Identifier { public: - MapObjectID primaryIdentifier; - constexpr MapObjectSubID(const IdentifierBase & value): - StaticIdentifier(value.getNum()) + Identifier(value.getNum()) {} constexpr MapObjectSubID(int32_t value = -1): - StaticIdentifier(value) + Identifier(value) {} MapObjectSubID & operator =(int32_t value) @@ -514,14 +512,28 @@ public: return *this; } - static si32 decode(const std::string & identifier); - static std::string encode(const si32 index); + static si32 decode(MapObjectID primaryID, const std::string & identifier); + static std::string encode(MapObjectID primaryID, si32 index); // TODO: Remove constexpr operator int32_t () const { return num; } + + template + void serializeIdentifier(Handler &h, const MapObjectID & primaryID, const int version) + { + std::string secondaryStringID; + + if (h.saving) + secondaryStringID = encode(primaryID, num); + + h & secondaryStringID; + + if (!h.saving) + num = decode(primaryID, secondaryStringID); + } }; class DLL_LINKAGE RoadId : public EntityIdentifier diff --git a/lib/gameState/CGameState.cpp b/lib/gameState/CGameState.cpp index 75158bdab..7c251a286 100644 --- a/lib/gameState/CGameState.cpp +++ b/lib/gameState/CGameState.cpp @@ -223,11 +223,6 @@ void CGameState::init(const IMapService * mapService, StartInfo * si, Load::Prog initVisitingAndGarrisonedHeroes(); initFogOfWar(); - // Explicitly initialize static variables - for(auto & elem : players) - { - CGKeys::playerKeyMap[elem.first] = std::set(); - } for(auto & elem : teams) { CGObelisk::visited[elem.first] = 0; diff --git a/lib/mapObjects/CGObjectInstance.h b/lib/mapObjects/CGObjectInstance.h index cea2a4dad..f85a9a550 100644 --- a/lib/mapObjects/CGObjectInstance.h +++ b/lib/mapObjects/CGObjectInstance.h @@ -140,7 +140,7 @@ public: h & subTypeName; h & pos; h & ID; - h & subID; + subID.serializeIdentifier(h, ID, version); h & id; h & tempOwner; h & blockVisit; diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index f2f9c23b0..944efa994 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -23,6 +23,7 @@ #include "../serializer/JsonSerializeFormat.h" #include "../GameConstants.h" #include "../constants/StringConstants.h" +#include "../CPlayerState.h" #include "../CSkillHandler.h" #include "../mapping/CMap.h" #include "../mapObjects/CGHeroInstance.h" @@ -34,8 +35,6 @@ VCMI_LIB_NAMESPACE_BEGIN -std::map > CGKeys::playerKeyMap; - //TODO: Remove constructor CQuest::CQuest(): qid(-1), @@ -777,24 +776,9 @@ void CGQuestGuard::serializeJsonOptions(JsonSerializeFormat & handler) quest->serializeJson(handler, "quest"); } -void CGKeys::reset() -{ - playerKeyMap.clear(); -} - -void CGKeys::setPropertyDer (ObjProperty what, ObjPropertyID identifier) -{ - if (what == ObjProperty::KEYMASTER_VISITED) - { - playerKeyMap[identifier.as()].insert(subID); - } - else - logGlobal->error("Unexpected properties requested to set: what=%d, val=%d", static_cast(what), identifier.getNum()); -} - bool CGKeys::wasMyColorVisited(const PlayerColor & player) const { - return playerKeyMap.count(player) && vstd::contains(playerKeyMap[player], subID); + return cb->getPlayerState(player)->visitedObjectsGlobal.count({ID, subID}) != 0; } std::string CGKeys::getHoverText(PlayerColor player) const @@ -817,7 +801,11 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const int txt_id; if (!wasMyColorVisited (h->getOwner()) ) { - cb->setObjPropertyID(id, ObjProperty::KEYMASTER_VISITED, h->tempOwner); + ChangeObjectVisitors cow; + cow.mode = ChangeObjectVisitors::VISITOR_GLOBAL; + cow.hero = h->id; + cow.object = id; + cb->sendAndApply(&cow); txt_id=19; } else diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index bfeaecd97..644059aa6 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -168,11 +168,6 @@ protected: class DLL_LINKAGE CGKeys : public CGObjectInstance //Base class for Keymaster and guards { public: - static std::map > playerKeyMap; //[players][keysowned] - //SubID 0 - lightblue, 1 - green, 2 - red, 3 - darkblue, 4 - brown, 5 - purple, 6 - white, 7 - black - - static void reset(); - bool wasMyColorVisited(const PlayerColor & player) const; std::string getObjectName() const override; //depending on color @@ -182,8 +177,6 @@ public: { h & static_cast(*this); } -protected: - void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override; }; class DLL_LINKAGE CGKeymasterTent : public CGKeys diff --git a/lib/mapObjects/MiscObjects.cpp b/lib/mapObjects/MiscObjects.cpp index ca35c3fdd..c3369810b 100644 --- a/lib/mapObjects/MiscObjects.cpp +++ b/lib/mapObjects/MiscObjects.cpp @@ -33,7 +33,6 @@ VCMI_LIB_NAMESPACE_BEGIN -std::map > CGMagi::eyelist; ui8 CGObelisk::obeliskCount = 0; //how many obelisks are on map std::map CGObelisk::visited; //map: team_id => how many obelisks has been visited @@ -1003,26 +1002,27 @@ void CGGarrison::serializeJsonOptions(JsonSerializeFormat& handler) CArmedInstance::serializeJsonOptions(handler); } -void CGMagi::reset() -{ - eyelist.clear(); -} - void CGMagi::initObj(CRandomGenerator & rand) { if (ID == Obj::EYE_OF_MAGI) - { blockVisit = true; - eyelist[subID].push_back(id); - } } + void CGMagi::onHeroVisit(const CGHeroInstance * h) const { if (ID == Obj::HUT_OF_MAGI) { h->showInfoDialog(61); - if (!eyelist[subID].empty()) + std::vector eyes; + + for (auto object : cb->gameState()->map->objects) + { + if (object && object->ID == Obj::EYE_OF_MAGI && object->subID == this->subID) + eyes.push_back(object); + } + + if (!eyes.empty()) { CenterView cv; cv.player = h->tempOwner; @@ -1033,10 +1033,8 @@ void CGMagi::onHeroVisit(const CGHeroInstance * h) const fw.mode = ETileVisibility::REVEALED; fw.waitForDialogs = true; - for(const auto & it : eyelist[subID]) + for(const auto & eye : eyes) { - const CGObjectInstance *eye = cb->getObj(it); - cb->getTilesInRange (fw.tiles, eye->pos, 10, ETileVisibility::HIDDEN, h->tempOwner); cb->sendAndApply(&fw); cv.pos = eye->pos; diff --git a/lib/mapObjects/MiscObjects.h b/lib/mapObjects/MiscObjects.h index d761b2a89..b3fbe421b 100644 --- a/lib/mapObjects/MiscObjects.h +++ b/lib/mapObjects/MiscObjects.h @@ -338,10 +338,6 @@ protected: class DLL_LINKAGE CGMagi : public CGObjectInstance { public: - static std::map > eyelist; //[subID][id], supports multiple sets as in H5 - - static void reset(); - void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; diff --git a/lib/mapObjects/ObjectTemplate.h b/lib/mapObjects/ObjectTemplate.h index 332227570..258e9aea0 100644 --- a/lib/mapObjects/ObjectTemplate.h +++ b/lib/mapObjects/ObjectTemplate.h @@ -164,7 +164,7 @@ public: h & animationFile; h & stringID; h & id; - h & subid; + subid.serializeIdentifier(h, id, version); h & printPriority; h & visitDir; h & editorAnimationFile; diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index f7f19c658..d161eb773 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -671,8 +671,6 @@ CMapEditManager * CMap::getEditManager() void CMap::resetStaticData() { - CGKeys::reset(); - CGMagi::reset(); CGObelisk::reset(); CGTownInstance::reset(); } diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index d1fbbc14d..7922df144 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -191,8 +191,6 @@ public: h & artInstances; // static members - h & CGKeys::playerKeyMap; - h & CGMagi::eyelist; h & CGObelisk::obeliskCount; h & CGObelisk::visited; h & CGTownInstance::merchantArtifacts; diff --git a/lib/networkPacks/NetPacksLib.cpp b/lib/networkPacks/NetPacksLib.cpp index 1dd78cabc..8faa7044a 100644 --- a/lib/networkPacks/NetPacksLib.cpp +++ b/lib/networkPacks/NetPacksLib.cpp @@ -1061,6 +1061,12 @@ void ChangeObjectVisitors::applyGs(CGameState * gs) const } break; + case VISITOR_GLOBAL: + { + CGObjectInstance * objectPtr = gs->getObjInstance(object); + gs->getPlayerState(gs->getHero(hero)->tempOwner)->visitedObjectsGlobal.insert({objectPtr->ID, objectPtr->subID}); + break; + } case VISITOR_REMOVE: gs->getHero(hero)->visitedObjects.erase(object); break; @@ -1488,7 +1494,7 @@ void NewObject::applyGs(CGameState *gs) const TerrainTile & t = gs->map->getTile(targetPos); terrainType = t.terType->getId(); - auto handler = VLC->objtypeh->getHandlerFor(ID, subID.getNum()); + auto handler = VLC->objtypeh->getHandlerFor(ID, subID); CGObjectInstance * o = handler->create(); handler->configureObject(o, gs->getRandomGenerator()); @@ -1504,7 +1510,7 @@ void NewObject::applyGs(CGameState *gs) cre->character = 2; cre->gainedArtifact = ArtifactID::NONE; cre->identifier = -1; - cre->addToSlot(SlotID(0), new CStackInstance(subID.as(), -1)); //add placeholder stack + cre->addToSlot(SlotID(0), new CStackInstance(subID.getNum(), -1)); //add placeholder stack } assert(!handler->getTemplates(terrainType).empty()); diff --git a/lib/networkPacks/ObjProperty.h b/lib/networkPacks/ObjProperty.h index 9d40028c4..67029d375 100644 --- a/lib/networkPacks/ObjProperty.h +++ b/lib/networkPacks/ObjProperty.h @@ -39,7 +39,6 @@ enum class ObjProperty : int8_t SEERHUT_VISITED, SEERHUT_COMPLETE, - KEYMASTER_VISITED, OBELISK_VISITED, //creature-bank specific diff --git a/lib/networkPacks/PacksForClient.h b/lib/networkPacks/PacksForClient.h index 44ef00595..ef7376ad5 100644 --- a/lib/networkPacks/PacksForClient.h +++ b/lib/networkPacks/PacksForClient.h @@ -784,7 +784,7 @@ struct DLL_LINKAGE NewObject : public CPackForClient /// Object ID to create MapObjectID ID; /// Object secondary ID to create - VariantIdentifier subID; + MapObjectSubID subID; /// Position of visitable tile of created object int3 targetPos; /// Which player initiated creation of this object @@ -797,7 +797,7 @@ struct DLL_LINKAGE NewObject : public CPackForClient template void serialize(Handler & h, const int version) { h & ID; - h & subID; + subID.serializeIdentifier(h, ID, version); h & targetPos; h & initiator; } @@ -1247,10 +1247,11 @@ struct DLL_LINKAGE ChangeObjectVisitors : public CPackForClient { 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) }; - ui32 mode = VISITOR_CLEAR; // uses VisitMode enum + VisitMode mode = VISITOR_CLEAR; // uses VisitMode enum ObjectInstanceID object; ObjectInstanceID hero; // note: hero owner will be also marked as "visited" this object @@ -1260,7 +1261,7 @@ struct DLL_LINKAGE ChangeObjectVisitors : public CPackForClient ChangeObjectVisitors() = default; - ChangeObjectVisitors(ui32 mode, const ObjectInstanceID & object, const ObjectInstanceID & heroID = ObjectInstanceID(-1)) + ChangeObjectVisitors(VisitMode mode, const ObjectInstanceID & object, const ObjectInstanceID & heroID = ObjectInstanceID(-1)) : mode(mode) , object(object) , hero(heroID)