diff options
| author | Kitzunu <24550914+Kitzunu@users.noreply.github.com> | 2021-04-15 12:04:34 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-04-15 12:04:34 +0200 |
| commit | 890792004063f3754be3a1ed0e36c6787f0c9351 (patch) | |
| tree | 4cf1bdd17aff16f86cd9474c0fcc4bd78279e41b /docs/Spell_system.md | |
| parent | d13a60f1aac8c132e781e6de8a620f251e0ac889 (diff) | |
| download | wiki-890792004063f3754be3a1ed0e36c6787f0c9351.tar.gz wiki-890792004063f3754be3a1ed0e36c6787f0c9351.tar.bz2 wiki-890792004063f3754be3a1ed0e36c6787f0c9351.zip | |
refactor(guide): Refactor installation guide (#427)
* Update Spell_system.md
* refactor(guide): Refactor installation guide
* Update Requirements.md
* Update Requirements.md
* da
* Update windows-requirements.md
* d
* test
* test
* windows
* windows
* help
* note to self: next windows
* core and server installation
* db
* finally
* Update client-setup.md
* Update final-server-steps.md
* Update windows-core-installation.md
* Update core-installation.md
* last
* Update database-installation.md
* Update database-installation.md
* Update server-setup.md
* Rename Requirements.md to requirements.md
* Y
* f
* ad
* Update windows-core-installation.md
* d
* Update installation-guide.md
* Update linux-requirements.md
Ubuntu installation with seperate MariaDB and Oracle MySQL
* Remove account
* Update installation-guide.md
* Update installation-guide.md
* Update installation-guide.md
Co-authored-by: Nolt <nolt@users.noreply.github.com>
Co-authored-by: Francesco Borzì <borzifrancesco@gmail.com>
Diffstat (limited to 'docs/Spell_system.md')
| -rw-r--r-- | docs/Spell_system.md | 270 |
1 files changed, 20 insertions, 250 deletions
diff --git a/docs/Spell_system.md b/docs/Spell_system.md index cd5f00c..5aaa00f 100644 --- a/docs/Spell_system.md +++ b/docs/Spell_system.md @@ -1,260 +1,30 @@ -# Spell system +##### This article was created and ported from Trinity core's wiki since their system is identical +# *Introduction: How do spells work in Azerothcore?* +This article is meant to introduce you to the concept of a spellcast. It's an important thing for every scripter to know in brief how this works because spells are one of the main "channel of interaction" between objects ingame. Whenever you pick someone's pocket as a rouge, init a trade with other player, use an item or simply hurt someone's ass using magic - a spell is involved. Each spell is identified by it's spellId entry in spell.dbc. SpellInfo class is a C++ interface to that table (among other related dbc and sql tables). Most of the cast process is handled inside a Spell class representing dynamic state of each cast. Behavior of that class can be affected using SpellScript class objects. A mechanism with such big impact on everything is complex, so to simplify our view of what's going on, let's split it into several phases. Ingame, they occur in the same order as in this article. -### How do spells work? +# *Spell prepare phase* +This phase is responsible for **handling the "request"** from a spell caster - object which has initiated the cast. A request can be made from **player** client (see SpellHandler.cpp), or from the **gameobjects** themselves. (GameObject::CastSpell, Unit::CastSpell, etc). Here, the worldserver validates and prepares an **explicit target** for whom the spell is targeting. -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. +### **What is an explicit spell target?** +Simply put, an explicit spell target is the target affected by a spell. For example, the explicit spell target of a Warrior's Heroic Strike is the current selected unit, be it a player or a non-playable character. -### Database +Server side explicit target is selected by the first parameter of Unit::CastSpell function. -The database part includes data extracted from client loaded from dbc files: +However, not all spells require explicit targets! An example can be an area of effect spell, which no matter what you select ingame, targets around you are affected. Therefore, explicit spell target can be **null**, and that's natural. -[Spell](Spell).dbc, ... +In case of many other spells, an explicit target can be also a position on the ground, which is the case for *blizzard* mage spell. -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: +### **What decides whether spell needs explicit target?** +As you may have read in Spell system , database decides about most of the spell's behavior, so is in this case. Spell.dbc's target, implicitTargetA, implicitTargetB decide about that, briefly - flag in target field or implicitTargetX set to one of TARGET_XXX_TARGET (TARGET_UNIT_TARGET for example) target modes mean spell requires an explicit target. The most important consequence of this is that you CANNOT cast anything you want on anything you wish, it's all precisely decided by client database and only thing you can do is to follow those rules. Besides explicit spell target validation and preparation there are also done some premature checks for correctness of cast request - a Spell::CheckCast funcion is called (which uses db data for checks), there are also some checks done in cast request functions unfortunately spread all over the core. On failure, error is sent, otherwise spell is enqueued for execution on next object update, or handled immediately in case of triggered spells. Triggered is a kind of "system" cast, it's not precisely defined what that means exactly as the term is overused too much to have any specific meaning, but setting the triggered cast flag alters a lot of spell's behavior and db data interpretation. As an example, spells cast with triggered flag (a parameter of CastSpell()) will ignore item requirements set in dbc, some caster state requirements and so on. After spell is prepared successfully casting begins, you see castbar ingame. You can follow the code of this phase by searching for Spell::Prepare function occurrences in core. -[spell\_dbc](spell_dbc) ****Note: ****Not longer used since Warlords of Draenor Expansion (see [Hotfixes](https://trinitycore.atlassian.net/wiki/display/tc/Hotfixes), at least spell, spell\_misc & spell\_effect) +# *Spell prepare phase* +Once castbar is full (in case of instant spells immediately) the cast phase begins. During that phase there are done several things, one of the most important is: spell implicit target selection. Spell implicit targets are ingame objects which will be affected by your spell. Their selection is based on implicitTargetX columns of Spell.dbc, conditions table and OnObject(Area)TargetSelect hooks of SpellScript. There's nothing done to the targets at this point (please don't break that rule while using scripts - it's unblizzlike), they're just selected for future processing. At the beginning of this phase another important thing is done: second call of Spell::CheckCast (which you can affect by using OnCheckCast spell script hook and conditions db table). Also, required items and power is taken in this phase from the caster. You can hook on this phase using BeforeCast, OnCast, AfterCast hooks. the whole phase is handled in Spell::cast. -[spelldifficulty\_dbc](spelldifficulty_dbc) - -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: - -[conditions](conditions) - --CONDITION\_SOURCE\_TYPE\_SPELL\_IMPLICIT\_TARGET - allows you to define requirements for implicit area targets of the spell, only matching targets will be added to spell target list - --CONDITION\_SOURCE\_TYPE\_SPELL - allows you to define requirements for caster/explicit target of the spell, if not met cast will fail - --CONDITION\_SOURCE\_TYPE\_SPELL\_PROC - allows you to define requirements for actor and actionTarget, if not met proc will fail - --CONDITION\_SOURCE\_TYPE\_SPELL\_CLICK\_EVENT - --CONDITION\_SOURCE\_TYPE\_SPELL\_LOOT\_TEMPLATE - --CONDITION\_SOURCE\_TYPE\_VEHICLE\_SPELL - -[spell\_area](spell_area) - defines if aura should be applied to an object in given area - -[spell\_enchant\_proc\_data](spell_enchant_proc_data) - defines behavior of item enchant procs - -[spell\_group\_stack\_rules](spell_group_stack_rules) - defines stacking rules for each group - -[spell\_group](spell_group) - allows grouping spells for convenient handling - -[spell\_learn\_spell](spell_learn_spell) - defines that learning a given spell should learn you other spell, if not provided in dbcs - -[spell\_linked\_spell](spell_linked_spell) - allows simple triggering spell casts on certain spell events - -[spell\_pet\_auras](spell_pet_auras) - defines if a certain aura on pet owner should be linked to other aura on pet - -spell\_proc\_event - defines a requirement which needs to be passed for proc event to occur - -[spell\_proc](spell_proc) - same as spell\_proc\_event, table in development - -[spell\_required](spell_required) - defines requirements for learning a spell - -[spell\_ranks](spell_ranks) - implements a concept of spell rank ingame - -[spell\_script\_names](spell_script_names) - binds spells to their spell scripts - -[spell\_target\_position](spell_target_position) - allows setting target location for certain spells - -[spell\_threat](spell_threat) - contains threat data for spells - -Developers are expected to fill those tables correctly to make spells work correctly. Please follow links for more details about each table. - -### Spell scripts - -Spell scripts are a way to provide a means of implementing more complicated behavior, which couldn't be placed in db. - -#### Generic structure - -``` cpp -// naming scheme: spell_[of who]_[spell name], for example spell_warl_banish, spell_deathwhisper_mana_barrier -class spell_class_your_name_here : public SpellScriptLoader -{ - public: - // should be the same as class name - name in "" is the one used by db in SpellScriptNames table - spell_class_your_name_here () : SpellScriptLoader("spell_class_your_name_here ") { } - - // SpellScript object - alters behavior of Spell object ingame - class spell_class_your_name_here_SpellScript : public SpellScript - { - PrepareSpellScript(spell_class_your_name_here_SpellScript); - // bool Load() {return true;} - optional - allows loading script only in specific circumstances - see Optional loading below - // bool Validate(SpellInfo const* /*spellInfo*/) - optional - allows checking data integrity - see Validation tests below - /* hooks here - described below*/ - - void Register() {/*hook registration here*/} - }; - // function creating script object - must be exactly like this one, but with your class name - SpellScript* GetSpellScript() const - { - return new spell_class_your_name_here_SpellScript(); - } - - // AuraScript object - alters behavior of Aura object ingame - class spell_class_your_name_here_AuraScript : public AuraScript - { - PrepareAuraScript(spell_class_your_name_here_AuraScript); - - // bool Load() {return true;} - optional - allows loading script only in specific circumstances - see Optional loading below - // bool Validate(SpellInfo const* /*spellInfo*/) - optional - allows checking data integrity - see Validation tests below - /* hooks here - described below*/ - void Register() {/*hook registration here*/} - }; - // function creating script object - must be exactly like this one, but with your class name - AuraScript* GetAuraScript() const - { - return new spell_class_your_name_here_AuraScript(); - } -}; -// this function call creates script loader object for us - seek that function at the end of the script file you're adding to -// if there's no such function see How-to_CustomScript -void AddSC_class_spell_scripts() -{ - // an entry for our spell script loader, there may be other entries aswell - /* ... */ - new spell_class_your_name_here(); -} -``` - -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](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](CustomScript) - -``` cpp -class spell_warl_unstable_affliction : public SpellScriptLoader -{ - public: - spell_warl_unstable_affliction() : SpellScriptLoader("spell_warl_unstable_affliction") { } - - class spell_warl_unstable_affliction_AuraScript : public AuraScript - { - // required PrepareAuraScript macro - PrepareAuraScript(spell_warl_unstable_affliction_AuraScript); - /*...*/ - }; - AuraScript* GetAuraScript() const - { - return new spell_warl_unstable_affliction_AuraScript(); - } - /*...*/ -}; -/*...*/ -void AddSC_warlock_spell_scripts() -{ - new spell_warl_unstable_affliction(); -/*...*/ -} -``` - -#### Validation tests - -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: - -``` cpp - bool Validate(SpellInfo const* /*spellInfo*/) - { - if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R1) || !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_IMPROVED_HEALTHSTONE_R2)) - return false; - return true; - } -``` - -#### Optional loading - -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: - -``` cpp - bool Load() - { - return GetCaster() && GetCaster()->GetTypeId() == TYPEID_PLAYER; - } -``` - -#### AuraScript hooks - -TODO"){.emoticon .emoticon-smile} - -#### SpellScript hooks - -TODO"){.emoticon .emoticon-smile} - -#### Good practices: - --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 - -``` cpp -// 781 - Disengage -class spell_hun_disengage : public SpellScriptLoader -``` - -#### Practical example on spell scripts - -``` cpp -// 781 - Disengage -class spell_hun_disengage : public SpellScriptLoader -{ - public: - spell_hun_disengage() : SpellScriptLoader("spell_hun_disengage") { } - class spell_hun_disengage_SpellScript : public SpellScript - { - PrepareSpellScript(spell_hun_disengage_SpellScript); - SpellCastResult CheckCast() - { - Unit* caster = GetCaster(); - if (caster->GetTypeId() == TYPEID_PLAYER && !caster->isInCombat()) - return SPELL_FAILED_CANT_DO_THAT_RIGHT_NOW; - return SPELL_CAST_OK; - } - void Register() - { - OnCheckCast += SpellCheckCastFn(spell_hun_disengage_SpellScript::CheckCast); - } - }; - SpellScript* GetSpellScript() const - { - return new spell_hun_disengage_SpellScript(); - } -}; -``` - -Going through line to line: - -TODO"){.emoticon .emoticon-smile} - -- [How do spells work?](#Spellsystem-Howdospellswork?) -- [Database](#Spellsystem-Database) -- [Spell scripts](#Spellsystem-Spellscripts) - - [Generic structure](#Spellsystem-Genericstructure) - - [Binding your script to a spell/spells](#Spellsystem-Bindingyourscripttoaspell/spells) - - [Validation tests](#Spellsystem-Validationtests) - - [Example - checks for existence of spells the script is using later:](#Spellsystem-Example-checksforexistenceofspellsthescriptisusinglater:) - - [Optional loading](#Spellsystem-Optionalloading) - - [Example - we want to script only player casts:](#Spellsystem-Example-wewanttoscriptonlyplayercasts:) - - [AuraScript hooks](#Spellsystem-AuraScripthooks) - - [SpellScript hooks](#Spellsystem-SpellScripthooks) - - [Good practices:](#Spellsystem-Goodpractices:) - - [Practical example on spell scripts](#Spellsystem-Practicalexampleonspellscripts) +# *Spell launch phase* +Launch comes immediately after cast is finished, it's the moment when spell "leaves hands of the caster", you can notice that for spells which are flying for some amount of time before they reach targets. Some spell effects are handled during this phase, for example damage from caster must be calculated at the moment spell is being launched instead of the moment of hit, charge must begin at launch, etc. When (and if) your spell reaches it's target (immediately for spells with no delay) the next (hit) phase begins. Launch is handled in Spell::HandleLaunchPhase. You can affect execution of spell effects during this phase by using OnEffectLaunch and OnEffectLaunchTarget hooks. The difference between those two is that OnEffectLaunch is called once for each spell effect the spell has, regardless of it's targets; while OnEffectLaunchTarget is called once for every available implicit target of the spell (selected in cast phase before). +# *Spell hit phase* +Hit is the phase where most of effects of the spell are handled. Spell effects are small operations affecting either caster of the spell or implicit spell targets. Each spell entry has EffectX columns (in dbc) which contain id of effects that spell applies to it's target. Each effect has it's own separate target list, so a single spell can operate on many different objects at once. For example, a spell may apply heal effect to your friends and give you a buff aura at once. Similarly to launch phase, you can override behavior of spell effects in this phase by OnEffectHit and OnEffectHitTarget SpellScript hooks. You can also use: BeforeHit, OnHit and AfterHit hooks. Important thing during this phase is spell proc - special event trigger system which does things accordingly to what auras are applied to caster and implicit target of the spell. Some parts of proc system should be handled by the proc system in other spell cast phases, but that's not implemented atm. +# *Spell finish phase* +The finish phase occurs after all spell targets are processed, it's just a cleanup, just a few relevant events happen in this phase. |
