aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/mangos_spell_check.sql48
-rw-r--r--src/game/Chat.cpp3
-rw-r--r--src/game/Chat.h1
-rw-r--r--src/game/Debugcmds.cpp9
-rw-r--r--src/game/SpellMgr.cpp266
-rw-r--r--src/game/SpellMgr.h12
-rw-r--r--src/shared/Util.h12
7 files changed, 337 insertions, 14 deletions
diff --git a/sql/mangos_spell_check.sql b/sql/mangos_spell_check.sql
new file mode 100644
index 00000000000..5f34ffdc64e
--- /dev/null
+++ b/sql/mangos_spell_check.sql
@@ -0,0 +1,48 @@
+/* This file contain expected by code spell properties from simple existance to spell family mask and icons. */
+
+/* This data let easy detect spell properties change at client version */
+/* support switch and update or drop problematic code associated with checked*/
+/* spell data. */
+
+/* sql file not required for server work but expected to be applied to */
+/* MaNGOS DB if you plan use .debug spellcheck command in console. */
+
+/* To code commiters: */
+/* */
+/* If you commit code that include explicit used spell id or checks base at */
+/* spell family mask (and etc) please include same data in this table always.*/
+/* At least for parts already added in table. */
+/* File not required sql updates way to support, and always expected full */
+/* re-adding before command use. So need just adding new line to file. */
+
+/* Current table fill progress state: */
+/* SpellEffect.cpp from start until end of Spell::EffectInstaKill */
+
+DROP TABLE IF EXISTS spell_check;
+CREATE TABLE `spell_check` (
+ spellid mediumint(7) unsigned NOT NULL default '0',
+ SpellFamilyName smallint(5) NOT NULL default '-1',
+ SpellFamilyMaskA bigint(30) NOT NULL default '-1', /* 0xFFFFFFFFFFFFFFFF */
+ SpellFamilyMaskB int(10) NOT NULL default '-1', /* 0xFFFFFFFF */
+ SpellIcon int(10) NOT NULL default '-1',
+ SpellVisual int(10) NOT NULL default '-1',
+ SpellCategory int(10) NOT NULL default '-1',
+ EffectType int(10) NOT NULL default '-1',
+ EffectAura int(10) NOT NULL default '-1',
+ EffectIdx tinyint(3) NOT NULL default '-1',
+ Name varchar(40) NOT NULL default '',
+ Code varchar(40) NOT NULL default '',
+ PRIMARY KEY (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,Code)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8;
+
+INSERT INTO spell_check (spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,EffectType,EffectAura,EffectIdx,Name,Code) VALUES
+/* sorted by spell ids */
+/*id fm familyMaskA fmMaskB icon vis cat eff aur ef name code */
+(18788,-1, -1, -1, -1, -1, -1, 1, -1,-1,'Demonic Sacrifice', 'Spell::EffectInstaKill'),
+(18789,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill'),
+(18790,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill'),
+(18791,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill'),
+(18792,-1, -1, -1, -1, -1, -1, -1, -1,-1,'', 'Spell::EffectInstaKill');
+/* sorted by spell names */
+/*id fm familyMaskA fmMaskB icon vis cat eff aur ef name code */
+/* no at this moment */
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp
index af643b50ce4..9ae5678c752 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -163,6 +163,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "setaurastate", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetAuraStateCommand, "", NULL },
{ "setitemflag", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetItemFlagCommand, "", NULL },
{ "setvalue", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetValueCommand, "", NULL },
+ { "spellcheck", SEC_CONSOLE, true, &ChatHandler::HandleDebugSpellCheckCommand, "", NULL },
{ "spawnvehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSpawnVehicle, "", NULL },
{ "setvid", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSetVehicleId, "", NULL },
{ "entervehicle", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugEnterVehicle, "", NULL },
@@ -628,7 +629,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "go", SEC_MODERATOR, false, NULL, "", goCommandTable },
{ "learn", SEC_MODERATOR, false, NULL, "", learnCommandTable },
{ "modify", SEC_MODERATOR, false, NULL, "", modifyCommandTable },
- { "debug", SEC_MODERATOR, false, NULL, "", debugCommandTable },
+ { "debug", SEC_MODERATOR, true, NULL, "", debugCommandTable },
{ "tele", SEC_MODERATOR, true, NULL, "", teleCommandTable },
{ "character", SEC_GAMEMASTER, false, NULL, "", characterCommandTable},
{ "event", SEC_GAMEMASTER, false, NULL, "", eventCommandTable },
diff --git a/src/game/Chat.h b/src/game/Chat.h
index a2bc23217ac..55116bae711 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -148,6 +148,7 @@ class ChatHandler
bool HandleDebugEnterVehicle(const char * args);
bool HandleDebugSetValueCommand(const char* args);
bool HandleDebugSpawnVehicle(const char * args);
+ bool HandleDebugSpellCheckCommand(const char* args);
bool HandleDebugUpdateCommand(const char* args);
bool HandleDebugUpdateWorldStateCommand(const char* args);
diff --git a/src/game/Debugcmds.cpp b/src/game/Debugcmds.cpp
index eabf720f17b..e504865cbc3 100644
--- a/src/game/Debugcmds.cpp
+++ b/src/game/Debugcmds.cpp
@@ -36,6 +36,7 @@
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
+#include "SpellMgr.h"
bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args)
{
@@ -800,6 +801,14 @@ bool ChatHandler::HandleDebugSpawnVehicle(const char* args)
return true;
}
+bool ChatHandler::HandleDebugSpellCheckCommand(const char* /*args*/)
+{
+ sLog.outString( "Check expected in code spell properties base at table 'spell_check' content...");
+ spellmgr.CheckUsedSpells("spell_check");
+ return true;
+}
+
+
bool ChatHandler::HandleDebugSendLargePacketCommand(const char* /*args*/)
{
const char* stuffingString = "This is a dummy string to push the packet's size beyond 128000 bytes. ";
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp
index 9e0b69d0777..dfa25cef538 100644
--- a/src/game/SpellMgr.cpp
+++ b/src/game/SpellMgr.cpp
@@ -889,17 +889,6 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI
return false;
}
-bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId)
-{
- SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
- if (!spellproto) return false;
-
- for (int i = 0; i < 3; ++i)
- if (spellproto->EffectApplyAuraName[i] == auraType)
- return true;
- return false;
-}
-
SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
{
// talents that learn spells can have stance requirements that need ignore
@@ -2593,6 +2582,261 @@ void SpellMgr::LoadSkillLineAbilityMap()
sLog.outString(">> Loaded %u SkillLineAbility MultiMap Data", count);
}
+void SpellMgr::CheckUsedSpells(char const* table)
+{
+ uint32 countSpells = 0;
+ uint32 countMasks = 0;
+
+ // 0 1 2 3 4 5 6 7 8 9 10 11
+ QueryResult *result = WorldDatabase.PQuery("SELECT spellid,SpellFamilyName,SpellFamilyMaskA,SpellFamilyMaskB,SpellIcon,SpellVisual,SpellCategory,EffectType,EffectAura,EffectIdx,Name,Code FROM %s",table);
+
+ if( !result )
+ {
+ barGoLink bar( 1 );
+
+ bar.step();
+
+ sLog.outString();
+ sLog.outErrorDb("`%s` table is empty!",table);
+ return;
+ }
+
+ barGoLink bar( result->GetRowCount() );
+
+ do
+ {
+ Field *fields = result->Fetch();
+
+ bar.step();
+
+ uint32 spell = fields[0].GetUInt32();
+ int32 family = fields[1].GetInt32();
+ uint64 familyMaskA = fields[2].GetUInt64();
+ uint32 familyMaskB = fields[3].GetUInt32();
+ flag96 familyMask(familyMaskA, familyMaskB);
+ int32 spellIcon = fields[4].GetInt32();
+ int32 spellVisual = fields[5].GetInt32();
+ int32 category = fields[6].GetInt32();
+ int32 effectType = fields[7].GetInt32();
+ int32 auraType = fields[8].GetInt32();
+ int32 effectIdx = fields[9].GetInt32();
+ std::string name = fields[10].GetCppString();
+ std::string code = fields[11].GetCppString();
+
+ // checks of correctness requirements itself
+
+ if (family < -1 || family > SPELLFAMILY_PET)
+ {
+ sLog.outError("Table '%s' for spell %u have wrong SpellFamily value(%u), skipped.",table,spell,family);
+ continue;
+ }
+
+ // TODO: spellIcon check need dbc loading
+ if (spellIcon < -1)
+ {
+ sLog.outError("Table '%s' for spell %u have wrong SpellIcon value(%u), skipped.",table,spell,spellIcon);
+ continue;
+ }
+
+ // TODO: spellVisual check need dbc loading
+ if (spellVisual < -1)
+ {
+ sLog.outError("Table '%s' for spell %u have wrong SpellVisual value(%u), skipped.",table,spell,spellVisual);
+ continue;
+ }
+
+ // TODO: for spellCategory better check need dbc loading
+ if (category < -1 || category >=0 && sSpellCategoryStore.find(category) == sSpellCategoryStore.end())
+ {
+ sLog.outError("Table '%s' for spell %u have wrong SpellCategory value(%u), skipped.",table,spell,category);
+ continue;
+ }
+
+ if (effectType < -1 || effectType >= TOTAL_SPELL_EFFECTS)
+ {
+ sLog.outError("Table '%s' for spell %u have wrong SpellEffect type value(%u), skipped.",table,spell,effectType);
+ continue;
+ }
+
+ if (auraType < -1 || auraType >= TOTAL_AURAS)
+ {
+ sLog.outError("Table '%s' for spell %u have wrong SpellAura type value(%u), skipped.",table,spell,auraType);
+ continue;
+ }
+
+ if (effectIdx < -1 || effectIdx >= 3)
+ {
+ sLog.outError("Table '%s' for spell %u have wrong EffectIdx value(%u), skipped.",table,spell,effectIdx);
+ continue;
+ }
+
+ // now checks of requirements
+
+ if(spell)
+ {
+ ++countSpells;
+
+ SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell);
+ if(!spellEntry)
+ {
+ sLog.outError("Spell %u '%s' not exist but used in %s.",spell,name.c_str(),code.c_str());
+ continue;
+ }
+
+ if(family >= 0 && spellEntry->SpellFamilyName != family)
+ {
+ sLog.outError("Spell %u '%s' family(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellFamilyName,family,code.c_str());
+ continue;
+ }
+
+ if(familyMaskA != UI64LIT(0xFFFFFFFFFFFFFFFF) || familyMaskB != 0xFFFFFFFF)
+ {
+ if(familyMaskA == UI64LIT(0x0000000000000000) && familyMaskB == 0x00000000)
+ {
+ if(spellEntry->SpellFamilyFlags)
+ {
+ sLog.outError("Spell %u '%s' not fit to (" I64FMT "," I32FMT ") but used in %s.",spell,name.c_str(),familyMaskA,familyMaskB,code.c_str());
+ continue;
+ }
+
+ }
+ else
+ {
+ if(!(spellEntry->SpellFamilyFlags & familyMask))
+ {
+ sLog.outError("Spell %u '%s' not fit to (" I64FMT "," I32FMT ") but used in %s.",spell,name.c_str(),familyMaskA,familyMaskB,code.c_str());
+ continue;
+ }
+
+ }
+ }
+
+ if(spellIcon >= 0 && spellEntry->SpellIconID != spellIcon)
+ {
+ sLog.outError("Spell %u '%s' icon(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellIconID,spellIcon,code.c_str());
+ continue;
+ }
+
+ if(spellVisual >= 0 && spellEntry->SpellVisual[0] != spellVisual)
+ {
+ sLog.outError("Spell %u '%s' visual(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->SpellVisual[0],spellVisual,code.c_str());
+ continue;
+ }
+
+ if(category >= 0 && spellEntry->Category != category)
+ {
+ sLog.outError("Spell %u '%s' category(%u) <> %u but used in %s.",spell,name.c_str(),spellEntry->Category,category,code.c_str());
+ continue;
+ }
+
+ if(effectIdx >= 0)
+ {
+ if(effectType >= 0 && spellEntry->Effect[effectIdx] != effectType)
+ {
+ sLog.outError("Spell %u '%s' effect%d <> %u but used in %s.",spell,name.c_str(),effectIdx+1,effectType,code.c_str());
+ continue;
+ }
+
+ if(auraType >= 0 && spellEntry->EffectApplyAuraName[effectIdx] != auraType)
+ {
+ sLog.outError("Spell %u '%s' aura%d <> %u but used in %s.",spell,name.c_str(),effectIdx+1,auraType,code.c_str());
+ continue;
+ }
+
+ }
+ else
+ {
+ if(effectType >= 0 && !IsSpellHaveEffect(spellEntry,SpellEffects(effectType)))
+ {
+ sLog.outError("Spell %u '%s' not have effect %u but used in %s.",spell,name.c_str(),effectType,code.c_str());
+ continue;
+ }
+
+ if(auraType >= 0 && !IsSpellHaveAura(spellEntry,AuraType(auraType)))
+ {
+ sLog.outError("Spell %u '%s' not have aura %u but used in %s.",spell,name.c_str(),auraType,code.c_str());
+ continue;
+ }
+ }
+ }
+ else
+ {
+ ++countMasks;
+
+ bool found = false;
+ for(uint32 spellId = 1; spellId < sSpellStore.GetNumRows(); ++spellId)
+ {
+ SpellEntry const* spellEntry = sSpellStore.LookupEntry(spellId);
+ if(!spellEntry)
+ continue;
+
+ if(family >=0 && spellEntry->SpellFamilyName != family)
+ continue;
+
+ if(familyMaskA != UI64LIT(0xFFFFFFFFFFFFFFFF) || familyMaskB != 0xFFFFFFFF)
+ {
+ if(familyMaskA == UI64LIT(0x0000000000000000) && familyMaskB == 0x00000000)
+ {
+ if(spellEntry->SpellFamilyFlags)
+ continue;
+ }
+ else
+ {
+ if(!(spellEntry->SpellFamilyFlags & familyMask))
+ continue;
+ }
+ }
+
+ if(spellIcon >= 0 && spellEntry->SpellIconID != spellIcon)
+ continue;
+
+ if(spellVisual >= 0 && spellEntry->SpellVisual[0] != spellVisual)
+ continue;
+
+ if(category >= 0 && spellEntry->Category != category)
+ continue;
+
+ if(effectIdx >= 0)
+ {
+ if(effectType >=0 && spellEntry->Effect[effectIdx] != effectType)
+ continue;
+
+ if(auraType >=0 && spellEntry->EffectApplyAuraName[effectIdx] != auraType)
+ continue;
+ }
+ else
+ {
+ if(effectType >=0 && !IsSpellHaveEffect(spellEntry,SpellEffects(effectType)))
+ continue;
+
+ if(auraType >=0 && !IsSpellHaveAura(spellEntry,AuraType(auraType)))
+ continue;
+ }
+
+ found = true;
+ break;
+ }
+
+ if(!found)
+ {
+ if(effectIdx >= 0)
+ sLog.outError("Spells '%s' not found for family %i (" I64FMT "," I32FMT ") icon(%i) visual(%i) category(%i) effect%d(%i) aura%d(%i) but used in %s",
+ name.c_str(),family,familyMaskA,familyMaskB,spellIcon,spellVisual,category,effectIdx+1,effectType,effectIdx+1,auraType,code.c_str());
+ else
+ sLog.outError("Spells '%s' not found for family %i (" I64FMT "," I32FMT ") icon(%i) visual(%i) category(%i) effect(%i) aura(%i) but used in %s",
+ name.c_str(),family,familyMaskA,familyMaskB,spellIcon,spellVisual,category,effectType,auraType,code.c_str());
+ continue;
+ }
+ }
+
+ } while( result->NextRow() );
+
+ delete result;
+
+ sLog.outString();
+ sLog.outString( ">> Checked %u spells and %u spell masks", countSpells, countMasks );
+}
+
DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered)
{
// Explicit Diminishing Groups
diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h
index e087ec66502..2dde39abba9 100644
--- a/src/game/SpellMgr.h
+++ b/src/game/SpellMgr.h
@@ -193,6 +193,14 @@ inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect)
return false;
}
+inline bool IsSpellHaveAura(SpellEntry const *spellInfo, AuraType aura)
+{
+ for(int i= 0; i < 3; ++i)
+ if(SpellEffects(spellInfo->EffectApplyAuraName[i])==aura)
+ return true;
+ return false;
+}
+
//bool IsNoStackAuraDueToAura(uint32 spellId_1, uint32 effIndex_1, uint32 spellId_2, uint32 effIndex_2);
inline bool IsSealSpell(SpellEntry const *spellInfo)
@@ -256,8 +264,6 @@ bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool de
bool IsSingleTargetSpell(SpellEntry const *spellInfo);
bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2);
-bool IsAuraAddedBySpell(uint32 auraType, uint32 spellId);
-
extern bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS];
inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo)
@@ -1045,6 +1051,8 @@ class SpellMgr
public:
static SpellMgr& Instance();
+ void CheckUsedSpells(char const* table);
+
// Loading data at server startup
void LoadSpellChains();
void LoadSpellRequired();
diff --git a/src/shared/Util.h b/src/shared/Util.h
index ddbf968b2c2..496411d8be3 100644
--- a/src/shared/Util.h
+++ b/src/shared/Util.h
@@ -321,6 +321,11 @@ uint32 CreatePIDFile(const std::string& filename);
#ifndef _FLAG96
#define _FLAG96
+#ifndef PAIR64_HIPART
+#define PAIR64_HIPART(x) (uint32)((uint64(x) >> 32) & UI64LIT(0x00000000FFFFFFFF))
+#define PAIR64_LOPART(x) (uint32)(uint64(x) & UI64LIT(0x00000000FFFFFFFF))
+#endif
+
class flag96
{
private:
@@ -333,6 +338,13 @@ public:
part[2]=p3;
}
+ flag96(uint64 p1, uint32 p2)
+ {
+ part[0]=PAIR64_LOPART(p1);
+ part[1]=PAIR64_HIPART(p1);
+ part[2]=p2;
+ }
+
inline bool IsEqual(uint32 p1=0, uint32 p2=0, uint32 p3=0) const
{
return (