diff options
-rw-r--r-- | src/server/game/DataStores/DBCStores.cpp | 44 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.h | 6 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStructure.h | 32 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCfmt.h | 1 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 74 |
5 files changed, 90 insertions, 67 deletions
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 731266f1ca3..34df901175a 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -20,6 +20,7 @@ #include "DBCFileLoader.h" #include "DBCfmt.h" #include "Errors.h" +#include "IteratorPair.h" #include "Log.h" #include "ObjectDefines.h" #include "Regex.h" @@ -37,7 +38,6 @@ typedef std::map<uint32, uint32> AreaFlagByMapID; typedef std::tuple<int16, int8, int32> WMOAreaTableKey; typedef std::map<WMOAreaTableKey, WMOAreaTableEntry const*> WMOAreaInfoByTripple; -typedef std::multimap<uint32, CharSectionsEntry const*> CharSectionsMap; DBCStorage <AreaTableEntry> sAreaTableStore(AreaTableEntryfmt); DBCStorage <AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt); @@ -53,10 +53,12 @@ DBCStorage <BankBagSlotPricesEntry> sBankBagSlotPricesStore(BankBagSlotPricesEnt DBCStorage <BannedAddOnsEntry> sBannedAddOnsStore(BannedAddOnsfmt); DBCStorage <BattlemasterListEntry> sBattlemasterListStore(BattlemasterListEntryfmt); DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore(BarberShopStyleEntryfmt); +DBCStorage <CharacterFacialHairStylesEntry> sCharacterFacialHairStylesStore(CharacterFacialHairStylesfmt); +std::unordered_map<uint32, CharacterFacialHairStylesEntry const*> sCharFacialHairMap; +DBCStorage <CharSectionsEntry> sCharSectionsStore(CharSectionsEntryfmt); +std::unordered_multimap<uint32, CharSectionsEntry const*> sCharSectionMap; DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore(CharStartOutfitEntryfmt); std::map<uint32, CharStartOutfitEntry const*> sCharStartOutfitMap; -DBCStorage <CharSectionsEntry> sCharSectionsStore(CharSectionsEntryfmt); -CharSectionsMap sCharSectionMap; DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt); DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt); DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt); @@ -283,8 +285,9 @@ void LoadDBCStores(const std::string& dataPath) LOAD_DBC(sBannedAddOnsStore, "BannedAddOns.dbc"); LOAD_DBC(sBattlemasterListStore, "BattlemasterList.dbc"); LOAD_DBC(sBarberShopStyleStore, "BarberShopStyle.dbc"); - LOAD_DBC(sCharStartOutfitStore, "CharStartOutfit.dbc"); + LOAD_DBC(sCharacterFacialHairStylesStore, "CharacterFacialHairStyles.dbc"); LOAD_DBC(sCharSectionsStore, "CharSections.dbc"); + LOAD_DBC(sCharStartOutfitStore, "CharStartOutfit.dbc"); LOAD_DBC(sCharTitlesStore, "CharTitles.dbc"); LOAD_DBC(sChatChannelsStore, "ChatChannels.dbc"); LOAD_DBC(sChrClassesStore, "ChrClasses.dbc"); @@ -395,13 +398,17 @@ void LoadDBCStores(const std::string& dataPath) #undef LOAD_DBC_EXT - for (CharStartOutfitEntry const* outfit : sCharStartOutfitStore) - sCharStartOutfitMap[outfit->Race | (outfit->Class << 8) | (outfit->Gender << 16)] = outfit; + for (CharacterFacialHairStylesEntry const* entry : sCharacterFacialHairStylesStore) + if (entry->Race && ((1 << (entry->Race - 1)) & RACEMASK_ALL_PLAYABLE) != 0) // ignore nonplayable races + sCharFacialHairMap.insert({ entry->Race | (entry->Gender << 8) | (entry->Variation << 16), entry }); for (CharSectionsEntry const* entry : sCharSectionsStore) - if (entry->Race && ((1 << (entry->Race - 1)) & RACEMASK_ALL_PLAYABLE) != 0) //ignore Nonplayable races + if (entry->Race && ((1 << (entry->Race - 1)) & RACEMASK_ALL_PLAYABLE) != 0) // ignore nonplayable races sCharSectionMap.insert({ entry->GenType | (entry->Gender << 8) | (entry->Race << 16), entry }); + for (CharStartOutfitEntry const* outfit : sCharStartOutfitStore) + sCharStartOutfitMap[outfit->Race | (outfit->Class << 8) | (outfit->Gender << 16)] = outfit; + for (EmotesTextSoundEntry const* entry : sEmotesTextSoundStore) sEmotesTextSoundMap[EmotesTextSoundKey(entry->EmotesTextId, entry->RaceId, entry->SexId)] = entry; @@ -854,10 +861,10 @@ uint32 GetLiquidFlags(uint32 liquidType) return 0; } -CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender) +CharacterFacialHairStylesEntry const* GetCharFacialHairEntry(uint8 race, uint8 gender, uint8 facialHairID) { - std::map<uint32, CharStartOutfitEntry const*>::const_iterator itr = sCharStartOutfitMap.find(race | (class_ << 8) | (gender << 16)); - if (itr == sCharStartOutfitMap.end()) + auto itr = sCharFacialHairMap.find(uint32(race) | uint32(gender << 8) | uint32(facialHairID << 16)); + if (itr == sCharFacialHairMap.end()) return nullptr; return itr->second; @@ -865,16 +872,25 @@ CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, ui CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color) { - std::pair<CharSectionsMap::const_iterator, CharSectionsMap::const_iterator> eqr = sCharSectionMap.equal_range(uint32(genType) | uint32(gender << 8) | uint32(race << 16)); - for (CharSectionsMap::const_iterator itr = eqr.first; itr != eqr.second; ++itr) + uint32 const key = uint32(genType) | uint32(gender << 8) | uint32(race << 16); + for (auto const& section : Trinity::Containers::MapEqualRange(sCharSectionMap, key)) { - if (itr->second->Type == type && itr->second->Color == color) - return itr->second; + if (section.second->Type == type && section.second->Color == color) + return section.second; } return nullptr; } +CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender) +{ + std::map<uint32, CharStartOutfitEntry const*>::const_iterator itr = sCharStartOutfitMap.find(race | (class_ << 8) | (gender << 16)); + if (itr == sCharStartOutfitMap.end()) + return nullptr; + + return itr->second; +} + /// Returns LFGDungeonEntry for a specific map and difficulty. Will return first found entry if multiple dungeons use the same map (such as Scarlet Monastery) LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty) { diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index fec15aae55e..8bcc9c5ac24 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -71,8 +71,9 @@ TC_GAME_API uint32 GetLiquidFlags(uint32 liquidType); TC_GAME_API PvPDifficultyEntry const* GetBattlegroundBracketByLevel(uint32 mapid, uint32 level); TC_GAME_API PvPDifficultyEntry const* GetBattlegroundBracketById(uint32 mapid, BattlegroundBracketId id); -TC_GAME_API CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender); +TC_GAME_API CharacterFacialHairStylesEntry const* GetCharFacialHairEntry(uint8 race, uint8 gender, uint8 facialHairID); TC_GAME_API CharSectionsEntry const* GetCharSectionEntry(uint8 race, CharSectionType genType, uint8 gender, uint8 type, uint8 color); +TC_GAME_API CharStartOutfitEntry const* GetCharStartOutfitEntry(uint8 race, uint8 class_, uint8 gender); TC_GAME_API LFGDungeonEntry const* GetLFGDungeon(uint32 mapId, Difficulty difficulty); @@ -98,8 +99,9 @@ TC_GAME_API extern DBCStorage <BannedAddOnsEntry> sBannedAddOnsStore; TC_GAME_API extern DBCStorage <BarberShopStyleEntry> sBarberShopStyleStore; TC_GAME_API extern DBCStorage <BattlemasterListEntry> sBattlemasterListStore; TC_GAME_API extern DBCStorage <ChatChannelsEntry> sChatChannelsStore; -TC_GAME_API extern DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore; +TC_GAME_API extern DBCStorage <CharacterFacialHairStylesEntry> sCharacterFacialHairStylesStore; TC_GAME_API extern DBCStorage <CharSectionsEntry> sCharSectionsStore; +TC_GAME_API extern DBCStorage <CharStartOutfitEntry> sCharStartOutfitStore; TC_GAME_API extern DBCStorage <CharTitlesEntry> sCharTitlesStore; TC_GAME_API extern DBCStorage <ChrClassesEntry> sChrClassesStore; TC_GAME_API extern DBCStorage <ChrRacesEntry> sChrRacesStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index b5ee0f89d6b..b0bea8ca81c 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -302,18 +302,12 @@ struct BattlemasterListEntry //uint32 SomeLevel; // 31, may be max level }; -#define MAX_OUTFIT_ITEMS 24 - -struct CharStartOutfitEntry +struct CharacterFacialHairStylesEntry { - //uint32 Id; // 0 - uint8 Race; // 1 - uint8 Class; // 2 - uint8 Gender; // 3 - //uint8 Unused; // 4 - int32 ItemId[MAX_OUTFIT_ITEMS]; // 5-28 - //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 29-52 not required at server side - //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 53-76 not required at server side + uint32 Race; + uint32 Gender; + uint32 Variation; + // uint32 Geoset[5]; }; enum CharSectionFlags @@ -341,6 +335,22 @@ struct CharSectionsEntry uint32 Flags; uint32 Type; uint32 Color; + + inline bool HasFlag(CharSectionFlags flag) const { return !!(Flags & flag); } +}; + +#define MAX_OUTFIT_ITEMS 24 + +struct CharStartOutfitEntry +{ + //uint32 Id; // 0 + uint8 Race; // 1 + uint8 Class; // 2 + uint8 Gender; // 3 + //uint8 Unused; // 4 + int32 ItemId[MAX_OUTFIT_ITEMS]; // 5-28 + //int32 ItemDisplayId[MAX_OUTFIT_ITEMS]; // 29-52 not required at server side + //int32 ItemInventorySlot[MAX_OUTFIT_ITEMS]; // 53-76 not required at server side }; struct CharTitlesEntry diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 5d8132ea6a6..dd74cffb51e 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -32,6 +32,7 @@ char const BankBagSlotPricesEntryfmt[] = "ni"; char const BannedAddOnsfmt[] = "nxxxxxxxxxx"; char const BarberShopStyleEntryfmt[] = "nixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxiii"; char const BattlemasterListEntryfmt[] = "niiiiiiiiixssssssssssssssssxiixx"; +char const CharacterFacialHairStylesfmt[] = "iiixxxxx"; char const CharStartOutfitEntryfmt[] = "dbbbXiiiiiiiiiiiiiiiiiiiiiiiixxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char const CharSectionsEntryfmt[] = "diiixxxiii"; char const CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi"; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c757645606d..4b8a7b57742 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -26526,54 +26526,48 @@ void Player::SendSupercededSpell(uint32 oldSpell, uint32 newSpell) const bool Player::ValidateAppearance(uint8 race, uint8 class_, uint8 gender, uint8 hairID, uint8 hairColor, uint8 faceID, uint8 facialHair, uint8 skinColor, bool create /*=false*/) { - // Check skin color - // For Skin type is always 0 - if (CharSectionsEntry const* entry = GetCharSectionEntry(race, SECTION_TYPE_SKIN, gender, 0, skinColor)) - { // Skin Color defined as Face color, too, we check skin & face in one pass - if (CharSectionsEntry const* entry2 = GetCharSectionEntry(race, SECTION_TYPE_FACE, gender, faceID, skinColor)) - { - // Check DeathKnight exclusive - if (((entry->Flags & SECTION_FLAG_DEATH_KNIGHT) || (entry2->Flags & SECTION_FLAG_DEATH_KNIGHT)) && class_ != CLASS_DEATH_KNIGHT) - return false; - if (create && !((entry->Flags & SECTION_FLAG_PLAYER) && (entry2->Flags & SECTION_FLAG_PLAYER))) - return false; - } - else + auto validateCharSection = [class_, create](CharSectionsEntry const* entry) -> bool + { + if (!entry) return false; - } - else + + // Check Death Knight exclusive + if (class_ != CLASS_DEATH_KNIGHT && entry->HasFlag(SECTION_FLAG_DEATH_KNIGHT)) + return false; + + // Character creation/customize has some limited sections (as opposed to barbershop) + if (create && !entry->HasFlag(SECTION_FLAG_PLAYER)) + return false; + + return true; + }; + + // For Skin type is always 0 + CharSectionsEntry const* skinEntry = GetCharSectionEntry(race, SECTION_TYPE_SKIN, gender, 0, skinColor); + if (!validateCharSection(skinEntry)) return false; - // These combinations don't have an entry of Type SECTION_TYPE_FACIAL_HAIR, exclude them from that check - bool excludeCheck = (race == RACE_TAUREN) || (race == RACE_DRAENEI) || (gender == GENDER_FEMALE && race != RACE_NIGHTELF && race != RACE_UNDEAD_PLAYER); + // Skin Color defined as Face color, too + CharSectionsEntry const* faceEntry = GetCharSectionEntry(race, SECTION_TYPE_FACE, gender, faceID, skinColor); + if (!validateCharSection(faceEntry)) + return false; // Check Hair - if (CharSectionsEntry const* entry = GetCharSectionEntry(race, SECTION_TYPE_HAIR, gender, hairID, hairColor)) + CharSectionsEntry const* hairEntry = GetCharSectionEntry(race, SECTION_TYPE_HAIR, gender, hairID, hairColor); + if (!validateCharSection(hairEntry)) + return false; + + // These combinations don't have an entry of Type SECTION_TYPE_FACIAL_HAIR, exclude them from that check + bool const excludeCheck = (race == RACE_TAUREN) || (race == RACE_DRAENEI) || (gender == GENDER_FEMALE && race != RACE_NIGHTELF && race != RACE_UNDEAD_PLAYER); + if (!excludeCheck) { - if ((entry->Flags & SECTION_FLAG_DEATH_KNIGHT) && class_ != CLASS_DEATH_KNIGHT) + CharSectionsEntry const* facialHairEntry = GetCharSectionEntry(race, SECTION_TYPE_FACIAL_HAIR, gender, facialHair, hairColor); + if (!validateCharSection(facialHairEntry)) return false; - if (create && !(entry->Flags & SECTION_FLAG_PLAYER)) - return false; - - if (!excludeCheck) - { - if (CharSectionsEntry const* entry2 = GetCharSectionEntry(race, SECTION_TYPE_FACIAL_HAIR, gender, facialHair, hairColor)) - { - if ((entry2->Flags & SECTION_FLAG_DEATH_KNIGHT) && class_ != CLASS_DEATH_KNIGHT) - return false; - if (create && !(entry2->Flags & SECTION_FLAG_PLAYER)) - return false; - } - else - return false; - } - else - { - // @TODO: Bound checking for facialHair ID (used clientside for markings, tauren beard, etc.) - // Not present in DBC - } } - else + + CharacterFacialHairStylesEntry const* entry = GetCharFacialHairEntry(race, gender, facialHair); + if (!entry) return false; return true; |