-
Notifications
You must be signed in to change notification settings - Fork 29
Attribute Functions
Attribute functions are used to allow you to modify an attribute's value and have that change in value carry over to another attribute. For example, by default when a point is added to Constitution a point is also added to Max Health. Attribute Functions are fully customisable and can be removed/added to any attribute. Attribute functions contain only three pieces of information: the key belonging to the attribute that is being modified (i.e. for the aforementioned example this would be minecraft:generic.max_health
); the type of function; and the multiplier.
A given attribute can only have one attribute function per attribute - so Constitution couldn't have two different attribute functions affecting Max Health, for example. The attribute that gets modified by the attribute function is defined using the attribute tag in JSON, and refers to the attribute key. This is usually modid:attribute_name
.
An attribute function can be one of two types: FLAT
or DIMINISHING
. This value defines how the attribute should be modified. Using the previous example, the attribute modifier type is FLAT
by default - this means that when adding a point to Constitution, 1 point is also added to Max Health. The equation for this is:
final value = current value + adding value
I.e. a "flat" addition. The DIMINISHING
type is more complicated: It defines an addition that has diminishing (i.e. not fully stackable) returns. Following on from the previous example, when a point is added to Constitution, 0.01 points is also added to Knockback Resistance. So lets say we have a current Knockback Resistance of 0.2, and we add 5 points to Constitution; a FLAT
type would mean we end up with 0.25 Knockback Resistance, however because it is DIMINISHING
, we end up with ~0.2381 (this assumes that the maximum value for Knockback Resistance is 1.0 - which it is by default). The equation for this is:
// if limit is equal to zero
final value = current value
// if adding value is positive (addition)
final value = limit * ((current value + adding value) / (limit + adding value))
// if adding value is negative (subtraction)
final value = adding value + current value - (current value * adding value / limit)
This form of addition results in two primary consequences: firstly, no matter how many points we add to Constitution, Knockback Resistance will never reach it's maximum value - it will only tend towards it, and it will require greater and greater numbers to increase it; secondly, this form of addition uses the attribute's maximum value as the limit parameter in the equation - this is why it is important to not just set the maximum value of every attribute to 9999999, since the greater the limit is relative to the amount being added, the more the result will trend closer towards a FLAT
addition. I.e. taking the previous example, if the maximum value for Knockback Resistance was 100, the result would have been ~0.2499 - essentially a FLAT
addition.
You may have noticed that in the previous example, when we added 1 point to Constitution we only added 0.01 points to Knockback Resistance. This is due to the multiplier being defined as 0.01. The multiplier works by multiplying the modified value before any logic happens - for the aforementioned example involving Max Health, the multiplier would have been 1.0, as a single point changed in Constitution results in a single point changed in Max Health.
There are three things to keep in mind with attribute functions:
Lets say you define an attribute function for Constitution that adds a point in Armor; and then you define another function for Armor that adds a point to Melee Attack Damage; and then you add another function for Melee Attack Damage that adds a point to Constitution. What will happen is: a point is added to Constitution -> a point is added to Armor -> a point is added to Melee Attack Damage -> a point is added to Constitution -> cycle repeats in an infinite recursive loop
.
Now, there won't actually be a recursive cycle because there is code to handle and prevent recursion, however it's something to keep in mind and attribute functions that would otherwise have caused recursion will instead not do anything at all, so they should be avoided.
The type tag that defines either a FLAT
or DIMINISHING
addition type will only apply to attribute modifiers if their operation is that of ADDITION. Multiplier operations can be safely used without worrying about weird numbers coming up. This is of course intentional as multiplier operations would have rendered the addition formula irrelevant anyway (multiplication > addition). It should be noted that attribute functions occur whenever the value of an attribute changes - this includes modifiers being applied, so long as they are applied through the PlayerEx API system.
There are cases in Minecraft where changing the value of an attribute may not cause its attribute functions to fire. These are cases where the attribute is modified outside of the PlayerEx API. Examples of these are:
- Equipping/Unequipping armor; the player's armor value changes but does not trigger attribute functions as this is a vanilla implementation.
- Equipping/Unequipping items; when holding a sword, for example, your damage and attack speed change but attribute functions would not be triggered here.
- Any potion effects, or implementation of the attribute modifier usage in
ItemStack
.