Jump to content

Canceling re-equip animation but only for tooltip updates?


uglyluigi

Recommended Posts

Hi! This is my first post here. Currently, I'm making a mod that allows you to brew various teas at various temperatures. A feature of this mod is that when you boil water in a furnace, it begins cooling when you take it out (the temperature is stored in an NBTTag and each tick a small amount of temperature is removed. Eventually it'll have a temperature that just causes it to return to a normal cup of water). Since tooltip updates cause a reequip animation to be fired and the tooltip is updated every tick, the item undergoes the reequip animation around 20 times a second which is very distracting. I was able to fix this issue for a while by overriding the shouldCauseReequipAnimation to always return false, however now that I added the ability to actually drink the hot water, I ran into a weird issue. Apparently, if shouldCauseReequipAnimation always returns false, if your item is a drink, it cannot undergo the drinking animation and will not be consumed. If I simply return super.shouldCauseReequipAnimation, it's pretty much the same issue since the game considers the item stacks different because of the slightly different tooltips. The goal is to disable the reequip animation but allow the item to undergo the drinking animation, however the only way I can think of accomplishing this is to ignore tooltip changes but let everything else pass. Unfortunately, this is a weird edge case and I understandably did not find a straightforward way to do this. Any help or advice would be greatly appreciated.

 

package ugly.HeckingTea.items;

import net.minecraft.client.Minecraft;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import ugly.HeckingTea.HeckingTeaMain;
import ugly.HeckingTea.items.effects.TeaEffect;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

//ItemDrinkableTea is the parent class containing the getItemUseAction and getMaxItemUseDuration overrides.
public class ItemHotWater extends ItemDrinkableTea {

	public ItemHotWater(String name) {
		super(name, TeaEffect.teaEffect1);
	}

	@Override
	public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, ITooltipFlag flagIn) {
		super.addInformation(stack, worldIn, tooltip, flagIn);

		if (stack.hasTagCompound() && stack.getTagCompound().hasKey("FTemp")) {
			tooltip.add("Temp: " + Math.round(stack.getTagCompound().getFloat("FTemp")) + "°F");
		}
	}

	@Override
	public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
		//return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged);
		return false;
	}

	@Override
	public void onUpdate(ItemStack stack, World worldIn, Entity entityIn, int itemSlot, boolean isSelected) {
		super.onUpdate(stack, worldIn, entityIn, itemSlot, isSelected);

		NBTTagCompound nbt;

		if (stack.hasTagCompound()) {
			nbt = stack.getTagCompound();
		} else {
			nbt = new NBTTagCompound();
		}

		if (nbt.hasKey("FTemp")) {
			nbt.setFloat("FTemp", nbt.getFloat("FTemp") - 0.005F);
		} else {
			nbt.setFloat("FTemp", 212.0F);
			stack.setTagCompound(nbt);
		}
	}
}

 

Link to comment
Share on other sites

3 minutes ago, uglyluigi said:

Unfortunately, this is a weird edge case and I understandably did not find a straightforward way to do this.

Compare the NBTTag of the old stack to the new stack.

Edited by Animefan8888

VANILLA MINECRAFT CLASSES ARE THE BEST RESOURCES WHEN MODDING

I will be posting 1.15.2 modding tutorials on this channel. If you want to be notified of it do the normal YouTube stuff like subscribing, ect.

Forge and vanilla BlockState generator.

Link to comment
Share on other sites

Hmm, I tried this and it got the reequip to stop happening each time the tooltip updates, but the drink animation still appears to be canceled midway through each time. I suspected the tooltip updates mid-animation but removing them does not fix the issue, however if I continue to hold right click it gets to different parts of the animation each time and sometimes even makes it to the first sipping sound. Any ideas?

 

Edit: it appears you edited your post and I had not refreshed to see the edit, I am going to try your solution and report back.

Edited by uglyluigi
Link to comment
Share on other sites

OK, I have made some progress. I did what you said and then I made some changes to ItemDrinkableTea in the onItemRightClick method and made the ActionResult it returns contain an EnumActionResult.PASS. Since that I have located the method calls that are causing the animation to cancel, and it's lines 57 and 59 of ItemHotWater. Apparently updating an NBT value cancels a use animation. My first idea is to check if the item is being used and simply do not update the tag values while this is happening, but I am unaware of a field or method that is capable of giving me this information. I'm going to include both files to give the most complete picture.

 

package ugly.HeckingTea.items;

import net.minecraft.client.Minecraft;
import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.biome.Biome;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import ugly.HeckingTea.HeckingTeaMain;
import ugly.HeckingTea.items.effects.TeaEffect;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class ItemHotWater extends ItemDrinkableTea {

	public ItemHotWater(String name) {
		super(name, TeaEffect.teaEffect1);
	}

	@Override
	public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, ITooltipFlag flagIn) {
		super.addInformation(stack, worldIn, tooltip, flagIn);

		if (stack.hasTagCompound() && stack.getTagCompound().hasKey("FTemp")) {
			tooltip.add("Temp: " + Math.round(stack.getTagCompound().getFloat("FTemp")) + "°F");
		}
	}

	@Override
	public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
		NBTTagCompound tagCompound = oldStack.getTagCompound(), tagCompound2 = newStack.getTagCompound();
		return !tagCompound.equals(tagCompound2);
	}

	@Override
	public void onUpdate(ItemStack stack, World worldIn, Entity entityIn, int itemSlot, boolean isSelected) {
		super.onUpdate(stack, worldIn, entityIn, itemSlot, isSelected);

		NBTTagCompound nbt;

		if (stack.hasTagCompound()) {
			nbt = stack.getTagCompound();
		} else {
			nbt = new NBTTagCompound();
		}

		if (nbt.hasKey("FTemp")) {
			nbt.setFloat("FTemp", nbt.getFloat("FTemp") - 0.005F);
		} else {
			nbt.setFloat("FTemp", 212.0F);
			stack.setTagCompound(nbt);
		}
	}
}

 

package ugly.HeckingTea.items;

import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.Event;
import ugly.HeckingTea.items.effects.EventSipsTea;
import ugly.HeckingTea.items.effects.TeaEffect;
import ugly.HeckingTea.proxy.TeaModEffectHandler;

public class ItemDrinkableTea extends ItemTeaItem {

	private final TeaEffect effect;

	public ItemDrinkableTea(String name, TeaEffect teaEffect) {
		super(name, 1);
		this.effect = teaEffect;
	}

	@Override
	public EnumAction getItemUseAction(ItemStack stack) {
		MinecraftForge.EVENT_BUS.post(new EventSipsTea((ItemDrinkableTea) stack.getItem()));
		return EnumAction.DRINK;
	}

	public TeaEffect getEffect() {
		return effect;
	}

	@Override
	public int getMaxItemUseDuration(ItemStack stack) {
		return 32;
	}

	@Override
	public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn) {
		playerIn.setActiveHand(handIn);
		return new ActionResult<ItemStack>(EnumActionResult.PASS, playerIn.getHeldItem(handIn));
	}
}

 

Like I said, if I remove both lines that set NBT values, the problem is "solved" in that the item is consumable and the reequip animation does not occur, but obviously the NBT values will no longer update. I'll continue looking for a solution but I would appreciate some input because this is a rather weird situation since I must be able to write to these tags.

Link to comment
Share on other sites

7 minutes ago, uglyluigi said:

return !tagCompound.equals(tagCompound2);

If your NBT changes, this will return true, causing the re-equip animation.

You should be checking if the item changes and ignoring the NBT.

 

Or using Capabilities.

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

I'm so silly. I didn't put that together for whatever reason. Who'd've thought the NBT conditional and modifying the NBTs were related?? Not me, apparently.

 

Anyway, this is the boolean method now:

 

	@Override
	public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
		return oldStack.getItem() != newStack.getItem();
	}

Which is what I assumed you meant. This works as desired and stops the reequip animation from taking place while allowing the drinik animation to begin. I believe I've devised a solution that simply sets a flag to true while ignoring updates (so the onUpdate method just returns if the flag is true) after a right click and then resumes NBT updates after the item use is complete.

In order to help anyone else who may stumble upon this topic, this is what my code ended up being: 

package ugly.HeckingTea.items;

import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.Event;
import ugly.HeckingTea.items.effects.EventSipsTea;
import ugly.HeckingTea.items.effects.TeaEffect;
import ugly.HeckingTea.proxy.TeaModEffectHandler;

public class ItemDrinkableTea extends ItemTeaItem {

	private final TeaEffect effect;
	public boolean IGNORE_NBT_UPDATES = false; //The new flag, used in subclasses that are consumable but also undergo NBT updates each tick

	public ItemDrinkableTea(String name, TeaEffect teaEffect) {
		super(name, 1);
		this.effect = teaEffect;
	}

	@Override
	public ItemStack onItemUseFinish(ItemStack stack, World worldIn, EntityLivingBase entityLiving) {
		IGNORE_NBT_UPDATES = false;
		return stack;
	}

	@Override
	public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn) {
		IGNORE_NBT_UPDATES = true;
		playerIn.setActiveHand(handIn);
		return new ActionResult<ItemStack>(EnumActionResult.PASS, playerIn.getHeldItem(handIn));
	}

	@Override
	public EnumAction getItemUseAction(ItemStack stack) {
		MinecraftForge.EVENT_BUS.post(new EventSipsTea((ItemDrinkableTea) stack.getItem()));
		return EnumAction.DRINK;
	}

	public TeaEffect getEffect() {
		return effect;
	}

	@Override
	public int getMaxItemUseDuration(ItemStack stack) {
		return 16;
	}
}
package ugly.HeckingTea.items;

import net.minecraft.client.util.ITooltipFlag;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.world.World;
import ugly.HeckingTea.items.effects.TeaEffect;

import javax.annotation.Nullable;
import java.util.List;

public class ItemHotWater extends ItemDrinkableTea {

	//NOTE: IGNORE_NBT_UPDATES is a field inherited and controlled by the parent class ItemDrinkableTea.

	public ItemHotWater(String name) {
		super(name, TeaEffect.teaEffect1);
	}

	@Override
	public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, ITooltipFlag flagIn) {
		super.addInformation(stack, worldIn, tooltip, flagIn);

		if (stack.hasTagCompound() && stack.getTagCompound().hasKey("FTemp")) {
			tooltip.add("Temp: " + Math.round(stack.getTagCompound().getFloat("FTemp")) + "°F");
		}
	}

	@Override
	public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged) {
		return oldStack.getItem() != newStack.getItem();
	}

	@Override
	public void onUpdate(ItemStack stack, World worldIn, Entity entityIn, int itemSlot, boolean isSelected) {
		super.onUpdate(stack, worldIn, entityIn, itemSlot, isSelected);

		if (IGNORE_NBT_UPDATES) return;

		NBTTagCompound nbt;

		if (stack.hasTagCompound()) {
			nbt = stack.getTagCompound();
		} else {
			nbt = new NBTTagCompound();
		}

		if (nbt.hasKey("FTemp")) {
			nbt.setFloat("FTemp", nbt.getFloat("FTemp") - 0.005F);
		} else {
			nbt.setFloat("FTemp", 212.0F);
			stack.setTagCompound(nbt);
		}
	}
}

So my issue is resolved.

However, if you don't mind me asking, as I'm a bit of a Forge noob, what are you referring to when you recommend using "capabilities?" Is this somehow related to the "capabilities" field in the EntityPlayer class, and what solution did you have in mind that used it? Like I said I'm a bit of a Forge noob and I'm trying to soak up as much info as possible because the last time I used Forge it didn't even have the event bus system as it exists now (which is really intuitive by the way). 

 

Thanks!!

Edited by uglyluigi
Link to comment
Share on other sites

22 minutes ago, uglyluigi said:

what are you referring to when you recommend using "capabilities?" Is this somehow related to the "capabilities" field in the EntityPlayer class, and what solution did you have in mind that used it?

http://mcforge.readthedocs.io/en/latest/datastorage/capabilities/

Apparently I'm a complete and utter jerk and come to this forum just like to make fun of people, be confrontational, and make your personal life miserable.  If you think this is the case, JUST REPORT ME.  Otherwise you're just going to get reported when you reply to my posts and point it out, because odds are, I was trying to be nice.

 

Exception: If you do not understand Java, I WILL NOT HELP YOU and your thread will get locked.

 

DO NOT PM ME WITH PROBLEMS. No help will be given.

Link to comment
Share on other sites

21 hours ago, Originem said:

Well, I have the same question, but after i used this function.I found that, though reequip animation is stopped, if I update the nbt, the item's name showed above the hp bar will appear again .

It sounds like each time you're returning a new ItemStack which is what usually causes the hotbar tooltip to appear. Try returning the same item stack you started with.

 

Edit: I've found a solution. You can disable held item tooltips on the fly with:

Minecraft.getMinecraft().gameSettings.heldItemTooltips = false;

And then just set it to true after you perform your updates.

Edited by uglyluigi
  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Announcements



  • Recently Browsing

    • No registered users viewing this page.
  • Posts

    • client version: forge-1.7.10-10.13.4.1614-1.7.10 forgegradle version:forge-1.7.10-10.13.4.1614-1.7.10   i can run my mod in forgegradle but i put my mod into a client ,it will crash  cpw.mods.fml.common.LoaderException: java.lang.NoSuchMethodError: com.example.examplemod.items.CommonItem.setUnlocalizedName(Ljava/lang/String;)Lnet/minecraft/item/Item;     at cpw.mods.fml.common.LoadController.transition(LoadController.java:163)     at cpw.mods.fml.common.Loader.preinitializeMods(Loader.java:559)     at cpw.mods.fml.client.FMLClientHandler.beginMinecraftLoading(FMLClientHandler.java:243)     at net.minecraft.client.Minecraft.func_71384_a(Minecraft.java:480)     at net.minecraft.client.Minecraft.func_99999_d(Minecraft.java:878)     at net.minecraft.client.main.Main.main(SourceFile:148)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:497)     at net.minecraft.launchwrapper.Launch.launch(Launch.java:135)     at net.minecraft.launchwrapper.Launch.main(Launch.java:28)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:497)     at oolloo.jlw.Wrapper.invokeMain(Wrapper.java:71)     at oolloo.jlw.Wrapper.main(Wrapper.java:51) Caused by: java.lang.NoSuchMethodError: com.example.examplemod.items.CommonItem.setUnlocalizedName(Ljava/lang/String;)Lnet/minecraft/item/Item;     at com.example.examplemod.items.CommonItem.<init>(CommonItem.java:10)     at com.example.examplemod.proxy.register.ItemRegister.createItem(ItemRegister.java:30)     at com.example.examplemod.proxy.register.ItemRegister.registerItems(ItemRegister.java:18)     at com.example.examplemod.proxy.CommonProxy.preInit(CommonProxy.java:12)     at com.example.examplemod.proxy.ClientProxy.preInit(ClientProxy.java:6)     at com.example.examplemod.ExampleMod.preInit(ExampleMod.java:26)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:497)     at cpw.mods.fml.common.FMLModContainer.handleModStateEvent(FMLModContainer.java:532)     at sun.reflect.GeneratedMethodAccessor4.invoke(Unknown Source)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:497)     at com.google.common.eventbus.EventSubscriber.handleEvent(EventSubscriber.java:74)     at com.google.common.eventbus.SynchronizedEventSubscriber.handleEvent(SynchronizedEventSubscriber.java:47)     at com.google.common.eventbus.EventBus.dispatch(EventBus.java:322)     at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:304)     at com.google.common.eventbus.EventBus.post(EventBus.java:275)     at cpw.mods.fml.common.LoadController.sendEventToModContainer(LoadController.java:212)     at cpw.mods.fml.common.LoadController.propogateStateMessage(LoadController.java:190)     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)     at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)     at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)     at java.lang.reflect.Method.invoke(Method.java:497)     at com.google.common.eventbus.EventSubscriber.handleEvent(EventSubscriber.java:74)     at com.google.common.eventbus.SynchronizedEventSubscriber.handleEvent(SynchronizedEventSubscriber.java:47)     at com.google.common.eventbus.EventBus.dispatch(EventBus.java:322)     at com.google.common.eventbus.EventBus.dispatchQueuedEvents(EventBus.java:304)     at com.google.common.eventbus.EventBus.post(EventBus.java:275)     at cpw.mods.fml.common.LoadController.distributeStateMessage(LoadController.java:119)     at cpw.mods.fml.common.Loader.preinitializeMods(Loader.java:556)     ... 16 more  
    • In mine forge 1.16.5 was written net.minecraftforge.fml.config.ConfigFileTypeHandler$ConfigLoadingException: Failed loading config file forge-common.toml of type COMMON for modid forge. How can I fix it?
    • So, that would mean remove the oculus mod aswell, ill try that
  • Topics

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.