diff --git a/src/main/java/supersymmetry/api/blocks/IForcedStates.java b/src/main/java/supersymmetry/api/blocks/IForcedStates.java new file mode 100644 index 000000000..343b8ef0a --- /dev/null +++ b/src/main/java/supersymmetry/api/blocks/IForcedStates.java @@ -0,0 +1,6 @@ +package supersymmetry.api.blocks; + +public interface IForcedStates { + void setForcedState(int val); + int getForcedState(); +} diff --git a/src/main/java/supersymmetry/api/unification/material/info/SuSyMaterialIconType.java b/src/main/java/supersymmetry/api/unification/material/info/SuSyMaterialIconType.java index 9a5732de2..50e59f937 100644 --- a/src/main/java/supersymmetry/api/unification/material/info/SuSyMaterialIconType.java +++ b/src/main/java/supersymmetry/api/unification/material/info/SuSyMaterialIconType.java @@ -6,6 +6,8 @@ public class SuSyMaterialIconType { public static MaterialIconType catalystBed = new MaterialIconType("catalystBed"); public static MaterialIconType catalystPellet = new MaterialIconType("catalystPellet"); public static MaterialIconType sheetedFrame = new MaterialIconType("sheetedFrame"); + public static MaterialIconType sheetedFrameAll = new MaterialIconType("sheetedFrameAll"); + public static MaterialIconType sheetedFrameEnd = new MaterialIconType("sheetedFrameEnd"); // Could not get this working unless I referenced it in a model public static MaterialIconType sifted = new MaterialIconType("sifted"); public static MaterialIconType flotated = new MaterialIconType("flotated"); public static MaterialIconType concentrate = new MaterialIconType("concentrate"); diff --git a/src/main/java/supersymmetry/common/blocks/BlockSheetedFrame.java b/src/main/java/supersymmetry/common/blocks/BlockSheetedFrame.java index cad88a00c..c5cce67b5 100644 --- a/src/main/java/supersymmetry/common/blocks/BlockSheetedFrame.java +++ b/src/main/java/supersymmetry/common/blocks/BlockSheetedFrame.java @@ -1,16 +1,23 @@ package supersymmetry.common.blocks; import gregtech.api.GregTechAPI; +import gregtech.api.capability.GregtechDataCodes; +import gregtech.api.items.toolitem.ToolClasses; +import gregtech.api.pipenet.block.BlockPipe; +import gregtech.api.pipenet.block.ItemBlockPipe; +import gregtech.api.pipenet.tile.IPipeTile; +import gregtech.api.pipenet.tile.TileEntityPipeBase; import gregtech.api.recipes.ModHandler; import gregtech.api.unification.material.Materials; import gregtech.api.util.GTUtility; +import gregtech.common.blocks.BlockFrame; +import gregtech.common.blocks.MetaBlocks; import gregtech.common.blocks.properties.PropertyMaterial; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import net.minecraft.block.Block; -import net.minecraft.block.BlockRotatedPillar; +import net.minecraft.block.BlockAir; import net.minecraft.block.SoundType; import net.minecraft.block.material.EnumPushReaction; -import net.minecraft.block.properties.IProperty; import net.minecraft.block.properties.PropertyEnum; import net.minecraft.block.state.BlockFaceShape; import net.minecraft.block.state.BlockStateContainer; @@ -20,8 +27,11 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; import net.minecraft.item.Item; +import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.*; import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; @@ -35,14 +45,20 @@ import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.jetbrains.annotations.NotNull; +import supersymmetry.api.SusyLog; +import supersymmetry.api.blocks.IForcedStates; import supersymmetry.api.unification.material.info.SuSyMaterialIconType; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Map; +import static supersymmetry.common.blocks.SuSyMetaBlocks.SHEETED_FRAMES; + public class BlockSheetedFrame extends Block { + public static final int UPDATE_ROTATION_STATE = GregtechDataCodes.assignId(); + public static final PropertyEnum SHEETED_FRAME_AXIS = PropertyEnum.create("axis", BlockSheetedFrame.FrameEnumAxis.class); public final PropertyMaterial variantProperty; @@ -68,7 +84,7 @@ public BlockSheetedFrame(Material[] materials) * IBlockstate */ @Override @NotNull - public IBlockState getStateForPlacement(World worldIn, BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, EntityLivingBase placer) + public IBlockState getStateForPlacement(@NotNull World worldIn, @NotNull BlockPos pos, EnumFacing facing, float hitX, float hitY, float hitZ, int meta, @NotNull EntityLivingBase placer) { return this.getStateFromMeta(meta).withProperty(SHEETED_FRAME_AXIS, BlockSheetedFrame.FrameEnumAxis.fromFacingAxis(facing.getAxis())); } @@ -89,40 +105,37 @@ protected BlockStateContainer createBlockState() { * fine. */ @Override - public IBlockState withRotation(IBlockState state, Rotation rot) + public @NotNull IBlockState withRotation(@NotNull IBlockState state, Rotation rot) { - switch (rot) - { - case COUNTERCLOCKWISE_90: - case CLOCKWISE_90: - - switch (state.getValue(SHEETED_FRAME_AXIS)) - { - case X: - return state.withProperty(SHEETED_FRAME_AXIS, BlockSheetedFrame.FrameEnumAxis.Z); - case Z: - return state.withProperty(SHEETED_FRAME_AXIS, BlockSheetedFrame.FrameEnumAxis.X); - default: - return state; - } + switch (rot) { + case COUNTERCLOCKWISE_90, CLOCKWISE_90 -> { + return switch (state.getValue(SHEETED_FRAME_AXIS)) { + case X -> state.withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.Z); + case Z -> state.withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.X); + default -> state; + }; + } - default: + default -> { return state; + } } } - public static enum FrameEnumAxis implements IStringSerializable + public enum FrameEnumAxis implements IStringSerializable { - X("x"), - Y("y"), - Z("z"), - NONE("none"); + X("x", EnumFacing.Axis.X), + Y("y", EnumFacing.Axis.Y), + Z("z", EnumFacing.Axis.Z), + NONE("none", null); private final String name; - private FrameEnumAxis(String name) - { + private final EnumFacing.Axis axis; + + FrameEnumAxis(String name, EnumFacing.Axis axis) { this.name = name; + this.axis = axis; } public String toString() @@ -132,31 +145,33 @@ public String toString() public static FrameEnumAxis fromFacingAxis(EnumFacing.Axis axis) { - switch (axis) - { - case X: - return X; - case Y: - return Y; - case Z: - return Z; - default: - return NONE; - } + if (axis == null) return NONE; + return switch (axis) { + case X -> X; + case Y -> Y; + case Z -> Z; + }; } - public String getName() + public @NotNull String getName() { return this.name; } + + public @Nullable EnumFacing.Axis getAxis() { return this.axis; } + + // never returns none + public static FrameEnumAxis fromFacing(EnumFacing facing) { + return FrameEnumAxis.values()[facing.getAxis().ordinal()]; + } } @Override @Deprecated - public boolean isOpaqueCube(IBlockState state) { return false; } + public boolean isOpaqueCube(@NotNull IBlockState state) { return false; } @Override @SideOnly(Side.CLIENT) - public BlockRenderLayer getRenderLayer() + public @NotNull BlockRenderLayer getRenderLayer() { return BlockRenderLayer.CUTOUT_MIPPED; } @@ -212,14 +227,41 @@ public void getSubBlocks(@Nonnull CreativeTabs tab, @Nonnull NonNullList list.add(getItem(blockState))); } - public static ItemStack getItem(IBlockState blockState) { - return GTUtility.toItem(blockState); + // returns null to indicate an invalid/ non-existent sheeted frame state equivalent, or returns the equivalent sheeted state + public static IBlockState determineSheetedState(IBlockAccess world, BlockPos pos) { + IBlockState state = world.getBlockState(pos); + + if (state.getBlock() instanceof BlockSheetedFrame) { + return state; + } + + if (state.getBlock() instanceof BlockPipe) { + IPipeTile pipetile = ((BlockPipe) state.getBlock()).getPipeTileEntity(world, pos); + if (pipetile == null) return null; + + int rotationOrdinal = ((IForcedStates) pipetile).getForcedState() - 1; + Material mat = pipetile.getFrameMaterial(); + if (rotationOrdinal < 0 || mat == null) return null; + + return SHEETED_FRAMES.get(mat).getDefaultState().withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.values()[rotationOrdinal]); + } + + return null; } public ItemStack getItem(Material material) { return getItem(this.getDefaultState().withProperty(this.variantProperty, material).withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.Y)); } + // doesn't really make sense to drop based on orientation, but the method is here just in case + public ItemStack getItem(Material material, int orientationOrdinal) { + return getItem(this.getDefaultState().withProperty(this.variantProperty, material).withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.values()[orientationOrdinal])); + } + + public static ItemStack getItem(IBlockState blockState) { + return GTUtility.toItem(blockState); + } + public IBlockState getBlock(Material material) { return getDefaultState().withProperty(this.variantProperty, material).withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.Y); } @@ -237,107 +279,231 @@ public boolean canCreatureSpawn(@Nonnull IBlockState state, @Nonnull IBlockAcces return false; } - /* - public boolean replaceWithFramedPipe(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, ItemStack stackInHand, EnumFacing facing) { - BlockPipe blockPipe = (BlockPipe)((ItemBlockPipe)stackInHand.getItem()).getBlock(); - if (((IPipeType)blockPipe.getItemPipeType(stackInHand)).getThickness() < 1.0F) { - ItemBlock itemBlock = (ItemBlock)stackInHand.getItem(); - IBlockState pipeState = blockPipe.getDefaultState(); - itemBlock.placeBlockAt(stackInHand, playerIn, worldIn, pos, facing, 0.0F, 0.0F, 0.0F, pipeState); - IPipeTile pipeTile = blockPipe.getPipeTileEntity(worldIn, pos); - if (pipeTile instanceof TileEntityPipeBase) { - ((TileEntityPipeBase)pipeTile).setFrameMaterial(this.getGtMaterial(this.getMetaFromState(state))); - SoundType type = blockPipe.getSoundType(state, worldIn, pos, playerIn); - worldIn.playSound(playerIn, pos, type.getPlaceSound(), SoundCategory.BLOCKS, (type.getVolume() + 1.0F) / 2.0F, type.getPitch() * 0.8F); - if (!playerIn.capabilities.isCreativeMode) { - stackInHand.shrink(1); - } + public enum ToolReactions { + ROTATE, + MUTATE, + NONE; - return true; - } else { - GTLog.logger.error("Pipe was not placed!"); - return false; + public static ToolReactions getReaction(ItemStack stack) { + if (stack == null) return NONE; + if (stack.getItem().getToolClasses(stack).contains(ToolClasses.SCREWDRIVER)) return ROTATE; + if (stack.getItem().getToolClasses(stack).contains(ToolClasses.HARD_HAMMER)) return MUTATE; + return NONE; + } + + public static int reactionResult(ToolReactions reaction, int state) { + if (reaction == ROTATE) { + return (state + 1) % 3; + } else if (reaction == MUTATE) { + return state == FrameEnumAxis.NONE.ordinal() ? FrameEnumAxis.Y.ordinal() : FrameEnumAxis.NONE.ordinal(); } - } else { - return false; + + return state; } } - public boolean removeFrame(World world, BlockPos pos, EntityPlayer player, ItemStack stack) { - TileEntity te = world.getTileEntity(pos); - if (te instanceof TileEntityPipeBase && ((IPipeTile)te).getFrameMaterial() != null) { - TileEntityPipeBase pipeTile = (TileEntityPipeBase)te; - Material frameMaterial = pipeTile.getFrameMaterial(); - pipeTile.setFrameMaterial((Material)null); - Block.spawnAsEntity(world, pos, this.getItem(frameMaterial)); - ToolHelper.damageItem(stack, player); - ToolHelper.playToolSound(stack, player); - return true; - } else { - return false; - } + @Override + public boolean onBlockActivated(@NotNull World world, @NotNull BlockPos pos, @NotNull IBlockState state, + @NotNull EntityPlayer player, @NotNull EnumHand hand, @NotNull EnumFacing facing, + float hitX, float hitY, float hitZ) { + return onBlockActivated(false, world, pos, state, player, hand, facing, hitX, hitY, hitZ); } - public boolean onBlockActivated(@Nonnull World worldIn, @Nonnull BlockPos pos, @Nonnull IBlockState state, EntityPlayer playerIn, @Nonnull EnumHand hand, @Nonnull EnumFacing facing, float hitX, float hitY, float hitZ) { - ItemStack stackInHand = playerIn.getHeldItem(hand); - if (stackInHand.isEmpty()) { - return false; - } else if (stackInHand.getItem() instanceof ItemBlockPipe) { - return this.replaceWithFramedPipe(worldIn, pos, state, playerIn, stackInHand, facing); - } else if (stackInHand.getItem().getToolClasses(stackInHand).contains("crowbar")) { - return this.removeFrame(worldIn, pos, playerIn, stackInHand); - } else if (!(stackInHand.getItem() instanceof FrameItemBlock)) { + public boolean onBlockActivated(boolean isPipe, @NotNull World world, @NotNull BlockPos pos, @NotNull IBlockState state, + @NotNull EntityPlayer player, @NotNull EnumHand hand, @NotNull EnumFacing facing, + float hitX, float hitY, float hitZ) { + ItemStack stack = player.getHeldItem(hand); + if (stack.isEmpty()) { return false; + } + + // pipes surrounded by frames have their own implementation within the mixin for these behaviors + if (!isPipe) { + // special action on screwdrivers and wrenches, though changes are done pipe-side if blockstate "contains" pipe + if ((state.getBlock() instanceof BlockSheetedFrame)) { + if (ToolReactions.getReaction(stack) == ToolReactions.MUTATE) { + world.setBlockState(pos, state.withProperty(SHEETED_FRAME_AXIS, state.getValue(SHEETED_FRAME_AXIS) == FrameEnumAxis.NONE ? FrameEnumAxis.Y : FrameEnumAxis.NONE)); + return true; + } else if (ToolReactions.getReaction(stack) == ToolReactions.ROTATE) { + world.setBlockState(pos, state.withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.values()[(state.getValue(SHEETED_FRAME_AXIS).ordinal() + 1) % 3])); + return true; + } + } + + // replace frame with pipe and set the frame material to this frame + if (stack.getItem() instanceof ItemBlockPipe) { + return replaceWithFramedPipe(world, pos, state, player, stack, facing); + } + } + + // check if frame block, return if not + BlockSheetedFrame sheetedFrameBlock = getFrameBlockFromItem(stack); + if (sheetedFrameBlock == null) return false; + + BlockPos.PooledMutableBlockPos blockPos = BlockPos.PooledMutableBlockPos.retain(); + blockPos.setPos(pos); + + // determine ordinal for orientation + int rotationOrdinal; + if (state.getBlock() instanceof BlockSheetedFrame) { + rotationOrdinal = state.getValue(SHEETED_FRAME_AXIS).ordinal(); } else { - BlockPos.PooledMutableBlockPos blockPos = BlockPos.PooledMutableBlockPos.retain(); - blockPos.setPos(pos); - - for(int i = 0; i < 32; ++i) { - if (worldIn.getBlockState(blockPos).getBlock() instanceof BlockFrame) { - blockPos.move(EnumFacing.UP); - } else { - TileEntity te = worldIn.getTileEntity(blockPos); - if (!(te instanceof IPipeTile) || ((IPipeTile)te).getFrameMaterial() == null) { - if (this.canPlaceBlockAt(worldIn, blockPos)) { - worldIn.setBlockState(blockPos, ((FrameItemBlock)stackInHand.getItem()).getBlockState(stackInHand)); - SoundType type = this.getSoundType(stackInHand); - worldIn.playSound((EntityPlayer)null, pos, type.getPlaceSound(), SoundCategory.BLOCKS, (type.getVolume() + 1.0F) / 2.0F, type.getPitch() * 0.8F); - if (!playerIn.capabilities.isCreativeMode) { - stackInHand.shrink(1); - } - - blockPos.release(); - return true; - } else if (te instanceof TileEntityPipeBase && ((TileEntityPipeBase)te).getFrameMaterial() == null) { - Material material = ((BlockFrame)((FrameItemBlock)stackInHand.getItem()).getBlock()).getGtMaterial(stackInHand.getMetadata()); - ((TileEntityPipeBase)te).setFrameMaterial(material); - SoundType type = this.getSoundType(stackInHand); - worldIn.playSound((EntityPlayer)null, pos, type.getPlaceSound(), SoundCategory.BLOCKS, (type.getVolume() + 1.0F) / 2.0F, type.getPitch() * 0.8F); - if (!playerIn.capabilities.isCreativeMode) { - stackInHand.shrink(1); - } - - blockPos.release(); - return true; - } else { - blockPos.release(); - return false; - } + // in theory te should always be pipe, otherwise this would never be called, but other methods double check, so I will too + TileEntity te = world.getTileEntity(blockPos); + + // get rotationOrdinal from stack in hand if state at pos is not valid + if (!(te instanceof IPipeTile) || ((IPipeTile) te).getFrameMaterial() == null || ((IForcedStates) te).getForcedState() == 0) { // stored state of 0 implies no value, so 0 - 1 -> no value/ default 0) { + rotationOrdinal = getStateFromMeta(stack.getMetadata()).getValue(SHEETED_FRAME_AXIS).ordinal(); // always going to be y + } else { + rotationOrdinal = ((IForcedStates) te).getForcedState() - 1; // stored state of 0 implies no value, so 0 - 1 -> no value/ default + } + } + + EnumFacing currBaseDir = facing; // default to side clicked if orientation is NONE + if (rotationOrdinal != FrameEnumAxis.NONE.ordinal()) { + try { + // default to positive, or do neg if target has NONE in pos dir and only sheeted frame ahead (this is a monster) + currBaseDir = EnumFacing.getFacingFromAxis(EnumFacing.AxisDirection.POSITIVE, FrameEnumAxis.values()[rotationOrdinal].axis); + IBlockState currDirState = world.getBlockState((pos.offset(currBaseDir))); + Block oppDirBlock = world.getBlockState((pos.offset(currBaseDir.getOpposite()))).getBlock(); + if (!(currDirState.getBlock() instanceof BlockAir) && oppDirBlock instanceof BlockAir || + oppDirBlock instanceof BlockSheetedFrame && + currDirState.getBlock() instanceof BlockSheetedFrame && + currDirState.getValue(SHEETED_FRAME_AXIS) == FrameEnumAxis.NONE) { + + currBaseDir = currBaseDir.getOpposite(); + } + } catch (Exception e) { + // if an error occurred, don't try to place + return false; + } + } + + // attempts to place more frames vertically, up to 32 block tall tower + for (int i = 0; i < 32; i++) { + IBlockState targetState = world.getBlockState(blockPos); + if (targetState.getBlock() instanceof BlockFrame || targetState.getBlock() instanceof BlockSheetedFrame) { + blockPos.move(currBaseDir); + continue; + } + + // skips over pipes with non-null frame materials (has frame around it) + TileEntity te = world.getTileEntity(blockPos); + if (te instanceof IPipeTile && ((IPipeTile) te).getFrameMaterial() != null) { + blockPos.move(currBaseDir); + continue; + } + + // try to place frame block if allowed, and if not check if the obstruction is a pipe base which can be framed + if (canPlaceBlockAt(world, blockPos)) { + // ensure placed block orientation matches base + world.setBlockState(blockPos, + sheetedFrameBlock.getStateFromMeta(stack.getItem().getMetadata(stack.getItemDamage())).withProperty(SHEETED_FRAME_AXIS, FrameEnumAxis.fromFacing(currBaseDir))); + + SoundType type = ModHandler.isMaterialWood(sheetedFrameBlock.getGtMaterial(stack)) ? SoundType.WOOD : SoundType.METAL; + world.playSound(null, pos, type.getPlaceSound(), SoundCategory.BLOCKS, (type.getVolume() + 1.0F) / 2.0F, + type.getPitch() * 0.8F); + if (!player.capabilities.isCreativeMode) { + stack.shrink(1); + } + blockPos.release(); + return true; + } else if (te instanceof TileEntityPipeBase pipeTile && pipeTile.getFrameMaterial() == null) { + // "sheet" pipe if it is blocking further frame scaffolding + pipeTile.setFrameMaterial(sheetedFrameBlock.getGtMaterial(stack)); + ((IForcedStates) pipeTile).setForcedState(rotationOrdinal + 1); // should work with mixin to store orientation + + // clear "blocked" connections [setConnection is the connection facing relative to the one calling, the connection state, and if the neighbor is the one making the call/ "updating" caller + if (rotationOrdinal != BlockSheetedFrame.FrameEnumAxis.NONE.ordinal()) { + for (EnumFacing.Axis currAxis : EnumFacing.Axis.values()) { + if (currAxis.ordinal() == rotationOrdinal) continue; // don't prune connections on axis + pipeTile.setConnection(EnumFacing.getFacingFromAxis(EnumFacing.AxisDirection.POSITIVE, currAxis), false, false); + pipeTile.setConnection(EnumFacing.getFacingFromAxis(EnumFacing.AxisDirection.NEGATIVE, currAxis), false, false); } + } - blockPos.move(EnumFacing.UP); + SoundType type = ModHandler.isMaterialWood(getGtMaterial(stack)) ? SoundType.WOOD : SoundType.METAL; + world.playSound(null, pos, type.getPlaceSound(), SoundCategory.BLOCKS, (type.getVolume() + 1.0F) / 2.0F, + type.getPitch() * 0.8F); + if (!player.capabilities.isCreativeMode) { + stack.shrink(1); } + + blockPos.release(); + return true; + } else { // stops at obstructions, rather than continuing + blockPos.release(); + return false; } + } - blockPos.release(); - return false; + blockPos.release(); + return false; + } + + public boolean replaceWithFramedPipe(World worldIn, BlockPos pos, IBlockState state, EntityPlayer playerIn, + ItemStack stackInHand, EnumFacing facing) { + BlockPipe blockPipe = (BlockPipe) ((ItemBlockPipe) stackInHand.getItem()).getBlock(); + if (blockPipe.getItemPipeType(stackInHand).getThickness() < 1) { + ItemBlock itemBlock = (ItemBlock) stackInHand.getItem(); + IBlockState pipeState = blockPipe.getDefaultState(); + + // these 0 values are not actually used by forge + itemBlock.placeBlockAt(stackInHand, playerIn, worldIn, pos, facing, 0, 0, 0, pipeState); + + IPipeTile pipeTile = blockPipe.getPipeTileEntity(worldIn, pos); + if (pipeTile instanceof TileEntityPipeBase) { + ((TileEntityPipeBase) pipeTile).setFrameMaterial(getGtMaterial(state)); + ((IForcedStates) pipeTile).setForcedState(state.getValue(SHEETED_FRAME_AXIS).ordinal() + 1); + } else { + SusyLog.logger.atError().log("Pipe was not placed!"); + return false; + } + + SoundType type = blockPipe.getSoundType(state, worldIn, pos, playerIn); + worldIn.playSound(playerIn, pos, type.getPlaceSound(), SoundCategory.BLOCKS, + (type.getVolume() + 1.0F) / 2.0F, type.getPitch() * 0.8F); + if (!playerIn.capabilities.isCreativeMode) { + stackInHand.shrink(1); + } + return true; } + + return false; + } + + public SoundType getSoundType(ItemStack stack) { + return ModHandler.isMaterialWood(getGtMaterial(stack)) ? SoundType.WOOD : SoundType.METAL; + } + + public Material getGtMaterial(ItemStack stack) { + return variantProperty.getAllowedValues().get(stack.getMetadata() & 3); + } + + public static BlockFrame getFrameFromSheeted(ItemStack stack) { + BlockSheetedFrame itemBlock = getFrameBlockFromItem(stack); + if (itemBlock == null) return null; + + return MetaBlocks.FRAMES.get(itemBlock.getGtMaterial(stack)); + } + + public static @Nullable BlockSheetedFrame getFrameBlockFromItem(ItemStack stack) { + Item item = stack.getItem(); + if (item instanceof ItemBlock) { + Block block = ((ItemBlock)item).getBlock(); + if (block instanceof BlockSheetedFrame) { + return (BlockSheetedFrame)block; + } + } + + return null; } - */ public void onEntityCollision(@Nonnull World worldIn, @Nonnull BlockPos pos, @Nonnull IBlockState state, Entity entityIn) { + // this is only called when the "shorter" side is collided with for some reason. Colliding with a solid block does nothing entityIn.motionX = MathHelper.clamp(entityIn.motionX, -0.15, 0.15); entityIn.motionZ = MathHelper.clamp(entityIn.motionZ, -0.15, 0.15); + entityIn.fallDistance = 0.0F; if (entityIn.motionY < -0.15) { entityIn.motionY = -0.15; @@ -358,16 +524,16 @@ public EnumPushReaction getPushReaction(@Nonnull IBlockState state) { return EnumPushReaction.NORMAL; } + @Override @NotNull public AxisAlignedBB getCollisionBoundingBox(@Nonnull IBlockState blockState, @Nonnull IBlockAccess worldIn, @Nonnull BlockPos pos) { - AxisAlignedBB boundingBox = switch (this.getMetaFromState(blockState) >>> 2) { + return switch (this.getMetaFromState(blockState) >>> 2) { //x - case (0) -> new AxisAlignedBB(0.05, 0.0, 0.00, 0.95, 1.0, 1.00); + case (0) -> new AxisAlignedBB(0.05, 0.0, 0.0, 0.95, 1.0, 1.0); //z - case (2) -> new AxisAlignedBB(0.00, 0.0, 0.05, 1.0, 1.0, 0.95); + case (2) -> new AxisAlignedBB(0.0, 0.0, 0.05, 1.0, 1.0, 0.95); //NONE (all sided) or y [1] as the climbable axis would be on the top of the block - default -> new AxisAlignedBB(0.00, 0.0, 0.00, 1.0, 1.0, 1.0); + default -> new AxisAlignedBB(0.0, 0.0, 0.0, 1.0, 1.0, 1.0); }; - return boundingBox; } @Nonnull @@ -392,7 +558,7 @@ public void onModelRegister() { } */ - //function adapted by me from tictem's original implementation + // function adapted by me [Eight/EightXOR8] from tictem's original implementation @SideOnly(Side.CLIENT) public void onModelRegister() { Map map = new Object2ObjectOpenHashMap<>(); diff --git a/src/main/java/supersymmetry/common/blocks/SheetedFrameItemBlock.java b/src/main/java/supersymmetry/common/blocks/SheetedFrameItemBlock.java index aee70bd5c..cabece816 100644 --- a/src/main/java/supersymmetry/common/blocks/SheetedFrameItemBlock.java +++ b/src/main/java/supersymmetry/common/blocks/SheetedFrameItemBlock.java @@ -1,11 +1,14 @@ package supersymmetry.common.blocks; import gregtech.api.unification.material.Material; +import gregtech.client.utils.TooltipHelper; import gregtech.common.ConfigHolder; import net.minecraft.block.state.IBlockState; +import net.minecraft.client.resources.I18n; import net.minecraft.client.util.ITooltipFlag; import net.minecraft.item.ItemBlock; import net.minecraft.item.ItemStack; +import net.minecraft.util.text.TextFormatting; import net.minecraft.world.World; import supersymmetry.api.unification.ore.SusyOrePrefix; @@ -44,6 +47,13 @@ public String getItemStackDisplayName(@Nonnull ItemStack stack) { @Override public void addInformation(@Nonnull ItemStack stack, @Nullable World worldIn, @Nonnull List tooltip, @Nonnull ITooltipFlag flagIn) { super.addInformation(stack, worldIn, tooltip, flagIn); + tooltip.add(I18n.format("tile.sheeted_frame_block.tooltip")); + if (TooltipHelper.isShiftDown()) { + tooltip.add(TextFormatting.GREEN + I18n.format("tile.sheeted_frame_block.tooltip_extra")); + } else { + tooltip.add(TextFormatting.DARK_GRAY + I18n.format("gregtech.tooltip.hold_shift")); + } + if (ConfigHolder.misc.debug) { tooltip.add("MetaItem Id: sheeted_frame" + frameBlock.getGtMaterial(stack.getMetadata()).toCamelCaseString()); } diff --git a/src/main/java/supersymmetry/mixins/gregtech/BlockPipeMixin.java b/src/main/java/supersymmetry/mixins/gregtech/BlockPipeMixin.java new file mode 100644 index 000000000..b122ded42 --- /dev/null +++ b/src/main/java/supersymmetry/mixins/gregtech/BlockPipeMixin.java @@ -0,0 +1,186 @@ +package supersymmetry.mixins.gregtech; + +import codechicken.lib.raytracer.CuboidRayTraceResult; +import gregtech.api.block.BuiltInRenderBlock; +import gregtech.api.cover.Cover; +import gregtech.api.items.toolitem.ToolClasses; +import gregtech.api.pipenet.IBlockAppearance; +import gregtech.api.pipenet.PipeNet; +import gregtech.api.pipenet.WorldPipeNet; +import gregtech.api.pipenet.block.BlockPipe; +import gregtech.api.pipenet.block.IPipeType; +import gregtech.api.pipenet.tile.IPipeTile; +import gregtech.api.pipenet.tile.TileEntityPipeBase; +import gregtech.api.unification.material.Material; +import gregtech.integration.ctm.IFacadeWrapper; +import net.minecraft.block.ITileEntityProvider; +import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.init.Blocks; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.EnumHand; +import net.minecraft.util.NonNullList; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import supersymmetry.api.blocks.IForcedStates; +import supersymmetry.common.blocks.BlockSheetedFrame; + +import java.util.List; + +import static supersymmetry.common.blocks.SuSyMetaBlocks.SHEETED_FRAMES; + +@Mixin(value = BlockPipe.class, remap = false) +public abstract class BlockPipeMixin & IPipeType, NodeDataType, + WorldPipeNetType extends WorldPipeNet>> + extends BuiltInRenderBlock implements ITileEntityProvider, IFacadeWrapper, IBlockAppearance { + + @Shadow @Final + protected ThreadLocal> tileEntities; + + @Shadow + public abstract IPipeTile getPipeTileEntity(IBlockAccess world, BlockPos selfPos); + + @Shadow + public abstract ItemStack getDropItem(IPipeTile var1); + + @Shadow + public abstract boolean canPipesConnect(IPipeTile var1, EnumFacing var2, IPipeTile var3); + + @Shadow + public abstract boolean canPipeConnectToBlock(IPipeTile var1, EnumFacing var2, @Nullable TileEntity var3); + + // shouldn't ever be used + public BlockPipeMixin(net.minecraft.block.material.Material materialIn) { + super(materialIn); + } + + //@Inject(method = "onPipeActivated", at = @At(value = "INVOKE_ASSIGN", ordinal = 0)) + // mixin methods always return void, with any potential "return" calls done through callBackInfo + // removing descriptor causes unable to locate obfuscation mapping + @Inject(method = "onPipeActivated(Lnet/minecraft/world/World;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/EnumHand;Lnet/minecraft/util/EnumFacing;Lcodechicken/lib/raytracer/CuboidRayTraceResult;Lgregtech/api/pipenet/tile/IPipeTile;)Z", + at = @At("HEAD"), cancellable = true) + protected void onOnPipeActivated(World world, IBlockState state, BlockPos pos, EntityPlayer entityPlayer, EnumHand hand, + EnumFacing side, CuboidRayTraceResult hit, IPipeTile pipeTile, CallbackInfoReturnable callBackInfoR) { + if (pipeTile == null || pipeTile.getFrameMaterial() == null || ((IForcedStates) pipeTile).getForcedState() == 0) return; + + ItemStack handStack = entityPlayer.getHeldItem(hand); + + // try to change its saved orientation + BlockSheetedFrame.ToolReactions itemToolCheck = BlockSheetedFrame.ToolReactions.getReaction(handStack); + if (itemToolCheck != BlockSheetedFrame.ToolReactions.NONE) { + int resultOrdinal = BlockSheetedFrame.ToolReactions.reactionResult(itemToolCheck, ((IForcedStates) pipeTile).getForcedState() - 1); + ((IForcedStates) pipeTile).setForcedState(resultOrdinal + 1); + + // clear "blocked" connections [setConnection is the connection facing relative to the one calling, the connection state, and if the neighbor is the one making the call/ "updating" caller + if (resultOrdinal != BlockSheetedFrame.FrameEnumAxis.NONE.ordinal()) { + for (EnumFacing.Axis currAxis : EnumFacing.Axis.values()) { + if (currAxis.ordinal() == resultOrdinal) continue; // don't prune connections on axis + pipeTile.setConnection(EnumFacing.getFacingFromAxis(EnumFacing.AxisDirection.POSITIVE, currAxis), false, false); + pipeTile.setConnection(EnumFacing.getFacingFromAxis(EnumFacing.AxisDirection.NEGATIVE, currAxis), false, false); + } + } + + callBackInfoR.setReturnValue(true); + } + + if (handStack.getItem().getToolClasses(handStack).contains(ToolClasses.CROWBAR)) { + final Material prevMat = pipeTile.getFrameMaterial(); + ((IForcedStates) pipeTile).setForcedState(0); + ((TileEntityPipeBase) pipeTile).setFrameMaterial(null); + spawnAsEntity(world, pos, SHEETED_FRAMES.get(prevMat).getItem(prevMat)); + callBackInfoR.setReturnValue(true); + } + } + + @Inject(method = "activateFrame(Lnet/minecraft/world/World;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/util/EnumHand;Lcodechicken/lib/raytracer/CuboidRayTraceResult;Lgregtech/api/pipenet/tile/IPipeTile;)Z", + at = @At("HEAD"), cancellable = true) + private void onOnActivateFrame(World world, IBlockState state, BlockPos pos, EntityPlayer entityPlayer, EnumHand hand, CuboidRayTraceResult hit, IPipeTile pipeTile, CallbackInfoReturnable callBackInfoR) { + if (pipeTile.getFrameMaterial() == null || ((IForcedStates) pipeTile).getForcedState() == 0) return; // cancel custom logic early if normal block or no frame exists + callBackInfoR.setReturnValue(SHEETED_FRAMES.get(pipeTile.getFrameMaterial()).onBlockActivated(true, world, pos, state, entityPlayer, hand, hit.sideHit, (float) hit.hitVec.x, (float) hit.hitVec.y, (float) hit.hitVec.z)); + } + + //@Inject(method = "onEntityCollision", at = @At(value = "INVOKE_ASSIGN", ordinal = 1), cancellable = true, locals = LocalCapture.CAPTURE_FAILHARD) + @Inject(method = "onEntityCollision(Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/entity/Entity;)V", + at = @At("HEAD"), cancellable = true) + public void onOnEntityCollision(World worldIn, BlockPos pos, IBlockState state, Entity entityIn, CallbackInfo callbackInfo) { + // only called when shorter side of frame is collided with for some reason + IPipeTile pipeTile = getPipeTileEntity(worldIn, pos); + if (pipeTile == null || pipeTile.getFrameMaterial() == null || ((IForcedStates) pipeTile).getForcedState() == 0) return; + SHEETED_FRAMES.get(pipeTile.getFrameMaterial()).onEntityCollision(worldIn, pos, state, entityIn); + callbackInfo.cancel(); // don't do frame logic + } + + // - https://fabricmc.net/wiki/tutorial:mixin_injects : because generics dont exist at run time, they arent needed + @Inject(method = "getDrops(Lnet/minecraft/util/NonNullList;Lnet/minecraft/world/IBlockAccess;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/state/IBlockState;I)V", + at = @At("HEAD"), cancellable = true) + public void onGetDrops(@NotNull NonNullList drops, @NotNull IBlockAccess world, @NotNull BlockPos pos, @NotNull IBlockState state, int fortune, CallbackInfo callbackInfo) { + IPipeTile pipeTile = this.tileEntities.get() == null ? this.getPipeTileEntity(world, pos) : this.tileEntities.get(); + if (pipeTile.getFrameMaterial() == null || ((IForcedStates) pipeTile).getForcedState() == 0) return; + BlockSheetedFrame sheetedFrame = SHEETED_FRAMES.get(pipeTile.getFrameMaterial()); + drops.add(sheetedFrame.getItem(pipeTile.getFrameMaterial())); + drops.add(getDropItem(pipeTile)); + + callbackInfo.cancel(); + } + + @Inject(method = "canConnect(Lgregtech/api/pipenet/tile/IPipeTile;Lnet/minecraft/util/EnumFacing;)Z", at = @At("HEAD"), cancellable = true) + public void onCanConnect(IPipeTile selfTile, EnumFacing facing, CallbackInfoReturnable callbackInfoR) { + if (((IForcedStates) selfTile).getForcedState() == 0) return; + int rotationOrdinal = ((IForcedStates) selfTile).getForcedState() - 1; + + boolean result = rotationOrdinal == BlockSheetedFrame.FrameEnumAxis.NONE.ordinal() || rotationOrdinal == facing.getAxis().ordinal(); + if (!result) callbackInfoR.setReturnValue(false); + + // simplified version of blockPipe check which only attempts to set the result to true + result = false; // reset result to default as false, now that frame check has been done + if (selfTile.getPipeWorld().getBlockState(selfTile.getPipePos().offset(facing)).getBlock() != Blocks.AIR) { + Cover cover = selfTile.getCoverableImplementation().getCoverAtSide(facing); + if (cover == null || cover.canPipePassThrough()) { + TileEntity other = selfTile.getNeighbor(facing); + if (other instanceof IPipeTile) { + cover = ((IPipeTile)other).getCoverableImplementation().getCoverAtSide(facing.getOpposite()); + result = (cover == null || cover.canPipePassThrough()) && this.canPipesConnect(selfTile, facing, (IPipeTile) other); + } else { + result = this.canPipeConnectToBlock(selfTile, facing, other); + } + } + } + + callbackInfoR.setReturnValue(result); + } + + // - generics are not real + @Inject(method = "addCollisionBoxToList(Lnet/minecraft/block/state/IBlockState;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/math/AxisAlignedBB;Ljava/util/List;Lnet/minecraft/entity/Entity;Z)V", + at = @At("HEAD"), cancellable = true) + public void onAddCollisionBoxToList(@NotNull IBlockState state, @NotNull World worldIn, @NotNull BlockPos pos, + @NotNull AxisAlignedBB entityBox, @NotNull List collidingBoxes, + @Nullable Entity entityIn, boolean isActualState, CallbackInfo callBackInfo) { + IPipeTile pipeTile = getPipeTileEntity(worldIn, pos); + if (pipeTile == null || pipeTile.getFrameMaterial() == null || ((IForcedStates) pipeTile).getForcedState() == 0) return; + + // get equivalent state for calculation of bounding box + IBlockState equivalentSheetedState = BlockSheetedFrame.determineSheetedState(worldIn, pos); + if (equivalentSheetedState == null) return; // if something went wrong in getting state, let typical logic run + + // do custom bounding box based on sheeted frame + AxisAlignedBB box = SHEETED_FRAMES.get(pipeTile.getFrameMaterial()).getCollisionBoundingBox(equivalentSheetedState, worldIn, pos).offset(pos); + if (box.intersects(entityBox)) collidingBoxes.add(box); + + // only hitbox is frame + callBackInfo.cancel(); + } +} diff --git a/src/main/java/supersymmetry/mixins/gregtech/PipeRendererMixin.java b/src/main/java/supersymmetry/mixins/gregtech/PipeRendererMixin.java new file mode 100644 index 000000000..f6c9ee013 --- /dev/null +++ b/src/main/java/supersymmetry/mixins/gregtech/PipeRendererMixin.java @@ -0,0 +1,205 @@ +package supersymmetry.mixins.gregtech; + +import codechicken.lib.render.BlockRenderer; +import codechicken.lib.render.CCRenderState; +import codechicken.lib.render.pipeline.ColourMultiplier; +import codechicken.lib.render.pipeline.IVertexOperation; +import codechicken.lib.vec.Cuboid6; +import codechicken.lib.vec.Translation; +import codechicken.lib.vec.uv.IconTransformation; +import codechicken.lib.vec.uv.UV; +import codechicken.lib.vec.uv.UVTransformation; +import gregtech.api.pipenet.tile.IPipeTile; +import gregtech.api.unification.material.Material; +import gregtech.api.util.GTUtility; +import gregtech.client.renderer.pipe.PipeRenderer; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.texture.TextureAtlasSprite; +import net.minecraft.util.EnumFacing; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import supersymmetry.api.blocks.IForcedStates; +import supersymmetry.api.unification.material.info.SuSyMaterialIconType; +import supersymmetry.common.blocks.BlockSheetedFrame; + +@Mixin(value = PipeRenderer.class, remap = false) +public abstract class PipeRendererMixin { + + @Shadow + @Final @NotNull + protected static ThreadLocal blockFaces; + + @Inject(method = "renderFrame(Lgregtech/api/pipenet/tile/IPipeTile;Lnet/minecraft/util/math/BlockPos;Lcodechicken/lib/render/CCRenderState;I)V", + at = @At("HEAD"), cancellable = true) + private static void renderFrame(IPipeTile pipeTile, BlockPos pos, CCRenderState renderState, int connections, CallbackInfo callbackInfo) { + // assumes pipeTile is not null + Material frameMaterial = pipeTile.getFrameMaterial(); + if (frameMaterial == null || ((IForcedStates) pipeTile).getForcedState() == 0) return; + + int rotationOrdinal = ((IForcedStates) pipeTile).getForcedState() - 1; + EnumFacing.Axis axis = BlockSheetedFrame.FrameEnumAxis.values()[rotationOrdinal].getAxis(); + + ResourceLocation rl = axis == null ? SuSyMaterialIconType.sheetedFrameAll.getBlockTexturePath(frameMaterial.getMaterialIconSet()) : + SuSyMaterialIconType.sheetedFrame.getBlockTexturePath(frameMaterial.getMaterialIconSet()); + + // if an array initializer is used for pipeline, modifying elements causes color to disappear + final TextureAtlasSprite sprite = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(rl.toString()); + + // rotate standing model over towards positive direction for Z and negative for X + final boolean isX = EnumFacing.Axis.X.equals(axis); + final boolean isZ = EnumFacing.Axis.Z.equals(axis); + final boolean isHorizontal = isX || isZ; + + final boolean isY = EnumFacing.Axis.Y.equals(axis); + final boolean isAll = axis == null; + + final UVTransformation rotCW = new UVTransformation(){ + @Override + public void apply(UV uv) { + // translate center to middle of texture + uv.u -= 0.5; + uv.v -= 0.5; + + // do rotation with matrix [[cos-90, -sin-90],[sin-90, cos-90]] -> [[0, 1],[-1, 0]]; * A = + double tempStorage = uv.u; + uv.u = uv.v; + uv.v = -tempStorage; + + // undo translation + uv.u += 0.5; + uv.v += 0.5; + + uv.u = sprite.getInterpolatedU(uv.u * 16.0); + uv.v = sprite.getInterpolatedV(uv.v * 16.0); + } + + // coordinate swap undoes itself + @Override + public UVTransformation inverse() { + return this; + } + }; + + final UVTransformation rotCCW = new UVTransformation() { + @Override + public void apply(UV uv) { + // translate center to middle of texture + uv.u -= 0.5; + uv.v -= 0.5; + + // do rotation with matrix [[cos90, -sin90],[sin90, cos90]] -> [[0, -1],[1, 0]]; * A = <-v, u> + double tempStorage = uv.u; + uv.u = -uv.v; + uv.v = tempStorage; + + // undo translation + uv.u += 0.5; + uv.v += 0.5; + + uv.u = sprite.getInterpolatedU(uv.u * 16.0); + uv.v = sprite.getInterpolatedV(uv.v * 16.0); + } + + @Override + public UVTransformation inverse() { + return this; + } + }; + + IVertexOperation[] pipeline = { + new Translation(pos), + renderState.lightMatrix, + new IconTransformation(sprite), + new ColourMultiplier(GTUtility.convertRGBtoOpaqueRGBA_CL(frameMaterial.getMaterialRGB())) + }; + + IVertexOperation[] pipelineRotateCCW = { + new Translation(pos), + renderState.lightMatrix, + rotCCW, + new ColourMultiplier(GTUtility.convertRGBtoOpaqueRGBA_CL(frameMaterial.getMaterialRGB())) + }; + + IVertexOperation[] pipelineRotateCW = { + new Translation(pos), + renderState.lightMatrix, + rotCW, + new ColourMultiplier(GTUtility.convertRGBtoOpaqueRGBA_CL(frameMaterial.getMaterialRGB())) + }; + + // stops z-fighting for pipes on axis + final Cuboid6 FULL_SHEETED_CUBOID = new Cuboid6(isX || isAll ? 0.00009 : 0, isY || isAll ? 0.00009 : 0, + isZ || isAll ? 0.00009 : 0, isX || isAll ? 0.99996 : 1, isY || isAll ? 0.99996 : 1, isZ || isAll ? 0.99996 : 1); + + // renders interior + final Cuboid6 INTERIOR_SHEETED_CUBOID = new Cuboid6(isX || isAll ? 0.9999 : 0.9375, isY || isAll ? 0.9999 : 0.9375, + isZ || isAll ? 0.9999 : 0.9375, isX || isAll ? 0.0001 : 0.0625, isY || isAll ? 0.0001 : 0.0625, isZ || isAll ? 0.0001 :0.0625); + + EnumFacing[] skippedFacings = new EnumFacing[2]; + int index = 0; + + for (EnumFacing side : EnumFacing.VALUES) { + EnumFacing.Axis sideAxis = side.getAxis(); + + // skips sides "on axis" with sheeted frame + if (sideAxis.equals(axis)) { + skippedFacings[index++] = side; + continue; + } + + final boolean isSideY = side.getAxis().equals(EnumFacing.Axis.Y); + final boolean shouldRot = isHorizontal && !(isSideY && !isX); + + // only render frame if it doesn't have a cover + if ((connections & 1 << (12 + side.getIndex())) == 0) { + BlockRenderer.BlockFace blockFace = blockFaces.get(); + blockFace.loadCuboidFace(FULL_SHEETED_CUBOID, side.getIndex()); + renderState.setPipeline(blockFace, 0, blockFace.verts.length, shouldRot ? + (side.getAxisDirection().getOffset() < 0 ? pipelineRotateCW : pipelineRotateCCW) : pipeline); + renderState.render(); + + // render interior for off axis sides + blockFace.loadCuboidFace(INTERIOR_SHEETED_CUBOID, side.getIndex()); + renderState.setPipeline(blockFace, 0, blockFace.verts.length, (isX || isZ) && !(isSideY && !isX) ? + (side.getAxisDirection().getOffset() > 0 ? pipelineRotateCW : pipelineRotateCCW) : pipeline); + renderState.render(); + } + } + + // will be done, but not used, if orientation is none + // must be included in a model at some point to be included into the texture atlas + rl = SuSyMaterialIconType.sheetedFrameEnd.getBlockTexturePath(frameMaterial.getMaterialIconSet()); + TextureAtlasSprite endSprite = Minecraft.getMinecraft().getTextureMapBlocks().getAtlasSprite(rl.toString()); + //pipeline[3] = new IconTransformation(sprite); this does not work for some reason, the entire thing must be re-initialized + pipeline = new IVertexOperation[] { + new Translation(pos), + renderState.lightMatrix, + new IconTransformation(endSprite), + new ColourMultiplier(GTUtility.convertRGBtoOpaqueRGBA_CL(frameMaterial.getMaterialRGB())), + }; + + for (int i = 0; i < index; ++i) { + // only render frame if it doesn't have a cover + if ((connections & 1 << (12 + skippedFacings[i].getIndex())) == 0) { + BlockRenderer.BlockFace blockFace = blockFaces.get(); + blockFace.loadCuboidFace(FULL_SHEETED_CUBOID, skippedFacings[i].getIndex()); + renderState.setPipeline(blockFace, 0, blockFace.verts.length, pipeline); + renderState.render(); + + // render interior for off axis sides + blockFace.loadCuboidFace(INTERIOR_SHEETED_CUBOID, skippedFacings[i].getIndex()); + renderState.setPipeline(blockFace, 0, blockFace.verts.length, pipeline); + renderState.render(); + } + } + + callbackInfo.cancel(); + } +} diff --git a/src/main/java/supersymmetry/mixins/gregtech/TileEntityPipeBaseMixin.java b/src/main/java/supersymmetry/mixins/gregtech/TileEntityPipeBaseMixin.java new file mode 100644 index 000000000..9ab3f8e46 --- /dev/null +++ b/src/main/java/supersymmetry/mixins/gregtech/TileEntityPipeBaseMixin.java @@ -0,0 +1,90 @@ +package supersymmetry.mixins.gregtech; + +import gregtech.api.capability.GregtechDataCodes; +import gregtech.api.metatileentity.NeighborCacheTileEntityBase; +import gregtech.api.pipenet.block.IPipeType; +import gregtech.api.pipenet.tile.IPipeTile; +import gregtech.api.pipenet.tile.TileEntityPipeBase; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.PacketBuffer; +import net.minecraft.util.EnumFacing; +import org.jetbrains.annotations.NotNull; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import supersymmetry.api.blocks.IForcedStates; +import supersymmetry.common.blocks.BlockSheetedFrame; + +@Mixin(value = TileEntityPipeBase.class, remap = false) +public abstract class TileEntityPipeBaseMixin & IPipeType, NodeDataType> extends NeighborCacheTileEntityBase implements IPipeTile, IForcedStates { + int rotationState = 0; + + @Shadow + private TileEntityPipeBase tickingPipe; + + public void setForcedState(int val) { + rotationState = val; + if (world != null && world.isRemote) { + this.writeCustomData(GregtechDataCodes.UPDATE_FRAME_MATERIAL, (buf) -> buf.writeVarInt(val)); + } + } + + public int getForcedState() { + return rotationState; + } + + @Inject(method = "setConnection(Lnet/minecraft/util/EnumFacing;ZZ)V", at = @At("HEAD"), cancellable = true) + public void onSetConnection(EnumFacing side, boolean connected, boolean fromNeighbor, CallbackInfo callbackInfo) { + int rotationOrdinal = ((IForcedStates) this).getForcedState() - 1; + if (this.getWorld().isRemote || rotationOrdinal < 0) return; + + // cancel if connection is attempted on non axis sides for rotations that are not omnidirectional + if (rotationOrdinal != BlockSheetedFrame.FrameEnumAxis.NONE.ordinal() && rotationOrdinal != side.getAxis().ordinal() && connected) { + callbackInfo.cancel(); + } + } + + // (DIIIZ)Lcom/mypackage/ThingType; + @Inject(method = "transferDataFrom(Lgregtech/api/pipenet/tile/IPipeTile;)V", at = @At("TAIL")) + public void onTransferDataFrom(IPipeTile tileEntity, CallbackInfo callbackInfo) { + setForcedState(((IForcedStates) tileEntity).getForcedState()); // this method should only ever be called with TE instance of this mixin's target + } + + @Inject(method = "writeToNBT(Lnet/minecraft/nbt/NBTTagCompound;)Lnet/minecraft/nbt/NBTTagCompound;", at = @At("TAIL")) + public void onWriteToNBT(@NotNull NBTTagCompound compound, CallbackInfoReturnable callbackInfoR) { + compound.setInteger("rotationState", getForcedState()); + } + + @Inject(method = "readFromNBT(Lnet/minecraft/nbt/NBTTagCompound;)V", at = @At("HEAD")) + public void onReadFromNBT(@NotNull NBTTagCompound compound, CallbackInfo callbackInfo) { + // actual read from is only called when tickingPipe is null + if (tickingPipe == null) { + setForcedState(compound.getInteger("rotationState")); + } + } + + @Inject(method = "writeInitialSyncData(Lnet/minecraft/network/PacketBuffer;)V", at = @At("HEAD")) + public void onWriteInitialSyncData(PacketBuffer buf, CallbackInfo callbackInfo) { + buf.writeInt(getForcedState()); + } + + @Inject(method = "receiveInitialSyncData(Lnet/minecraft/network/PacketBuffer;)V", at = @At("HEAD")) + public void onReceiveInitialSyncData(PacketBuffer buf, CallbackInfo callbackInfo) { + // actual receive only called when tickingPipe is null + if (tickingPipe == null) { + setForcedState(buf.readInt()); + } + } + + @Inject(method = "receiveCustomData(ILnet/minecraft/network/PacketBuffer;)V", at = @At("HEAD")) + public void onReceiveCustomData(int discriminator, PacketBuffer buf, CallbackInfo callbackInfo) { + if (tickingPipe == null && discriminator == BlockSheetedFrame.UPDATE_ROTATION_STATE) { + rotationState = buf.readInt(); + + this.scheduleChunkForRenderUpdate(); + } + } +} diff --git a/src/main/java/supersymmetry/mixins/xnet/FacadeItemBlockMixin.java b/src/main/java/supersymmetry/mixins/xnet/FacadeItemBlockMixin.java index 71dd9163d..ed2dc88d2 100644 --- a/src/main/java/supersymmetry/mixins/xnet/FacadeItemBlockMixin.java +++ b/src/main/java/supersymmetry/mixins/xnet/FacadeItemBlockMixin.java @@ -16,7 +16,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -@Mixin(value = FacadeItemBlock.class) +@Mixin(value = FacadeItemBlock.class, remap = false) public class FacadeItemBlockMixin { @Inject(method = "onItemUse(Lnet/minecraft/entity/player/EntityPlayer;Lnet/minecraft/world/World;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/util/EnumHand;Lnet/minecraft/util/EnumFacing;FFF)Lnet/minecraft/util/EnumActionResult;", diff --git a/src/main/resources/assets/gregtech/blockstates/material_sets/dull/sheeted_frame.json b/src/main/resources/assets/gregtech/blockstates/material_sets/dull/sheeted_frame.json index c484077b3..758f0403c 100644 --- a/src/main/resources/assets/gregtech/blockstates/material_sets/dull/sheeted_frame.json +++ b/src/main/resources/assets/gregtech/blockstates/material_sets/dull/sheeted_frame.json @@ -3,15 +3,17 @@ "defaults": { "model": "susy:no_cull_tinted_cube_column", "textures": { - "end": "gregtech:blocks/material_sets/dull/frame_gt", - "side": "gregtech:blocks/material_sets/dull/sheeted_frame" + "end": "gregtech:blocks/material_sets/dull/sheeted_frame_end", + "side": "gregtech:blocks/material_sets/dull/sheeted_frame", + "all": "gregtech:blocks/material_sets/dull/sheeted_frame_all" } }, "variants": { "axis": { "none": { "textures": { - "end": "#side" + "end": "#all", + "side": "#all" } }, "x": { diff --git a/src/main/resources/assets/gregtech/textures/blocks/material_sets/dull/sheeted_frame_all.png b/src/main/resources/assets/gregtech/textures/blocks/material_sets/dull/sheeted_frame_all.png new file mode 100644 index 000000000..0077d1f22 Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/material_sets/dull/sheeted_frame_all.png differ diff --git a/src/main/resources/assets/gregtech/textures/blocks/material_sets/dull/sheeted_frame_end.png b/src/main/resources/assets/gregtech/textures/blocks/material_sets/dull/sheeted_frame_end.png new file mode 100644 index 000000000..d7abce63b Binary files /dev/null and b/src/main/resources/assets/gregtech/textures/blocks/material_sets/dull/sheeted_frame_end.png differ diff --git a/src/main/resources/assets/susy/lang/en_us.lang b/src/main/resources/assets/susy/lang/en_us.lang index beb8e78aa..fef01d1b5 100644 --- a/src/main/resources/assets/susy/lang/en_us.lang +++ b/src/main/resources/assets/susy/lang/en_us.lang @@ -95,6 +95,9 @@ tile.home_block.home_renewal_brutalist.tooltip=Renewal Brutalist tile.home_block.home_scifi.name=Home Block tile.home_block.home_scifi.tooltip=Sci-Fi +tile.sheeted_frame_block.tooltip=Use a screwdriver to rotate, a hammer to toggle omnidirectionality, and a crowbar to remove from pipes. +tile.sheeted_frame_block.tooltip_extra=Lighting bugs are known. Pipe connections may only be made "on axis" with the surrounding sheeted frame, save for the omnidirectional state. Omnidirectional sheeted frames, when clicked with a frame, place with the orientation of the side clicked. All sheeted frames adjacent to an omnidirectional sheeted frame place away from it, on their axis, when they are clicked with a sheeted frame. The side facing open air will be preferred for all frames, or the positive direction if neither/ both face open air. Contact me (Eight) for any other bugs. + # Rocks tile.susy_stone_smooth.gabbro.name=Gabbro tile.susy_stone_smooth.gneiss.name=Gneiss diff --git a/src/main/resources/mixins.susy.gregtech.json b/src/main/resources/mixins.susy.gregtech.json index 8afc7773f..ef8d23745 100644 --- a/src/main/resources/mixins.susy.gregtech.json +++ b/src/main/resources/mixins.susy.gregtech.json @@ -5,6 +5,11 @@ "minVersion" : "0.8", "compatibilityLevel" : "JAVA_8", "mixins" : [ - "BlockMachineMixin" + "BlockMachineMixin", + "BlockPipeMixin", + "TileEntityPipeBaseMixin" + ], + "client" : [ + "PipeRendererMixin" ] } diff --git a/src/main/resources/mixins.susy.json b/src/main/resources/mixins.susy.json new file mode 100644 index 000000000..793a1a4cc --- /dev/null +++ b/src/main/resources/mixins.susy.json @@ -0,0 +1,10 @@ +{ + "package": "supersymmetry.mixins", + "refmap": "mixins.susy.refmap.json", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [], + "client": [], + "server": [] +}