-
Notifications
You must be signed in to change notification settings - Fork 0
2. Introduction to Spells & Spell Trees
This page will go into detail on how data driven spells and spell trees work. A good way to understand all of this is to read the introduction followed by the examples. There is also a small "Getting Started" guide at the bottom.
Spell trees are what the name suggests. They are made of nodes that represent entries of spells in the tree. Every spell tree starts with a root node and nodes may have any amount of children nodes attached to them. Once you unlock a node you get access to this node's child nodes and may unlock them as well. It is important to understand that spells can be in multiple spell trees and also multiple times in the same spell tree with different configurations.
Every node in a spell tree has requirements to be able to unlocked together with the cost in xp levels and you having to have the parent unlocked already, of course. These requirements allow you to restrict the unlocking further so the player has to achieve more than just grinding xp. There are two different kinds of requirements, however:
- Hidden requirements must be fulfilled in order to be able to see the spell node in the spell tree at all. If the hidden requirements of the root node are not met then the entire spell tree will not show up for the player.
- Learn requirements must be fulfilled in order to be able to learn the spell once you already see it.
The basic spell trees of this mod barely use hidden requirements as transparency and ease of use was a goal. But this should not stop anyone from finding nice uses for this feature.
Another important aspect is synchronization. Clients do not need to have the same spell tree file contents as the server counter part. Instead, they get synchronized on demand (when the client opens the spell progression menu in the enchantment table). This not only allows servers to change spell trees on demand without clients needing any adjust ments, but also allows servers to hide features of spell trees until they are discovered by the player (eg. by using hidden requirements). So you can give the clients empty spell tree files and then only fill the contents of these files on the server to hide the spell progression to players up to the point they discovered it themselves.
Finally, please understand that you can modify spell trees (which is very easy to do) without having to enter the world of data generated spells which is far more advanced and complex. You can combine any collection of spells and then make your own spell trees out of them without ever directly touching the files of these spells.
The node ID is used to save the progression of a player and also their equipped spells. It uniquely identifies a node inside a spell tree. This ID must be unique within the spell tree and should never change. By eg. swapping the node ID of two different nodes A and B you basically swap said aspects for players: Every player who had learned node A now has learned node B and vice versa (this is not a problem if the player had learned both already, of course) and if they have node A equipped in a certain spell slots they now have node B equipped in it instead and vice versa.
It is recommended to not leave out this element (it is optional in JSON) unless you know for certain that you are not going to edit this spell tree ever again. In this case the mod auto assigns them. A recommended scheme for choosing these IDs is by simply starting at 1 and counting up. You can always add a new spell node anywhere in the tree without any problems that way.
To summarize:
- Within a spell tree, every spell node ID must be unique.
- Start with ID
1and simply count up. - If you remove a spell from a spell tree, do not re-use the node ID again.
This section is about spells. Again, you do not need to work through this if you only intend on editing spell trees.
Important node already: You have the power to make spells however you like. But this also means that you have the power to lag out the server, destroy the world or even in very rare circumstances crash the game (everything is coded in a way that should always avoid crashes but there may have been slight oversights that can result in a crash in very specific circumstances; please report an issue in case this happens).
First we talk about the when and the what (eg. when a spell does something and what it does). Every spell is made up of spell actions that are sequentially executed. These can be anything, eg. the first action picks the entity the spell owner is currently looking at and the second action applies damage to it. Every spell action has an activation defined. Basically, depending on which activation is active spell actions are either executed or ignored. The initial activation that starts the whole execution is also called an event. Events can be eg. active when a spell is activated by its owner, on_equip when a spell is equipped into a spell slot, or on_unequip when a spell is unequipped, respectively, to name a few. Every spell has a list of events it hooks into and events that are not listed in there are ignored. A lot of actions allow you to interact with your own activations like when certain conditions are met or when a timer has run out activation x is activated.
If certain requirements of a spell action are not met then the spell action simply does nothing. Examples are requiring only exactly 1 target inside a target group, requiring specific elements to be set (in JSON) and so on. This is mentioned in the documentation for each spell action type.
Next up we talk about the who and how. Spell actions typically interact with target groups either by basically finding/defining them or by doing something with/to them. These target groups contain targets as the name suggests. There are different kind of target types that are explained below and certain actions find/define/require certain target types, eg. the damage action requires targets of type living_entity (eg. players, cows, zombies, the dragon, but not paintings, armor stands, or projectiles). Some spell actions also require single targets which simply means that there may only be a single target within the target group.
These are all the existing target types:
-
position: Anything in a world that has a position, eg. block positions or entities. -
entity: Entities, eg. Armor Stands, Arrow Projectiles, Thrown Potions, Chickens, Spiders. -
living_entity: Entities with health, eg. Players, Spiders, the Dragon, Sheep. -
player: Players. -
static: Anything in a world that has a static position, eg. block positions. -
item: Items that are usually part of some inventory.
The inheritance chain is as follows (see the first point for explanation):
-
static>position(every target of typestaticis also automatically of typepositionbut not the other way around). -
player>living_entity>entity>position.
Spell actions require you to supply values to configure them. These value types have names that are listed here:
-
int: integers, eg.0,7,-87. -
double: floating point numbers, eg.0.0,1.7,-43.467,2. -
vec3: 3-dimensional vector made of 3doubleelements representing any position or motion in a world. -
boolean: Logical values, eg. eithertrueorfalse. -
tag: NBT compound tags (see Minecraft wiki). -
string: A sequence of characters, eg.'hello','ABCdef123_','Let us also do some Spaces!!'.
The last important concept to talk about are variables. These allow you numerous different things that include defining a variable and its value (of course), using them for spell actions instead of writing direct values, or getting certain values from targets (eg. player health, item count, entity uuid) that are deposited in variables. Almost every value of a spell action allows you to substitute it with a variable. You can also use them to make tiered spells - by defining variables for the entire spell you can set them dependent on their position in a spell tree.
If a spell action has an element that has the prefix d/ you can always subtitute it with a variable. You do that by supplying a string instead of format <<variable_name>>. This prefix also allows you to do in-line math expressions (eg. << a + b >>) which is explained on another page in this wiki.
JSON keys can look overwhelming but they are not hard to grasp and are instead meant to help you without having to constantly look at documentation. Prefixes are delimited by / and the name of the key is always behind the last one.
The prefixes (eg. s1, s2, s3, n1, n2, t1, t2, etc.) are primarily there to order the elements and to help with keeping a proper oversight. They have no meaning except that s is used for Spells, n is used for Spell Nodes, and t is used for Spell Trees.
-
a/: The activation to interact with (eg. enable or disable).- The activation that a spell ations requires to be fired will always just be named
activation
- The activation that a spell ations requires to be fired will always just be named
-
ad/: The delayed activation to interact with.- These types of activations happen after a certain amount of time and restart the entire list of actions from the top (eg. targets and variables are reset)
-
t/: A target group containing a single target to interact with. -
ts/: A target group containing any amount of targets to interact with. -
td/: A target group to add a new target to. You may leave this empty to not add it to any target group. -
d/: Allows you to substitute a value with a variable (see the section about variables) or with an in-line math expression. -
[variable-type]/: Tells you what type is expected. -
var/: The variable to deposit a value in. You may leave this empty to not assign this value to any variable.
This action replenishes mana for all living entities in a given target group by a set amount.
Explanation:
-
d/double/mana_amount: The amount of mana replenished.-
d/: You may substitute this value with a variable. -
double/: Adoublevalue is expected. -
mana_amountThe name of the key (i.e. something that describes it further).
-
-
ts/targets: The target group to replenish mana for.-
ts/: The target group may be of any size. -
targets: The name of the key (i.e. something that describes it further).
-
Next, built-in values are explained. These are defined for certain events and circumstances but you can always modify or interact with them however you like (if you edit eg. a variable that came built-in the change will only apply for this context so the next time the spell is fired the variable comes with it's proper value again). Not all of them are listed here as some are only interacting with certain spell actions.
Here are some examples of events (built-in activations):
-
activeactivation: When the spell in a spell slot of a player is fired by pressing the key bind. -
on_equipactivation: When a spell is equipped into a spell slot of a player (eg. when equipping a spell in the spell progression menu). -
on_unequipactivation: When a spell in a spell slot of a player is unequipped or overridden by another spell.
These always come with the following target groups and variables (other events might not come with these):
-
ownertarget group (playertype): The player that has/had this spell equipped. -
spell_slotvariable (inttype): The integer representing the slot the spell was equipped in starting at0.
The following variable always exists no matter the activation:
-
mana_costvariable (doubletype): Contains the set mana cost of a spell.
Note: The mod comes with a bunch of spells that can also serve as examples. You can view them in this repository by going back to the "code" tab and then into: /src/main/resources/data/spells_and_shields/spells_and_shields/spells.
Let us make a spell that damages the target we are looking at. We are omitting everything except for the spell actions and we also do not include any spell action parameters that are not important for this example right now (this means that this example is not usable just like that and requires minor additions to work like the proper key prefixes):
...
{
"type": "spells_and_shields:look_at_target",
"activation": "active",
"miss_activation": "",
"block_hit_activation": "",
"entity_hit_activation": "looked_at_entity",
"range": 50.0,
"source": "owner"
},
{
"type": "spells_and_shields:simple_mana_check",
"activation": "looked_at_entity",
"target": "owner"
},
{
"type": "spells_and_shields:sourced_damage",
"activation": "looked_at_entity",
"damage": 2.0,
"source": "owner",
"targets": "entity_hit"
},
...These spell actions do the following:
- The first action is activated by
active(firing the spell using a key bind). We try to find an entity the source (the owner of the spell) is looking at. If such an entity exists we enable thelooked_at_entityactivation with the entity automatically being put into theentity_hittarget group. If no such entity was found (theownerdoes not look at an entity but rather at a block or air) then this is not enabled. - Next, if we found such an entity we perform a simple mana check. If the target (
owner) has enough mana then this mana is burned otherwise thelooked_at_entityactivation is automatically disabled. - Finally, if the
looked_at_entityactivation is still active (there was enough mana) we damage the targets (we damageentity_hitwhich is the entity theownerlooked at) withownerbeing the source of the damage.
To summarize:
- We look for the entity the owner is looking at
- If this entity exists we perform the mana check
- If the mana check succeeds we damage the entity
Notes:
- The amount of mana checked and burned is set in the base of the spell and not as part of the action. There are other actions to do this with a custom amount.
- If the entity is not of type
living_entitythen this damage action would simply do nothing. - If we were to do the mana check first then we might burn the mana but then not find an entity to perform the damage on
{ "type": "spells_and_shields:replenish_mana", "activation": String, "d/double/mana_amount": Double, "ts/targets": String },