Skip to content

Adding Artifacts ​

Artifacts are passive items that can change aspects in spells or modify the cast values in specific contexts. EBWR splits the work in two parts: ArtifactItem (the actual item) and IArtifactEffect (the effects of the artifact). The mod listens to the events inside the artifact item when it's equipped, so a new artifact is usually one registered item plus a small effect class.

The good part of using the built-in artifact system is that the mod handles all the work to be compatible with Accessories/Curios/Trinkets/Vanilla hotbar.

Prerequisites ​

You need to know how item registration works in your loader and how to read an event listener. You don't need to register artifact events yourself. EBEventHelper does that already. For a practical example of how EBWR events are used, see Adding Spell Cast Items.

These are the classes you'll touch:

  • ArtifactItem: base item. Stores the effect and builds the tooltip.
  • IArtifactEffect: the callback interface.
  • ArtifactEffectContext: gives you the stack that triggered the callback.
  • ArtifactChannel: creates the item and queries equipped artifacts through the active integration.
  • QuickArtifactEffect: ready-made effects for common cases.
  • ArtifactUtils: shared helpers such as wand lookup, melee checks and the lightning effect.

How Artifacts Work ​

ArtifactItem is a normal Item with stacksTo(1) and a Rarity. The effect is optional. Pass null if you only want the automatic compatibility with the artifact integrations and plan to handle the behavior yourself, for example from a spell, a mixin or another system. In that case, check for the item with ArtifactChannel.isEquipped.

ArtifactChannel handles the integration with the different accessory mods on each loader. It resolves the integration at runtime:

  1. Accessories, if it's loaded.
  2. Curios on Forge or Trinkets on Fabric, if loaded.
  3. The vanilla fallback: hotbar and hands.

The same item then works in an accessory slot on a modded client and in the hotbar on a vanilla one.

Effect Callbacks ​

IArtifactEffect extends IEventEffect<ArtifactEffectContext> and provides default methods. Override only the ones you need:

CallbackFires whenCommon uses
onTick(user, level, context)Every tick while the artifact is activeRegeneration, periodic checks, auras
onHurtEntity(user, target, source, amount, canceled, context)The player hurts another entityOn-hit rings, extra damage, effects on the target
onUserHurt(user, source, amount, canceled, context)The player takes damageDamage reduction, immunities, retaliation
onKillEntity(user, deadEntity, source, context)The player kills an entitySouls, stacking buffs, drops
onSpellPreCast(event, context)Before a spell is castChanging SpellModifiers (potency, cost, cooldown...)
onSpellPostCast(event, context)After a spell is castHealing nearby allies, extra effects

WARNING

These callbacks can fire on both sides depending on the case. If your artifact spawns entities, changes blocks or runs any server-side logic, guard it with if (level.isClientSide) return;.

Creating an Effect ​

Implement IArtifactEffect and override only the hooks you need. context.getArtifact() returns the stack that fired the callback.

Passive tick effect ​

java
public class VitalityCharmEffect implements IArtifactEffect {
    private static final int INTERVAL = 40;

    @Override
    public void onTick(LivingEntity user, Level level, ArtifactEffectContext context) {
        if (level.isClientSide || user.tickCount % INTERVAL != 0) return;
        if (!(user instanceof Player player)) return;
        if (player.getHealth() >= player.getMaxHealth()) return;

        player.heal(1.0F);
    }
}

Spell modifier effect ​

java
public class StormCharmEffect implements IArtifactEffect {
    @Override
    public void onSpellPreCast(SpellCastEvent.Pre event, ArtifactEffectContext context) {
        if (event.getSpell().getElement() != Elements.LIGHTNING) return;

        event.getModifiers().multiply(SpellModifiers.POTENCY, 1.25F);
    }
}

Damage hook ​

onHurtEntity and onUserHurt receive the damage amount as an AtomicDouble and the cancellation flag as an AtomicBoolean. Both are mutable:

java
public class WardingAmuletEffect implements IArtifactEffect {
    @Override
    public void onUserHurt(LivingEntity user, DamageSource source, AtomicDouble amount, AtomicBoolean canceled, ArtifactEffectContext context) {
        if (EBDamageSources.isMagic(source)) {
            amount.set(amount.get() * 0.8F);
        }
    }
}

Registering the Item ​

WARNING

Create artifacts with ArtifactChannel.createArtifact(rarity, effect), not new ArtifactItem(...). A plain ArtifactItem still works from the hotbar, but Accessories, Curios and Trinkets won't recognize it in their slots.

Apart from that, artifacts register like any other item.

java
// Forge
public static final RegistryObject<Item> RING_OF_ECHOES = ITEMS.register("ring_of_echoes",
        () -> ArtifactChannel.createArtifact(Rarity.RARE, new EchoRingEffect()));

// Fabric
public static final Item RING_OF_ECHOES = Registry.register(
        Registries.ITEM,
        new ResourceLocation(MyMod.MOD_ID, "ring_of_echoes"),
        ArtifactChannel.createArtifact(Rarity.RARE, new EchoRingEffect())
);

Before finishing ​

Item Tags ​

You need to add your item to the tags used by the integrations you want to support.

TypeAccessoriesCuriosTrinkets
Ringaccessories:ringcurios:ringtrinkets:hand/ring
Necklaceaccessories:necklacecurios:necklacetrinkets:chest/necklace
Charmaccessories:charmcurios:charmtrinkets:hand/glove

Quick Effects ​

QuickArtifactEffect is a very useful utility class with the common artifact patterns used in the mod. Some of them are:

MethodEffect
changeModifiers(key, value, operation)Modifies a SpellModifiers key on every cast
changeModifiersIfCasting(spell, key, value, operation)Modifies a key only when a specific spell is cast
changeModifiersIfCastingType(type, key, value, operation)Modifies a key only for a spell type (minion, projectile...)
changeDamageIfSource(damageType, value, operation)Changes incoming damage of a specific damage type

Each of these returns a valid IArtifactEffect that you can use when registering the artifact item.

Tooltip and Translations ​

ArtifactItem.appendHoverText adds a gray line using <descriptionId>.desc, so add that translation key to keep a quick description of what your artifact does.

json
{
  "item.mymod.ring_of_echoes": "Ring of Echoes",
  "item.mymod.ring_of_echoes.desc": "Spells of the lightning element are 25% more potent"
}