package gregtech.common.items.behaviors; import java.util.Arrays; import java.util.List; import java.util.function.BooleanSupplier; import net.minecraft.block.Block; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.init.Blocks; import net.minecraft.item.ItemStack; import net.minecraft.tileentity.TileEntity; import net.minecraft.world.World; import net.minecraftforge.common.util.ForgeDirection; import net.minecraftforge.oredict.OreDictionary; import appeng.api.parts.IPartHost; import appeng.api.util.IOrientable; import appeng.tile.misc.TileInterface; import gregtech.api.enums.SoundResource; import gregtech.api.items.MetaBaseItem; import gregtech.api.items.MetaGeneratedTool; import gregtech.api.util.GTLanguageManager; import gregtech.api.util.GTUtility; import ic2.api.tile.IWrenchable; public class BehaviourWrench extends BehaviourNone { private final int mCosts; private final String mTooltip = GTLanguageManager .addStringLocalization("gt.behaviour.wrench", "Rotates Blocks on Rightclick"); public BehaviourWrench(int aCosts) { this.mCosts = aCosts; } @Override public boolean onItemUseFirst(MetaBaseItem aItem, ItemStack aStack, EntityPlayer aPlayer, World aWorld, int aX, int aY, int aZ, ForgeDirection side, float hitX, float hitY, float hitZ) { final Block aBlock = aWorld.getBlock(aX, aY, aZ); if (aBlock == null) { return false; } final int aMeta = aWorld.getBlockMetadata(aX, aY, aZ); final short targetSideOrdinal = (short) GTUtility.determineWrenchingSide(side, hitX, hitY, hitZ) .ordinal(); final TileEntity aTileEntity = aWorld.getTileEntity(aX, aY, aZ); final WrenchHandler handler = new WrenchHandler( aBlock, aMeta, targetSideOrdinal, aTileEntity, aPlayer, aWorld, aX, aY, aZ, aStack, (MetaGeneratedTool) aItem, mCosts); try { return handler.handle() && !aWorld.isRemote; } catch (Throwable ignored) {} return false; } /** *
* A class to simplify wrenching operation, * stopping "checking creative", "trying to damage tool", * "doing the logic" and "playing sound" again and again. * This should have been a record, but it's not available in Java 8. *
** {@link WrenchHandler#handle()} is the entry point of main logic. *
*/ private static class WrenchHandler { boolean handle() { ForgeDirection direction = ForgeDirection.getOrientation(targetSideOrdinal); // AE2 logic // default to change the up facing // sneak to change the forward facing if (tileEntity instanceof IOrientable orientable) { if (!orientable.canBeRotated()) return false; ForgeDirection front = orientable.getForward(); ForgeDirection up = orientable.getUp(); // mainly for me-interfaces, whose initial orientation is UNKNOWN if (front == ForgeDirection.UNKNOWN) { if (direction == ForgeDirection.UP || direction == ForgeDirection.DOWN) front = ForgeDirection.NORTH; else front = ForgeDirection.UP; } ForgeDirection back = front.getOpposite(); ForgeDirection down = up.getOpposite(); if (tileEntity instanceof TileInterface) { if (player.isSneaking()) return false; if (direction == down) { return doWrenchOperation(costs, () -> { orientable.setOrientation(ForgeDirection.UNKNOWN, ForgeDirection.UNKNOWN); return true; }); } // interface's up-side is opposite to the arrow on texture // make it intuitive by rotating it to the opposite side. direction = direction.getOpposite(); up = up.getOpposite(); } else if (direction == up || direction == front) { // rotate around the direction axis final var tempFront = front; final var tempUp = up; if (!player.isSneaking() && direction == up) return doWrenchOperation(costs, () -> { orientable.setOrientation(tempFront.getRotation(tempUp), tempUp); return true; }); if (player.isSneaking() && direction == front) return doWrenchOperation(costs, () -> { orientable.setOrientation( tempFront, tempUp.getRotation(tempFront) .getRotation(tempFront)); return true; }); } if (player.isSneaking()) { if (direction == up || direction == down) { orientable.setOrientation(direction, down.getRotation(front.getRotation(direction))); } else orientable.setOrientation(direction, up); } else { if (direction == front || direction == back) { orientable.setOrientation(back.getRotation(up.getRotation(direction)), direction); } else orientable.setOrientation(front, direction); } return damageWrench(costs); } if (world.isRemote) return false; // IC2 Wrenchable if (tileEntity instanceof IWrenchable wrenchable) { if (wrenchable.wrenchCanSetFacing(player, targetSideOrdinal)) { return doWrenchOperation(costs, () -> { wrenchable.setFacing(targetSideOrdinal); return true; }); } return false; } if (block == Blocks.powered_repeater || block == Blocks.unpowered_repeater || block == Blocks.powered_comparator || block == Blocks.unpowered_comparator) return setBlockMeta(costs, meta / 4 * 4 + (meta % 4 + 1) % 4); // hopper cannot face sky if (block == Blocks.hopper && targetSideOrdinal != 1) return setBlockMeta(costs, targetSideOrdinal); if (isVanillaAllSideRotatable(block)) if (meta < 6) return setBlockMeta(costs, targetSideOrdinal); // blocks like chests and furnaces have only four directions if (isVanillaCantFaceAxisY(block)) { if (targetSideOrdinal > 1) return setBlockMeta(costs, targetSideOrdinal); else return false; } if (tileEntity instanceof IPartHost) return false; final int logWoodId = OreDictionary.getOreID("logWood"); if (Arrays.stream(OreDictionary.getOreIDs(new ItemStack(block))) .anyMatch(id -> id == logWoodId)) { // The meta just work return setBlockMeta(costs, (meta + 4) % 12); } // vanilla block rotate logic if ((Arrays.asList(block.getValidRotations(world, x, y, z)) .contains(direction))) return rotateBlock(costs, direction); return false; // GT blocks' rotations are done by blocks themselves after this returning false } private final Block block; private final short targetSideOrdinal; private final TileEntity tileEntity; private final EntityPlayer player; private final World world; private final int x, y, z, meta; private final ItemStack stack; private final MetaGeneratedTool item; private final int costs; public WrenchHandler(Block block, int meta, short targetSideOrdinal, TileEntity tileEntity, EntityPlayer player, World world, int x, int y, int z, ItemStack stack, MetaGeneratedTool item, int costs) { this.block = block; this.meta = meta; this.targetSideOrdinal = targetSideOrdinal; this.tileEntity = tileEntity; this.player = player; this.world = world; this.x = x; this.y = y; this.z = z; this.stack = stack; this.item = item; this.costs = costs; } /** * this will run the operation, damage the tool and play the sound if possible (creative mode or * {@link MetaGeneratedTool#canWrench(EntityPlayer, int, int, int)}) * * @param damage damage to be applied to the wrench * @param operation the real operation of the click * @return true if the operation was successful * @see #setBlockMeta(int, int) * @see #rotateBlock(int, ForgeDirection) * @see #rotateBlock(int, ForgeDirection) */ boolean doWrenchOperation(int damage, BooleanSupplier operation) { if (player.capabilities.isCreativeMode || item.canWrench(player, x, y, z)) { if (operation.getAsBoolean()) { item.doDamage(stack, damage); GTUtility.sendSoundToPlayers(world, SoundResource.IC2_TOOLS_WRENCH, 1.0F, -1.0F, x, y, z); return true; } } return false; } boolean setBlockMeta(int damage, int newMeta) { return doWrenchOperation(damage, () -> setBlockMetadataWithNotify(newMeta)); } boolean rotateBlock(int damage, ForgeDirection direction) { return doWrenchOperation(damage, () -> block.rotateBlock(world, x, y, z, direction)); } boolean damageWrench(int damage) { return doWrenchOperation(damage, () -> true); } private boolean setBlockMetadataWithNotify(int newMeta) { return world.setBlockMetadataWithNotify(x, y, z, newMeta, 3); } } public static boolean isVanillaRotatable(Block block) { return isVanillaCantFaceAxisY(block) || isVanillaAllSideRotatable(block) || block == Blocks.hopper; } public static boolean isVanillaCantFaceAxisY(Block block) { return GTUtility.arrayContains( block, Blocks.pumpkin, Blocks.lit_pumpkin, Blocks.furnace, Blocks.lit_furnace, Blocks.chest, Blocks.trapped_chest, Blocks.ender_chest); } public static boolean isVanillaAllSideRotatable(Block block) { return GTUtility.arrayContains(block, Blocks.piston, Blocks.sticky_piston, Blocks.dispenser, Blocks.dropper); } @Override public List