IceMetalPunk Posted October 9, 2016 Posted October 9, 2016 I'm making a custom sword which is supposed to deal extra damage to Nether mobs (which I've defined as "any mobs immune to fire except shulkers and the Ender Dragon", since that seems to cover all of them exactly, for now). At first I was going to deal with it via event hooks, but then I realized I can just override the ItemSword#hitEntity method and deal the extra damage there. Or so I thought. During testing, if I set the extra damage to something insane like 10000, it works perfectly and one-hit kills Nether mobs. But when I lower the extra damage to be the strength of the full sword damage, the entities aren't taking the proper amount of damage. Here's the code I'm using: package com.IceMetalPunk.amethystic.AmethysticItems; import com.IceMetalPunk.amethystic.Amethystic; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.boss.EntityDragon; import net.minecraft.entity.monster.EntityShulker; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemSword; import net.minecraft.util.DamageSource; public class ItemAmethystSword extends ItemSword { public final float realAttackDamage; protected ItemAmethystSword() { super(Amethystic.AMETHYST_MATERIAL); this.realAttackDamage = 3.0f + Amethystic.AMETHYST_MATERIAL.getDamageVsEntity(); this.setUnlocalizedName("amethyst_sword").setRegistryName(Amethystic.MODID, "amethyst_sword"); this.setCreativeTab(Amethystic.AMETHYSTIC_TAB); } // Make it deal more damage to Withers and Nether mobs @Override public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase attacker) { super.hitEntity(stack, target, attacker); if (target.isImmuneToFire() && !(target instanceof EntityShulker) && !(target instanceof EntityDragon)) { System.out.println(this.realAttackDamage); if (attacker instanceof EntityPlayer) { target.attackEntityFrom(DamageSource.causePlayerDamage((EntityPlayer) attacker), 1.0f + this.realAttackDamage); } else { target.attackEntityFrom(DamageSource.causeMobDamage(attacker), 1.0f + this.realAttackDamage); } } return true; } } The numbers I'm using come from the fact that a sword's base attackDamage is 3 plus the material's damage (but since attackDamage is a private member, I'm creating my own here). And then that gets added via attribute modifiers to the player's own 1 damage from punching whenever they equip the sword. The sword does 7 damage (same as diamond), but when I hit a Nether mob, it does less damage somehow. For example, using /entitydata for inspection, one hit dropped an unarmored zombie pigman from 20 health to 13.114-and-change health, less than 7 damage. What am I missing here? This is supposed to be simple Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
Animefan8888 Posted October 9, 2016 Posted October 9, 2016 The code you provided only attacks the enemy with the real attack damage + 1 (for both). Quote 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.
IceMetalPunk Posted October 9, 2016 Author Posted October 9, 2016 The code you provided only attacks the enemy with the real attack damage + 1 (for both). Well, yes. The ItemSword#hitEntity base code doesn't actually deal any damage; in fact, all it does is take item durability in a single line and then return I figured since the actual damage is coming from the attribute modifiers of the sword and being applied elsewhere internally, if I just use attackEntityFrom() in the method, it'll stack on top of the usual damage, hence it should be a net total of 2*damage. I tested that by setting the damage amounts to 0 in both calls, and it worked fine, hitting the mobs for normal diamond sword amounts... Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
Leviathan143 Posted October 9, 2016 Posted October 9, 2016 The sword does 7 damage (same as diamond), but when I hit a Nether mob, it does less damage somehow. For example, using /entitydata for inspection, one hit dropped an unarmored zombie pigman from 20 health to 13.114-and-change health, less than 7 damage. What am I missing here? This is supposed to be simple Pigmen have natural armor. Magma cubes, slimes, shulkers, zombies, and The Wither are other mobs that also have natural armor. Item#hitEntity() is called after the damage from items is done, so your implementation will do more damage than you expect. A better method to override would be ItemSword#getDamageVsEntity() . Quote
IceMetalPunk Posted October 9, 2016 Author Posted October 9, 2016 The sword does 7 damage (same as diamond), but when I hit a Nether mob, it does less damage somehow. For example, using /entitydata for inspection, one hit dropped an unarmored zombie pigman from 20 health to 13.114-and-change health, less than 7 damage. What am I missing here? This is supposed to be simple Pigmen have natural armor. Magma cubes, slimes, shulkers, zombies, and The Wither are other mobs that also have natural armor. Item#hitEntity() is called after the damage from items is done, so your implementation will do more damage than you expect. A better method to override would be ItemSword#getDamageVsEntity() . O_o How did I never know these guys had some natural armor? *Sigh* Okay, then...that makes sense now. The getDamageVsEntity method doesn't have any parameters, so how would I determine which mob is being hit in the override of that? (I'm getting the feeling I'll end up *needing* to handle this in an event handler, though that seems less efficient since it would fire for every attack rather than just attacks with this particular sword...) Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
jeffryfisher Posted October 9, 2016 Posted October 9, 2016 A better method to override would be ItemSword#getDamageVsEntity(). I'm unsure about that. getDamageVsEntity() seems to be called only when a mob is deciding whether or not to pick up a fallen item (and wants to compare it to what it already has). Does it also figure into the player's attack calculation? One could try setting a break point to see if it is called during combat. I actually like using the hitEntity method to deal the extra damage. One thing to look out for though: You might then be bypassing the weapon cool-down, re-enabling the hit-spamming that was nerfed by Minecraft's combat upgrade. Decide if you need/want to mimic the partial damage of quick hits. Quote The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.
IceMetalPunk Posted October 10, 2016 Author Posted October 10, 2016 A better method to override would be ItemSword#getDamageVsEntity(). I'm unsure about that. getDamageVsEntity() seems to be called only when a mob is deciding whether or not to pick up a fallen item (and wants to compare it to what it already has). Does it also figure into the player's attack calculation? One could try setting a break point to see if it is called during combat. I actually like using the hitEntity method to deal the extra damage. One thing to look out for though: You might then be bypassing the weapon cool-down, re-enabling the hit-spamming that was nerfed by Minecraft's combat upgrade. Decide if you need/want to mimic the partial damage of quick hits. Yeah, I don't see it being used anywhere else, either. I'm not sure how it would be bypassing the cooldown; you can't trigger the extra damage unless you hit with the sword anyway. As far as I can see, the cooldown is only reset when calling PlayerControllerMP#attackEntity or EntityPlayer#attackTargetEntityWithCurrentItem, but not with attackEntityFrom. Am I missing something? *EDIT* Okay, so some more testing shows that the armor points aren't the problem here, nor am I dealing more damage than expected. In fact, the sword is dealing exactly the same amount of damage as a normal diamond sword. As I said, if I set the extra damage to something crazy like 10000, it works, but with it set to the same strength as normal, it doesn't seem to do any extra damage. I'm confused... Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
Draco18s Posted October 10, 2016 Posted October 10, 2016 I think the point is that hitEntity is called even while the cooldown is active. So you get full bonus damage even if the sword itself is dealing 0.1 damage. Quote 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.
IceMetalPunk Posted October 10, 2016 Author Posted October 10, 2016 I think the point is that hitEntity is called even while the cooldown is active. So you get full bonus damage even if the sword itself is dealing 0.1 damage. Ah, yes, that makes sense. Well, the actual amount of bonus damage still needs calibration, so I may end up recalculating based on the cooldown anyway. But first I need to figure out why it doesn't seem to be doing any bonus damage unless the amount is turned way, way up... *EDIT* Okay, odd...I threw in some debugging console logging, and it seems like target.attackEntityFrom() is returning false. Unfortunately, there are many reasons that would happen, so I guess it's time for me to step through and find out what's going on... *EDIT 2* Well...the importance of breakpoints, everyone. So it turns out the problem here is in hit-based resistance times. After getting hit, there's a small amount of time where an entity will only take damage if the attack is stronger than the previous attack. Since this attack is equivalent, and applied immediately after the first attack, it was being ignored by the resistance time. When I was testing with very high values, those were more than the "previous attack" strength, so it let them through. Okay, then. Now I have a place to start with fixing this. Time to see if I can actually reset the resistance timer in order to apply the second damage... *EDIT 3* If anyone's still reading this... So it turns out the resistance timer is a public member, so that was easy enough. However, in order to take cooldown into consideration, it introduces another problem: the bonus damage is triggering when the cooldown has just been reset from the normal attack, meaning it actually does no damage if cooldown is considered, and this code will only see a value of 0 for the cooldown no matter what. So is there a way to get the "previous cooldown", as it were? Or should I just be ignoring cooldown and having full bonus damage? (I thought about just dealing the equivalent of target.lastDamage, but that's also protected...I hope I won't have to hack my way through this via reflection...) Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
Animefan8888 Posted October 10, 2016 Posted October 10, 2016 I think the point is that hitEntity is called even while the cooldown is active. So you get full bonus damage even if the sword itself is dealing 0.1 damage. Ah, yes, that makes sense. Well, the actual amount of bonus damage still needs calibration, so I may end up recalculating based on the cooldown anyway. But first I need to figure out why it doesn't seem to be doing any bonus damage unless the amount is turned way, way up... *EDIT* Okay, odd...I threw in some debugging console logging, and it seems like target.attackEntityFrom() is returning false. Unfortunately, there are many reasons that would happen, so I guess it's time for me to step through and find out what's going on... *EDIT 2* Well...the importance of breakpoints, everyone. So it turns out the problem here is in hit-based resistance times. After getting hit, there's a small amount of time where an entity will only take damage if the attack is stronger than the previous attack. Since this attack is equivalent, and applied immediately after the first attack, it was being ignored by the resistance time. When I was testing with very high values, those were more than the "previous attack" strength, so it let them through. Okay, then. Now I have a place to start with fixing this. Time to see if I can actually reset the resistance timer in order to apply the second damage... *EDIT 3* If anyone's still reading this... So it turns out the resistance timer is a public member, so that was easy enough. However, in order to take cooldown into consideration, it introduces another problem: the bonus damage is triggering when the cooldown has just been reset from the normal attack, meaning it actually does no damage if cooldown is considered, and this code will only see a value of 0 for the cooldown no matter what. So is there a way to get the "previous cooldown", as it were? Or should I just be ignoring cooldown and having full bonus damage? (I thought about just dealing the equivalent of target.lastDamage, but that's also protected...I hope I won't have to hack my way through this via reflection...) I am currently wondering where entities are actually damage by the player now... the best way to figure this out would probably be to see where getDamageVsEntity() is called. Quote 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.
IceMetalPunk Posted October 10, 2016 Author Posted October 10, 2016 It's got nothing to do with ItemSword#getDamageVsEntity(). Entities are damaged by the player via a call to PlayerControllerMP#attackEntity, which is called in Minecraft#clickMouse. It just uses the player's strength modifier, which is updated whenever you change your equipped item. (In other words, the sword doesn't deal any damage, it just "makes you hit stronger" by an amount equal to the sword's attack strength when it's equipped; that strength is calculated in the item's constructor based on its material's attack damage.) Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
Animefan8888 Posted October 10, 2016 Posted October 10, 2016 It's got nothing to do with ItemSword#getDamageVsEntity(). If this isnt called when an entity is attacked by an item from the player there would be no point in the methods existence. PlayerControllerMp#attackEntity calls other methods figure out which one happens before resistance times are sent that are in the items class that give you the target then deal the damage there (hopefully without recursion). Quote 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.
IceMetalPunk Posted October 10, 2016 Author Posted October 10, 2016 As previously mentioned, ItemSword#getDamageVsEntity is only called in one place: when a mob that can pick up loot decides whether to pick up the item or not. It's how the mobs decide which swords are better and which should be dropped, since the ItemSword#attackDamage member is private and so couldn't be accessed from inside the mob class. As for cooldown times... there are literally only two lines of code between when the game gets the normal attack strength based on the current cooldown and when it resets that cooldown. And those two lines are just straight arithmetic. There's nowhere to hook into between them...which is extremely unfortunate... However, just before that calculation is made, Forge does fire an onPlayerAttackTarget event...so even though I was trying to avoid doing this with events due to its niche situations, I might have to. And the player class does have a special CooldownTracker that's public, so if I hook into it there, I should have access to the full cooldown information. Maybe; it seems quite decoupled from the ticksSinceLastSwing that all the damage calculations use, so I'll have to see what I can finagle out of it. Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
jeffryfisher Posted October 10, 2016 Posted October 10, 2016 You've reached the conclusion that I saw yesterday. Since you seem to want to preserve the weapon recharge rule, but still use hitEntity, I suggest this: Write a simple event handler that detects your sword, peeks at the cooldown percentage, and then stores it somewhere to use a moment later in your implementation of hitEntity. Because the code-path between storage and use is so short (within one method), you might even be safe using a public field inside class ItemAmethystSword. If you nest your event handler class inside your sword class, then the field might not even need to be public. Then rewrite hitEntity to implement the partial damage and also compensate for or bypass the mob's temporary immunity. Quote The debugger is a powerful and necessary tool in any IDE, so learn how to use it. You'll be able to tell us more and get better help here if you investigate your runtime problems in the debugger before posting.
IceMetalPunk Posted October 11, 2016 Author Posted October 11, 2016 If I'm going to end up using event handlers anyway (which I think I'm just going to have to live with), then there's a more elegant solution: there's the PlayerEvent.AttackEntityEvent, which fires in the same method as damage calculations, but shortly before the damage calculations, i.e. when the cooldown is exactly the value needed to calculate cooldown-adjusted damage. So I can hook into that, check if the item in the player's main hand is the custom sword, and if so, cancel the default event and recalculate the damage and deal it myself (essentially copy-pasting the default code as much as possible, but with a different damage value). I was hoping to avoid event handlers since they fire more generically than I'm going to need, but I guess a little inefficiency will just have to exist in this. Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
Draco18s Posted October 11, 2016 Posted October 11, 2016 An "is this my sword?" check is super cheap. Don't even worry about it. Quote 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.
IceMetalPunk Posted October 11, 2016 Author Posted October 11, 2016 An "is this my sword?" check is super cheap. Don't even worry about it. Fair enough. I have a habit of trying to over-optimize to the point of impracticality But yeah, my idea with the AttackEntityEvent hook--and a big, copypasta method for recreating the attack plus a multiplier--worked perfectly! So I guess this thread can be considered solved now. Thanks for everyone's help! Quote Whatever Minecraft needs, it is most likely not yet another tool tier.
Recommended Posts
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.