refactor(Core): Use database tables for Shaman totems and Druid shapeshift forms. (#22104)

Co-authored-by: DoctorKraft <25483209+doctorkraft@users.noreply.github.com>
Co-authored-by: Shauren <shauren.trinity@gmail.com>
Co-authored-by: ForesterDev <11771800+ForesterDev@users.noreply.github.com>
This commit is contained in:
Benjamin Jackson 2025-05-27 09:40:57 -04:00 committed by GitHub
parent c10e4dc015
commit db18d5b035
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 356 additions and 298 deletions

View file

@ -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);

View file

@ -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)

View file

@ -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())
{

View file

@ -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;

View file

@ -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;

View file

@ -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>());
uint8 race = fields[1].Get<uint8>();
uint32 displayId = fields[2].Get<uint32>();
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>());
uint8 race = fields[1].Get<uint8>();
uint8 customizationID = fields[2].Get<uint8>();
uint8 genderID = Gender(fields[3].Get<uint8>());
uint32 modelId = fields[4].Get<uint32>();
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();

View file

@ -711,6 +711,10 @@ struct DungeonEncounter
typedef std::list<DungeonEncounter const*> DungeonEncounterList;
typedef std::unordered_map<uint32, DungeonEncounterList> DungeonEncounterContainer;
typedef std::map<std::pair<SummonSlot /*TotemSlot*/, Races /*RaceId*/>, uint32 /*DisplayId*/> PlayerTotemModelMap;
typedef std::map<std::tuple<ShapeshiftForm /*ShapeshiftID*/, uint8 /*RaceID*/, uint8 /*CustomizationID*/, uint8 /*GenderID*/>, uint32 /*ModelID*/> PlayerShapeshiftModelMap;
static constexpr uint32 MAX_QUEST_MONEY_REWARDS = 10;
typedef std::array<uint32, MAX_QUEST_MONEY_REWARDS> QuestMoneyRewardArray;
typedef std::unordered_map<uint32, QuestMoneyRewardArray> QuestMoneyRewardStore;
@ -777,6 +781,10 @@ public:
[[nodiscard]] ItemTemplateContainer const* GetItemTemplateStore() const { return &_itemTemplateStore; }
[[nodiscard]] std::vector<ItemTemplate*> 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<uint32> _transportMaps; // Helper container storing map ids that are for transports only, loaded from gameobject_template
PlayerTotemModelMap _playerTotemModel;
PlayerShapeshiftModelMap _playerShapeshiftModel;
QuestMoneyRewardStore _questMoneyRewards;
struct GameobjectInstanceSavedState

View file

@ -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;

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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,