diff --git a/data/sql/updates/pending_db_world/get-model-for-player.sql b/data/sql/updates/pending_db_world/get-model-for-player.sql new file mode 100644 index 000000000..74cddd2a3 --- /dev/null +++ b/data/sql/updates/pending_db_world/get-model-for-player.sql @@ -0,0 +1,173 @@ +DROP TABLE IF EXISTS `player_shapeshift_model`; +DROP TABLE IF EXISTS `player_totem_model`; + +CREATE TABLE IF NOT EXISTS `player_shapeshift_model` ( + `ShapeshiftID` TINYINT unsigned NOT NULL, + `RaceID` TINYINT unsigned NOT NULL, + `CustomizationID` TINYINT unsigned NOT NULL, + `GenderID` TINYINT unsigned NOT NULL, + `ModelID` INT unsigned NOT NULL, + PRIMARY KEY (`ShapeshiftID`, `RaceID`, `CustomizationID`, `GenderID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 PACK_KEYS=0; + +CREATE TABLE IF NOT EXISTS `player_totem_model` ( + `TotemID` TINYINT unsigned NOT NULL, + `RaceID` TINYINT unsigned NOT NULL, + `ModelID` INT unsigned NOT NULL, + PRIMARY KEY (`TotemID`, `RaceID`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 PACK_KEYS=0; + +DELETE FROM `player_shapeshift_model`; +INSERT INTO `player_shapeshift_model` (`ShapeshiftID`, `RaceID`, `CustomizationID`, `GenderID`, `ModelID`) VALUES +-- Cat Form +-- Night Elf +(1, 4, 0, 2, 29407), -- Green +(1, 4, 1, 2, 29407), -- Light Green +(1, 4, 2, 2, 29407), -- Dark Green +(1, 4, 3, 2, 29406), -- Light Blue +(1, 4, 4, 2, 29408), -- White +(1, 4, 7, 2, 29405), -- Violet +(1, 4, 8, 2, 29405), -- Violet +(1, 4, 255, 2, 892), -- Dark Blue +-- Tauren +-- Male +(1, 6, 12, 0, 29409), -- White +(1, 6, 13, 0, 29409), -- White +(1, 6, 14, 0, 29409), -- White +(1, 6, 18, 0, 29409), -- Completely White +(1, 6, 9, 0, 29410), -- Light Brown +(1, 6, 10, 0, 29410), -- Light Brown +(1, 6, 11, 0, 29410), -- Light Brown +(1, 6, 6, 0, 29411), -- Brown +(1, 6, 7, 0, 29411), -- Brown +(1, 6, 8, 0, 29411), -- Brown +(1, 6, 0, 0, 29412), -- Dark +(1, 6, 1, 0, 29412), -- Dark +(1, 6, 2, 0, 29412), -- Dark +(1, 6, 3, 0, 29412), -- Dark Grey +(1, 6, 4, 0, 29412), -- Dark Grey +(1, 6, 5, 0, 29412), -- Dark Grey +(1, 6, 255, 0, 8571), -- Grey +-- Female +(1, 6, 10, 1, 29409), -- White +(1, 6, 6, 1, 29410), -- Light Brown +(1, 6, 7, 1, 29410), -- Light Brown +(1, 6, 4, 1, 29411), -- Brown +(1, 6, 5, 1, 29411), -- Brown +(1, 6, 0, 1, 29412), -- Dark +(1, 6, 1, 1, 29412), -- Dark +(1, 6, 2, 1, 29412), -- Dark +(1, 6, 3, 1, 29412), -- Dark +(1, 6, 255, 1, 8571), -- Grey +-- Bear Form +-- Night Elf +(5, 4, 0, 2, 29413), -- Green (29415?) +(5, 4, 1, 2, 29413), -- Light Green (29415?) +(5, 4, 2, 2, 29413), -- Dark Green (29415?) +(5, 4, 6, 2, 29414), -- Dark Blue +(5, 4, 4, 2, 29416), -- White +(5, 4, 3, 2, 29417), -- Light Blue +(5, 4, 255, 2, 2281), -- Violet +-- Dire Bear Form +(8, 4, 0, 2, 29413), -- Green (29415?) +(8, 4, 1, 2, 29413), -- Light Green (29415?) +(8, 4, 2, 2, 29413), -- Dark Green (29415?) +(8, 4, 6, 2, 29414), -- Dark Blue +(8, 4, 4, 2, 29416), -- White +(8, 4, 3, 2, 29417), -- Light Blue +(8, 4, 255, 2, 2281), -- Violet +-- Bear Form +-- Tauren +-- Male +(5, 6, 0, 0, 29418), -- Dark (Black) +(5, 6, 1, 0, 29418), -- Dark (Black) +(5, 6, 2, 0, 29418), -- Dark (Black) +(5, 6, 3, 0, 29419), -- White +(5, 6, 4, 0, 29419), -- White +(5, 6, 5, 0, 29419), -- White +(5, 6, 12, 0, 29419), -- White +(5, 6, 13, 0, 29419), -- White +(5, 6, 14, 0, 29419), -- White +(5, 6, 9, 0, 29420), -- Light Brown/Grey +(5, 6, 10, 0, 29420), -- Light Brown/Grey +(5, 6, 11, 0, 29420), -- Light Brown/Grey +(5, 6, 15, 0, 29420), -- Light Brown/Grey +(5, 6, 16, 0, 29420), -- Light Brown/Grey +(5, 6, 17, 0, 29420), -- Light Brown/Grey +(5, 6, 18, 0, 29421), -- Completely White +(5, 6, 255, 0, 2289), -- Brown +-- Dire Bear Form +(8, 6, 0, 0, 29418), -- Dark (Black) +(8, 6, 1, 0, 29418), -- Dark (Black) +(8, 6, 2, 0, 29418), -- Dark (Black) +(8, 6, 3, 0, 29419), -- White +(8, 6, 4, 0, 29419), -- White +(8, 6, 5, 0, 29419), -- White +(8, 6, 12, 0, 29419), -- White +(8, 6, 13, 0, 29419), -- White +(8, 6, 14, 0, 29419), -- White +(8, 6, 9, 0, 29420), -- Light Brown/Grey +(8, 6, 10, 0, 29420), -- Light Brown/Grey +(8, 6, 11, 0, 29420), -- Light Brown/Grey +(8, 6, 15, 0, 29420), -- Light Brown/Grey +(8, 6, 16, 0, 29420), -- Light Brown/Grey +(8, 6, 17, 0, 29420), -- Light Brown/Grey +(8, 6, 18, 0, 29421), -- Completely White +(8, 6, 255, 0, 2289), -- Brown +-- Bear Form +-- Female +(5, 6, 0, 1, 29418), -- Dark (Black) +(5, 6, 1, 1, 29418), -- Dark (Black) +(5, 6, 2, 1, 29419), -- White +(5, 6, 3, 1, 29419), -- White +(5, 6, 6, 1, 29420), -- Light Brown/Grey +(5, 6, 7, 1, 29420), -- Light Brown/Grey +(5, 6, 8, 1, 29420), -- Light Brown/Grey +(5, 6, 9, 1, 29420), -- Light Brown/Grey +(5, 6, 10, 1, 29421), -- Completely White +(5, 6, 255, 1, 2289), -- Brown +-- Dire Bear Form +(8, 6, 0, 1, 29418), -- Dark (Black) +(8, 6, 1, 1, 29418), -- Dark (Black) +(8, 6, 2, 1, 29419), -- White +(8, 6, 3, 1, 29419), -- White +(8, 6, 6, 1, 29420), -- Light Brown/Grey +(8, 6, 7, 1, 29420), -- Light Brown/Grey +(8, 6, 8, 1, 29420), -- Light Brown/Grey +(8, 6, 9, 1, 29420), -- Light Brown/Grey +(8, 6, 10, 1, 29421), -- Completely White +(8, 6, 255, 1, 2289), -- Brown + -- Epic Flight Form +(27, 4, 255, 2, 21243), +(27, 6, 255, 2, 21244), +-- Flight Form +(29, 4, 255, 2, 20857), +(29, 6, 255, 2, 20872); + +DELETE FROM `player_totem_model`; +INSERT INTO `player_totem_model` (`TotemID`, `RaceID`, `ModelID`) VALUES +-- Orc +(1, 2, 30758), -- Fire +(2, 2, 30757), -- Earth +(3, 2, 30759), -- Water +(4, 2, 30756), -- Air +-- Dwarf +(1, 3, 30754), +(2, 3, 30753), +(3, 3, 30755), +(4, 3, 30736), +-- Troll +(1, 8, 30762), +(2, 8, 30761), +(3, 8, 30763), +(4, 8, 30760), +-- Tauren +(1, 6, 4589), +(2, 6, 4588), +(3, 6, 4587), +(4, 6, 4590), +-- Draenei +(1, 11, 19074), +(2, 11, 19073), +(3, 11, 19075), +(4, 11, 19071); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp index 22a5595d3..39a7836ff 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundRV.cpp @@ -96,7 +96,7 @@ void BattlegroundRV::PostUpdateImpl(uint32 diff) if (player->GetPositionZ() < 27.0f) TeleportUnitToNewZ(player, 28.28f, true); - for (uint8 i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) + for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i) if (player->m_SummonSlot[i]) if (Creature* totem = GetBgMap()->GetCreature(player->m_SummonSlot[i])) if (totem->GetPositionZ() < 28.0f) diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 97cde0794..159c5aed7 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -51,17 +51,17 @@ void Totem::InitStats(uint32 duration) if (Unit* owner = ObjectAccessor::GetUnit(*this, m_owner)) { uint32 slot = m_Properties->Slot; - if (owner->IsPlayer() && slot >= SUMMON_SLOT_TOTEM && slot < MAX_TOTEM_SLOT) + if (owner->IsPlayer() && slot >= SUMMON_SLOT_TOTEM_FIRE && slot < MAX_TOTEM_SLOT) { WorldPackets::Totem::TotemCreated data; data.Totem = GetGUID(); - data.Slot = slot - SUMMON_SLOT_TOTEM; + data.Slot = slot - SUMMON_SLOT_TOTEM_FIRE; data.Duration = duration; data.SpellID = GetUInt32Value(UNIT_CREATED_BY_SPELL); owner->ToPlayer()->SendDirectMessage(data.Write()); // set display id depending on caster's race - SetDisplayId(owner->GetModelForTotem(PlayerTotemType(m_Properties->Id))); + SetDisplayId(sObjectMgr->GetModelForTotem(SummonSlot(slot), Races(owner->getRace()))); } SetLevel(owner->GetLevel()); @@ -133,7 +133,7 @@ void Totem::UnSummon(uint32 msTime) if (Unit* owner = GetOwner()) { // clear owner's totem slot - for (uint8 i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) + for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i) { if (owner->m_SummonSlot[i] == GetGUID()) { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 7085f0b5b..f90f520e5 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -19230,7 +19230,7 @@ uint32 Unit::GetCombatRatingDamageReduction(CombatRating cr, float rate, float c return CalculatePct(damage, percent); } -uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const +uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) { // Hardcoded cases switch (spellId) @@ -19245,177 +19245,8 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const if (IsPlayer()) { - switch (form) - { - case FORM_CAT: - // Based on Hair color - if (getRace() == RACE_NIGHTELF) - { - uint8 hairColor = GetByteValue(PLAYER_BYTES, 3); - switch (hairColor) - { - case 7: // Violet - case 8: - return 29405; - case 3: // Light Blue - return 29406; - case 0: // Green - case 1: // Light Green - case 2: // Dark Green - return 29407; - case 4: // White - return 29408; - default: // original - Dark Blue - return 892; - } - } - // Based on Skin color - else if (getRace() == RACE_TAUREN) - { - uint8 skinColor = GetByteValue(PLAYER_BYTES, 0); - // Male - if (getGender() == GENDER_MALE) - { - switch (skinColor) - { - case 12: // White - case 13: - case 14: - case 18: // Completly White - return 29409; - case 9: // Light Brown - case 10: - case 11: - return 29410; - case 6: // Brown - case 7: - case 8: - return 29411; - case 0: // Dark - case 1: - case 2: - case 3: // Dark Grey - case 4: - case 5: - return 29412; - default: // original - Grey - return 8571; - } - } - // Female - else switch (skinColor) - { - case 10: // White - return 29409; - case 6: // Light Brown - case 7: - return 29410; - case 4: // Brown - case 5: - return 29411; - case 0: // Dark - case 1: - case 2: - case 3: - return 29412; - default: // original - Grey - return 8571; - } - } - else if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE) - return 892; - else - return 8571; - case FORM_DIREBEAR: - case FORM_BEAR: - // Based on Hair color - if (getRace() == RACE_NIGHTELF) - { - uint8 hairColor = GetByteValue(PLAYER_BYTES, 3); - switch (hairColor) - { - case 0: // Green - case 1: // Light Green - case 2: // Dark Green - return 29413; // 29415? - case 6: // Dark Blue - return 29414; - case 4: // White - return 29416; - case 3: // Light Blue - return 29417; - default: // original - Violet - return 2281; - } - } - // Based on Skin color - else if (getRace() == RACE_TAUREN) - { - uint8 skinColor = GetByteValue(PLAYER_BYTES, 0); - // Male - if (getGender() == GENDER_MALE) - { - switch (skinColor) - { - case 0: // Dark (Black) - case 1: - case 2: - return 29418; - case 3: // White - case 4: - case 5: - case 12: - case 13: - case 14: - return 29419; - case 9: // Light Brown/Grey - case 10: - case 11: - case 15: - case 16: - case 17: - return 29420; - case 18: // Completly White - return 29421; - default: // original - Brown - return 2289; - } - } - // Female - else switch (skinColor) - { - case 0: // Dark (Black) - case 1: - return 29418; - case 2: // White - case 3: - return 29419; - case 6: // Light Brown/Grey - case 7: - case 8: - case 9: - return 29420; - case 10: // Completly White - return 29421; - default: // original - Brown - return 2289; - } - } - else if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE) - return 2281; - else - return 2289; - case FORM_FLIGHT: - if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE) - return 20857; - return 20872; - case FORM_FLIGHT_EPIC: - if (Player::TeamIdForRace(getRace()) == TEAM_ALLIANCE) - return 21243; - return 21244; - default: - break; - } + if (uint32 ModelId = sObjectMgr->GetModelForShapeshift(form, ToPlayer())) + return ModelId; } uint32 modelid = 0; @@ -19441,104 +19272,6 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form, uint32 spellId) const return modelid; } -uint32 Unit::GetModelForTotem(PlayerTotemType totemType) -{ - switch (getRace()) - { - case RACE_ORC: - { - switch (totemType) - { - case SUMMON_TYPE_TOTEM_FIRE: // fire - return 30758; - case SUMMON_TYPE_TOTEM_EARTH: // earth - return 30757; - case SUMMON_TYPE_TOTEM_WATER: // water - return 30759; - case SUMMON_TYPE_TOTEM_AIR: // air - return 30756; - } - break; - } - case RACE_DWARF: - { - switch (totemType) - { - case SUMMON_TYPE_TOTEM_FIRE: // fire - return 30754; - case SUMMON_TYPE_TOTEM_EARTH: // earth - return 30753; - case SUMMON_TYPE_TOTEM_WATER: // water - return 30755; - case SUMMON_TYPE_TOTEM_AIR: // air - return 30736; - } - break; - } - case RACE_TROLL: - { - switch (totemType) - { - case SUMMON_TYPE_TOTEM_FIRE: // fire - return 30762; - case SUMMON_TYPE_TOTEM_EARTH: // earth - return 30761; - case SUMMON_TYPE_TOTEM_WATER: // water - return 30763; - case SUMMON_TYPE_TOTEM_AIR: // air - return 30760; - } - break; - } - case RACE_TAUREN: - { - switch (totemType) - { - case SUMMON_TYPE_TOTEM_FIRE: // fire - return 4589; - case SUMMON_TYPE_TOTEM_EARTH: // earth - return 4588; - case SUMMON_TYPE_TOTEM_WATER: // water - return 4587; - case SUMMON_TYPE_TOTEM_AIR: // air - return 4590; - } - break; - } - case RACE_DRAENEI: - { - switch (totemType) - { - case SUMMON_TYPE_TOTEM_FIRE: // fire - return 19074; - case SUMMON_TYPE_TOTEM_EARTH: // earth - return 19073; - case SUMMON_TYPE_TOTEM_WATER: // water - return 19075; - case SUMMON_TYPE_TOTEM_AIR: // air - return 19071; - } - break; - } - default: // One standard for other races. - { - switch (totemType) - { - case SUMMON_TYPE_TOTEM_FIRE: // fire - return 4589; - case SUMMON_TYPE_TOTEM_EARTH: // earth - return 4588; - case SUMMON_TYPE_TOTEM_WATER: // water - return 4587; - case SUMMON_TYPE_TOTEM_AIR: // air - return 4590; - } - break; - } - } - return 0; -} - Unit* Unit::GetRedirectThreatTarget() const { return _redirectThreatInfo.GetTargetGUID() ? ObjectAccessor::GetUnit(*this, _redirectThreatInfo.GetTargetGUID()) : nullptr; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index eedca78a0..e7689a1c1 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -26,6 +26,7 @@ #include "ItemTemplate.h" #include "MotionMaster.h" #include "Object.h" +#include "SharedDefines.h" #include "SpellAuraDefines.h" #include "SpellDefines.h" #include "ThreatMgr.h" @@ -599,23 +600,6 @@ enum ReactiveType MAX_REACTIVE }; -#define SUMMON_SLOT_PET 0 -#define SUMMON_SLOT_TOTEM 1 -#define MAX_TOTEM_SLOT 5 -#define SUMMON_SLOT_MINIPET 5 -#define SUMMON_SLOT_QUEST 6 -#define MAX_SUMMON_SLOT 7 - -#define MAX_GAMEOBJECT_SLOT 4 - -enum PlayerTotemType -{ - SUMMON_TYPE_TOTEM_FIRE = 63, - SUMMON_TYPE_TOTEM_EARTH = 81, - SUMMON_TYPE_TOTEM_WATER = 82, - SUMMON_TYPE_TOTEM_AIR = 83, -}; - /// Spell cooldown flags sent in SMSG_SPELL_COOLDOWN enum SpellCooldownFlags { @@ -1897,8 +1881,7 @@ public: void RestoreDisplayId(); void SetNativeDisplayId(uint32 displayId) { SetUInt32Value(UNIT_FIELD_NATIVEDISPLAYID, displayId); } - [[nodiscard]] uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId) const; - uint32 GetModelForTotem(PlayerTotemType totemType); + [[nodiscard]] uint32 GetModelForForm(ShapeshiftForm form, uint32 spellId); // Unit positons [[nodiscard]] virtual bool IsInWater() const; diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index a4033ca1c..330ee91e0 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1785,6 +1785,139 @@ void ObjectMgr::LoadCreatureModelInfo() LOG_INFO("server.loading", " "); } +void ObjectMgr::LoadPlayerTotemModels() +{ + uint32 oldMSTime = getMSTime(); + + QueryResult result = WorldDatabase.Query("SELECT TotemID, RaceID, ModelID from player_totem_model"); + + if (!result) + { + LOG_INFO("server.loading", ">> Loaded 0 player totem model records. DB table `player_totem_model` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + SummonSlot totemSlot = SummonSlot(fields[0].Get()); + uint8 race = fields[1].Get(); + uint32 displayId = fields[2].Get(); + + if (totemSlot < SUMMON_SLOT_TOTEM_FIRE || totemSlot >= MAX_TOTEM_SLOT) + { + LOG_ERROR("sql.sql", "Wrong TotemSlot {} in `player_totem_model` table, skipped.", totemSlot); + continue; + } + + ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race); + if (!raceEntry) + { + LOG_ERROR("sql.sql", "Race {} defined in `player_totem_model` does not exists, skipped.", uint32(race)); + continue; + } + + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(displayId); + if (!displayEntry) + { + LOG_ERROR("sql.sql", "TotemSlot: {} defined in `player_totem_model` has non-existing model ({}), skipped.", totemSlot, displayId); + continue; + } + + _playerTotemModel[std::make_pair(totemSlot, Races(race))] = displayId; + ++count; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} player totem model records in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); +} + +uint32 ObjectMgr::GetModelForTotem(SummonSlot totemSlot, Races race) const +{ + auto itr = _playerTotemModel.find(std::make_pair(totemSlot, race)); + if (itr != _playerTotemModel.end()) + return itr->second; + + LOG_ERROR("misc", "TotemSlot {} with RaceID ({}) have no totem model data defined, set to default model.", totemSlot, race); + return 0; +} + +void ObjectMgr::LoadPlayerShapeshiftModels() +{ + uint32 oldMSTime = getMSTime(); + + QueryResult result = WorldDatabase.Query("SELECT ShapeshiftID, RaceID, CustomizationID, GenderID, ModelID from player_shapeshift_model"); + + if (!result) + { + LOG_INFO("server.loading", ">> Loaded 0 player shapeshift model records. DB table `player_shapeshift_model` is empty."); + return; + } + + uint32 count = 0; + do + { + Field* fields = result->Fetch(); + + ShapeshiftForm shapeshiftForm = ShapeshiftForm(fields[0].Get()); + uint8 race = fields[1].Get(); + uint8 customizationID = fields[2].Get(); + uint8 genderID = Gender(fields[3].Get()); + uint32 modelId = fields[4].Get(); + + ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race); + if (!raceEntry) + { + LOG_ERROR("sql.sql", "Race {} defined in `player_shapeshift_model` does not exists, skipped.", uint32(race)); + continue; + } + + CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(modelId); + if (!displayEntry) + { + LOG_ERROR("sql.sql", "ShapeshiftForm: {}, Race: {} defined in `player_shapeshift_model` has non-existing model ({}), skipped.", shapeshiftForm, race, modelId); + continue; + } + + _playerShapeshiftModel[std::make_tuple(shapeshiftForm, race, customizationID, genderID)] = modelId; + ++count; + } while (result->NextRow()); + + LOG_INFO("server.loading", ">> Loaded {} player totem model records in {} ms", count, GetMSTimeDiffToNow(oldMSTime)); + LOG_INFO("server.loading", " "); +} + +uint32 ObjectMgr::GetModelForShapeshift(ShapeshiftForm form, Player* player) const +{ + uint8 customizationID; + + if (player->GetTeamId() == TEAM_ALLIANCE) + customizationID = player->GetByteValue(PLAYER_BYTES, 3); // Use Hair Color + else + customizationID = player->GetByteValue(PLAYER_BYTES, 0); // Use Skin Color + + auto itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), customizationID, player->getGender())); + if (itr != _playerShapeshiftModel.end()) + return itr->second; // Explicit combination + + itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), customizationID, GENDER_NONE)); + if (itr != _playerShapeshiftModel.end()) + return itr->second; // Combination applied to both genders + + itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), 255, player->getGender())); + if (itr != _playerShapeshiftModel.end()) + return itr->second; // Default gender-dependent model + + itr = _playerShapeshiftModel.find(std::make_tuple(form, player->getRace(), 255, GENDER_NONE)); + if (itr != _playerShapeshiftModel.end()) + return itr->second; // Last resort + + LOG_DEBUG("entities.player", "ShapeshiftForm {} with RaceID ({}) have no shapeshift model data defined, using fallback data.", form, player->getRace()); + return 0; +} + void ObjectMgr::LoadLinkedRespawn() { uint32 oldMSTime = getMSTime(); diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 8826ce801..f6492ad31 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -711,6 +711,10 @@ struct DungeonEncounter typedef std::list DungeonEncounterList; typedef std::unordered_map DungeonEncounterContainer; +typedef std::map, uint32 /*DisplayId*/> PlayerTotemModelMap; + +typedef std::map, uint32 /*ModelID*/> PlayerShapeshiftModelMap; + static constexpr uint32 MAX_QUEST_MONEY_REWARDS = 10; typedef std::array QuestMoneyRewardArray; typedef std::unordered_map QuestMoneyRewardStore; @@ -777,6 +781,10 @@ public: [[nodiscard]] ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; } [[nodiscard]] std::vector const* GetItemTemplateStoreFast() const { return &_itemTemplateStoreFast; } + uint32 GetModelForTotem(SummonSlot totemSlot, Races race) const; + + uint32 GetModelForShapeshift(ShapeshiftForm form, Player* player) const; + ItemSetNameEntry const* GetItemSetNameEntry(uint32 itemId) { ItemSetNameContainer::iterator itr = _itemSetNameStore.find(itemId); @@ -1035,6 +1043,8 @@ public: void LoadCreatureAddons(); void LoadGameObjectAddons(); void LoadCreatureModelInfo(); + void LoadPlayerTotemModels(); + void LoadPlayerShapeshiftModels(); void LoadEquipmentTemplates(); void LoadCreatureMovementOverrides(); void LoadGameObjectLocales(); @@ -1622,6 +1632,10 @@ private: std::set _transportMaps; // Helper container storing map ids that are for transports only, loaded from gameobject_template + PlayerTotemModelMap _playerTotemModel; + + PlayerShapeshiftModelMap _playerShapeshiftModel; + QuestMoneyRewardStore _questMoneyRewards; struct GameobjectInstanceSavedState diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 01a81b097..7d6b5bd2e 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -686,7 +686,7 @@ void WorldSession::HandleTotemDestroyed(WorldPackets::Totem::TotemDestroyed& tot return; uint8 slotId = totemDestroyed.Slot; - slotId += SUMMON_SLOT_TOTEM; + slotId += SUMMON_SLOT_TOTEM_FIRE; if (slotId >= MAX_TOTEM_SLOT) return; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 75121d79c..6ea3044ab 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3952,7 +3952,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) { int32 basepoints0 = damage; // Cast Absorb on totems - for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot) + for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot) { if (!unitTarget->m_SummonSlot[slot]) continue; @@ -5260,7 +5260,7 @@ void Spell::EffectDestroyAllTotems(SpellEffIndex /*effIndex*/) return; int32 mana = 0; - for (uint8 slot = SUMMON_SLOT_TOTEM; slot < MAX_TOTEM_SLOT; ++slot) + for (uint8 slot = SUMMON_SLOT_TOTEM_FIRE; slot < MAX_TOTEM_SLOT; ++slot) { if (!m_caster->m_SummonSlot[slot]) continue; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 47a8de9e5..2069ca9a6 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -1402,6 +1402,12 @@ void World::SetInitialWorldSettings() LOG_INFO("server.loading", "Loading SpellInfo Custom Attributes..."); sSpellMgr->LoadSpellInfoCustomAttributes(); + LOG_INFO("server.loading", "Loading Player Totem models..."); + sObjectMgr->LoadPlayerTotemModels(); + + LOG_INFO("server.loading", "Loading Player Shapeshift models..."); + sObjectMgr->LoadPlayerShapeshiftModels(); + LOG_INFO("server.loading", "Loading GameObject Models..."); LoadGameObjectModelList(_dataPath); diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index ba882e45b..46e9e9ca9 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -152,7 +152,7 @@ class spell_sha_totemic_mastery : public AuraScript { PreventDefaultAction(); - for (uint8 i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) + for (uint8 i = SUMMON_SLOT_TOTEM_FIRE; i < MAX_TOTEM_SLOT; ++i) if (!GetTarget()->m_SummonSlot[i]) return; diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index 1f0dac07d..6d5f6558b 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -3306,6 +3306,22 @@ enum SummonType SUMMON_TYPE_JEEVES = 12 }; +enum SummonSlot +{ + SUMMON_SLOT_PET = 0, + SUMMON_SLOT_TOTEM_FIRE = 1, + SUMMON_SLOT_TOTEM_EARTH = 2, + SUMMON_SLOT_TOTEM_WATER = 3, + SUMMON_SLOT_TOTEM_AIR = 4, + SUMMON_SLOT_MINIPET = 5, + SUMMON_SLOT_QUEST = 6, + + MAX_SUMMON_SLOT +}; + +#define MAX_TOTEM_SLOT 5 +#define MAX_GAMEOBJECT_SLOT 4 + enum EventId { EVENT_CHARGE = 1003,