package fi.dy.masa.enderutilities.item.tool; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.Nonnull; import javax.annotation.Nullable; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import net.minecraft.block.Block; import net.minecraft.block.BlockDirt; import net.minecraft.block.material.Material; import net.minecraft.block.state.IBlockState; import net.minecraft.client.renderer.block.model.ModelResourceLocation; import net.minecraft.client.resources.I18n; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.enchantment.Enchantment; import net.minecraft.enchantment.EnchantmentHelper; import net.minecraft.enchantment.EnumEnchantmentType; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.SharedMonsterAttributes; import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.item.EntityItem; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.init.Blocks; import net.minecraft.init.SoundEvents; import net.minecraft.inventory.EntityEquipmentSlot; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.tileentity.TileEntityEnderChest; import net.minecraft.util.EnumActionResult; import net.minecraft.util.EnumFacing; import net.minecraft.util.EnumHand; import net.minecraft.util.EnumParticleTypes; import net.minecraft.util.NonNullList; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundCategory; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import net.minecraftforge.common.IPlantable; import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.event.world.BlockEvent.HarvestDropsEvent; import net.minecraftforge.fml.common.FMLCommonHandler; import net.minecraftforge.items.CapabilityItemHandler; import net.minecraftforge.items.IItemHandler; import fi.dy.masa.enderutilities.config.Configs; import fi.dy.masa.enderutilities.effects.Effects; import fi.dy.masa.enderutilities.event.PlayerItemPickupEvent; import fi.dy.masa.enderutilities.item.base.IAnvilRepairable; import fi.dy.masa.enderutilities.item.base.IModule; import fi.dy.masa.enderutilities.item.base.ItemLocationBoundModular; import fi.dy.masa.enderutilities.item.base.ItemModule.ModuleType; import fi.dy.masa.enderutilities.item.part.ItemEnderCapacitor; import fi.dy.masa.enderutilities.item.part.ItemEnderPart; import fi.dy.masa.enderutilities.item.part.ItemLinkCrystal; import fi.dy.masa.enderutilities.reference.HotKeys; import fi.dy.masa.enderutilities.reference.HotKeys.EnumKey; import fi.dy.masa.enderutilities.reference.Reference; import fi.dy.masa.enderutilities.reference.ReferenceNames; import fi.dy.masa.enderutilities.registry.EnderUtilitiesItems; import fi.dy.masa.enderutilities.util.BlockUtils; import fi.dy.masa.enderutilities.util.ChunkLoading; import fi.dy.masa.enderutilities.util.EntityUtils; import fi.dy.masa.enderutilities.util.InventoryUtils; import fi.dy.masa.enderutilities.util.nbt.NBTUtils; import fi.dy.masa.enderutilities.util.nbt.OwnerData; import fi.dy.masa.enderutilities.util.nbt.TargetData; import fi.dy.masa.enderutilities.util.nbt.UtilItemModular; public class ItemEnderTool extends ItemLocationBoundModular implements IAnvilRepairable { public static final int ENDER_CHARGE_COST = 50; public float efficiencyOnProperMaterial; private final ToolMaterial material; public ItemEnderTool(String name) { super(name); this.material = ToolMaterial.ENDER_ALLOY_ADVANCED; this.efficiencyOnProperMaterial = this.material.getEfficiencyOnProperMaterial(); this.setMaxStackSize(1); this.setMaxDamage(0); this.setHasSubtypes(true); this.setNoRepair(); } @Override public String getTranslationKey(ItemStack stack) { ToolType toolType = ToolType.fromStack(stack); if (toolType != ToolType.INVALID) { return super.getTranslationKey() + "_" + toolType.getName(); } return super.getTranslationKey(); } @Override public EnumActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing side, float hitX, float hitY, float hitZ) { ItemStack stack = player.getHeldItem(hand); TileEntity te = world.getTileEntity(pos); // When sneak-right-clicking on an inventory or an Ender Chest, and the installed Link Crystal is a block type crystal, // then bind the crystal to the block clicked on. if (player.isSneaking() && te != null && (te.getClass() == TileEntityEnderChest.class || te.hasCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, side)) && UtilItemModular.getSelectedModuleTier(stack, ModuleType.TYPE_LINKCRYSTAL) == ItemLinkCrystal.TYPE_BLOCK) { if (world.isRemote == false) { UtilItemModular.setTarget(stack, player,pos, side, hitX, hitY, hitZ, false, false); } return EnumActionResult.SUCCESS; } // Hoe else if (ToolType.fromStack(stack) == ToolType.HOE) { if (world.isRemote == false) { if (PowerStatus.fromStack(stack) == PowerStatus.POWERED) { // Didn't till any soil; try to plant stuff from the player inventory or a remote inventory if (this.useHoeArea(stack, player, world, pos, side, 1, 1) == false) { this.useHoeToPlantArea(stack, player, hand, world, pos, side, hitX, hitY, hitZ, 1, 1); } } else { // Didn't till any soil; try to plant stuff from the player inventory or a remote inventory if (this.useHoe(stack, player, world, pos, side) == false) { this.useHoeToPlant(stack, player, hand, world, pos, side, hitX, hitY, hitZ); } } } return EnumActionResult.SUCCESS; } // Try to place a block from the slot right to the currently selected tool (or from slot 1 if tool is in slot 9) else { return this.placeBlock(stack, player, hand, world, pos, side, hitX, hitY, hitZ); } } private EnumActionResult placeBlock(ItemStack stack, EntityPlayer playerIn, EnumHand hand, World worldIn, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) { // Items in the off-hand, let vanilla handle that case if (playerIn.getHeldItemOffhand().isEmpty() == false) { return EnumActionResult.PASS; } int origSlot = playerIn.inventory.currentItem; int slot = (origSlot >= InventoryPlayer.getHotbarSize() - 1 ? 0 : origSlot + 1); ItemStack targetStack = playerIn.inventory.getStackInSlot(slot); // If the tool is in the first slot of the hotbar and there is no ItemBlock in the second slot, we fall back to the last slot if (origSlot == 0 && (targetStack.isEmpty() || (targetStack.getItem() instanceof ItemBlock) == false)) { slot = InventoryPlayer.getHotbarSize() - 1; targetStack = playerIn.inventory.getStackInSlot(slot); } // If the target stack is an ItemBlock, we try to place that in the world if (targetStack.isEmpty() == false && targetStack.getItem() instanceof ItemBlock) { // Check if we can place the block if (BlockUtils.checkCanPlaceBlockAt(worldIn, pos, side, ((ItemBlock) targetStack.getItem()).getBlock())) { ItemStack stackTool = playerIn.getHeldItem(hand); playerIn.inventory.setInventorySlotContents(slot, ItemStack.EMPTY); EntityUtils.setHeldItemWithoutEquipSound(playerIn, hand, targetStack); int sizeOrig = targetStack.getCount(); EnumActionResult result = EnumActionResult.PASS; try { result = targetStack.onItemUse(playerIn, worldIn, pos, hand, side, hitX, hitY, hitZ); } catch (Exception e) {} EntityUtils.setHeldItemWithoutEquipSound(playerIn, hand, stackTool); if (playerIn.capabilities.isCreativeMode) { targetStack.setCount(sizeOrig); } // Return the items to their original slot playerIn.inventory.setInventorySlotContents(slot, targetStack.isEmpty() ? ItemStack.EMPTY : targetStack); playerIn.inventoryContainer.detectAndSendChanges(); return result; } } return EnumActionResult.PASS; } public boolean useHoeArea(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side, int rWidth, int rHeight) { boolean northSouth = (((int)MathHelper.floor(player.rotationYaw * 4.0f / 360.0f + 0.5f)) & 1) == 0; boolean retValue = false; if (northSouth == false) { int tmp = rWidth; rWidth = rHeight; rHeight = tmp; } for (int x = pos.getX() - rWidth; x <= (pos.getX() + rWidth); ++x) { for (int z = pos.getZ() - rHeight; z <= (pos.getZ() + rHeight); ++z) { retValue |= this.useHoe(stack, player, world, new BlockPos(x, pos.getY(), z), side); } } return retValue; } public boolean useHoe(ItemStack stack, EntityPlayer player, World world, BlockPos pos, EnumFacing side) { if (player.canPlayerEdit(pos, side, stack) == false) { return false; } int hook = net.minecraftforge.event.ForgeEventFactory.onHoeUse(stack, player, world, pos); if (hook != 0) { return hook > 0; } IBlockState state = world.getBlockState(pos); Block block = state.getBlock(); if (side != EnumFacing.DOWN && world.isAirBlock(pos.up())) { IBlockState newBlockState = null; if (block == Blocks.GRASS) { newBlockState = Blocks.FARMLAND.getDefaultState(); } else if (block == Blocks.DIRT) { if (state.getValue(BlockDirt.VARIANT) == BlockDirt.DirtType.DIRT || state.getValue(BlockDirt.VARIANT) == BlockDirt.DirtType.COARSE_DIRT) { newBlockState = Blocks.FARMLAND.getDefaultState(); } else { return false; } } else { return false; } if (newBlockState != null) { world.setBlockState(pos, newBlockState, 3); this.addToolDamage(stack, 1, player, player); world.playSound(null, pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d, SoundEvents.ITEM_HOE_TILL, SoundCategory.BLOCKS, 1.0f, 1.0f); return true; } } return false; } public boolean useHoeToPlantArea(ItemStack stack, EntityPlayer player, EnumHand hand, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ, int rWidth, int rHeight) { boolean northSouth = (((int)MathHelper.floor(player.rotationYaw * 4.0f / 360.0f + 0.5f)) & 1) == 0; boolean retValue = false; if (northSouth == false) { int tmp = rWidth; rWidth = rHeight; rHeight = tmp; } for (int x = pos.getX() - rWidth; x <= (pos.getX() + rWidth); ++x) { for (int z = pos.getZ() - rHeight; z <= (pos.getZ() + rHeight); ++z) { retValue |= this.useHoeToPlant(stack, player, hand, world, new BlockPos(x, pos.getY(), z), side, hitX, hitY, hitZ); } } return retValue; } public boolean useHoeToPlant(ItemStack toolStack, EntityPlayer player, EnumHand hand, World world, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) { if (UtilItemModular.useEnderCharge(toolStack, ENDER_CHARGE_COST, true) == false) { return false; } IItemHandler inv = this.getLinkedInventoryWithChecks(toolStack, player); if (inv != null) { for (int slot = 0; slot < inv.getSlots(); slot++) { if (this.plantItemFromInventorySlot(world, player, hand, inv, slot, pos, side, hitX, hitY, hitZ)) { // Use Ender Charge if planting from a remote inventory if (DropsMode.fromStack(toolStack) == DropsMode.REMOTE) { UtilItemModular.useEnderCharge(toolStack, ENDER_CHARGE_COST, false); } Effects.addItemTeleportEffects(world, pos); return true; } } } return false; } private boolean plantItemFromInventorySlot(World world, EntityPlayer player, EnumHand hand, IItemHandler inv, int slot, BlockPos pos, EnumFacing side, float hitX, float hitY, float hitZ) { boolean ret = false; ItemStack plantStack = inv.getStackInSlot(slot); if (plantStack.isEmpty() == false && plantStack.getItem() instanceof IPlantable) { plantStack = inv.extractItem(slot, 1, false); if (plantStack.isEmpty()) { return false; } ItemStack stackHand = player.getHeldItem(hand); EntityUtils.setHeldItemWithoutEquipSound(player, hand, plantStack); if (plantStack.onItemUse(player, world, pos, hand, side, hitX, hitY, hitZ) == EnumActionResult.SUCCESS) { ret = true; } EntityUtils.setHeldItemWithoutEquipSound(player, hand, stackHand); if (plantStack.isEmpty() == false) { plantStack = InventoryUtils.tryInsertItemStackToInventory(inv, plantStack); if (plantStack.isEmpty() == false) { player.dropItem(plantStack, false, true); } } player.inventoryContainer.detectAndSendChanges(); } return ret; } @Override public boolean shouldCauseBlockBreakReset(ItemStack oldStack, ItemStack newStack) { return newStack.getItem() != oldStack.getItem() || newStack.getMetadata() != oldStack.getMetadata(); } @Override public boolean isEnchantable(ItemStack stack) { return true; } @Override public Set<String> getToolClasses(ItemStack stack) { String tc = ToolType.fromStack(stack).getToolClass(); return tc != null ? ImmutableSet.of(tc) : super.getToolClasses(stack); } @Override public boolean isDamageable() { return true; } @Override public int getMaxDamage(ItemStack stack) { return this.material.getMaxUses(); } @Override public boolean isDamaged(ItemStack stack) { return this.getDamage(stack) > 0; } @Override public int getDamage(ItemStack stack) { return NBTUtils.getShort(stack, null, "ToolDamage"); } @Override public void setDamage(ItemStack stack, int damage) { damage = MathHelper.clamp(damage, 0, this.material.getMaxUses()); NBTUtils.setShort(stack, null, "ToolDamage", (short)damage); } @Override public boolean showDurabilityBar(ItemStack stack) { return this.isDamaged(stack); } @Override public double getDurabilityForDisplay(ItemStack stack) { return (double)this.getDamage(stack) / (double)this.getMaxDamage(stack); } public boolean isToolBroken(ItemStack stack) { return NBTUtils.getShort(stack, null, "ToolDamage") >= this.material.getMaxUses(); } private boolean addToolDamage(ItemStack stack, int amount, EntityLivingBase living1, EntityLivingBase living2) { //System.out.println("addToolDamage(): living1: " + living1 + " living2: " + living2 + " remote: " + living2.worldObj.isRemote); if (this.isToolBroken(stack)) { return false; } if (amount > 0) { int unbreakingLevel = EnchantmentHelper.getEnchantmentLevel(Enchantment.getEnchantmentByLocation("unbreaking"), stack); int amountNegated = 0; for (int i = 0; unbreakingLevel > 0 && i < amount; i++) { if (itemRand.nextInt(amount + 1) > 0) { amountNegated++; } } amount -= amountNegated; if (amount <= 0) { return false; } } int damage = this.getDamage(stack); damage = Math.min(damage + amount, this.material.getMaxUses()); this.setDamage(stack, damage); // Tool just broke if (damage == this.material.getMaxUses()) { living1.renderBrokenItemStack(stack); } return true; } @Override public boolean repairItem(ItemStack stack, int amount) { if (amount == -1) { amount = this.material.getMaxUses(); } int damage = Math.max(this.getDamage(stack) - amount, 0); boolean repaired = damage != this.getDamage(stack); this.setDamage(stack, damage); return repaired; } @Override public boolean isRepairItem(@Nonnull ItemStack stackTool, @Nonnull ItemStack stackMaterial) { return InventoryUtils.areItemStacksEqual(stackMaterial, this.material.getRepairItemStack()); } @Override public boolean canApplyEnchantment(ItemStack stackTool, Enchantment enchantment) { if (enchantment.type == EnumEnchantmentType.ALL || enchantment.type == EnumEnchantmentType.BREAKABLE) { return true; } switch (ToolType.fromStack(stackTool)) { case SHOVEL: case PICKAXE: return enchantment.type == EnumEnchantmentType.DIGGER; case AXE: return enchantment.type == EnumEnchantmentType.WEAPON || enchantment.type == EnumEnchantmentType.DIGGER; case HOE: default: return false; } } @Override public boolean hitEntity(ItemStack stack, EntityLivingBase living1, EntityLivingBase living2) { this.addToolDamage(stack, 2, living1, living2); return true; } @Override public boolean onBlockDestroyed(ItemStack stack, World world, IBlockState state, BlockPos pos, EntityLivingBase livingBase) { //System.out.println("onBlockDestroyed(): living: " + living + " remote: " + living.worldObj.isRemote); if ((livingBase instanceof EntityPlayer) && this.isCreativeLikeBreakingEnabled(stack)) { ((EntityPlayer) livingBase).getCooldownTracker().setCooldown(this, 6); } // Don't use durability for breaking leaves with an axe if (state.getMaterial() == Material.LEAVES && ToolType.fromStack(stack).equals(ToolType.AXE)) { return false; } // Don't use durability on instant-minable blocks (hardness == 0.0f), or if the tool is already broken if (this.isToolBroken(stack) == false && state.getBlockHardness(world, pos) > 0.0f) { // Fast mode uses double the durability, but not while using the Creative Breaking upgrade int dmg = (PowerStatus.fromStack(stack) == PowerStatus.POWERED && this.isCreativeLikeBreakingEnabled(stack) == false ? 2 : 1); this.addToolDamage(stack, dmg, livingBase, livingBase); return true; } return false; } private IItemHandler getLinkedInventoryWithChecks(ItemStack toolStack, EntityPlayer player) { DropsMode mode = DropsMode.fromStack(toolStack); // Modes: 0: normal; 1: Add drops to player's inventory; 2: Transport drops to Link Crystal's bound destination // 0: normal mode; do nothing if (mode == DropsMode.NORMAL) { return null; } // 1: Add drops to player's inventory; To allow this, we require at least the lowest tier Ender Core (active) installed if (mode == DropsMode.PLAYER && this.getMaxModuleTier(toolStack, ModuleType.TYPE_ENDERCORE) >= ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_BASIC) { return player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP); // main inventory } // 2: Teleport drops to the Link Crystal's bound target; To allow this, we require an active second tier Ender Core // For cross-dimensional item teleport we require the third tier of active Ender Core else if (mode == DropsMode.REMOTE && this.getMaxModuleTier(toolStack, ModuleType.TYPE_ENDERCORE) >= ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_ENHANCED && UtilItemModular.useEnderCharge(toolStack, ENDER_CHARGE_COST, true)) { TargetData target = TargetData.getTargetFromSelectedModule(toolStack, ModuleType.TYPE_LINKCRYSTAL); // For cross-dimensional item teleport we require the third tier of active Ender Core if (target == null || (target.dimension != player.getEntityWorld().provider.getDimension() && this.getMaxModuleTier(toolStack, ModuleType.TYPE_ENDERCORE) != ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_ADVANCED)) { return null; } return UtilItemModular.getBoundInventory(toolStack, player, 15); } return null; } public void handleHarvestDropsEvent(ItemStack toolStack, HarvestDropsEvent event) { if (this.isToolBroken(toolStack) || event.getWorld() == null || event.getWorld().isRemote) { return; } DropsMode mode = DropsMode.fromStack(toolStack); // Modes: 0: normal; 1: Add drops to player's inventory; 2: Transport drops to Link Crystal's bound destination // 0: normal mode; do nothing if (mode == DropsMode.NORMAL) { return; } List<ItemStack> drops = event.getDrops(); EntityPlayer player = event.getHarvester(); boolean isSilk = event.isSilkTouching(); boolean transported = false; // Don't try to handle the drops via other means in the Remote mode until after we try to transport them here first if (mode == DropsMode.PLAYER && this.getMaxModuleTier(toolStack, ModuleType.TYPE_ENDERCORE) >= ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_BASIC && MinecraftForge.EVENT_BUS.post(new PlayerItemPickupEvent(player, drops))) { Effects.addItemTeleportEffects(event.getWorld(), event.getPos()); return; } IItemHandler inv = this.getLinkedInventoryWithChecks(toolStack, player); if (inv != null) { Iterator<ItemStack> iter = drops.iterator(); while (iter.hasNext()) { ItemStack stack = iter.next(); if (stack.isEmpty() == false && (isSilk || event.getWorld().rand.nextFloat() < event.getDropChance())) { ItemStack stackTmp = InventoryUtils.tryInsertItemStackToInventory(inv, stack.copy()); if (stackTmp.isEmpty()) { iter.remove(); transported = true; } else if (stackTmp.getCount() != stack.getCount()) { stack.setCount(stackTmp.getCount()); transported = true; } } } } // Location type Link Crystal, teleport/spawn the drops as EntityItems to the target spot else if (this.getSelectedModuleTier(toolStack, ModuleType.TYPE_LINKCRYSTAL) == ItemLinkCrystal.TYPE_LOCATION) { TargetData target = TargetData.getTargetFromSelectedModule(toolStack, ModuleType.TYPE_LINKCRYSTAL); // For cross-dimensional item teleport we require the third tier of active Ender Core if (OwnerData.canAccessSelectedModule(toolStack, ModuleType.TYPE_LINKCRYSTAL, player) == false || (target.dimension != player.getEntityWorld().provider.getDimension() && this.getMaxModuleTier(toolStack, ModuleType.TYPE_ENDERCORE) != ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_ADVANCED)) { return; } World targetWorld = FMLCommonHandler.instance().getMinecraftServerInstance().getWorld(target.dimension); if (targetWorld == null) { return; } // Chunk load the target for 30 seconds ChunkLoading.getInstance().loadChunkForcedWithPlayerTicket(player, target.dimension, target.pos.getX() >> 4, target.pos.getZ() >> 4, 30); Iterator<ItemStack> iter = drops.iterator(); while (iter.hasNext()) { ItemStack stack = iter.next(); if (stack.isEmpty() == false && (isSilk || event.getWorld().rand.nextFloat() < event.getDropChance())) { EntityItem entityItem = new EntityItem(targetWorld, target.dPosX, target.dPosY + 0.125d, target.dPosZ, stack.copy()); entityItem.motionX = entityItem.motionZ = 0.0d; entityItem.motionY = 0.15d; if (targetWorld.spawnEntity(entityItem)) { Effects.spawnParticles(targetWorld, EnumParticleTypes.PORTAL, target.dPosX, target.dPosY, target.dPosZ, 3, 0.2d, 1.0d); iter.remove(); transported = true; } } } } // At least something got transported somewhere... if (transported) { // Transported the drops to somewhere remote if (mode == DropsMode.REMOTE) { UtilItemModular.useEnderCharge(toolStack, ENDER_CHARGE_COST, false); } Effects.addItemTeleportEffects(event.getWorld(), event.getPos()); } // If we failed to handle the drops ourselves, then try to handle them via other means if (drops.size() > 0 && this.getMaxModuleTier(toolStack, ModuleType.TYPE_ENDERCORE) >= ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_BASIC && MinecraftForge.EVENT_BUS.post(new PlayerItemPickupEvent(player, drops))) { Effects.addItemTeleportEffects(event.getWorld(), event.getPos()); } // All items successfully transported somewhere, cancel the drops if (drops.size() == 0) { event.setDropChance(0.0f); } } @Override public float getDestroySpeed(ItemStack stack, IBlockState state) { if (this.isToolBroken(stack)) { return 0.2f; } ToolType tool = ToolType.fromStack(stack); if (this.isCreativeLikeBreakingEnabled(stack) && this.canHarvestBlock(state, stack)) { return 1600f; } // Allow instant mine of leaves with the axe if (state.getMaterial() == Material.LEAVES && tool.equals(ToolType.AXE)) { // This seems to be enough to instant mine leaves even when jumping/flying return 100.0f; } float eff = this.efficiencyOnProperMaterial; // 34 is the minimum to allow instant mining with just Efficiency V (= no beacon/haste) on cobble, // 124 is the minimum for iron blocks @ hardness 5.0f (which is about the highest of "normal" blocks), 1474 on obsidian. // So maybe around 160 might be ok? I don't want insta-mining on obsidian, but all other types of "rock". if (PowerStatus.fromStack(stack) == PowerStatus.POWERED) { if (EnchantmentHelper.getEnchantmentLevel(Enchantment.getEnchantmentByLocation("efficiency"), stack) >= 5) { eff = 124.0f; } // This is enough to give instant mining for sandstone and netherrack without any Efficiency enchants. else { eff = 24.0f; } } //if (ForgeHooks.isToolEffective(stack, block, meta)) if (state.getBlock().isToolEffective(tool.getToolClass(), state)) { //System.out.println("getStrVsBlock(); isToolEffective() true: " + eff); return eff; } if (this.canHarvestBlock(state, stack)) { //System.out.println("getStrVsBlock(); canHarvestBlock() true: " + eff); return eff; } //System.out.println("getStrVsBlock(); not effective: " + super.getStrVsBlock(stack, block, meta)); return super.getDestroySpeed(stack, state); } @Override public boolean canHarvestBlock(IBlockState state, ItemStack stack) { if (this.isToolBroken(stack)) { return false; } ToolType tool = ToolType.fromStack(stack); if (tool.equals(ToolType.PICKAXE)) // Ender Pickaxe { if (state.getMaterial() == Material.ROCK || state.getMaterial() == Material.GLASS || state.getMaterial() == Material.ICE || state.getMaterial() == Material.PACKED_ICE || state.getMaterial() == Material.REDSTONE_LIGHT || state.getMaterial() == Material.PISTON || state.getMaterial() == Material.IRON || state.getMaterial() == Material.ANVIL) { //System.out.println("canHarvestBlock(): true; Pickaxe"); return true; } } else if (tool.equals(ToolType.AXE)) // Ender Axe { if (state.getMaterial() == Material.WOOD || state.getMaterial() == Material.LEAVES || state.getMaterial() == Material.GOURD || state.getMaterial() == Material.CARPET || state.getMaterial() == Material.CLOTH || state.getMaterial() == Material.PLANTS || state.getMaterial() == Material.VINE) { //System.out.println("canHarvestBlock(): true; Axe"); return true; } } else if (tool.equals(ToolType.SHOVEL)) // Ender Shovel { if (state.getMaterial() == Material.GROUND || state.getMaterial() == Material.GRASS || state.getMaterial() == Material.SAND || state.getMaterial() == Material.SNOW || state.getMaterial() == Material.CRAFTED_SNOW || state.getMaterial() == Material.CLAY) { //System.out.println("canHarvestBlock(): true; Shovel"); return true; } } //System.out.println("canHarvestBlock(): false"); return false; } @Override public int getHarvestLevel(ItemStack stack, String toolClass, @Nullable EntityPlayer player, @Nullable IBlockState blockState) { //System.out.println("getHarvestLevel(stack, \"" + toolClass + "\")"); if (this.isToolBroken(stack) == false && ToolType.fromStack(stack).getToolClass().equals(toolClass)) { return this.material.getHarvestLevel(); } return -1; } @Override public int getItemEnchantability(ItemStack stack) { return this.material.getEnchantability(); } @Override public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot equipmentSlot, ItemStack stack) { Multimap<String, AttributeModifier> multimap = super.getAttributeModifiers(equipmentSlot, stack); //System.out.println("getAttributeModifiers()"); double dmg = 0.5f; // Default to almost no damage if the tool is broken ToolType toolType = ToolType.fromStack(stack); // Broken not tool if (this.isToolBroken(stack) == false) { dmg = toolType.getAttackDamage(); } if (equipmentSlot == EntityEquipmentSlot.MAINHAND) { String modifierName = toolType == ToolType.HOE ? "Weapon modifier" : "Tool modifier"; multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(), new AttributeModifier(ATTACK_DAMAGE_MODIFIER, modifierName, dmg, 0)); multimap.put(SharedMonsterAttributes.ATTACK_SPEED.getName(), new AttributeModifier(ATTACK_SPEED_MODIFIER, modifierName, toolType.getAttackSpeed(), 0)); } return multimap; } public void cyclePoweredMode(ItemStack stack) { NBTUtils.cycleByteValue(stack, null, "Powered", 1); } public void cycleCreativeBreakingMode(ItemStack stack) { if (this.getInstalledModuleCount(stack, ModuleType.CREATIVE_BREAKING) > 0) { NBTUtils.cycleByteValue(stack, null, "CreativeBreaking", 1); } else { NBTUtils.setByte(stack, null, "CreativeBreaking", (byte) 0); } } public void cycleDropsMode(ItemStack stack) { NBTUtils.cycleByteValue(stack, null, "DropsMode", 2); } public boolean isCreativeLikeBreakingEnabled(ItemStack stack) { return this.getInstalledModuleCount(stack, ModuleType.CREATIVE_BREAKING) > 0 && CreativeBreaking.fromStack(stack) == CreativeBreaking.ENABLED; } @Override public boolean doKeyBindingAction(EntityPlayer player, ItemStack stack, int key) { // Just Toggle mode key: Change the dig mode if (key == HotKeys.KEYBIND_ID_TOGGLE_MODE) { this.cyclePoweredMode(stack); return true; } // Shift + Toggle mode: Toggle the block drops handling mode: normal, player, remote else if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_SHIFT)) { this.cycleDropsMode(stack); return true; } // Ctrl + Shift + Toggle mode: Toggle the creative-like breaking mode on/off else if (EnumKey.TOGGLE.matches(key, HotKeys.MOD_SHIFT_CTRL) && ToolType.fromStack(stack) != ToolType.HOE) { this.cycleCreativeBreakingMode(stack); return true; } else { return super.doKeyBindingAction(player, stack, key); } } @Override public int getMaxModules(ItemStack containerStack) { return ToolType.fromStack(containerStack) == ToolType.HOE ? 5 : 6; } @Override public int getMaxModules(ItemStack containerStack, ModuleType moduleType) { if (moduleType.equals(ModuleType.TYPE_ENDERCORE)) { return 1; } if (moduleType.equals(ModuleType.TYPE_ENDERCAPACITOR)) { return 1; } if (moduleType.equals(ModuleType.CREATIVE_BREAKING) && ToolType.fromStack(containerStack) != ToolType.HOE) { return 1; } if (moduleType.equals(ModuleType.TYPE_LINKCRYSTAL)) { return 3; } return 0; } @Override public int getMaxModules(ItemStack containerStack, ItemStack moduleStack) { if (moduleStack.isEmpty() || (moduleStack.getItem() instanceof IModule) == false) { return 0; } IModule imodule = (IModule) moduleStack.getItem(); ModuleType moduleType = imodule.getModuleType(moduleStack); int tier = imodule.getModuleTier(moduleStack); // Allow the in-world/location and block/inventory type Link Crystals if (moduleType.equals(ModuleType.TYPE_LINKCRYSTAL) && (tier != ItemLinkCrystal.TYPE_LOCATION && tier != ItemLinkCrystal.TYPE_BLOCK)) { return 0; } if (moduleType.equals(ModuleType.TYPE_ENDERCORE) && (tier < ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_BASIC || tier > ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_ADVANCED)) { return 0; } return this.getMaxModules(containerStack, moduleType); } @Override public String getItemStackDisplayName(ItemStack stack) { return DropsMode.fromStack(stack) == DropsMode.REMOTE ? super.getItemStackDisplayName(stack) : this.getBaseItemDisplayName(stack); } @Override public void addTooltipLines(ItemStack stack, EntityPlayer player, List<String> list, boolean verbose) { ItemStack linkCrystalStack = this.getSelectedModuleStack(stack, ModuleType.TYPE_LINKCRYSTAL); ItemStack capacitorStack = this.getSelectedModuleStack(stack, ModuleType.TYPE_ENDERCAPACITOR); int coreTier = this.getSelectedModuleTier(stack, ModuleType.TYPE_ENDERCORE); String rst = TextFormatting.RESET.toString() + TextFormatting.GRAY.toString(); String preDGreen = TextFormatting.DARK_GREEN.toString(); String preBlue = TextFormatting.BLUE.toString(); // Drops mode ToolType type = ToolType.fromStack(stack); DropsMode mode = DropsMode.fromStack(stack); boolean powered = PowerStatus.fromStack(stack) == PowerStatus.POWERED; String str = (mode == DropsMode.NORMAL ? "enderutilities.tooltip.item.normal" : mode == DropsMode.PLAYER ? "enderutilities.tooltip.item.endertool.playerinv" : "enderutilities.tooltip.item.endertool.remote"); str = I18n.format(str); list.add(I18n.format("enderutilities.tooltip.item.endertool.dropsmode") + ": " + preDGreen + str + rst); if (type == ToolType.HOE) { str = (powered ? "enderutilities.tooltip.item.3x3" : "enderutilities.tooltip.item.1x1"); str = I18n.format(str); list.add(I18n.format("enderutilities.tooltip.item.mode") + ": " + preDGreen + str + rst); } else { // Dig mode (normal/fast) str = (powered ? "enderutilities.tooltip.item.fast" : "enderutilities.tooltip.item.normal"); str = I18n.format(str); list.add(I18n.format("enderutilities.tooltip.item.endertool.digmode") + ": " + preDGreen + str + rst); } // Installed Ender Core type str = I18n.format("enderutilities.tooltip.item.endercore") + ": "; if (coreTier >= ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_BASIC && coreTier <= ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_ADVANCED) { String coreType = (coreTier == ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_BASIC ? "enderutilities.tooltip.item.basic" : (coreTier == ItemEnderPart.ENDER_CORE_TYPE_ACTIVE_ENHANCED ? "enderutilities.tooltip.item.enhanced" : "enderutilities.tooltip.item.advanced")); coreType = I18n.format(coreType); str += preDGreen + coreType + rst + " (" + preBlue + I18n.format("enderutilities.tooltip.item.tier") + " " + (coreTier + 1) + rst + ")"; } else { String preRed = TextFormatting.RED.toString(); str += preRed + I18n.format("enderutilities.tooltip.item.none") + rst; } list.add(str); if (type != ToolType.HOE) { if (this.getInstalledModuleCount(stack, ModuleType.CREATIVE_BREAKING) > 0) { str = TextFormatting.GREEN.toString() + I18n.format("enderutilities.tooltip.item.yes") + rst; } else { str = TextFormatting.RED.toString() + I18n.format("enderutilities.tooltip.item.no") + rst; } list.add(I18n.format("enderutilities.tooltip.item.creative_breaking_installed", str)); if (this.isCreativeLikeBreakingEnabled(stack)) { str = TextFormatting.GREEN.toString() + I18n.format("enderutilities.tooltip.item.yes") + rst; } else { str = TextFormatting.RED.toString() + I18n.format("enderutilities.tooltip.item.no") + rst; } list.add(I18n.format("enderutilities.tooltip.item.creative_breaking_enabled", str)); } // Link Crystals installed if (linkCrystalStack.isEmpty() == false && linkCrystalStack.getItem() instanceof ItemLinkCrystal) { String preWhiteIta = TextFormatting.WHITE.toString() + TextFormatting.ITALIC.toString(); // Valid target set in the currently selected Link Crystal if (TargetData.itemHasTargetTag(linkCrystalStack)) { ((ItemLinkCrystal) linkCrystalStack.getItem()).addTooltipLines(linkCrystalStack, player, list, verbose); } else { list.add(I18n.format("enderutilities.tooltip.item.notargetset")); } int num = UtilItemModular.getInstalledModuleCount(stack, ModuleType.TYPE_LINKCRYSTAL); int sel = UtilItemModular.getClampedModuleSelection(stack, ModuleType.TYPE_LINKCRYSTAL) + 1; String dName = (linkCrystalStack.hasDisplayName() ? preWhiteIta + linkCrystalStack.getDisplayName() + rst + " " : ""); list.add(I18n.format("enderutilities.tooltip.item.selectedlinkcrystal.short") + String.format(" %s(%s%d%s / %s%d%s)", dName, preBlue, sel, rst, preBlue, num, rst)); } else { list.add(I18n.format("enderutilities.tooltip.item.nolinkcrystals")); } // Capacitor installed if (capacitorStack.isEmpty() == false && capacitorStack.getItem() instanceof ItemEnderCapacitor) { ((ItemEnderCapacitor) capacitorStack.getItem()).addTooltipLines(capacitorStack, player, list, verbose); } } @Override public void getSubItemsCustom(CreativeTabs creativeTab, NonNullList<ItemStack> list) { for (int i = 0; i < 4; i++) { list.add(new ItemStack(this, 1, i)); } } @Override public boolean hasEffect(ItemStack stack) { return false; } public enum DropsMode { NORMAL ("enderutilities.tooltip.item.normal"), PLAYER ("enderutilities.tooltip.item.endertool.playerinv"), REMOTE ("enderutilities.tooltip.item.endertool.remote"); //private final int id; private final String unlocalized; private DropsMode(String unlocalized) { //this.id = id; this.unlocalized = unlocalized; } public static DropsMode fromStack(ItemStack stack) { int mode = MathHelper.clamp(NBTUtils.getByte(stack, null, "DropsMode"), 0, 2); return values()[mode]; } public String getDisplayName() { return I18n.format(this.unlocalized); } } public enum PowerStatus { UNPOWERED, POWERED; private PowerStatus() { } public static PowerStatus fromStack(ItemStack stack) { int mode = MathHelper.clamp(NBTUtils.getByte(stack, null, "Powered"), 0, 1); return values()[mode]; } } public enum CreativeBreaking { DISABLED, ENABLED; private CreativeBreaking() { } public static CreativeBreaking fromStack(ItemStack stack) { int mode = MathHelper.clamp(NBTUtils.getByte(stack, null, "CreativeBreaking"), 0, 1); return values()[mode]; } } public enum ToolType { PICKAXE (0, "pickaxe", ReferenceNames.NAME_ITEM_ENDER_PICKAXE, 5.0f, -2.7f), AXE (1, "axe", ReferenceNames.NAME_ITEM_ENDER_AXE, 9.0f, -2.9f), SHOVEL (2, "shovel", ReferenceNames.NAME_ITEM_ENDER_SHOVEL, 5.5f, -2.9f), HOE (3, "hoe", ReferenceNames.NAME_ITEM_ENDER_HOE, 1.0f, 0.1f), INVALID (-1, "null", "null", 0.0f, 0.0f); private final int id; private final String toolClass; private final String name; private final float attackDamage; private final float attackSpeed; private static final Map<String, ToolType> TYPES = new HashMap<String, ToolType>(); static { for (ToolType type : ToolType.values()) { TYPES.put(type.getToolClass(), type); } } private ToolType(int id, String toolClass, String name, float attackDamage, float attackSpeed) { this.id = id; this.toolClass = toolClass; this.name = name; this.attackDamage = attackDamage; this.attackSpeed = attackSpeed; } public int getId() { return this.id; } public String getName() { return this.name; } public float getAttackDamage() { return this.attackDamage; } public float getAttackSpeed() { return this.attackSpeed; } public String getToolClass() { return this.toolClass; } public boolean equals(ToolType other) { return this.id == other.id; } public static ToolType fromToolClass(String toolClass) { ToolType type = TYPES.get(toolClass); return type != null ? type : SHOVEL; } public static ToolType fromStack(ItemStack stack) { int meta = MathHelper.clamp(stack.getMetadata(), 0, values().length - 2); return values()[meta]; } } public static enum ToolMaterial { ENDER_ALLOY_ADVANCED(3120, 12.0F, 0.0F, 15); private final int maxUses; private final float efficiencyOnProperMaterial; private final float damageVsEntity; private final int enchantability; //private ItemStack repairMaterial = ItemStack.EMPTY; private ToolMaterial(int maxUses, float efficiency, float damageVsEntity, int enchantability) { this.maxUses = maxUses; this.efficiencyOnProperMaterial = efficiency; this.damageVsEntity = damageVsEntity; this.enchantability = enchantability; } public int getMaxUses() { return this.maxUses; } public float getEfficiencyOnProperMaterial() { return this.efficiencyOnProperMaterial; } public float getDamageVsEntity() { return this.damageVsEntity; } public int getHarvestLevel() { return Configs.harvestLevelEnderAlloyAdvanced; } public int getEnchantability() { return this.enchantability; } /* public ToolMaterial setRepairItem(ItemStack stack) { if (this.repairMaterial.isEmpty() == false) { throw new RuntimeException("Repair material has already been set"); } this.repairMaterial = stack; return this; } */ public ItemStack getRepairItemStack() { return new ItemStack(EnderUtilitiesItems.ENDER_PART, 1, 2); } } @Override public ResourceLocation[] getItemVariants() { return new ResourceLocation[] { new ModelResourceLocation(Reference.MOD_ID + ":item_endertool", "inventory") }; } @Override public ModelResourceLocation getModelLocation(ItemStack stack) { return new ModelResourceLocation(Reference.MOD_ID + ":item_endertool", "inventory"); } }