From a30e7ba321e81423fafa2a60beeb4f0824af82f0 Mon Sep 17 00:00:00 2001 From: Ivan Savenko Date: Fri, 25 Aug 2023 21:40:19 +0300 Subject: [PATCH] Remove bitmasks of PlayerColor's. Add encode/decode methods --- lib/constants/EntityIdentifiers.cpp | 10 ++++ lib/constants/EntityIdentifiers.h | 43 +++++++--------- lib/gameState/TavernHeroesPool.cpp | 4 +- lib/gameState/TavernHeroesPool.h | 4 +- lib/mapObjects/CGPandoraBox.cpp | 18 ++----- lib/mapObjects/CGPandoraBox.h | 2 +- lib/mapping/CMap.cpp | 2 +- lib/mapping/CMap.h | 2 +- lib/mapping/MapFormatH3M.cpp | 4 +- lib/mapping/MapFormatJson.cpp | 22 ++------ lib/mapping/MapReaderH3M.cpp | 11 ++++ lib/mapping/MapReaderH3M.h | 1 + lib/serializer/JsonSerializeFormat.h | 75 ---------------------------- 13 files changed, 56 insertions(+), 142 deletions(-) diff --git a/lib/constants/EntityIdentifiers.cpp b/lib/constants/EntityIdentifiers.cpp index 1f7a6b9d6..bc0e92843 100644 --- a/lib/constants/EntityIdentifiers.cpp +++ b/lib/constants/EntityIdentifiers.cpp @@ -228,6 +228,16 @@ std::string PlayerColor::getStrCap(bool L10n) const return ret; } +si32 PlayerColor::decode(const std::string & identifier) +{ + return vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, identifier); +} + +std::string PlayerColor::encode(const si32 index) +{ + return GameConstants::PLAYER_COLOR_NAMES[index]; +} + si32 FactionID::decode(const std::string & identifier) { auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeGame(), entityType(), identifier); diff --git a/lib/constants/EntityIdentifiers.h b/lib/constants/EntityIdentifiers.h index a96ed5017..1cdb4300f 100644 --- a/lib/constants/EntityIdentifiers.h +++ b/lib/constants/EntityIdentifiers.h @@ -59,6 +59,11 @@ public: } }; + template void serialize(Handler &h, const int version) + { + h & num; + } + constexpr void advance(int change) { num += change; @@ -87,11 +92,6 @@ public: :IdentifierBase(_num) {} - template void serialize(Handler &h, const int version) - { - h & BaseClass::num; - } - constexpr bool operator == (const Identifier & b) const { return BaseClass::num == b.num; } constexpr bool operator <= (const Identifier & b) const { return BaseClass::num <= b.num; } constexpr bool operator >= (const Identifier & b) const { return BaseClass::num >= b.num; } @@ -122,21 +122,11 @@ class IdentifierWithEnum : public BaseClass static_assert(std::is_same_v, int32_t>, "Entity Identifier must use int32_t"); public: - constexpr int32_t getNum() const - { - return BaseClass::num; - } - constexpr EnumType toEnum() const { return static_cast(BaseClass::num); } - template void serialize(Handler &h, const int version) - { - h & BaseClass::num; - } - constexpr IdentifierWithEnum(const EnumType & enumValue) { BaseClass::num = static_cast(enumValue); @@ -230,7 +220,7 @@ public: } }; -class PlayerColor : public Identifier +class DLL_LINKAGE PlayerColor : public Identifier { public: using Identifier::Identifier; @@ -240,19 +230,20 @@ public: PLAYER_LIMIT_I = 8, }; - using Mask = uint8_t; + static const PlayerColor SPECTATOR; //252 + static const PlayerColor CANNOT_DETERMINE; //253 + static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks) + static const PlayerColor NEUTRAL; //255 + static const PlayerColor PLAYER_LIMIT; //player limit per map - DLL_LINKAGE static const PlayerColor SPECTATOR; //252 - DLL_LINKAGE static const PlayerColor CANNOT_DETERMINE; //253 - DLL_LINKAGE static const PlayerColor UNFLAGGABLE; //254 - neutral objects (pandora, banks) - DLL_LINKAGE static const PlayerColor NEUTRAL; //255 - DLL_LINKAGE static const PlayerColor PLAYER_LIMIT; //player limit per map + bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral) + bool isSpectator() const; - DLL_LINKAGE bool isValidPlayer() const; //valid means < PLAYER_LIMIT (especially non-neutral) - DLL_LINKAGE bool isSpectator() const; + std::string getStr(bool L10n = false) const; + std::string getStrCap(bool L10n = false) const; - DLL_LINKAGE std::string getStr(bool L10n = false) const; - DLL_LINKAGE std::string getStrCap(bool L10n = false) const; + static si32 decode(const std::string& identifier); + static std::string encode(const si32 index); }; class TeamID : public Identifier diff --git a/lib/gameState/TavernHeroesPool.cpp b/lib/gameState/TavernHeroesPool.cpp index 70f441f98..bad731896 100644 --- a/lib/gameState/TavernHeroesPool.cpp +++ b/lib/gameState/TavernHeroesPool.cpp @@ -74,7 +74,7 @@ void TavernHeroesPool::setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, bool TavernHeroesPool::isHeroAvailableFor(HeroTypeID hero, PlayerColor color) const { if (perPlayerAvailability.count(hero)) - return perPlayerAvailability.at(hero) & (1 << color.getNum()); + return perPlayerAvailability.at(hero).count(color) != 0; return true; } @@ -131,7 +131,7 @@ void TavernHeroesPool::addHeroToPool(CGHeroInstance * hero) heroesPool[HeroTypeID(hero->subID)] = hero; } -void TavernHeroesPool::setAvailability(HeroTypeID hero, PlayerColor::Mask mask) +void TavernHeroesPool::setAvailability(HeroTypeID hero, std::set mask) { perPlayerAvailability[hero] = mask; } diff --git a/lib/gameState/TavernHeroesPool.h b/lib/gameState/TavernHeroesPool.h index 97c54879c..fb5dc136f 100644 --- a/lib/gameState/TavernHeroesPool.h +++ b/lib/gameState/TavernHeroesPool.h @@ -44,7 +44,7 @@ class DLL_LINKAGE TavernHeroesPool /// list of which players are able to purchase specific hero /// if hero is not present in list, he is available for everyone - std::map perPlayerAvailability; + std::map> perPlayerAvailability; /// list of heroes currently available in taverns std::vector currentTavern; @@ -71,7 +71,7 @@ public: void addHeroToPool(CGHeroInstance * hero); /// Marks hero as available to only specific set of players - void setAvailability(HeroTypeID hero, PlayerColor::Mask mask); + void setAvailability(HeroTypeID hero, std::set mask); /// Makes hero available in tavern of specified player void setHeroForPlayer(PlayerColor player, TavernHeroSlot slot, HeroTypeID hero, CSimpleArmy & army, TavernSlotRole role); diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 2e64f438b..9a4dd89e8 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -443,8 +443,9 @@ void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) void CGEvent::onHeroVisit( const CGHeroInstance * h ) const { - if(!(availableFor & (1 << h->tempOwner.getNum()))) + if(availableFor.count(h->tempOwner) == 0) return; + if(cb->getPlayerSettings(h->tempOwner)->isControlledByHuman()) { if(humanActivate) @@ -490,20 +491,7 @@ void CGEvent::serializeJsonOptions(JsonSerializeFormat & handler) handler.serializeBool("aIActivable", computerActivate, true, false, false); handler.serializeBool("humanActivable", humanActivate, true, false, true); handler.serializeBool("removeAfterVisit", removeAfterVisit, true, false, false); - - { - auto decodePlayer = [](const std::string & id)->si32 - { - return vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, id); - }; - - auto encodePlayer = [](si32 idx)->std::string - { - return GameConstants::PLAYER_COLOR_NAMES[idx]; - }; - - handler.serializeIdArray("availableFor", availableFor, GameConstants::ALL_PLAYERS, decodePlayer, encodePlayer); - } + handler.serializeIdArray("availableFor", availableFor); } VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CGPandoraBox.h b/lib/mapObjects/CGPandoraBox.h index b6c94a7bd..6cef83266 100644 --- a/lib/mapObjects/CGPandoraBox.h +++ b/lib/mapObjects/CGPandoraBox.h @@ -72,7 +72,7 @@ class DLL_LINKAGE CGEvent : public CGPandoraBox //event objects { public: bool removeAfterVisit = false; //true if event is removed after occurring - ui8 availableFor = 0; //players whom this event is available for + std::set availableFor; //players whom this event is available for bool computerActivate = false; //true if computer player can activate this event bool humanActivate = false; //true if human player can activate this event diff --git a/lib/mapping/CMap.cpp b/lib/mapping/CMap.cpp index c73c1e578..9805821a2 100644 --- a/lib/mapping/CMap.cpp +++ b/lib/mapping/CMap.cpp @@ -35,7 +35,7 @@ void Rumor::serializeJson(JsonSerializeFormat & handler) handler.serializeString("text", text); } -DisposedHero::DisposedHero() : heroId(0), portrait(255), players(0) +DisposedHero::DisposedHero() : heroId(0), portrait(255) { } diff --git a/lib/mapping/CMap.h b/lib/mapping/CMap.h index 957263133..4e3402eb5 100644 --- a/lib/mapping/CMap.h +++ b/lib/mapping/CMap.h @@ -59,7 +59,7 @@ struct DLL_LINKAGE DisposedHero HeroTypeID heroId; HeroTypeID portrait; /// The portrait id of the hero, -1 is default. std::string name; - PlayerColor::Mask players; /// Who can hire this hero (bitfield). + std::set players; /// Who can hire this hero (bitfield). template void serialize(Handler & h, const int version) diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index cd8d341a6..2c3862745 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -692,7 +692,7 @@ void CMapLoaderH3M::readDisposedHeroes() map->disposedHeroes[g].heroId = reader->readHero().getNum(); map->disposedHeroes[g].portrait = reader->readHeroPortrait(); map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId)); - map->disposedHeroes[g].players = reader->readUInt8(); + reader->readBitmaskPlayers(map->disposedHeroes[g].players, false); } } } @@ -995,7 +995,7 @@ CGObjectInstance * CMapLoaderH3M::readEvent(const int3 & mapPosition) readBoxContent(object, mapPosition); - object->availableFor = reader->readUInt8(); + reader->readBitmaskPlayers(object->availableFor, false); object->computerActivate = reader->readBool(); object->removeAfterVisit = reader->readBool(); diff --git a/lib/mapping/MapFormatJson.cpp b/lib/mapping/MapFormatJson.cpp index e8bd5fbe1..917bc623f 100644 --- a/lib/mapping/MapFormatJson.cpp +++ b/lib/mapping/MapFormatJson.cpp @@ -729,18 +729,16 @@ void CMapFormatJson::readDisposedHeroes(JsonSerializeFormat & handler) { HeroTypeID type(HeroTypeID::decode(entry.first)); - ui8 mask = 0; + std::set mask; for(const JsonNode & playerData : entry.second["availableFor"].Vector()) { PlayerColor player = PlayerColor(vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, playerData.String())); if(player.isValidPlayer()) - { - mask |= 1 << player.getNum(); - } + mask.insert(player); } - if(mask != 0 && mask != GameConstants::ALL_PLAYERS && type.getNum() >= 0) + if(!mask.empty() && mask.size() != PlayerColor::PLAYER_LIMIT_I && type.getNum() >= 0) { DisposedHero hero; @@ -760,24 +758,14 @@ void CMapFormatJson::writeDisposedHeroes(JsonSerializeFormat & handler) auto definitions = handler.enterStruct("predefinedHeroes");//DisposedHeroes are part of predefinedHeroes in VCMI map format - for(const DisposedHero & hero : map->disposedHeroes) + for(DisposedHero & hero : map->disposedHeroes) { std::string type = HeroTypeID::encode(hero.heroId); auto definition = definitions->enterStruct(type); JsonNode players(JsonNode::JsonType::DATA_VECTOR); - - for(int playerNum = 0; playerNum < PlayerColor::PLAYER_LIMIT_I; playerNum++) - { - if((1 << playerNum) & hero.players) - { - JsonNode player(JsonNode::JsonType::DATA_STRING); - player.String() = GameConstants::PLAYER_COLOR_NAMES[playerNum]; - players.Vector().push_back(player); - } - } - definition->serializeRaw("availableFor", players, std::nullopt); + definition->serializeIdArray("availableFor", hero.players); } } diff --git a/lib/mapping/MapReaderH3M.cpp b/lib/mapping/MapReaderH3M.cpp index 45b418737..f7328b9de 100644 --- a/lib/mapping/MapReaderH3M.cpp +++ b/lib/mapping/MapReaderH3M.cpp @@ -35,6 +35,12 @@ SpellID MapReaderH3M::remapIdentifier(const SpellID & identifier) return identifier; } +template<> +PlayerColor MapReaderH3M::remapIdentifier(const PlayerColor & identifier) +{ + return identifier; +} + template Identifier MapReaderH3M::remapIdentifier(const Identifier & identifier) { @@ -227,6 +233,11 @@ void MapReaderH3M::readBitmaskFactions(std::set & dest, bool invert) readBitmask(dest, features.factionsBytes, features.factionsCount, invert); } +void MapReaderH3M::readBitmaskPlayers(std::set & dest, bool invert) +{ + readBitmask(dest, 1, 8, invert); +} + void MapReaderH3M::readBitmaskResources(std::set & dest, bool invert) { readBitmask(dest, features.resourcesBytes, features.resourcesCount, invert); diff --git a/lib/mapping/MapReaderH3M.h b/lib/mapping/MapReaderH3M.h index c8e6bf6ff..ca2029722 100644 --- a/lib/mapping/MapReaderH3M.h +++ b/lib/mapping/MapReaderH3M.h @@ -47,6 +47,7 @@ public: void readBitmaskBuildings(std::set & dest, std::optional faction); void readBitmaskFactions(std::set & dest, bool invert); + void readBitmaskPlayers(std::set & dest, bool invert); void readBitmaskResources(std::set & dest, bool invert); void readBitmaskHeroClassesSized(std::set & dest, bool invert); void readBitmaskHeroes(std::vector & dest, bool invert); diff --git a/lib/serializer/JsonSerializeFormat.h b/lib/serializer/JsonSerializeFormat.h index a2be34204..1d1ff4848 100644 --- a/lib/serializer/JsonSerializeFormat.h +++ b/lib/serializer/JsonSerializeFormat.h @@ -335,81 +335,6 @@ public: } } - ///si32-convertible identifier set <-> Json array of string - ///Type U is only used for code & decode - ///TODO: Auto deduce U based on T? - template - void serializeIdArray(const std::string & fieldName, std::set & value, const std::set & defaultValue) - { - std::vector temp; - - if(saving && value != defaultValue) - { - temp.reserve(value.size()); - - for(const T & vitem : value) - { - si32 item = static_cast(vitem); - temp.push_back(item); - } - serializeInternal(fieldName, temp, &U::decode, &U::encode); - } - - if(!saving) - { - JsonNode node; - serializeRaw(fieldName, node, std::nullopt); - if(node.Vector().empty()) - { - value = defaultValue; - } - else - { - value.clear(); - - for(const auto & id : node.Vector()) - { - VLC->identifiers()->requestIdentifier(U::entityType(), id, [&value](int32_t identifier) - { - value.emplace(identifier); - }); - } - } - } - } - - ///bitmask <-> Json array of string - template - void serializeIdArray(const std::string & fieldName, T & value, const T & defaultValue, const TDecoder & decoder, const TEncoder & encoder) - { - static_assert(8 * sizeof(T) >= Size, "Mask size too small"); - - std::vector temp; - temp.reserve(Size); - - if(saving && value != defaultValue) - { - for(si32 i = 0; i < Size; i++) - if(value & (1 << i)) - temp.push_back(i); - serializeInternal(fieldName, temp, decoder, encoder); - } - - if(!saving) - { - serializeInternal(fieldName, temp, decoder, encoder); - - if(temp.empty()) - value = defaultValue; - else - { - value = 0; - for(auto i : temp) - value |= (1 << i); - } - } - } - ///si32-convertible instance identifier <-> Json string template void serializeInstance(const std::string & fieldName, T & value, const T & defaultValue)