Jump to content

IceMetalPunk

Members
  • Posts

    402
  • Joined

  • Last visited

Everything posted by IceMetalPunk

  1. I know...which is what I don't understand. As I said, the method that calls the item damaging has a debug output that prints player.isServerWorld(). Every output from that is true; i.e. it's only being called on the server (or at least it *is* being called on the server). And yet everything else regarding the damage suggests it's only being called on the client. How is it possible for isServerWorld (which just wraps !world.isRemote anyway) to return true but the next line of code to run only on the client?
  2. Your post made me think of something, so I did a little more debugging, and I'm getting close to finding the problem. In the onItemRightClick method (where the tags are changed), I put a little debug message; the result was that I saw the damage on the server stay at 0 even while the damage on the client increased. Which is very confusing to me, because if I output player.isServerWorld() in the block collision code (where the item is damaged), it always outputs true. So if the damaging it being done only on the server, then how is the damage only actually applying on the client?
  3. I am...of you look at the code, the damageItem call doesn't check for remoteness, and so it should be called on both client and server, right? *EDIT* In fact, a quick test suggests that it's only called on the server, not on the client.
  4. I don't know about the 1.7.10 version, but skimming over the 1.10.2 source code of Biomes O' Plenty, it seems quicksand is not actually a block, but a fluid registered as such with the name "sand". The full source is here if you want to look over it yourself: https://github.com/Glitchfiend/BiomesOPlenty/blob/BOP-1.10.2-5.0.x/src/main/java/biomesoplenty/ There's also a 1.7.10 branch at that repo, but I couldn't actually find the quicksand class in that version.
  5. On collision with a block, if the item is being held, I'm calling player.getHeldItemMainHand().damageItem to take 1 durability from it. The damage is being applied correctly, and it's only resetting when I trigger the item use method above. *EDIT* Okay, so I seem to have solved the problem by changing the early return statement from if (world.isRemote){} to if (!world.isRemote){}... unless I'm getting it backwards (which is possible, since I often mix it up in my head), world.isRemote is true if it's the client, so by returning when it's false, I'm actually running the code on the client only, yes? I don't understand why that would be the solution (or why running it on the server-side would cause the problem), but that's good, I guess. However, there's another problem: the item damage isn't being saved with the world. I thought that was something which occurred naturally? The ItemStack#writeToNBT method seems to write the damage automatically, and I'm not overriding anything in there; and the damageItem() call isn't in any kind of side-checked conditional (i.e. it runs on both server and client), so what other reasons would there be for the damage to be unsaved? If it helps, here's the item's code: package com.IceMetalPunk.amethystic.AmethysticItems; import com.IceMetalPunk.amethystic.Amethystic; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; 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.util.math.BlockPos; import net.minecraft.util.text.TextComponentString; import net.minecraft.world.World; public class ItemPortkey extends Item { public ItemPortkey() { super(); this.setMaxStackSize(1); this.setMaxDamage(100); this.setUnlocalizedName("portkey").setRegistryName(Amethystic.MODID, "portkey"); this.setCreativeTab(Amethystic.AMETHYSTIC_TAB); } // Link portkey to current position when used @Override public ActionResult<ItemStack> onItemRightClick(ItemStack stack, World worldIn, EntityPlayer playerIn, EnumHand hand) { if (!worldIn.isRemote) { return new ActionResult(EnumActionResult.SUCCESS, stack); } stack = playerIn.getHeldItem(hand); NBTTagCompound current = new NBTTagCompound(); if (stack.hasTagCompound()) { current = stack.getTagCompound(); } BlockPos pos = playerIn.getPosition(); int x = pos.getX(), y = pos.getY(), z = pos.getZ(); current.setInteger("linkX", x); current.setInteger("linkY", y); current.setInteger("linkZ", z); playerIn.addChatComponentMessage(new TextComponentString("Portkey linked to (" + x + ", " + y + ", " + z + ")")); stack.setTagCompound(current); return new ActionResult(EnumActionResult.SUCCESS, stack); } } And the damage is being done in a block's collision method, using this code: @Override public void onEntityCollidedWithBlock(World world, BlockPos pos, IBlockState state, Entity entity) { System.out.println("Collision occured!"); if (entity instanceof EntityPlayer) { System.out.println("Player collided!"); EntityPlayer player = (EntityPlayer) entity; ItemStack mainItem = player.getHeldItemMainhand(); ItemStack offItem = player.getHeldItemOffhand(); if (mainItem != null && mainItem.getItem() == Amethystic.items.PORTKEY && mainItem.hasTagCompound()) { NBTTagCompound tag = mainItem.getTagCompound(); int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ"); mainItem.damageItem(1, player); player.setPositionAndUpdate(x, y, z); player.setFire(1); System.out.println("Has portkey in main hand linked to (" + x + ", " + y + ", " + z + ")!"); // TODO: Portkey teleportation sound } else if (offItem != null && offItem.getItem() == Amethystic.items.PORTKEY && offItem.hasTagCompound()) { NBTTagCompound tag = offItem.getTagCompound(); int x = tag.getInteger("linkX"), y = tag.getInteger("linkY"), z = tag.getInteger("linkZ"); offItem.damageItem(1, player); player.setPositionAndUpdate(x, y, z); player.setFire(1); // TODO: Portkey teleportation sound System.out.println("Has portkey in offhand linked to (" + x + ", " + y + ", " + z + ")!"); } else { System.out.println("No portkey!"); } } } Any ideas?
  6. I have an item which is just supposed to store coordinates. It can also take damage when those coordinates are used by another block. This all works fine...except for some reason, when it stores new coordinates, it also removes all damage done to it before. Which is not intended behavior. Here's the relevant code in the item's class: @Override public ActionResult<ItemStack> onItemRightClick(ItemStack stack, World worldIn, EntityPlayer playerIn, EnumHand hand) { if (worldIn.isRemote) { return new ActionResult(EnumActionResult.SUCCESS, stack); } NBTTagCompound current = new NBTTagCompound(); if (stack.hasTagCompound()) { current = stack.getTagCompound(); } BlockPos pos = playerIn.getPosition(); int x = pos.getX(), y = pos.getY(), z = pos.getZ(); current.setInteger("linkX", x); current.setInteger("linkY", y); current.setInteger("linkZ", z); playerIn.addChatComponentMessage(new TextComponentString("Portkey linked to (" + x + ", " + y + ", " + z + ")")); stack.setTagCompound(current); return new ActionResult(EnumActionResult.SUCCESS, stack); } Especially since I'm making modifications to the existing stack's tags and not starting a new one, I don't understand why the damage is getting reset when this code fires?
  7. Fair enough; I'll go that route and see what I can manage. Thanks.
  8. Perhaps I'm mistaken, but isn't reflection generally frowned upon? As in, a last-ditch effort if nothing else works?
  9. I have a custom block (BlockAmethyst) which is a beacon base material. I would like the UI of the beacon to change if its entire base is made of BlockAmethyst, to allow different effects to be chosen. Changing the UI is a simple container replacement in the GuiOpen event. Detecting the material of the beacon base would be easy as well. The hard part is that there's a HashSet of valid beacon effects in TileEntityBeacon, and without reflection, it's private. The primary and secondary effects are also private members with no setters. In fact, it seems the only way to change the beacon effects is to use readFromNBT, but that inherently verifies the beacon effects against that HashSet. So I'm thinking the best approach would be to replace the default Minecraft TileEntityBeacon with a custom one...but I have no idea how to do that. Is there even a way to replace a vanilla tile entity with my own? Or should I just be hooking into the beacon block placement, removing the instance of the tile entity naturally created, and then creating an instance of my own that extends it?
  10. 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!
  11. 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.
  12. 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.
  13. 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.)
  14. 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...)
  15. 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...
  16. 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...)
  17. 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...
  18. 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
  19. In the events replace the gui or container, or in your GuiHandler. O_o I didn't realize those were replaceable via event hooks...shows how often I deal with GUIs in Minecraft, eh? Thank you!
  20. And how would I make that subclassed container/UI show when the player uses a normal enchantment table, though? Isn't the default UI hardcoded to be displayed for that?
  21. slotIndex is the inventory slot that the Slot represents. slotNumber is the index of the Slot in the Container . If a Container has slots from two inventories (e.g. a chest and a player), slotIndex will be 0 to X for the first inventory and 0 to Y for the second inventory, while slotNumber will be 0 to X+Y. I don't think there is. Ah, okay! That makes sense; thanks for the explanation. I wish I could make stack transfers work, but honestly, it's such a small detail that it's not worth remaking the enchantment container entirely just for it. Players will have to be content with click-and-drop Thanks again!
  22. That worked, thank you! It does leave me curious: what's are the functions of slotNumber vs. index? Clearly they're two different things, and one is set in the slot constructor while the other isn't; but their names are confusingly similar. And another quality-of-life question that's not super-important: is there a way (without replacing the entire container with a custom one) to make shift-clicking of amethysts into the lapis/gem slot work? The transferStackInSlot() code seems pretty set in the ContainerEnchantment class, but perhaps there's an elegant way to "override" it without replacing the entire class?
  23. Okay...we're definitely getting closer, as now, it always accepts the amethyst item. The only problem is that even if I drop the amethyst in the lapis slot, it always moves to the first, enchanted item slot instead. I thought maybe the client-side container had the slots indexed differently, but it's just another instance of the same ContainerEnchantment class, so it should be the same index, right? So why would it move to the other slot instead of the one I drop it in? I don't usually like to do this, but here's all the relevant code in my event handler: // Method to replace the lapis slot on any passed ContainerEnchantment, to // allow amethyst private void ReplaceLapisSlot(ContainerEnchantment ench) { Slot newSlot = new Slot(ench.tableInventory, 1, 35, 47) { List<ItemStack> ores = OreDictionary.getOres("gemLapis"); public boolean isItemValid(@Nullable ItemStack stack) { if (stack.getItem() == Amethystic.items.AMETHYST) { return true; } for (ItemStack ore : ores) if (OreDictionary.itemMatches(ore, stack, false)) return true; return false; } }; ench.inventorySlots.set(1, newSlot); } // Server-side replacement of lapis slot @SubscribeEvent public void onOpenContainer(PlayerContainerEvent.Open ev) { Container cont = ev.getContainer(); if (cont instanceof ContainerEnchantment) { ContainerEnchantment ench = (ContainerEnchantment) cont; ReplaceLapisSlot(ench); } } // Client-side replacement of lapis slot @SubscribeEvent public void onOpenGui(GuiOpenEvent ev) { Gui gui = ev.getGui(); if (gui instanceof GuiEnchantment) { ContainerEnchantment ench = (ContainerEnchantment) ((GuiEnchantment) gui).inventorySlots; ReplaceLapisSlot(ench); } }
  24. I meant normally placing the items into the lapis slot. If I shift-click, it puts them in the main item slot (i.e. the place where you put the item you're trying to enchant). But if I place them manually into the lapis slot, it occasionally accepts them, but more often rejects them with the player hand movement you get when switching items (or, for example, when your shield takes damage; that "hand bobbing" motion). Screenshots won't show anything; visually, everything looks normal (except the hand bobbing which is too fast to catch in a screenshot). It's the behavior of accepting/rejecting the items in the lapis slot that's a problem. Give me about 10 or 15 minutes and I'll try to capture a GIF of it for you; that would show the issue much better than a screenshot. *EDIT* In recording the GIF, I discovered the glitch is very much a visual one: if I place the amethysts in the lapis slot, it seems not to accept them, but then if I put the rest of the stack in my inventory and click it again, the display updates and shows the correct number of items in the lapis slot. Here's the GIF; note that I am not holding shift at any point here, just clicking normally with the left and right mouse button:
  25. Ah! Sounds like the elegant way to do it, thanks! Unfortunately, it's not quite working. It's behaving oddly: it will occasionally accept the stack of amethysts (the gem item I'm trying to make it accept), but it usually won't. When I try placing the amethysts in the slot and it doesn't accept it, my player's hand blinks in the background as if switching items, even though I'm not. And if I double-click in the lapis slot with amethysts, even when it doesn't accept them, it'll still pull a full stack from my inventory as if the gems were properly in the slot. Here's my event handler code: @SubscribeEvent public void onOpenContainer(PlayerContainerEvent.Open ev) { Container cont = ev.getContainer(); if (cont instanceof ContainerEnchantment) { ContainerEnchantment ench = (ContainerEnchantment) cont; Slot newSlot = new Slot(ench.tableInventory, 1, 35, 47) { List<ItemStack> ores = OreDictionary.getOres("gemLapis"); public boolean isItemValid(@Nullable ItemStack stack) { if (stack.getItem() == Amethystic.items.AMETHYST) { return true; } for (ItemStack ore : ores) { if (OreDictionary.itemMatches(ore, stack, false)) { return true; } } return false; } }; ench.inventorySlots.set(1, newSlot); } } Am I missing something here?
×
×
  • Create New...

Important Information

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