Fabric API

Fabric API

106M Downloads

Hooks for ItemStack NBT update animation

Draylar opened this issue · 5 comments

commented

Issue

When an ItemStack's NBT updates, a quick "reload" animation is played (which involves the item dipping down and then pulling back up). This quickly becomes an issue when you have items that reload NBT quickly, like a tick counter item:

Registry.register(
        Registry.ITEM,
        new Identifier("nbtissue", "testitem"),
        new Item(new Item.Settings()) {
            @Override
            public void inventoryTick(ItemStack stack, World world, Entity entity, int slot, boolean selected) {
                float currentTicks = stack.getOrCreateTag().getFloat("ticks");
                stack.getOrCreateTag().putFloat("ticks", currentTicks + 1);
                super.inventoryTick(stack, world, entity, slot, selected);
            }
        }
);

(result)

Context

HeldItemRenderer#updateHeldItems is where this animation update takes place. Forge has a hook here that allows you to define whether an item should cause said re-equip animation, and I think providing something similar in the API would be a good idea.

Current Solutions

In Inmis, I use an entirely cursed solution for getting around this issue. This will, obviously, fail as soon as 2 people try to do it (I claimed it first!!!!). I'm writing this issue up now because several other people are encountering this issue and need a solution.

Potential Solutions

Forge provides a method in their Item class that allows you to define whether the re-equip animation should occur between stack A and stack B. We could do something similar in usage, like:

FabricItemUpdateAnimations.register(Items.MY_ITEM, (originalStack, newStack) -> {
        return [...]
});

An event might also make sense (so multiple people can have a say in whether a diamond sword triggers the reload animation):

ItemUpdateAnimationCallback.event(Items.DIAMOND_SWORD).register((originalStack, newStack) -> {
        return ActionResult.FAIL;
});
commented

Implemented in #1790.

commented

Arguably, this may be a case where a FabricItemSettings could define these extra quirks item settings can have. Where we add another entry to settings that specifies this logic.

Otherwise maybe an interface you would implement onto your item that handles that logic?

commented

For your solution, why not just use a cancelled HEAD inject mixin, so that multiple Mixins are compatible.

commented

For your solution, why not just use a cancelled HEAD inject mixin, so that multiple Mixins are compatible.

That's a short circuting injector, which is just a silent overwrite.

commented

If you only cancel in your special case, it will fall through to other injections and eventually the default implementation:

@Mixin(ItemStack.class)
public class ItemStackMixin {

    @Inject(at = @At("HEAD"), method = "equals", cancelable = true)
    public void equals(Object obj, CallbackInfoRetrunable<Boolean> info) {
        if (obj instanceof ItemStack) {
            ItemStack thisStack = (ItemStack) (Object) this;
            ItemStack checkStack = (ItemStack) obj;

            Item thisStackItem = thisStack.getItem();
            Item checkStackItem = checkStack.getItem();

            if (thisStackItem instanceof BackpackItem && checkStackItem instanceof BackpackItem) {
                info.setRetunrValue(true);
            }
        }
    }
}