Spellcasting Implementation

High Level Concepts & Information

Items, Inventory, and Persistence

Spells are a variant of “item” with their own dedicated inventory. Like items, spells use integer-based IDs and are defined in a datatable. Spells have persistence of their own using integer/float item stats. Note that some specific subsets of spells do not have persistence - this is noted wherever it occurs.

All spell-capable characters have a dedicated spell inventory accessible in either of these ways:

Learning and forgetting spells is as easy as calling “AddSpell” or “DeleteItemsByTemplateID” on the spell inventory. Do note that “AddSpell” only exists on SpellInventory.

Console Commands

These commands exist for the purpose of testing:

LearnSpell <ID>

ForgetSpell <ID>

LearnAllSpells

Teaches all spells in the spell tree. More on this later. If you cannot learn your spell via this command, then its Parent is either set wrong, or does not ultimately point to the root.

ForgetSpellCheats

Removes all non-category spells, except those known from the Tome of Kurak.

Data Templated, Blueprint Backed

Spells are templated from the SpellTable datatable as much as possible. The SpellTable primarily houses metadata about a spell (tree layout, description, corruption, montage, etc). Functionality is implemented in blueprints and is often unique to that spell.

The SpellTable is located at: /Game/Sorcery/Spells/SpellTable

The parent blueprint for all spell items is: /Game/Sorcery/Spells/BP_SpellItem

Tree Structure

Spells are organized into a conceptual tree. As a player, you see the structure of the tree by navigating through the various spells available when selecting a spell to cast.

Roughly in order, the standardized levels of the tree are:

  1. Root (invisible to the player)

  2. School/Domain

  3. Tier 1 Spells

  4. Tier 2 Spells

  5. Tier 3 Spells

The root of the standard Exiles spell tree is spell ID 1.

Tree Design Principles

These are the design principles we followed for the vanilla Exiles spell tree:

Spell Types

Spells fall into a limited set of types:

Executable spells are the individual spells that you cast. In the graph on the right, they are represented with green text, and in rounded bubbles. Executable spells CANNOT have children.

Category spells are branches of the tree. Each time the casting system presents the player with a choice of runes, they are navigating the tree from the point of view of a category spell. In the graph on the right, they are represented with blue text in square boxes. Category spells have children spells, and do not have gameplay effects.

Category spells MUST use the SpellClass BP_SpellCategory and their parent must be a category or root spell.

Root spells are a conceptual type of category spells that only exist at the root of the tree. In the graph, they are represented with golden text in a circle.

Root spells MUST use the SpellClass BP_SpellCategory_InWorld_Root and their parent must be ID 0.

Unless you are making your own separate spell tree, with its own staff item, there will be no reason to make or modify a Root spell. Even then, integrating into the existing casting tree will probably be better.

In the editor you may note a “Pact” spell type. This type is unsupported, subject to change without warning, and not fully implemented.

Outside the Tree

Uncategorized/parent-less spells are fully supported for use in custom content, to be cast by unique items/weapons, NPCs, or whatever else the design team/modders can think up (I have faith).

The system supports multiple roots, so it is possible to create your own spell tree that can live alongside yet separate from the vanilla Exiles spell tree. It’s probably not a great idea, but it is possible

Parts of a Spell

Trigger

All spells need some trigger or source to start them casting. The Arcane Staff weapon in Exiles already handles this functionality for the standard Exiles root (and children spells).

Some additional functionality exists that can be tapped into for custom content.

Most spells won’t need a custom trigger mechanism. Once you get a spell hooked up to the spell tree in the SpellTable, it’ll be accessible through the Arcane Staff weapon.

Combo Rules

The table DT_ComboRules has a SpellTemplateID column. If it has a value set, then executing this combo step will instead cast a spell.

Note: A value of 0 will pull the spell to cast from, in this order:

  1. GetSpellcastTemplateID function overridden on the weapon’s gameitem BP

  2. The SpellcastID int stat set on the weapon item

  3. The SpellcastID value of the weapon's template in the ItemTable

A value >= 1 will cast the specified spell directly.

The caster must know the spell that they are going to attempt to cast. Spells such as Root (ID: 1) are automatically learned when the player learns a child spell.

CastSpell

BPI_SpellcastSystem provides the event “CastSpell” which can be used to start casting a spell. This will route the spellcast action through the standard combat system.

Note that like any other casting trigger, the character needs to know the spell.

SpellTable

Location: /Game/Sorcery/Spells/SpellTable

The spell’s version of the ItemTable. A couple of fields are identical to the ItemTable, and most are self-explanatory. These fields in particular may require some additional explanation:

DisableTags

Specifies gameplay tags for what circumstances disable casting this spell.

Level designers can make use of these tags by placing out BP_SorceryCastingBlockerVolume

Parent

The Parent field is how the spell tree is built. Each spell points at its parent’s spell ID.

For example, traversing up the tree from Mass Cull:

  1. Mass Cull (ID 109) is a Tier 2 Thaumaturgy spell. Mass Cull’s parent is set to 16.

  2. Spell 16 is Thaumaturgy Tier 2 (also known as “Escalate”). Its parent is set to 10.

  3. Spell 10 is Thaumaturgy. Its parent is set to 1.

  4. Spell 1 is the root spell.

Spells that exist outside of the tree should have their Parent set to 0.

When a spell is learned, the player automatically learns all parent spells going up to the root.

This means that when adding a spell, you only need to teach the spell itself, and the player will be able to navigate to it through the tree.

SpellType

The vast majority of spells should be of the type Executable. This is the type of spell where an effect happens.

Spell Selection Art

The fields Rune Material, Category Particle, Category Sound Cues, Male Word of Power, and Female Word of Power are all used to create the spell selection experience with the Arcane Staff.

These fields are unused: Rune Color, Icon, Icon Layers

Autolearn

Teaches this spell to all players on login. Incompatible with DLCPackage - DLCPackage automatically does this already.

This flag doesn’t care if the player has unlocked sorcery via the Tome of Kurak

Autolearn spells do not have persistence.

Castable Without Being Learned

Some spells need to be able to be cast without first being learned. For example, let’s say you are making a sword that has a unique special ability to summon a demon (instead of a kick). The spell to summon the demon should have this flag.

Spells cast without being learned do not have persistence.

Preferred Radial Location

This field specifies where in the lineup of runes this spell should attempt to sort itself. Lower numbers sort to the left, higher numbers sort to the right.

Radial Visibility

This is perhaps the most confusing aspect of the spellcasting system. For the most case, simply copying what an equivalent existing spell has is the right approach. If you need the advanced info, check it out in the Misc Info section at the bottom of the page.

Cast Movement Type/Cast Movement Multiplier

Spells do not have to lock the player in place. For vanilla Exiles, we decided this was how we wanted to implement them, but the functionality exists to allow the player to move while casting. You can use this parameter to apply a movement speed multiplier while casting.

Cast Movement Type only has any effect if the spell’s Montage/Animation do not Lock Root Motion

This is an unsupported and deprecated feature. We make no guarantees that it will work as is, or even continue to exist in future updates.

Aim Mode / Lock Aim Trigger Flags

Aim Mode allows you to specify the “Aim Location” of the spell. This is automatically calculated for you by the system, and in your spell blueprint can be accessed by calling the function “Get Aim Location”.

Lock Aim Trigger Flags allow you to specify when the “Aim Location” should become fixed and not update any more. By default it does this when the first Spell Execute notification is received from the montage.

The spell aim can also be manually locked via the BPI_SpellcastSystem interface:

SpellHandled is currently unimplemented. If you need custom aiming, then please implement it in your spell blueprint instead of calling GetAimLocation

Corruption / Potency / Reagents / Hints

Most of these fields should be fairly self explanatory. Some quick notes about them:

Spell Montage

Montages for spells are setup similarly to attack/combo montages. Many of the same features are available (cancel windows, queue windows, hyper armor, etc). Please refer to documentation on combat montages for how to set those up.

Category spells do not use their montages, but still have them set to prevent the DataTable checks from complaining.

The Root spell has a custom montage that is tightly integrated with BP_AC_SpellcastFXHandler . If you need changes done to this, please contact ChrisMe.

There are 2 spell-specific anim notifies for use in Spellcasting montages (shown in purple in the screenshot):

BP_SpellExecute - Triggers the SpellExecute phase of the spell, which in turn runs OnSpellExecute in the spell blueprint. It is possible to have multiple of these in a single montage, in which case OnSpellExecute will run multiple times.

BP_SpellCustomNotify - Calls OnCustomNotify in the spell blueprint. The Payload option can point at any UObject asset, and you can specify any Name that you want.

Do not use “Execute” as the name in BP_SpellCustomNotify.

These anim notifies are instantaneous, not states like most combat system notifies.
They also will trigger on dedicated server by default.

SpellPhases

While being cast, a spell goes through several phases. Criteria and effects can be defined on a per-phase basis, and are checked/executed when the phase triggers.

In addition to the concrete triggerable phases, there are virtual phases. A virtual phase can be explicitly defined with custom values, or if left undefined, will automatically generate what the engine thinks are reasonable values.

Virtual Phase - UI

This virtual phase determines what is displayed in the UI when the player aims at a spell. It is purely visual, and should represent the spellcast in its totality.

When autocalculated, all defined concrete triggerable phases are summed together.

Spells with non-standard executions/timings (such as spells that SpellExecute more than once) will have the wrong value autocalculated for this phase. Please explicitly define the value.

Virtual Phase - ToCast

In order the start casting this spell, the player must meet the criteria in ToCast. They do not need to commit to the criteria (ie reagents will not be consumed), they simply must meet it.

Autocalculated by summing Start and SpellExecute together.

This could be used with non-standard spells, like a channeled spell, to require the player have enough reagents pay the start cost and the first 2 ticks. The remaining 4 ticks can continue to execute if the player has enough reagents.

Concrete Phases

Spell Blueprints

Spell blueprints are where a spell’s functionality is implemented. It is possible to reuse a blueprint for multiple rows in the SpellTable (Category spells already do this).

Event Driven

On the whole, the spell system is event driven. It is the system back-end and montage’s responsibilities to trigger events at the correct time, and the spell blueprint to catch and respond to those events.

In BP_SpellItem, you can see a list of all available events to hook into in the “Implementable Events” graph. Generally speaking, the events correspond to a spell phase, and will execute after fulfilling the phase’s requirements. These are the events currently available:

The behavior when a phase’s requirements are not met can be customized by overriding the function “InterruptWhenNotEnoughReagents”.

Additionally, there are passive events that are not related to a spell’s phase:

Best Practices & Network Replication

Replication is already handled in the back-end. Adhere to these guidelines and you’ll be fine:

Spell Actors & Buffs

Many spells create an ongoing effect - either by spawning an actor into the game world, or applying a buff to a character. Applying a buff is fairly simple and works within the existing buff system, so it is not covered here. Spell actors require custom setup and, as they all have unique effects, have no standard form.

Lightning Storm is a perfect example for how the spell blueprint looks:

When the spell executes, it limits the spawning logic to only run on the server (so that clients don’t spawn their own copy of the lightning storm that does no damage).

It then takes the Aim Location and the Guild ID of the caster and uses that information to spawn the Lightning Storm Actor at the Aim Location. The Guild ID is used to prevent the Lightning Storm from damaging allied players.

Finally, it registers the Lightning Storm Actor for concurrency limiting.

Concurrency Limiting

Sometimes it will be desirable to limit how many instances of a spell actor a player can have active at a single time. In order to do this, the RegisterConcurrentActor function allows tracking of spawned spell actors on an arbitrarily assigned category basis.

When the number of spawned concurrent actors in the specified category exceeds the category limit, the oldest actor is deactivated. You can implement a custom selection process by overriding SelectConcurrentActorToDeactivate in your spell blueprint. Note that if spell actors from different spell blueprints share the same concurrency category, it is the spell that triggered cleanup that will have the choice of which actor to deactivate.

In order to allow for a graceful deactivation of an existing spell actor (and avoid graphical popping or orphaned gameplay effects), the system does its best to find the spellitem that spawned the actor that is being deactivated, and call DeactivateOverlimitConcurrencyActor on it. In the event that is not feasible (if perhaps the spell was cast without being known, or has since been unlearned), it will instead ask the triggering spellitem. Failing that, it simply outright destroys the actor.

A spell actor can be registered with multiple concurrency categories at the same time.

You can ensure that your spell actor will catch any non-optimal concurrency deactivation events by using the Event Destroyed to initiate cleanup. As a bonus, when you Destroy Actor from within itself when it is done with its lifespan, it’ll cleanup that way too!

Area Exclusivity

Some spells don’t overlap nicely. The solution to this is the Area Exclusivity system, which allows conflicting spell actors to destroy each other in an efficient manner.

Setting up a spell actor for Area Exclusivity is fairly easy:

  1. Replace the root component of your spell actor with BP_AC_AreaExclusiveSpell

  2. Add a Sphere Collision component under it. Configure it such:

    • Set Collision → Collision Preset to NoCollision

    • Set Navigation → Area Class to NavArea_Default

    • Set Shape → Sphere Radius to the radius of the actor’s effect area

Now, when this spell is cast overlapping another spell that it conflicts with, the prior spell will be destroyed.

If you implement the interface BPI_AreaExclusiveEffect on the spell actor, you can add the GracefullyDespawn event to handle despawning instead of simply being destroyed.

Misc Notes & Advanced Info

This is where the things that are worth noting but don’t fit anywhere else go.

Combat Integration

Integration of spellcasting into the existing combat system is handled by BaseBPCombat and BP_SpellcastingSystem.

The actual casting and navigation of the spell tree is handled primarily by BP_AC_SpellcastFXHandler and BP_AC_SpellcastPlayerInput.

For the most part, all of these pieces will “just work” and you won’t need to worry about them.

SpellTable - Radial Visibility - Advanced

The entries are split into “Executable” and “Category”. You should only use the entries that are applicable to the type of spell you are making.

Executable: Unlearned

When this flag is set, the spell does not need to be learned by the player to appear in spell selection. This can be useful if you want to show players that a spell exists at a location, and they simply have not learned yet how to cast it.

This is how you make a “CastableWithoutBeingLearned” spell appear in the radial.

Executable: Uncastable is ignored for spells made visible in the radial by this flag.

Executable: Uncastable

When this flag is set, the player does not need to meet the casting requirements for the spell to appear in the spell selection. Enabled by default.

Category: Any child known

The category (f.ex: “School: Thaumaturgy”) will be visible as long as any spell in that category is known.

Category: Any child castable

The category will be visible as long as any spell in that category meets the requirements to be cast, irregardless of whether it is known.

Category: Any child visible

The category will be visible as long as any spell in that category meets its own personal requirements to be visible in the category.

Category: Recursive children searching

Modifies the other radial visibility category flags so that if there are any categories nested inside of this category, it will search them recursively using the current category’s radial visibility settings.

Talk to a coder or ChrisM before setting this flag, as it has potential to impose an undesirable performance cost.

Dynamic SpellTable Data

Many aspects of the data obtained from the SpellTable can be adjusted/modified in the spell blueprint. Generally this is done by overriding a function and changing what is returned:

Dynamic SpellTable Data do not work for unlearned spells during spell selection.

Gather Preloadable Assets

The logic surrounding this function was cut prior to launch. If you have exceptionally heavy/long to load assets that your spell utilizes, please use this approach instead: