Hooks for ItemStack NBT update animation
Draylar opened this issue · 5 comments
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;
});
Implemented in #1790.
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?
For your solution, why not just use a cancelled HEAD inject mixin, so that multiple Mixins are compatible.
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.
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);
}
}
}
}