aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/DataStores/DBCStores.cpp44
-rw-r--r--src/server/game/DataStores/DBCStores.h6
-rw-r--r--src/server/game/DataStores/DBCStructure.h32
-rw-r--r--src/server/game/DataStores/DBCfmt.h1
-rw-r--r--src/server/game/Entities/Player/Player.cpp74
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;