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.
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.
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
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:
Root (invisible to the player)
School/Domain
Tier 1 Spells
Tier 2 Spells
Tier 3 Spells
The root of the standard Exiles spell tree is spell ID 1.
These are the design principles we followed for the vanilla Exiles spell tree:
School/Domain tier is only schools of magic. These are all “Category” spells, and none of them have requirements set.
While spells have names internally (ie in the datatable), while player-facing they are unnamed and only represented by their descriptions. This helps to make the sorcery feel darker and less controlled.
Tier 1 spells have no corruption requirement, and all cost 1 Burlap Pouch (11150)
Tier 2 spells all require the caster to have at least 20% Corruption (400) and cost 1 Cloth Pouch (11151)
Tier 3 spells all require the caster to have at least 40% Corruption (800) and cost 1 Leather Pouch (11152)
Corruption added to the caster as a result of casting the spell is proportional to the spell’s power. Spells that have utility in combat generally yield more corruption.
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.
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
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.
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:
GetSpellcastTemplateID
function overridden on the weapon’s gameitem BP
The
SpellcastID
int stat set on the weapon item
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.
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.
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:
Specifies gameplay tags for what circumstances disable casting this spell.
Sorcery.Cast.Disable.Hazard
- Disallows casting in areas where "hazardous" spells are not allowed (currently none, but may be used in the future for example in areas where we want to prevent player griefing).
Sorcery.Cast.Disable.Mobility
- Disallows casting in areas where mobility should be restricted. Some vaults may block this tag.
Sorcery.Cast.Disable.InDungeon
- Disallows casting in dungeons/vaults.
Sorcery.Cast.Disable
- If the spell does not have any tag starting with “Sorcery.Cast.Disable”, then it should have this tag. Due to gameplay tag inheritance, the other disable tags implicitly add this tag.
Level designers can make use of these tags by placing out
BP_SorceryCastingBlockerVolume
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:
Mass Cull (ID 109) is a Tier 2 Thaumaturgy spell. Mass Cull’s parent is set to 16.
Spell 16 is Thaumaturgy Tier 2 (also known as “Escalate”). Its parent is set to 10.
Spell 10 is Thaumaturgy. Its parent is set to 1.
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.
The vast majority of spells should be of the type Executable. This is the type of spell where an effect happens.
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.
Rune Material
- An array of decal materials, these are faded onto the rock in the order they are set in the array.
Category Particle
- Particle played on the Arcane Staff on selecting that spell/category
Category Sound Cues
,
<Gender> Word of Power
- Played when a rune is selected. The Category entries are always played, and the Word of Power are only played for the relevant gender
These fields are unused: Rune Color, Icon, Icon Layers
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.
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.
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.
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.
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 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
Most of these fields should be fairly self explanatory. Some quick notes about them:
The fields with both a Value and a Curve option can have either or both fields set. By default it will use the Value option. If you override related functions (Eval***) in your spell blueprint, you can instead pull values from the Curve field.
Caster Add Corruption & Reagents map Spell Phase to their respective data.
For Add Corruption, it’s generally best to add them in Spell Execute
For Reagents, generally best to set them up in Start
There is more information about the advanced topic Spell Phases further down the page
Potency is unused by vanilla Exiles, but may be of value for mods, hence leaving it in. Put simply, it is a numeric value that you can assign in the SpellTable. Then, in the spell blueprint you can call
GetPotency
to fetch the appropriate value.
Spell Hint Type changes the displayed controls in the HUD while the spell is being cast. In the vast majority of cases this should be left as
Empty
.
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.
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
SpellExecute - Montage triggers the BP_SpellExecute notify
Start - When the spell initializes, before the montage starts. For reagents, these reagents must be successfully consumed for the spell to start casting (irrelevant of ToCast).
MontageStarted - When the spell montage starts playing
MontageEnded - Spell montage has ended for any reason
End - Spell is terminating, for any and all reasons
Custom1-3 - Surprise me!
In order to take advantage of a Custom phase, you will need to explicitly reference it in your spell blueprint, and setup the montage to trigger custom phases:
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).
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:
OnSpellExecute (phase: SpellExecute)
OnCustomNotify (no phase, recommend using Custom1-3, you must check requirements manually)
OnStartCasting (phase: Start) - The first event to run when a spell starts casting, even before the montage starts playing.
OnEndCasting (phase: End) - The final event to run when a spell has stopped casting for any reason, including interruption, character death, or successfully finished cast.
OnMontageStarted (phase: MontageStarted) - The montage has started playing on the caster
OnMontageFinished (phase: MontageEnded) - The spell was successfully cast
OnMontageInterrupted (phase: MontageEnded) - The spell cast has been interrupted
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:
OnLearned - The spell has been learned
OnUnlearned - The spell has been unlearned. At this point in time, the spell is already removed from the owner’s spell inventory, but Get Owner Character still returns a valid reference to the former owner.
OnAimLocationSet - This function can be overridden in order to adjust an aim location as it gets set.
OnInstantEffect - Used as a hook for special items like “Spell in a Bottle”
Replication is already handled in the back-end. Adhere to these guidelines and you’ll be fine:
Spells execute their events on server and client simultaneously. You shouldn’t need to (and can’t, literally, engine limitation) do any form of network replication within a Spell/SpellItem blueprint.
Always use
Get Owner Character
to get the caster.
Always use
Get Aim Location
to find out where the spell's effects should happen. If you want a custom aim location, override
OnAimLocationSet
.
Check
Is Server
before spawning anything, changing stats, dealing damage, or applying/removing buffs.
Check (Not)
Is Dedicated Server
before playing particles or sounds.
Things that have a duration (hazard areas, invisibility, blindness, etc) should be handled by spawning another actor or applying buffs/debuffs.
Never assume that the spell item will continue to exist after “
OnEndCasting
" is called. In many cases, it will cease to exist (yes, even if you keep a reference to it from elsewhere).
When you spawn an actor or apply a buff, immediately tell that actor/buff how potent its effect is and who spawned it. The actor/buff can cache those values until needed.
Stats are automatically replicated.
All persistence needs to be stored in item stats. Blueprint-style saved variables are not available in items.
Always use interfaces when interacting with other blueprints:
BPI_SpellcastSystem
to talk to the Spellcasting system. Through this interface you can interrupt the ongoing spell, queue up/start casting a different spell, interact with the aiming system, and more.
IsBaseBPCombatInterface
if you need to talk to
BaseBPCombat
(most of the time you won't).
If you need to check if a character is a player, use
IsPlayer?
Use
CanSpellDamageTarget
to determine whether a spell can deal damage to another entity
When spawning NPCs, the function
SummonNonThrallCompanion
is the new standard to do so:
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.
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!
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:
Replace the root component of your spell actor with BP_AC_AreaExclusiveSpell
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.
This is where the things that are worth noting but don’t fit anywhere else go.
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.
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.
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.
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: