-
Notifications
You must be signed in to change notification settings - Fork 117
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a guide for Custom Enchantments (#134)
Co-authored-by: Calum H. <[email protected]> Co-authored-by: Miroma <[email protected]>
- Loading branch information
1 parent
5a9c426
commit ad06683
Showing
14 changed files
with
268 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
--- | ||
title: Custom Enchantment Effects | ||
description: Learn how to create your enchantment effects. | ||
authors: | ||
- krizh-p | ||
--- | ||
|
||
# Custom Enchantments {#custom-enchantments} | ||
|
||
Starting from version 1.21, custom enchantments in Minecraft use a "data-driven" approach. This makes it easier to add simple enchantments, like increasing attack damage, but more challenging to create complex ones. The process involves breaking down enchantments into _effect components_. | ||
|
||
An effect component contains the code that defines the special effects of an enchantment. Minecraft supports various default effects, such as item damage, knockback, and experience. | ||
|
||
::: tip | ||
Be sure to check if the default Minecraft effects satisfy your needs by visiting [the Minecraft Wiki's Enchantment Effect Components page](https://minecraft.wiki/w/Enchantment_definition#Effect_components). This guide assumes you understand how to configure "simple" data-driven enchantments and focuses on creating custom enchantment effects that aren't supported by default. | ||
::: | ||
|
||
## Custom Enchantment Effects {#custom-enchantment-effects} | ||
|
||
Start by creating an `enchantment` folder, and within it, create an `effect` folder. Within that, we'll create the `LightningEnchantmentEffect` record. | ||
|
||
Next, we can create a constructor and override the `EnchantmentEntityEffect` interface methods. We'll also create a `CODEC` variable to encode and decode our effect; you can read more about [Codecs here](../codecs). | ||
|
||
The bulk of our code will go into the `apply()` event, which is called when the criteria for your enchantment to work is met. We'll later configure this `Effect` to be called when an entity is hit, but for now, let's write simple code to strike the target with lightning. | ||
|
||
@[code transcludeWith=#entrypoint](@/reference/latest/src/main/java/com/example/docs/enchantment/effect/LightningEnchantmentEffect.java) | ||
|
||
Here, the `amount` variable indicates a value scaled to the level of the enchantment. We can use this to modify how effective the enchantment is based on level. In the code above, we are using the level of the enchantment to determine how many lightning strikes are spawned. | ||
|
||
## Registering the Enchantment Effect {#registering-the-enchantment-effect} | ||
|
||
Like every other component of your mod, we'll have to add this `EnchantmentEffect` to Minecraft's registry. To do so, add a class `ModEnchantmentEffects` (or whatever you want to name it) and a helper method to register the enchantment. Be sure to call the `registerModEnchantmentEffects()` in your main class, which contains the `onInitialize()` method. | ||
|
||
@[code transcludeWith=#entrypoint](@/reference/latest/src/main/java/com/example/docs/enchantment/ModEnchantmentEffects.java) | ||
|
||
## Creating the Enchantment {#creating-the-enchantment} | ||
|
||
Now we have an enchantment effect! The final step is to create an enchantment that applies our custom effect. While this can be done by creating a JSON file similar to those in datapacks, this guide will show you how to generate the JSON dynamically using Fabric's data generation tools. To begin, create an `EnchantmentGenerator` class. | ||
|
||
Within this class, we'll first register a new enchantment, and then use the `configure()` method to create our JSON programmatically. | ||
|
||
@[code transcludeWith=#entrypoint](@/reference/latest/src/main/java/com/example/docs/data/EnchantmentGenerator.java) | ||
|
||
Before proceeding, you should ensure your project is configured for data generation; if you are unsure, [view the respective wiki page](https://fabricmc.net/wiki/tutorial:datagen_setup). | ||
|
||
Lastly, we must tell our mod to add our `EnchantmentGenerator` to the list of data generation tasks. To do so, simply add the `EnchantmentGenerator` to this inside of the `onInitializeDataGenerator` class. | ||
|
||
@[code transcludeWith=#initdatagen](@/reference/latest/src/main/java/com/example/docs/FabricDocsReferenceDataGenerator.java) | ||
|
||
Now, when you run your mod's data generation task, enchantment JSONs will be generated inside the `generated` folder. An example can be seen below: | ||
|
||
@[code](@/reference/latest/src/main/generated/data/fabric-docs-reference/enchantment/thundering.json) | ||
|
||
You should also add translations to your `en_us.json` file to give your enchantment a readable name: | ||
|
||
```json | ||
"enchantment.FabricDocsReference.thundering": "Thundering", | ||
``` | ||
|
||
You should now have a working custom enchantment effect! Test it by enchanting a weapon with the enchantment and hitting a mob. An example is given in the following video: | ||
|
||
<VideoPlayer src="/assets/develop/enchantment-effects/thunder.webm" title="Using the Lightning Effect" /> |
Binary file not shown.
2 changes: 2 additions & 0 deletions
2
reference/latest/src/main/generated/.cache/2f57c34eab409e8d6afbf3588c9395cda8694422
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// 1.21 2024-07-09T13:17:21.830983 Fabric docs reference/null | ||
b0b8d77fe2f55934f146827951450686de077dac data\fabric-docs-reference\enchantment\thundering.json |
36 changes: 36 additions & 0 deletions
36
reference/latest/src/main/generated/data/fabric-docs-reference/enchantment/thundering.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"anvil_cost": 5, | ||
"description": { | ||
"translate": "enchantment.fabric-docs-reference.thundering" | ||
}, | ||
"effects": { | ||
"minecraft:post_attack": [ | ||
{ | ||
"affected": "victim", | ||
"effect": { | ||
"type": "fabric-docs-reference:lightning_effect", | ||
"amount": { | ||
"type": "minecraft:linear", | ||
"base": 0.4, | ||
"per_level_above_first": 0.2 | ||
} | ||
}, | ||
"enchanted": "attacker" | ||
} | ||
] | ||
}, | ||
"max_cost": { | ||
"base": 1, | ||
"per_level_above_first": 15 | ||
}, | ||
"max_level": 3, | ||
"min_cost": { | ||
"base": 1, | ||
"per_level_above_first": 10 | ||
}, | ||
"slots": [ | ||
"hand" | ||
], | ||
"supported_items": "#minecraft:enchantable/weapon", | ||
"weight": 10 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
75 changes: 75 additions & 0 deletions
75
reference/latest/src/main/java/com/example/docs/data/EnchantmentGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package com.example.docs.data; | ||
|
||
import java.util.concurrent.CompletableFuture; | ||
|
||
import net.minecraft.component.EnchantmentEffectComponentTypes; | ||
import net.minecraft.component.type.AttributeModifierSlot; | ||
import net.minecraft.enchantment.Enchantment; | ||
import net.minecraft.enchantment.EnchantmentLevelBasedValue; | ||
import net.minecraft.enchantment.effect.EnchantmentEffectTarget; | ||
import net.minecraft.registry.RegistryKey; | ||
import net.minecraft.registry.RegistryKeys; | ||
import net.minecraft.registry.RegistryWrapper; | ||
import net.minecraft.registry.tag.ItemTags; | ||
import net.minecraft.util.Identifier; | ||
|
||
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; | ||
import net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider; | ||
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceCondition; | ||
|
||
import com.example.docs.FabricDocsReference; | ||
import com.example.docs.enchantment.effect.LightningEnchantmentEffect; | ||
|
||
//#entrypoint | ||
public class EnchantmentGenerator extends FabricDynamicRegistryProvider { | ||
public static final RegistryKey<Enchantment> THUNDERING = EnchantmentGenerator.of("thundering"); | ||
|
||
public EnchantmentGenerator(FabricDataOutput output, CompletableFuture<RegistryWrapper.WrapperLookup> registriesFuture) { | ||
super(output, registriesFuture); | ||
System.out.println("REGISTERING ENCHANTS"); | ||
} | ||
|
||
@Override | ||
protected void configure(RegistryWrapper.WrapperLookup registries, Entries entries) { | ||
// Our new enchantment, "Thundering." | ||
register(entries, THUNDERING, Enchantment.builder( | ||
Enchantment.definition( | ||
registries.getWrapperOrThrow(RegistryKeys.ITEM).getOrThrow(ItemTags.WEAPON_ENCHANTABLE), | ||
// this is the "weight" or probability of our enchantment showing up in the table | ||
10, | ||
// the maximum level of the enchantment | ||
3, | ||
// base cost for level 1 of the enchantment, and min levels required for something higher | ||
Enchantment.leveledCost(1, 10), | ||
// same fields as above but for max cost | ||
Enchantment.leveledCost(1, 15), | ||
// anvil cost | ||
5, | ||
// valid slots | ||
AttributeModifierSlot.HAND | ||
) | ||
) | ||
.addEffect( | ||
// enchantment occurs POST_ATTACK | ||
EnchantmentEffectComponentTypes.POST_ATTACK, | ||
EnchantmentEffectTarget.ATTACKER, | ||
EnchantmentEffectTarget.VICTIM, | ||
new LightningEnchantmentEffect(EnchantmentLevelBasedValue.linear(0.4f, 0.2f)) // scale the enchantment linearly. | ||
) | ||
); | ||
} | ||
|
||
private void register(Entries entries, RegistryKey<Enchantment> key, Enchantment.Builder builder, ResourceCondition... resourceConditions) { | ||
entries.add(key, builder.build(key.getValue()), resourceConditions); | ||
} | ||
|
||
private static RegistryKey<Enchantment> of(String path) { | ||
Identifier id = Identifier.of(FabricDocsReference.MOD_ID, path); | ||
return RegistryKey.of(RegistryKeys.ENCHANTMENT, id); | ||
} | ||
|
||
@Override | ||
public String getName() { | ||
return "ReferenceDocEnchantmentGenerator"; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
...ce/latest/src/main/java/com/example/docs/enchantment/FabricDocsReferenceEnchantments.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.example.docs.enchantment; | ||
|
||
import net.fabricmc.api.ModInitializer; | ||
|
||
public class FabricDocsReferenceEnchantments implements ModInitializer { | ||
@Override | ||
public void onInitialize() { | ||
ModEnchantmentEffects.registerModEnchantmentEffects(); | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
reference/latest/src/main/java/com/example/docs/enchantment/ModEnchantmentEffects.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.example.docs.enchantment; | ||
|
||
import com.mojang.serialization.MapCodec; | ||
|
||
import net.minecraft.enchantment.effect.EnchantmentEntityEffect; | ||
import net.minecraft.registry.Registries; | ||
import net.minecraft.registry.Registry; | ||
import net.minecraft.util.Identifier; | ||
|
||
import com.example.docs.FabricDocsReference; | ||
import com.example.docs.enchantment.effect.LightningEnchantmentEffect; | ||
|
||
//#entrypoint | ||
public class ModEnchantmentEffects { | ||
public static MapCodec<LightningEnchantmentEffect> LIGHTNING_EFFECT = register("lightning_effect", LightningEnchantmentEffect.CODEC); | ||
|
||
private static <T extends EnchantmentEntityEffect> MapCodec<T> register(String id, MapCodec<T> codec) { | ||
return Registry.register(Registries.ENCHANTMENT_ENTITY_EFFECT_TYPE, Identifier.of(FabricDocsReference.MOD_ID, id), codec); | ||
} | ||
|
||
public static void registerModEnchantmentEffects() { | ||
FabricDocsReference.LOGGER.info("Registering EnchantmentEffects for" + FabricDocsReference.MOD_ID); | ||
} | ||
} |
44 changes: 44 additions & 0 deletions
44
.../latest/src/main/java/com/example/docs/enchantment/effect/LightningEnchantmentEffect.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
package com.example.docs.enchantment.effect; | ||
|
||
import com.mojang.serialization.MapCodec; | ||
import com.mojang.serialization.codecs.RecordCodecBuilder; | ||
|
||
import net.minecraft.enchantment.EnchantmentEffectContext; | ||
import net.minecraft.enchantment.EnchantmentLevelBasedValue; | ||
import net.minecraft.enchantment.effect.EnchantmentEntityEffect; | ||
import net.minecraft.entity.Entity; | ||
import net.minecraft.entity.EntityType; | ||
import net.minecraft.entity.LivingEntity; | ||
import net.minecraft.entity.SpawnReason; | ||
import net.minecraft.entity.player.PlayerEntity; | ||
import net.minecraft.server.world.ServerWorld; | ||
import net.minecraft.util.math.BlockPos; | ||
import net.minecraft.util.math.Vec3d; | ||
|
||
//#entrypoint | ||
public record LightningEnchantmentEffect(EnchantmentLevelBasedValue amount) implements EnchantmentEntityEffect { | ||
public static final MapCodec<LightningEnchantmentEffect> CODEC = RecordCodecBuilder.mapCodec(instance -> | ||
instance.group( | ||
EnchantmentLevelBasedValue.CODEC.fieldOf("amount").forGetter(LightningEnchantmentEffect::amount) | ||
).apply(instance, LightningEnchantmentEffect::new) | ||
); | ||
|
||
@Override | ||
public void apply(ServerWorld world, int level, EnchantmentEffectContext context, Entity target, Vec3d pos) { | ||
if (target instanceof LivingEntity victim) { | ||
if (context.owner() != null && context.owner() instanceof PlayerEntity player) { | ||
float numStrikes = this.amount.getValue(level); | ||
|
||
for (float i = 0; i < numStrikes; i++) { | ||
BlockPos position = victim.getBlockPos(); | ||
EntityType.LIGHTNING_BOLT.spawn(world, position, SpawnReason.TRIGGERED); | ||
} | ||
} | ||
} | ||
} | ||
|
||
@Override | ||
public MapCodec<? extends EnchantmentEntityEffect> getCodec() { | ||
return CODEC; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters