Behavior of each spell is defined in 3 places: database, spell scripts, and spell system code interpreting that database. Mortals should care mostly about first two which are discussed in this article.
The database part includes data extracted from client loaded from dbc files:
As this data is extracted from client. It doesn't contain spells needed only serverside, those need to be added inside supplementary db tables in core which are emulating dbcs and have the same structure:
spell_dbcNote: Not longer used since Warlords of Draenor Expansion (see Hotfixes, at least spell, spell_misc & spell_effect)
Data stored in dbcs is extracted from client, ergo it may not be interpreted by TC properly, so it's meaning is a subject of changes and fixing, as dbcs contain most of the data needed by spell system to work.
Data not needed by client for all spells is stored in following tables of world db:
Developers are expected to fill those tables correctly to make spells work correctly. Please follow links for more details about each table.
Spell scripts are a way to provide a means of implementing more complicated behavior, which couldn't be placed in db.
As you see, there are 2 kinds of scripts: SpellScript and AuraScript. You may define both or just one of them in a single script.
Binding your script to a spell/spells
To make sure your script is executed you have to do two things (these are c++ requirements, I wish this was simpler).
create spell_script_names entry - As you've seen above each SpellScript/AuraScript is put inside of SpellScriptLoader class. Constructor of that class contains single parameter, that parameter is the value of ScriptName column inside spell_script_names table. The table consists of pairs (spellIdToWhichYouBindTheScript, "spell_script_you_re_binding"). You can bind one script to many spells or many scripts to one spell. The latter is needed for example when you want to logically separate scripts of a spell when spell is affected by different talents.
properly override AuraScript* GetAuraScript() const or SpellScript* GetSpellScript() const to create objects of your script
make sure SpellScriptLoader of your script is created - add a call in AddSC_XXXXX function. For more details on that, see CustomScript
Overriding validate function allows you to check if data you're using in your script is present on core load. It's often used to check if spell used in script is still in database, as spells are often removed from client database when client version changes.
When this function will return false, a "Spell `%u` did not pass Validate() function of script `%s` - script will be not added to the spell" error will appear in log.
You don't need to check for presence of effects you bind hooks to - it's checked automatically and reported on startup by core for your convenience.
Example - checks for existence of spells the script is using later:
Sometimes you may decide that you want your script only be present in some cases, bool Load() provides you a way to do so. When the function returns false your script will not be loaded to the object (Aura or Spell) - it will have no effect
Example - we want to script only player casts:
-always use Validate(SpellInfo const*) function to check all data you use in script for presence
-check for runtime errors, they often signal bad script behaviors
-put full spell name in comment before the script - this helps finding the script by spell name