diff --git a/sql/updates/world/4.3.4/2023_01_10_01_world.sql b/sql/updates/world/4.3.4/2023_01_10_01_world.sql new file mode 100644 index 0000000000..7cb80c1cde --- /dev/null +++ b/sql/updates/world/4.3.4/2023_01_10_01_world.sql @@ -0,0 +1,34 @@ +ALTER TABLE `creature_template_addon` + ADD COLUMN `StandState` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `mount`, + ADD COLUMN `AnimTier` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `StandState`, + ADD COLUMN `VisFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `AnimTier`, + ADD COLUMN `SheathState` TINYINT UNSIGNED DEFAULT 1 NOT NULL AFTER `VisFlags`, + ADD COLUMN `PvPFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `SheathState`; + +UPDATE `creature_template_addon` SET `StandState`=`bytes1` & 0xFF; +UPDATE `creature_template_addon` SET `AnimTier`=(`bytes1` >> 24) & 0xFF; +UPDATE `creature_template_addon` SET `VisFlags`=(`bytes1` >> 16) & 0xFF; +UPDATE `creature_template_addon` SET `SheathState`=`bytes2` & 0xFF; +UPDATE `creature_template_addon` SET `PvPFlags`=(`bytes2` >> 8) & 0xFF; + +ALTER TABLE `creature_addon` + ADD COLUMN `StandState` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `mount`, + ADD COLUMN `AnimTier` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `StandState`, + ADD COLUMN `VisFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `AnimTier`, + ADD COLUMN `SheathState` TINYINT UNSIGNED DEFAULT 1 NOT NULL AFTER `VisFlags`, + ADD COLUMN `PvPFlags` TINYINT UNSIGNED DEFAULT 0 NOT NULL AFTER `SheathState`; + +UPDATE `creature_addon` SET `StandState`=`bytes1` & 0xFF; +UPDATE `creature_addon` SET `AnimTier`=(`bytes1` >> 24) & 0xFF; +UPDATE `creature_addon` SET `VisFlags`=(`bytes1` >> 16) & 0xFF; +UPDATE `creature_addon` SET `SheathState`=`bytes2` & 0xFF; +UPDATE `creature_addon` SET `PvPFlags`=(`bytes2` >> 8) & 0xFF; + +-- Conversion done, drop old columns +ALTER TABLE `creature_template_addon` + DROP COLUMN `bytes1`, + DROP COLUMN `bytes2`; + +ALTER TABLE `creature_addon` + DROP COLUMN `bytes1`, + DROP COLUMN `bytes2`; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index e6c5d6fb6d..55384e9226 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2532,72 +2532,55 @@ CreatureAddon const* Creature::GetCreatureAddon() const //creature_addon table bool Creature::LoadCreaturesAddon() { - CreatureAddon const* cainfo = GetCreatureAddon(); - if (!cainfo) + CreatureAddon const* creatureAddon = GetCreatureAddon(); + if (!creatureAddon) return false; - if (cainfo->mount != 0) - Mount(cainfo->mount); + if (creatureAddon->mount != 0) + Mount(creatureAddon->mount); - if (cainfo->bytes1 != 0) - { - // 0 StandState - // 1 FreeTalentPoints Pet only, so always 0 for default creature - // 2 StandFlags - // 3 StandMiscFlags + // UNIT_FIELD_BYTES_1 values + SetStandState(UnitStandStateType(creatureAddon->standState)); + SetAnimationTier(AnimationTier(creatureAddon->animTier)); + SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_VIS_FLAG, creatureAddon->visFlags); - SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, uint8(cainfo->bytes1 & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_PET_TALENTS, uint8((cainfo->bytes1 >> 8) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_PET_TALENTS, 0); - SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_VIS_FLAG, uint8((cainfo->bytes1 >> 16) & 0xFF)); + //! Suspected correlation between UNIT_FIELD_BYTES_1, offset 3, value 0x2: + //! If no inhabittype_fly (if no MovementFlag_DisableGravity or MovementFlag_CanFly flag found in sniffs) + //! Check using InhabitType as movement flags are assigned dynamically + //! basing on whether the creature is in air or not + //! Set MovementFlag_Hover. Otherwise do nothing. + if (CanHover()) + AddUnitMovementFlag(MOVEMENTFLAG_HOVER); - SetAnimationTier(static_cast((cainfo->bytes1 >> 24) & 0xFF)); + // UNIT_FIELD_BYTES_2 values + SetSheath(SheathState(creatureAddon->sheathState)); + SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, creatureAddon->pvpFlags); - //! Suspected correlation between UNIT_FIELD_BYTES_1, offset 3, value 0x2: - //! If no inhabittype_fly (if no MovementFlag_DisableGravity or MovementFlag_CanFly flag found in sniffs) - //! Check using InhabitType as movement flags are assigned dynamically - //! basing on whether the creature is in air or not - //! Set MovementFlag_Hover. Otherwise do nothing. - if (CanHover()) - AddUnitMovementFlag(MOVEMENTFLAG_HOVER); - } + // These fields must only be handled by core internals and must not be modified via scripts/DB data + SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, 0); + SetShapeshiftForm(FORM_NONE); - if (cainfo->bytes2 != 0) - { - // 0 SheathState - // 1 PvpFlags - // 2 PetFlags Pet only, so always 0 for default creature - // 3 ShapeshiftForm Must be determined/set by shapeshift spell/aura + if (creatureAddon->emote != 0) + SetUInt32Value(UNIT_NPC_EMOTESTATE, creatureAddon->emote); - SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHEATH_STATE, uint8(cainfo->bytes2 & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PVP_FLAG, uint8((cainfo->bytes2 >> 8) & 0xFF)); - //SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, uint8((cainfo->bytes2 >> 16) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_PET_FLAGS, 0); - //SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHAPESHIFT_FORM, uint8((cainfo->bytes2 >> 24) & 0xFF)); - SetByteValue(UNIT_FIELD_BYTES_2, UNIT_BYTES_2_OFFSET_SHAPESHIFT_FORM, 0); - } - - if (cainfo->emote != 0) - SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote); - - SetAIAnimKitId(cainfo->aiAnimKit); - SetMovementAnimKitId(cainfo->movementAnimKit); - SetMeleeAnimKitId(cainfo->meleeAnimKit); + SetAIAnimKitId(creatureAddon->aiAnimKit); + SetMovementAnimKitId(creatureAddon->movementAnimKit); + SetMeleeAnimKitId(creatureAddon->meleeAnimKit); // Check if visibility distance different - if (cainfo->visibilityDistanceType != VisibilityDistanceType::Normal) - SetVisibilityDistanceOverride(cainfo->visibilityDistanceType); + if (creatureAddon->visibilityDistanceType != VisibilityDistanceType::Normal) + SetVisibilityDistanceOverride(creatureAddon->visibilityDistanceType); // Load pathing data - if (cainfo->waypointPathId) - _waypointPathId = cainfo->waypointPathId; + if (creatureAddon->waypointPathId) + _waypointPathId = creatureAddon->waypointPathId; - if (cainfo->cyclicSplinePathId) - _cyclicSplinePathId = cainfo->cyclicSplinePathId; + if (creatureAddon->cyclicSplinePathId) + _cyclicSplinePathId = creatureAddon->cyclicSplinePathId; - if (!cainfo->auras.empty()) + if (!creatureAddon->auras.empty()) { - for (std::vector::const_iterator itr = cainfo->auras.begin(); itr != cainfo->auras.end(); ++itr) + for (std::vector::const_iterator itr = creatureAddon->auras.begin(); itr != creatureAddon->auras.end(); ++itr) { SpellInfo const* AdditionalSpellInfo = sSpellMgr->GetSpellInfo(*itr); if (!AdditionalSpellInfo) diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index 5d37cedc81..5193189630 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -336,8 +336,11 @@ struct CreatureAddon uint32 waypointPathId; uint32 cyclicSplinePathId; uint32 mount; - uint32 bytes1; - uint32 bytes2; + uint8 standState; + uint8 animTier; + uint8 sheathState; + uint8 pvpFlags; + uint8 visFlags; uint32 emote; uint16 aiAnimKit; uint16 movementAnimKit; diff --git a/src/server/game/Entities/Unit/UnitDefines.h b/src/server/game/Entities/Unit/UnitDefines.h index bc27785a79..2a6c4871bd 100644 --- a/src/server/game/Entities/Unit/UnitDefines.h +++ b/src/server/game/Entities/Unit/UnitDefines.h @@ -39,7 +39,9 @@ enum UnitStandStateType : uint8 UNIT_STAND_STATE_SIT_HIGH_CHAIR = 6, UNIT_STAND_STATE_DEAD = 7, UNIT_STAND_STATE_KNEEL = 8, - UNIT_STAND_STATE_SUBMERGED = 9 + UNIT_STAND_STATE_SUBMERGED = 9, + + MAX_UNIT_STAND_STATE }; // byte flag value (UNIT_FIELD_BYTES_1, 2) @@ -80,11 +82,13 @@ enum UnitBytes2Offsets : uint8 // UNIT_FIELD_BYTES_1 (UNIT_BYTES_1_OFFSET_ANIM_TIER) enum class AnimationTier : uint8 { - Ground = 0, - Swim = 1, - Hover = 2, - Fly = 3, - Submerged = 4, + Ground = 0, // plays ground tier animations + Swim = 1, // falls back to ground tier animations, not handled by the client, should never appear in sniffs, will prevent tier change animations from playing correctly if used + Hover = 2, // plays flying tier animations or falls back to ground tier animations, automatically enables hover clientside when entering visibility with this value + Fly = 3, // plays flying tier animations + Submerged = 4, + + Max }; // low byte (0 from 0..3) of UNIT_FIELD_BYTES_2 @@ -92,10 +96,10 @@ enum SheathState : uint8 { SHEATH_STATE_UNARMED = 0, // non prepared weapon SHEATH_STATE_MELEE = 1, // prepared melee weapon - SHEATH_STATE_RANGED = 2 // prepared ranged weapon -}; + SHEATH_STATE_RANGED = 2, // prepared ranged weapon -#define MAX_SHEATH_STATE 3 + MAX_SHEATH_STATE +}; // byte (1 from 0..3) of UNIT_FIELD_BYTES_2 enum UnitPVPStateFlags : uint8 diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 7b9af159d4..0d39b2c3d4 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -564,8 +564,8 @@ void ObjectMgr::LoadCreatureTemplateAddons() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 11 - QueryResult result = WorldDatabase.Query("SELECT entry, waypointPathId, cyclicSplinePathId, mount, bytes1, bytes2, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + QueryResult result = WorldDatabase.Query("SELECT entry, waypointPathId, cyclicSplinePathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_template_addon"); if (!result) { @@ -591,15 +591,18 @@ void ObjectMgr::LoadCreatureTemplateAddons() creatureAddon.waypointPathId = fields[1].GetUInt32(); creatureAddon.cyclicSplinePathId = fields[2].GetUInt32(); creatureAddon.mount = fields[3].GetUInt32(); - creatureAddon.bytes1 = fields[4].GetUInt32(); - creatureAddon.bytes2 = fields[5].GetUInt32(); - creatureAddon.emote = fields[6].GetUInt32(); - creatureAddon.aiAnimKit = fields[7].GetUInt16(); - creatureAddon.movementAnimKit = fields[8].GetUInt16(); - creatureAddon.meleeAnimKit = fields[9].GetUInt16(); - creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[10].GetUInt8()); + creatureAddon.standState = fields[4].GetUInt8(); + creatureAddon.animTier = fields[5].GetUInt8(); + creatureAddon.visFlags = fields[6].GetUInt8(); + creatureAddon.sheathState = fields[7].GetUInt8(); + creatureAddon.pvpFlags = fields[8].GetUInt8(); + creatureAddon.emote = fields[9].GetUInt32(); + creatureAddon.aiAnimKit = fields[10].GetUInt16(); + creatureAddon.movementAnimKit = fields[11].GetUInt16(); + creatureAddon.meleeAnimKit = fields[12].GetUInt16(); + creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[13].GetUInt8()); - Tokenizer tokens(fields[11].GetString(), ' '); + Tokenizer tokens(fields[14].GetString(), ' '); uint8 i = 0; creatureAddon.auras.resize(tokens.size()); for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) @@ -632,6 +635,26 @@ void ObjectMgr::LoadCreatureTemplateAddons() } } + if (creatureAddon.standState >= MAX_UNIT_STAND_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid unit stand state (%u) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.standState); + creatureAddon.standState = 0; + } + + if (AnimationTier(creatureAddon.animTier) >= AnimationTier::Max) + { + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid animation tier (%u) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.animTier); + creatureAddon.animTier = 0; + } + + if (creatureAddon.sheathState >= MAX_SHEATH_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid sheath state (%u) defined in `creature_template_addon`. Truncated to 0.", entry, creatureAddon.sheathState); + creatureAddon.sheathState = 0; + } + + // PvPFlags don't need any checking for the time being since they cover the entire range of a byte + if (!sEmotesStore.LookupEntry(creatureAddon.emote)) { TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid emote (%u) defined in `creature_template_addon`.", entry, creatureAddon.emote); @@ -1148,8 +1171,8 @@ void ObjectMgr::LoadCreatureAddons() { uint32 oldMSTime = getMSTime(); - // 0 1 2 3 4 5 6 7 8 9 10 11 - QueryResult result = WorldDatabase.Query("SELECT guid, waypointPathId, cyclicSplinePathId, mount, bytes1, bytes2, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon"); + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 + QueryResult result = WorldDatabase.Query("SELECT guid, waypointPathId, cyclicSplinePathId, mount, StandState, AnimTier, VisFlags, SheathState, PvPFlags, emote, aiAnimKit, movementAnimKit, meleeAnimKit, visibilityDistanceType, auras FROM creature_addon"); if (!result) { @@ -1203,15 +1226,18 @@ void ObjectMgr::LoadCreatureAddons() } creatureAddon.mount = fields[3].GetUInt32(); - creatureAddon.bytes1 = fields[4].GetUInt32(); - creatureAddon.bytes2 = fields[5].GetUInt32(); - creatureAddon.emote = fields[6].GetUInt32(); - creatureAddon.aiAnimKit = fields[7].GetUInt16(); - creatureAddon.movementAnimKit = fields[8].GetUInt16(); - creatureAddon.meleeAnimKit = fields[9].GetUInt16(); - creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[10].GetUInt8()); + creatureAddon.standState = fields[4].GetUInt8(); + creatureAddon.animTier = fields[5].GetUInt8(); + creatureAddon.visFlags = fields[6].GetUInt8(); + creatureAddon.sheathState = fields[7].GetUInt8(); + creatureAddon.pvpFlags = fields[8].GetUInt8(); + creatureAddon.emote = fields[9].GetUInt32(); + creatureAddon.aiAnimKit = fields[10].GetUInt16(); + creatureAddon.movementAnimKit = fields[11].GetUInt16(); + creatureAddon.meleeAnimKit = fields[12].GetUInt16(); + creatureAddon.visibilityDistanceType = VisibilityDistanceType(fields[13].GetUInt8()); - Tokenizer tokens(fields[11].GetString(), ' '); + Tokenizer tokens(fields[14].GetString(), ' '); uint8 i = 0; creatureAddon.auras.resize(tokens.size()); for (Tokenizer::const_iterator itr = tokens.begin(); itr != tokens.end(); ++itr) @@ -1244,6 +1270,26 @@ void ObjectMgr::LoadCreatureAddons() } } + if (creatureAddon.standState >= MAX_UNIT_STAND_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid unit stand state (%u) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.standState); + creatureAddon.standState = 0; + } + + if (AnimationTier(creatureAddon.animTier) >= AnimationTier::Max) + { + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid animation tier (%u) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.animTier); + creatureAddon.animTier = 0; + } + + if (creatureAddon.sheathState >= MAX_SHEATH_STATE) + { + TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid sheath state (%u) defined in `creature_addon`. Truncated to 0.", guid, creatureAddon.sheathState); + creatureAddon.sheathState = 0; + } + + // PvPFlags don't need any checking for the time being since they cover the entire range of a byte + if (!sEmotesStore.LookupEntry(creatureAddon.emote)) { TC_LOG_ERROR("sql.sql", "Creature (GUID: %u) has invalid emote (%u) defined in `creature_addon`.", guid, creatureAddon.emote);