aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/hotfixes/master/2021_11_18_00_hotfixes.sql94
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp21
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h9
-rw-r--r--src/server/game/DataStores/DB2LoadInfo.h95
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp15
-rw-r--r--src/server/game/DataStores/DB2Stores.h3
-rw-r--r--src/server/game/DataStores/DB2Structure.h63
-rw-r--r--src/server/game/DataStores/DBCEnums.h15
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h2
-rw-r--r--src/server/game/Miscellaneous/enuminfo_SharedDefines.cpp4
-rw-r--r--src/server/game/Spells/Spell.cpp16
-rw-r--r--src/server/game/Spells/SpellMgr.cpp36
12 files changed, 368 insertions, 5 deletions
diff --git a/sql/updates/hotfixes/master/2021_11_18_00_hotfixes.sql b/sql/updates/hotfixes/master/2021_11_18_00_hotfixes.sql
new file mode 100644
index 00000000000..01af175f5cb
--- /dev/null
+++ b/sql/updates/hotfixes/master/2021_11_18_00_hotfixes.sql
@@ -0,0 +1,94 @@
+--
+-- Table structure for table `spell_visual`
+--
+DROP TABLE IF EXISTS `spell_visual`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `spell_visual` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `MissileCastOffset1` float NOT NULL DEFAULT '0',
+ `MissileCastOffset2` float NOT NULL DEFAULT '0',
+ `MissileCastOffset3` float NOT NULL DEFAULT '0',
+ `MissileImpactOffset1` float NOT NULL DEFAULT '0',
+ `MissileImpactOffset2` float NOT NULL DEFAULT '0',
+ `MissileImpactOffset3` float NOT NULL DEFAULT '0',
+ `AnimEventSoundID` int(10) unsigned NOT NULL DEFAULT '0',
+ `Flags` int(11) NOT NULL DEFAULT '0',
+ `MissileAttachment` tinyint(4) NOT NULL DEFAULT '0',
+ `MissileDestinationAttachment` tinyint(4) NOT NULL DEFAULT '0',
+ `MissileCastPositionerID` int(10) unsigned NOT NULL DEFAULT '0',
+ `MissileImpactPositionerID` int(10) unsigned NOT NULL DEFAULT '0',
+ `MissileTargetingKit` int(11) NOT NULL DEFAULT '0',
+ `HostileSpellVisualID` int(10) unsigned NOT NULL DEFAULT '0',
+ `CasterSpellVisualID` int(10) unsigned NOT NULL DEFAULT '0',
+ `SpellVisualMissileSetID` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `DamageNumberDelay` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `LowViolenceSpellVisualID` int(10) unsigned NOT NULL DEFAULT '0',
+ `RaidSpellVisualMissileSetID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ReducedUnexpectedCameraMovementSpellVisualID` int(11) NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `spell_visual_effect_name`
+--
+DROP TABLE IF EXISTS `spell_visual_effect_name`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `spell_visual_effect_name` (
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ModelFileDataID` int(11) NOT NULL DEFAULT '0',
+ `BaseMissileSpeed` float NOT NULL DEFAULT '0',
+ `Scale` float NOT NULL DEFAULT '0',
+ `MinAllowedScale` float NOT NULL DEFAULT '0',
+ `MaxAllowedScale` float NOT NULL DEFAULT '0',
+ `Alpha` float NOT NULL DEFAULT '0',
+ `Flags` int(10) unsigned NOT NULL DEFAULT '0',
+ `TextureFileDataID` int(11) NOT NULL DEFAULT '0',
+ `EffectRadius` float NOT NULL DEFAULT '0',
+ `Type` int(10) unsigned NOT NULL DEFAULT '0',
+ `GenericID` int(11) NOT NULL DEFAULT '0',
+ `RibbonQualityID` int(10) unsigned NOT NULL DEFAULT '0',
+ `DissolveEffectID` int(11) NOT NULL DEFAULT '0',
+ `ModelPosition` int(11) NOT NULL DEFAULT '0',
+ `Unknown901` tinyint(4) NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
+
+--
+-- Table structure for table `spell_visual_missile`
+--
+DROP TABLE IF EXISTS `spell_visual_missile`;
+/*!40101 SET @saved_cs_client = @@character_set_client */;
+/*!50503 SET character_set_client = utf8mb4 */;
+CREATE TABLE `spell_visual_missile` (
+ `CastOffset1` float NOT NULL DEFAULT '0',
+ `CastOffset2` float NOT NULL DEFAULT '0',
+ `CastOffset3` float NOT NULL DEFAULT '0',
+ `ImpactOffset1` float NOT NULL DEFAULT '0',
+ `ImpactOffset2` float NOT NULL DEFAULT '0',
+ `ImpactOffset3` float NOT NULL DEFAULT '0',
+ `ID` int(10) unsigned NOT NULL DEFAULT '0',
+ `SpellVisualEffectNameID` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `SoundEntriesID` int(10) unsigned NOT NULL DEFAULT '0',
+ `Attachment` tinyint(4) NOT NULL DEFAULT '0',
+ `DestinationAttachment` tinyint(4) NOT NULL DEFAULT '0',
+ `CastPositionerID` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `ImpactPositionerID` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `FollowGroundHeight` int(11) NOT NULL DEFAULT '0',
+ `FollowGroundDropSpeed` int(10) unsigned NOT NULL DEFAULT '0',
+ `FollowGroundApproach` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `Flags` int(10) unsigned NOT NULL DEFAULT '0',
+ `SpellMissileMotionID` smallint(5) unsigned NOT NULL DEFAULT '0',
+ `AnimKitID` int(10) unsigned NOT NULL DEFAULT '0',
+ `ClutterLevel` tinyint(4) NOT NULL DEFAULT '0',
+ `DecayTimeAfterImpact` int(11) NOT NULL DEFAULT '0',
+ `SpellVisualMissileSetID` int(10) unsigned NOT NULL DEFAULT '0',
+ `VerifiedBuild` int(11) NOT NULL DEFAULT '0',
+ PRIMARY KEY (`ID`,`VerifiedBuild`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+/*!40101 SET character_set_client = @saved_cs_client */;
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp
index 81669da1420..d88ecc71d53 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.cpp
+++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp
@@ -1529,6 +1529,27 @@ void HotfixDatabaseConnection::DoPrepareStatements()
" FROM spell_totems WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
PREPARE_MAX_ID_STMT(HOTFIX_SEL_SPELL_TOTEMS, "SELECT MAX(ID) + 1 FROM spell_totems", CONNECTION_SYNCH);
+ // SpellVisual.db2
+ PrepareStatement(HOTFIX_SEL_SPELL_VISUAL, "SELECT ID, MissileCastOffset1, MissileCastOffset2, MissileCastOffset3, MissileImpactOffset1, "
+ "MissileImpactOffset2, MissileImpactOffset3, AnimEventSoundID, Flags, MissileAttachment, MissileDestinationAttachment, "
+ "MissileCastPositionerID, MissileImpactPositionerID, MissileTargetingKit, HostileSpellVisualID, CasterSpellVisualID, SpellVisualMissileSetID, "
+ "DamageNumberDelay, LowViolenceSpellVisualID, RaidSpellVisualMissileSetID, ReducedUnexpectedCameraMovementSpellVisualID FROM spell_visual"
+ " WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
+ PREPARE_MAX_ID_STMT(HOTFIX_SEL_SPELL_VISUAL, "SELECT MAX(ID) + 1 FROM spell_visual", CONNECTION_SYNCH);
+
+ // SpellVisualEffectName.db2
+ PrepareStatement(HOTFIX_SEL_SPELL_VISUAL_EFFECT_NAME, "SELECT ID, ModelFileDataID, BaseMissileSpeed, Scale, MinAllowedScale, MaxAllowedScale, "
+ "Alpha, Flags, TextureFileDataID, EffectRadius, Type, GenericID, RibbonQualityID, DissolveEffectID, ModelPosition, Unknown901"
+ " FROM spell_visual_effect_name WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
+ PREPARE_MAX_ID_STMT(HOTFIX_SEL_SPELL_VISUAL_EFFECT_NAME, "SELECT MAX(ID) + 1 FROM spell_visual_effect_name", CONNECTION_SYNCH);
+
+ // SpellVisualMissile.db2
+ PrepareStatement(HOTFIX_SEL_SPELL_VISUAL_MISSILE, "SELECT CastOffset1, CastOffset2, CastOffset3, ImpactOffset1, ImpactOffset2, ImpactOffset3, ID, "
+ "SpellVisualEffectNameID, SoundEntriesID, Attachment, DestinationAttachment, CastPositionerID, ImpactPositionerID, FollowGroundHeight, "
+ "FollowGroundDropSpeed, FollowGroundApproach, Flags, SpellMissileMotionID, AnimKitID, ClutterLevel, DecayTimeAfterImpact, "
+ "SpellVisualMissileSetID FROM spell_visual_missile WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
+ PREPARE_MAX_ID_STMT(HOTFIX_SEL_SPELL_VISUAL_MISSILE, "SELECT MAX(ID) + 1 FROM spell_visual_missile", CONNECTION_SYNCH);
+
// SpellVisualKit.db2
PrepareStatement(HOTFIX_SEL_SPELL_VISUAL_KIT, "SELECT ID, FallbackPriority, FallbackSpellVisualKitId, DelayMin, DelayMax, Flags1, Flags2"
" FROM spell_visual_kit WHERE (`VerifiedBuild` > 0) = ?", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h
index 73aa6c24e1c..ca7cfae3154 100644
--- a/src/server/database/Database/Implementation/HotfixDatabase.h
+++ b/src/server/database/Database/Implementation/HotfixDatabase.h
@@ -886,6 +886,15 @@ enum HotfixDatabaseStatements : uint32
HOTFIX_SEL_SPELL_TOTEMS,
HOTFIX_SEL_SPELL_TOTEMS_MAX_ID,
+ HOTFIX_SEL_SPELL_VISUAL,
+ HOTFIX_SEL_SPELL_VISUAL_MAX_ID,
+
+ HOTFIX_SEL_SPELL_VISUAL_EFFECT_NAME,
+ HOTFIX_SEL_SPELL_VISUAL_EFFECT_NAME_MAX_ID,
+
+ HOTFIX_SEL_SPELL_VISUAL_MISSILE,
+ HOTFIX_SEL_SPELL_VISUAL_MISSILE_MAX_ID,
+
HOTFIX_SEL_SPELL_VISUAL_KIT,
HOTFIX_SEL_SPELL_VISUAL_KIT_MAX_ID,
diff --git a/src/server/game/DataStores/DB2LoadInfo.h b/src/server/game/DataStores/DB2LoadInfo.h
index 1d4b28ccfaa..2ddfdadf5ef 100644
--- a/src/server/game/DataStores/DB2LoadInfo.h
+++ b/src/server/game/DataStores/DB2LoadInfo.h
@@ -5846,6 +5846,101 @@ struct SpellTotemsLoadInfo
}
};
+struct SpellVisualLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { false, FT_FLOAT, "MissileCastOffset1" },
+ { false, FT_FLOAT, "MissileCastOffset2" },
+ { false, FT_FLOAT, "MissileCastOffset3" },
+ { false, FT_FLOAT, "MissileImpactOffset1" },
+ { false, FT_FLOAT, "MissileImpactOffset2" },
+ { false, FT_FLOAT, "MissileImpactOffset3" },
+ { false, FT_INT, "AnimEventSoundID" },
+ { true, FT_INT, "Flags" },
+ { true, FT_BYTE, "MissileAttachment" },
+ { true, FT_BYTE, "MissileDestinationAttachment" },
+ { false, FT_INT, "MissileCastPositionerID" },
+ { false, FT_INT, "MissileImpactPositionerID" },
+ { true, FT_INT, "MissileTargetingKit" },
+ { false, FT_INT, "HostileSpellVisualID" },
+ { false, FT_INT, "CasterSpellVisualID" },
+ { false, FT_SHORT, "SpellVisualMissileSetID" },
+ { false, FT_SHORT, "DamageNumberDelay" },
+ { false, FT_INT, "LowViolenceSpellVisualID" },
+ { false, FT_INT, "RaidSpellVisualMissileSetID" },
+ { true, FT_INT, "ReducedUnexpectedCameraMovementSpellVisualID" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, SpellVisualMeta::Instance(), HOTFIX_SEL_SPELL_VISUAL);
+ return &loadInfo;
+ }
+};
+
+struct SpellVisualEffectNameLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_INT, "ID" },
+ { true, FT_INT, "ModelFileDataID" },
+ { false, FT_FLOAT, "BaseMissileSpeed" },
+ { false, FT_FLOAT, "Scale" },
+ { false, FT_FLOAT, "MinAllowedScale" },
+ { false, FT_FLOAT, "MaxAllowedScale" },
+ { false, FT_FLOAT, "Alpha" },
+ { false, FT_INT, "Flags" },
+ { true, FT_INT, "TextureFileDataID" },
+ { false, FT_FLOAT, "EffectRadius" },
+ { false, FT_INT, "Type" },
+ { true, FT_INT, "GenericID" },
+ { false, FT_INT, "RibbonQualityID" },
+ { true, FT_INT, "DissolveEffectID" },
+ { true, FT_INT, "ModelPosition" },
+ { true, FT_BYTE, "Unknown901" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, SpellVisualEffectNameMeta::Instance(), HOTFIX_SEL_SPELL_VISUAL_EFFECT_NAME);
+ return &loadInfo;
+ }
+};
+
+struct SpellVisualMissileLoadInfo
+{
+ static DB2LoadInfo const* Instance()
+ {
+ static DB2FieldMeta const fields[] =
+ {
+ { false, FT_FLOAT, "CastOffset1" },
+ { false, FT_FLOAT, "CastOffset2" },
+ { false, FT_FLOAT, "CastOffset3" },
+ { false, FT_FLOAT, "ImpactOffset1" },
+ { false, FT_FLOAT, "ImpactOffset2" },
+ { false, FT_FLOAT, "ImpactOffset3" },
+ { false, FT_INT, "ID" },
+ { false, FT_SHORT, "SpellVisualEffectNameID" },
+ { false, FT_INT, "SoundEntriesID" },
+ { true, FT_BYTE, "Attachment" },
+ { true, FT_BYTE, "DestinationAttachment" },
+ { false, FT_SHORT, "CastPositionerID" },
+ { false, FT_SHORT, "ImpactPositionerID" },
+ { true, FT_INT, "FollowGroundHeight" },
+ { false, FT_INT, "FollowGroundDropSpeed" },
+ { false, FT_SHORT, "FollowGroundApproach" },
+ { false, FT_INT, "Flags" },
+ { false, FT_SHORT, "SpellMissileMotionID" },
+ { false, FT_INT, "AnimKitID" },
+ { true, FT_BYTE, "ClutterLevel" },
+ { true, FT_INT, "DecayTimeAfterImpact" },
+ { false, FT_INT, "SpellVisualMissileSetID" },
+ };
+ static DB2LoadInfo const loadInfo(&fields[0], std::extent<decltype(fields)>::value, SpellVisualMissileMeta::Instance(), HOTFIX_SEL_SPELL_VISUAL_MISSILE);
+ return &loadInfo;
+ }
+};
+
struct SpellVisualKitLoadInfo
{
static DB2LoadInfo const* Instance()
diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp
index 2f629d2d2fe..21780c9935c 100644
--- a/src/server/game/DataStores/DB2Stores.cpp
+++ b/src/server/game/DataStores/DB2Stores.cpp
@@ -302,6 +302,9 @@ DB2Storage<SpellShapeshiftEntry> sSpellShapeshiftStore("SpellShap
DB2Storage<SpellShapeshiftFormEntry> sSpellShapeshiftFormStore("SpellShapeshiftForm.db2", SpellShapeshiftFormLoadInfo::Instance());
DB2Storage<SpellTargetRestrictionsEntry> sSpellTargetRestrictionsStore("SpellTargetRestrictions.db2", SpellTargetRestrictionsLoadInfo::Instance());
DB2Storage<SpellTotemsEntry> sSpellTotemsStore("SpellTotems.db2", SpellTotemsLoadInfo::Instance());
+DB2Storage<SpellVisualEntry> sSpellVisualStore("SpellVisual.db2", SpellVisualLoadInfo::Instance());
+DB2Storage<SpellVisualEffectNameEntry> sSpellVisualEffectNameStore("SpellVisualEffectName.db2", SpellVisualEffectNameLoadInfo::Instance());
+DB2Storage<SpellVisualMissileEntry> sSpellVisualMissileStore("SpellVisualMissile.db2", SpellVisualMissileLoadInfo::Instance());
DB2Storage<SpellVisualKitEntry> sSpellVisualKitStore("SpellVisualKit.db2", SpellVisualKitLoadInfo::Instance());
DB2Storage<SpellXSpellVisualEntry> sSpellXSpellVisualStore("SpellXSpellVisual.db2", SpellXSpellVisualLoadInfo::Instance());
DB2Storage<SummonPropertiesEntry> sSummonPropertiesStore("SummonProperties.db2", SummonPropertiesLoadInfo::Instance());
@@ -476,6 +479,7 @@ namespace
std::unordered_set<std::pair<int32, uint32>> _specsBySpecSet;
std::unordered_set<uint8> _spellFamilyNames;
SpellProcsPerMinuteModContainer _spellProcsPerMinuteMods;
+ std::unordered_map<int32, std::vector<SpellVisualMissileEntry const*>> _spellVisualMissilesBySet;
TalentsByPosition _talentsByPosition;
ToyItemIdsContainer _toys;
std::unordered_map<uint32, TransmogIllusionEntry const*> _transmogIllusionsByEnchantmentId;
@@ -870,6 +874,9 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
LOAD_DB2(sSpellShapeshiftFormStore);
LOAD_DB2(sSpellTargetRestrictionsStore);
LOAD_DB2(sSpellTotemsStore);
+ LOAD_DB2(sSpellVisualStore);
+ LOAD_DB2(sSpellVisualEffectNameStore);
+ LOAD_DB2(sSpellVisualMissileStore);
LOAD_DB2(sSpellVisualKitStore);
LOAD_DB2(sSpellXSpellVisualStore);
LOAD_DB2(sSummonPropertiesStore);
@@ -1361,6 +1368,9 @@ uint32 DB2Manager::LoadStores(std::string const& dataPath, LocaleConstant defaul
for (SpellProcsPerMinuteModEntry const* ppmMod : sSpellProcsPerMinuteModStore)
_spellProcsPerMinuteMods[ppmMod->SpellProcsPerMinuteID].push_back(ppmMod);
+ for (SpellVisualMissileEntry const* spellVisualMissile : sSpellVisualMissileStore)
+ _spellVisualMissilesBySet[spellVisualMissile->SpellVisualMissileSetID].push_back(spellVisualMissile);
+
for (TalentEntry const* talentInfo : sTalentStore)
{
ASSERT(talentInfo->ClassID < MAX_CLASSES);
@@ -2972,6 +2982,11 @@ std::vector<SpellProcsPerMinuteModEntry const*> DB2Manager::GetSpellProcsPerMinu
return std::vector<SpellProcsPerMinuteModEntry const*>();
}
+std::vector<SpellVisualMissileEntry const*> const* DB2Manager::GetSpellVisualMissiles(int32 spellVisualMissileSetId) const
+{
+ return Trinity::Containers::MapGetValuePtr(_spellVisualMissilesBySet, spellVisualMissileSetId);
+}
+
std::vector<TalentEntry const*> const& DB2Manager::GetTalentsByPosition(uint32 class_, uint32 tier, uint32 column) const
{
return _talentsByPosition[class_][tier][column];
diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h
index cd8e074fb7a..8a652478c06 100644
--- a/src/server/game/DataStores/DB2Stores.h
+++ b/src/server/game/DataStores/DB2Stores.h
@@ -225,6 +225,8 @@ TC_GAME_API extern DB2Storage<SpellShapeshiftEntry> sSpellShapes
TC_GAME_API extern DB2Storage<SpellShapeshiftFormEntry> sSpellShapeshiftFormStore;
TC_GAME_API extern DB2Storage<SpellTargetRestrictionsEntry> sSpellTargetRestrictionsStore;
TC_GAME_API extern DB2Storage<SpellTotemsEntry> sSpellTotemsStore;
+TC_GAME_API extern DB2Storage<SpellVisualEntry> sSpellVisualStore;
+TC_GAME_API extern DB2Storage<SpellVisualEffectNameEntry> sSpellVisualEffectNameStore;
TC_GAME_API extern DB2Storage<SpellVisualKitEntry> sSpellVisualKitStore;
TC_GAME_API extern DB2Storage<SpellXSpellVisualEntry> sSpellXSpellVisualStore;
TC_GAME_API extern DB2Storage<SummonPropertiesEntry> sSummonPropertiesStore;
@@ -449,6 +451,7 @@ public:
bool IsSpecSetMember(int32 specSetId, uint32 specId) const;
static bool IsValidSpellFamiliyName(SpellFamilyNames family);
std::vector<SpellProcsPerMinuteModEntry const*> GetSpellProcsPerMinuteMods(uint32 spellprocsPerMinuteId) const;
+ std::vector<SpellVisualMissileEntry const*> const* GetSpellVisualMissiles(int32 spellVisualMissileSetId) const;
std::vector<TalentEntry const*> const& GetTalentsByPosition(uint32 class_, uint32 tier, uint32 column) const;
static bool IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId);
bool IsToyItem(uint32 toy) const;
diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h
index 6742d4f893c..7a3e476542e 100644
--- a/src/server/game/DataStores/DB2Structure.h
+++ b/src/server/game/DataStores/DB2Structure.h
@@ -3548,6 +3548,69 @@ struct SpellTotemsEntry
int32 Totem[MAX_SPELL_TOTEMS];
};
+struct SpellVisualEntry
+{
+ uint32 ID;
+ float MissileCastOffset[3];
+ float MissileImpactOffset[3];
+ uint32 AnimEventSoundID;
+ int32 Flags;
+ int8 MissileAttachment;
+ int8 MissileDestinationAttachment;
+ uint32 MissileCastPositionerID;
+ uint32 MissileImpactPositionerID;
+ int32 MissileTargetingKit;
+ uint32 HostileSpellVisualID;
+ uint32 CasterSpellVisualID;
+ uint16 SpellVisualMissileSetID;
+ uint16 DamageNumberDelay;
+ uint32 LowViolenceSpellVisualID;
+ uint32 RaidSpellVisualMissileSetID;
+ int32 ReducedUnexpectedCameraMovementSpellVisualID;
+};
+
+struct SpellVisualEffectNameEntry
+{
+ uint32 ID;
+ int32 ModelFileDataID;
+ float BaseMissileSpeed;
+ float Scale;
+ float MinAllowedScale;
+ float MaxAllowedScale;
+ float Alpha;
+ uint32 Flags;
+ int32 TextureFileDataID;
+ float EffectRadius;
+ uint32 Type;
+ int32 GenericID;
+ uint32 RibbonQualityID;
+ int32 DissolveEffectID;
+ int32 ModelPosition;
+ int8 Unknown901;
+};
+
+struct SpellVisualMissileEntry
+{
+ float CastOffset[3];
+ float ImpactOffset[3];
+ uint32 ID;
+ uint16 SpellVisualEffectNameID;
+ uint32 SoundEntriesID;
+ int8 Attachment;
+ int8 DestinationAttachment;
+ uint16 CastPositionerID;
+ uint16 ImpactPositionerID;
+ int32 FollowGroundHeight;
+ uint32 FollowGroundDropSpeed;
+ uint16 FollowGroundApproach;
+ uint32 Flags;
+ uint16 SpellMissileMotionID;
+ uint32 AnimKitID;
+ int8 ClutterLevel;
+ int32 DecayTimeAfterImpact;
+ uint32 SpellVisualMissileSetID;
+};
+
struct SpellVisualKitEntry
{
uint32 ID;
diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h
index 26feffc2ac2..5f752cfe715 100644
--- a/src/server/game/DataStores/DBCEnums.h
+++ b/src/server/game/DataStores/DBCEnums.h
@@ -1493,6 +1493,21 @@ enum class SpellShapeshiftFormFlags : int32
DEFINE_ENUM_FLAG(SpellShapeshiftFormFlags);
+enum class SpellVisualEffectNameType : uint32
+{
+ Model = 0,
+ Item = 1,
+ Creature = 2,
+ UnitItemMainHand = 3,
+ UnitItemOffHand = 4,
+ UnitItemRanged = 5,
+ UnitAmmoBasic = 6,
+ UnitAmmoPreferred = 7,
+ UnitItemMainHandIgnoreDisarmed = 8,
+ UnitItemOffHandIgnoreDisarmed = 9,
+ UnitItemRangedIgnoreDisarmed = 10
+};
+
#define TaxiMaskSize 339
typedef std::array<uint8, TaxiMaskSize> TaxiMask;
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 4ffbbf36085..470109e3e93 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -769,7 +769,7 @@ enum SpellAttr10 : uint32
{
SPELL_ATTR10_UNK0 = 0x00000001, // TITLE Unknown attribute 0@Attr10
SPELL_ATTR10_UNK1 = 0x00000002, // TITLE Unknown attribute 1@Attr10
- SPELL_ATTR10_UNK2 = 0x00000004, // TITLE Unknown attribute 2@Attr10
+ SPELL_ATTR10_USES_RANGED_SLOT_COSMETIC_ONLY = 0x00000004, // TITLE Uses Ranged Slot (Cosmetic Only)
SPELL_ATTR10_UNK3 = 0x00000008, // TITLE Unknown attribute 3@Attr10
SPELL_ATTR10_WATER_SPOUT = 0x00000010, // TITLE NPC Knockback - ignore doors
SPELL_ATTR10_UNK5 = 0x00000020, // TITLE Unknown attribute 5@Attr10
diff --git a/src/server/game/Miscellaneous/enuminfo_SharedDefines.cpp b/src/server/game/Miscellaneous/enuminfo_SharedDefines.cpp
index 4c9293a97cf..df44e6cce30 100644
--- a/src/server/game/Miscellaneous/enuminfo_SharedDefines.cpp
+++ b/src/server/game/Miscellaneous/enuminfo_SharedDefines.cpp
@@ -1017,7 +1017,7 @@ TC_API_EXPORT EnumText EnumUtils<SpellAttr10>::ToString(SpellAttr10 value)
{
case SPELL_ATTR10_UNK0: return { "SPELL_ATTR10_UNK0", "Unknown attribute 0@Attr10", "" };
case SPELL_ATTR10_UNK1: return { "SPELL_ATTR10_UNK1", "Unknown attribute 1@Attr10", "" };
- case SPELL_ATTR10_UNK2: return { "SPELL_ATTR10_UNK2", "Unknown attribute 2@Attr10", "" };
+ case SPELL_ATTR10_USES_RANGED_SLOT_COSMETIC_ONLY: return { "SPELL_ATTR10_USES_RANGED_SLOT_COSMETIC_ONLY", "Uses Ranged Slot (Cosmetic Only)", "" };
case SPELL_ATTR10_UNK3: return { "SPELL_ATTR10_UNK3", "Unknown attribute 3@Attr10", "" };
case SPELL_ATTR10_WATER_SPOUT: return { "SPELL_ATTR10_WATER_SPOUT", "NPC Knockback - ignore doors", "" };
case SPELL_ATTR10_UNK5: return { "SPELL_ATTR10_UNK5", "Unknown attribute 5@Attr10", "" };
@@ -1061,7 +1061,7 @@ TC_API_EXPORT SpellAttr10 EnumUtils<SpellAttr10>::FromIndex(size_t index)
{
case 0: return SPELL_ATTR10_UNK0;
case 1: return SPELL_ATTR10_UNK1;
- case 2: return SPELL_ATTR10_UNK2;
+ case 2: return SPELL_ATTR10_USES_RANGED_SLOT_COSMETIC_ONLY;
case 3: return SPELL_ATTR10_UNK3;
case 4: return SPELL_ATTR10_WATER_SPOUT;
case 5: return SPELL_ATTR10_UNK5;
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index cbcf5218f14..997327c3147 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -4276,7 +4276,7 @@ void Spell::SendSpellStart()
if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_fromClient)
castFlags |= CAST_FLAG_PENDING;
- if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
+ if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR10_USES_RANGED_SLOT_COSMETIC_ONLY) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
castFlags |= CAST_FLAG_PROJECTILE;
if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
@@ -4377,7 +4377,7 @@ void Spell::SendSpellGo()
if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_fromClient)
castFlags |= CAST_FLAG_PENDING;
- if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
+ if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR10_USES_RANGED_SLOT_COSMETIC_ONLY) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA))
castFlags |= CAST_FLAG_PROJECTILE; // arrows/bullets visual
if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
@@ -4512,6 +4512,8 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo)
}
else if (Unit const* unitCaster = m_caster->ToUnit())
{
+ uint32 nonRangedAmmoDisplayID = 0;
+ uint32 nonRangedAmmoInventoryType = 0;
for (uint8 i = BASE_ATTACK; i < MAX_ATTACK; ++i)
{
if (uint32 item_id = unitCaster->GetVirtualItemId(i))
@@ -4535,6 +4537,10 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo)
ammoDisplayID = 5998; // is this need fixing?
ammoInventoryType = INVTYPE_AMMO;
break;
+ default:
+ nonRangedAmmoDisplayID = sDB2Manager.GetItemDisplayId(item_id, unitCaster->GetVirtualItemAppearanceMod(i));
+ nonRangedAmmoInventoryType = itemEntry->InventoryType;
+ break;
}
if (ammoDisplayID)
@@ -4543,6 +4549,12 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo)
}
}
}
+
+ if (!ammoDisplayID && !ammoInventoryType)
+ {
+ ammoDisplayID = nonRangedAmmoDisplayID;
+ ammoInventoryType = nonRangedAmmoInventoryType;
+ }
}
ammo.DisplayID = ammoDisplayID;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index 1dea10aee2e..50dc075c120 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -3223,6 +3223,42 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
}
spellInfoMutable->_InitializeExplicitTargetMask();
+
+ if (spellInfoMutable->Speed > 0.0f)
+ {
+ auto visualNeedsAmmo = [](SpellXSpellVisualEntry const* spellXspellVisual)
+ {
+ SpellVisualEntry const* spellVisual = sSpellVisualStore.LookupEntry(spellXspellVisual->SpellVisualID);
+ if (!spellVisual)
+ return false;
+
+ std::vector<SpellVisualMissileEntry const*> const* spellVisualMissiles = sDB2Manager.GetSpellVisualMissiles(spellVisual->SpellVisualMissileSetID);
+ if (!spellVisualMissiles)
+ return false;
+
+ for (SpellVisualMissileEntry const* spellVisualMissile : *spellVisualMissiles)
+ {
+ SpellVisualEffectNameEntry const* spellVisualEffectName = sSpellVisualEffectNameStore.LookupEntry(spellVisualMissile->SpellVisualEffectNameID);
+ if (!spellVisualEffectName)
+ continue;
+
+ SpellVisualEffectNameType type = SpellVisualEffectNameType(spellVisualEffectName->Type);
+ if (type == SpellVisualEffectNameType::UnitAmmoBasic || type == SpellVisualEffectNameType::UnitAmmoPreferred)
+ return true;
+ }
+
+ return false;
+ };
+
+ for (SpellXSpellVisualEntry const* spellXspellVisual : spellInfoMutable->_visuals)
+ {
+ if (visualNeedsAmmo(spellXspellVisual))
+ {
+ spellInfoMutable->AttributesCu |= SPELL_ATTR0_CU_NEEDS_AMMO_DATA;
+ break;
+ }
+ }
+ }
}
// addition for binary spells, omit spells triggering other spells