ModifierBuilder - yeelp/Distinct-Damage-Descriptions GitHub Wiki
DDD's ModifierBuilder is a system introduced in version 1.7.0 that lets you create dynamic modifiers to alter the distributions of items, mobs and projectiles, alongside altering armor distributions, shield distributions and mob resistances!
The ModifierBuilder is a hierarchy (with the ModifierBuilder ZenClass being at the root) with a couple different types, depending on the type of distribution you want to modify. Regardless of which one you use, you'll need to define a function to determine if the modifier is applicable to a given, item or mob.
Modifiers can only be applied to things that have a distribution/resistances defined in the config if definedItemsOnly and/or definedEntitiesOnly are true. Having definedItemsOnly set to false will allow modifiers to be applied to items regardless, but may cause some recipe conflicts. Setting definedEntitiesOnly to false will allow modifiers to be applied to any entity (even players!) but could maybe have problems (though highly unlikely). You may wish to have definedEntitiesOnly set to false if using modifier on entities to prevent confusion later on when they don't apply to everything they should.
Importing the class
It may be required to import the class to avoid any errors.
import mods.ddd.modifiers.ModifierBuilder;
Creating a Modifier
To create a modifier, you'll need to call a static ZenMethod on one of the subclasses of ModifierBuilder, depending on the kind of modifier you want to make. Each static ZenMethod requires a string as an argument; this is the modifier name! Much like the DamageTypeBuilder, the names provided here must be unique! Note that before 1.8, Mob Damage modifiers and Projectile Damage modifiers are registered in the same registry, so their names must be unique amongst each other. Other modifiers only need to have unique names amongst other modifiers of the same type.
Type of Modifier | ZenClass Used | Static ZenMethod called |
---|---|---|
Item Damage Distribution Modifier | mods.ddd.modifiers.ItemModifierBuilder |
createItemDamageModifier(string) |
Item Armor Distribution Modifier | mods.ddd.modifiers.ItemModifierBuilder |
createArmorModifier(string) |
Item Shield Distribution Modifier | mods.ddd.modifiers.ItemModifierBuilder |
createShieldModifier(string) |
Projectile Damage Distribution Modifier | mods.ddd.modifiers.ProjectileModifierBuilder |
create(string) |
Mob Damage Distribution Modifier | mods.ddd.modifiers.MobDamageModifierBuilder |
create(string) |
Mob Resistances Modifier | mods.ddd.modifiers.ResistancesModifierBuilder |
create(string) |
For example, to create a new Modifier Builder to alter item damage distributions, you'd do the following:
val itemMod = mods.ddd.modifiers.ItemModifierBuilder.createItemDamageModifier("example");
Building a Modifier
After you've set how the modifier will alter the capability and when it is applicable (See the section on ZenProperties), you should build the modifier with the build()
ZenMethod.
itemMod.build();
Making changes past this point won't change the modifier that was created. The modifier itself doesn't exist yet, all this does is queue the modifier to be registered. DDD does the registration itself a little later on startup.
When does the modifier apply?
The ModifierBuilder subclasses have a ZenProperty that determines if the modifier should apply to a given 'thing'. The type of this ZenProperty is a function, but the function arguments depend on what the modifier applies to. For example, for any modifier that modifies an item stack, their function will take an IItemStack as their argument. Check the specific wiki page for a particular ModifierBuilder subclass to learn what the function signature is.
Regardless of the signature, the function either returns true or false, depending on if the modifier is applicable to the given 'thing'.
Priority
Modifiers have a priority system to determine the order in which they apply. Sometimes, the order matters and this gives you control over which applies first. Modifiers have a ZenProperty called priority
which defaults to 0, and higher priority modifiers are applied before lower priority ones. Modifiers with the same priority are merged and applied together (if they are both applicable), but only if they either both reallocate or not (See the section below on reallocation versus non reallocation for modifiers).
Note that if the priority is equal, modifiers with shouldReallocate
being set to true are applied before modifiers with shouldReallocate
set to false.
How does the modifier apply?
The ModifierBuilder has a ZenProperty called shouldReallocate
. If set to true, the modifier will reallocate the existing weights of the distribution (regardless if the distribution it applies to needs to add to 100% or not) to the modifier. If set to false, the modifier will be applied as a 'flat bonus'. In the instance of a modifier that modifies damage distributions, the damage distribution will be scaled such that the weights all add to 100%. For example, with a 100% slashing damage distribution, applying a non-reallocating 50% radiant bonus modifier, the 50% radiant will be added to the distribution, making it 100% slashing and 50% radiant. It is then scaled to 100% total, giving 66.67% slashing, and 33.33% radiant (basically maintaining the 2:1 ratio of the weights). The same modifier, with reallocation enabled, will reallocate 50% of the slashing weight to the new 50% radiant weight, giving it a final distribution of 50% slashing and 50% radiant. the modifier reallocates evenly, not proportionally. Reallocating a 50% radiant modifier on a distribution with 10% slashing, 90% piercing, will subtract 25% from each weight (evenly making 'room' for the 50% radiant bonus). Since there's only a 10% slashing weight, the slashing weight is fully removed and the excess 15% from the 25% that should have been subtracted is evenly subtracted from what remains, in this case, the now 65% piercing, dropping it to 50% piercing and then adding the 50% radiant to the distribution.
Setting the modifier
To define what the modifier does, you need to set its weights. The weights determine what is changed when the modifier applies. This can be done with one of two ZenMethods:
setMod(IDDDDamageType, float)
setMod(string, float)
This is an overloaded ZenMethod. Note that the version that takes an IDDDDamageType only works for built-in types! In general, it's probably safer to use the string version. The string version requires you to specify the internal name of the damage type which should be all lowercase.
ZenProperties
ZenProperty Name | Type | Notes |
---|---|---|
name | string | This is the internal name of the modifier. This field gets set to whatever is passed into static creation ZenMethod, but it can be changed like a normal ZenProperty. Setting this to null and then trying to build the modifier will cause your script to throw an exception! |
shouldReallocate | boolean | See How does the modifier apply? |
priority | int | See When does the modifier apply? |
ZenMethods
void setMod(IDDDDamageType, float);
- Sets the weight of this modifier for this type to the specified amount. See Setting the modifier. Note this version only works with built-in types!
setMod(string, float);
- Sets the weight of this modifier for the type represented by the internal name that the string represents to the specified amount. See Setting the modifier.
Example
#loader contenttweaker
#modloaded distinctdamagedescriptions
import mods.ddd.modifiers.ItemModifierBuilder;
//creating an Item Damage Modifier using the ItemModifierBuilder.
val builder = mods.ddd.modifiers.ItemModifierBuilder.createItemDamageModifier("yeelp");
builder.shouldReallocate = false; //We don't reallocate existing weights
builder.setMod("example", 0.5); //This will add a flat 50% Example damage bonus. Note this is a custom type, so the type must exist!
//Our function to determine if the modifier is applicable. This varies depending on the kind of ModifierBuilder we are using! In this case, this is an isModifierApplicableForItemStack, and takes an IItemStack.
builder.isModifierApplicable = function(stack) {
return stack.displayName == "Yeelp";
};
builder.build();