diff --git a/src/main/java/com/imbgt/ibg/IBG.java b/src/main/java/com/imbgt/ibg/IBG.java index fc5d2d1..67e862e 100644 --- a/src/main/java/com/imbgt/ibg/IBG.java +++ b/src/main/java/com/imbgt/ibg/IBG.java @@ -10,6 +10,7 @@ import com.mojang.logging.LogUtils; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.blockentity.BlockEntityRenderers; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.common.MinecraftForge; @@ -71,6 +72,8 @@ public class IBG { var latheDef = GTRegistries.MACHINES.get( ResourceLocation.fromNamespaceAndPath("gtceu", "lv_lathe")); + latheDef.setShape(Block.box(-16, 0, 0, 32-16, 14, 16)); + @SuppressWarnings({ "unchecked", "rawtypes" }) BlockEntityRendererProvider provider = (BlockEntityRendererProvider) (ctx -> new AnimatedBlockRenderer<>( ctx)); diff --git a/src/main/java/com/imbgt/ibg/block/ModBlocks.java b/src/main/java/com/imbgt/ibg/block/ModBlocks.java index 3e40a9c..c48fb9c 100644 --- a/src/main/java/com/imbgt/ibg/block/ModBlocks.java +++ b/src/main/java/com/imbgt/ibg/block/ModBlocks.java @@ -1,9 +1,8 @@ package com.imbgt.ibg.block; -import java.util.function.Supplier; - import com.imbgt.ibg.IBG; import com.imbgt.ibg.block.custom.AnimatedBlock; +import com.imbgt.ibg.block.custom.PartBlock; import com.imbgt.ibg.item.ModItems; import net.minecraft.world.item.BlockItem; @@ -20,13 +19,11 @@ public class ModBlocks { public static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, IBG.MOD_ID); public static final RegistryObject ANIMATED_BLOCK = BLOCKS.register("animated_block", - () -> new AnimatedBlock(BlockBehaviour.Properties.copy(Blocks.IRON_BLOCK).noOcclusion())); + () -> new AnimatedBlock(BlockBehaviour.Properties.copy(Blocks.IRON_BLOCK).noOcclusion())); - private static RegistryObject registerBlock(String name, Supplier block) { - RegistryObject toReturn = BLOCKS.register(name, block); - registerBlockItem(name, toReturn); - return toReturn; - } + public static final RegistryObject PART = BLOCKS.register( + "part", + () -> new PartBlock(BlockBehaviour.Properties.of().noOcclusion().strength(2.0F))); private static RegistryObject registerBlockItem(String name, RegistryObject block) { return ModItems.ITEMS_REGISTER.register(name, () -> new BlockItem(block.get(), new Item.Properties())); diff --git a/src/main/java/com/imbgt/ibg/block/custom/PartBlock.java b/src/main/java/com/imbgt/ibg/block/custom/PartBlock.java new file mode 100644 index 0000000..854f290 --- /dev/null +++ b/src/main/java/com/imbgt/ibg/block/custom/PartBlock.java @@ -0,0 +1,96 @@ +package com.imbgt.ibg.block.custom; + +import com.imbgt.ibg.block.entity.PartBE; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.VoxelShape; + +public class PartBlock extends Block implements EntityBlock { + public PartBlock(Properties p) { + super(p.noOcclusion()); + } + + @Override + public BlockEntity newBlockEntity(BlockPos pos, BlockState st) { + return new PartBE(pos, st); + } + + @Override + public InteractionResult use(BlockState st, Level lvl, BlockPos pos, + Player pl, InteractionHand hand, BlockHitResult hit) { + if (lvl.isClientSide) { + // Let the hand animate; the server will run the real logic. + return InteractionResult.SUCCESS; + } + + BlockEntity be = lvl.getBlockEntity(pos); + if (!(be instanceof PartBE part)) + return InteractionResult.PASS; + + BlockPos masterPos = part.getMaster(); + if (masterPos == null) + return InteractionResult.PASS; + + BlockState mst = lvl.getBlockState(masterPos); + return mst.getBlock().use(mst, lvl, masterPos, pl, hand, hit); + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.INVISIBLE; + } + + // Optional: give the part a slice-shaped outline/collision on its own cell + @Override + public VoxelShape getShape(BlockState s, net.minecraft.world.level.BlockGetter g, BlockPos p, + net.minecraft.world.phys.shapes.CollisionContext c) { + // Example: the right-hand slice of a 2-wide machine (16..32 on X within this + // cell) + return Block.box(0, 0, 0, 16, 16, 16); // or custom slice if you want + } + + // When the part is broken, break the master (optional, but common) + @Override + public void playerWillDestroy(Level lvl, BlockPos pos, BlockState st, Player pl) { + BlockEntity be = lvl.getBlockEntity(pos); + if (!lvl.isClientSide && be instanceof PartBE part && part.getMaster() != null) { + BlockPos master = part.getMaster(); + lvl.setBlock(pos, Blocks.AIR.defaultBlockState(), + Block.UPDATE_CLIENTS | Block.UPDATE_KNOWN_SHAPE | Block.UPDATE_SUPPRESS_DROPS); + boolean drop = !pl.isCreative(); + lvl.destroyBlock(master, drop, pl); + } + super.playerWillDestroy(lvl, pos, st, pl); + } + + @Override + public void initializeClient( + java.util.function.Consumer consumer) { + consumer.accept(new net.minecraftforge.client.extensions.common.IClientBlockExtensions() { + + @Override + public boolean addDestroyEffects(BlockState state, Level level, BlockPos pos, + net.minecraft.client.particle.ParticleEngine engine) { + return true; + } + + @Override + public boolean addHitEffects(BlockState state, Level level, net.minecraft.world.phys.HitResult target, + net.minecraft.client.particle.ParticleEngine engine) { + return true; + } + }); + } + +} diff --git a/src/main/java/com/imbgt/ibg/block/entity/ModBlockEntities.java b/src/main/java/com/imbgt/ibg/block/entity/ModBlockEntities.java index 168dbd3..e5963d8 100644 --- a/src/main/java/com/imbgt/ibg/block/entity/ModBlockEntities.java +++ b/src/main/java/com/imbgt/ibg/block/entity/ModBlockEntities.java @@ -18,6 +18,10 @@ public class ModBlockEntities { .register("animated_block_entity", () -> BlockEntityType.Builder.of(AnimatedBlockEntity::new, ModBlocks.ANIMATED_BLOCK.get()).build(null)); + public static final RegistryObject> PART_BE = BLOCK_ENTITIES.register("part", + () -> BlockEntityType.Builder.of(PartBE::new, ModBlocks.PART.get()) + .build(null)); + public static void register(IEventBus eventBus) { BLOCK_ENTITIES.register(eventBus); } diff --git a/src/main/java/com/imbgt/ibg/block/entity/PartBE.java b/src/main/java/com/imbgt/ibg/block/entity/PartBE.java new file mode 100644 index 0000000..fa8c858 --- /dev/null +++ b/src/main/java/com/imbgt/ibg/block/entity/PartBE.java @@ -0,0 +1,48 @@ +package com.imbgt.ibg.block.entity; + + +import javax.annotation.Nullable; + +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class PartBE extends BlockEntity { + private BlockPos master; + public PartBE(BlockPos pos, BlockState st) { super(ModBlockEntities.PART_BE.get(), pos, st); } + public void setMaster(@Nullable BlockPos m) { + this.master = m; + setChanged(); + if (level != null && !level.isClientSide) { + level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), 3); + } + } + + @Nullable public BlockPos getMaster() { return master; } + + // ---- persistent NBT ---- + @Override protected void saveAdditional(CompoundTag tag) { + super.saveAdditional(tag); + if (master != null) tag.putLong("Master", master.asLong()); + } + @Override public void load(CompoundTag tag) { + super.load(tag); + master = tag.contains("Master") ? BlockPos.of(tag.getLong("Master")) : null; + } + + // ---- network sync (server → client) ---- + @Override public CompoundTag getUpdateTag() { + CompoundTag tag = super.getUpdateTag(); + saveAdditional(tag); + return tag; + } + @Override public void handleUpdateTag(CompoundTag tag) { + load(tag); + } + @Override public @Nullable ClientboundBlockEntityDataPacket getUpdatePacket() { + return ClientboundBlockEntityDataPacket.create(this); + } +} + diff --git a/src/main/java/com/imbgt/ibg/mixin/MetaMachineBlockMixin.java b/src/main/java/com/imbgt/ibg/mixin/MetaMachineBlockMixin.java index d785f5c..16b43ae 100644 --- a/src/main/java/com/imbgt/ibg/mixin/MetaMachineBlockMixin.java +++ b/src/main/java/com/imbgt/ibg/mixin/MetaMachineBlockMixin.java @@ -1,12 +1,18 @@ package com.imbgt.ibg.mixin; import com.gregtechceu.gtceu.api.block.AppearanceBlock; +import com.gregtechceu.gtceu.api.block.IMachineBlock; import com.gregtechceu.gtceu.api.block.MetaMachineBlock; +import com.gregtechceu.gtceu.api.data.RotationState; import com.gregtechceu.gtceu.api.machine.MachineDefinition; -import com.imbgt.ibg.IBG; +import com.imbgt.ibg.block.ModBlocks; +import com.imbgt.ibg.block.entity.PartBE; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.shapes.Shapes; @@ -16,6 +22,7 @@ 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; @Mixin(MetaMachineBlock.class) @@ -30,20 +37,88 @@ public abstract class MetaMachineBlockMixin extends AppearanceBlock { @Inject(method = "getRenderShape", at = @At("HEAD"), cancellable = true) private void gtceu$forceAnimatedRender(BlockState state, CallbackInfoReturnable cir) { - - // Whitelist: only override for the LV lathe if (getDefinition().getId().toString().equals("gtceu:lv_lathe")) { - IBG.LOGGER.info("MIXING IT IN"); cir.setReturnValue(RenderShape.ENTITYBLOCK_ANIMATED); } } - @Override public VoxelShape getOcclusionShape(BlockState state, BlockGetter level, BlockPos pos) { - var id = getDefinition().getId(); - if ("gtceu".equals(id.getNamespace()) && "lv_lathe".equals(id.getPath())) { + if (getDefinition().getId().toString().equals("gtceu:lv_lathe")) { return Shapes.empty(); // equivalent to Properties.noOcclusion() } return super.getOcclusionShape(state, level, pos); } + + @Inject(method = "getStateForPlacement", at = @At("HEAD"), cancellable = true) + private void ibg$latheFootprintCheck(BlockPlaceContext ctx, + CallbackInfoReturnable cir) { + MetaMachineBlock self = (MetaMachineBlock) (Object) this; + var id = self.getDefinition().getId(); + if (!("gtceu".equals(id.getNamespace()) && "lv_lathe".equals(id.getPath()))) + return; + + Level level = ctx.getLevel(); + BlockPos base = ctx.getClickedPos(); + + // Determine facing like MetaMachineBlock does: + Direction facing; + var rot = self.getDefinition().getRotationState(); + if (rot == RotationState.Y_AXIS) + facing = Direction.UP; // adjust if needed + else + facing = ctx.getHorizontalDirection().getOpposite(); + + // Your footprint: master + one part at +clockwise (adjust if your model + // differs) + BlockPos partPos = base.relative(facing.getCounterClockWise()); + + // If the part cell cannot be replaced => CANCEL placement + if (!level.getBlockState(partPos).canBeReplaced(ctx)) { + cir.setReturnValue(null); // makes placement fail cleanly + } + } + + @Inject(method = "onPlace", at = @At("TAIL")) + private void ibg$placePart(BlockState st, Level lvl, BlockPos pos, BlockState old, boolean moved, CallbackInfo ci) { + if (lvl.isClientSide) + return; + var def = getDefinition().getId(); + if (!("gtceu".equals(def.getNamespace()) && "lv_lathe".equals(def.getPath()))) + return; + + var rot = ((IMachineBlock) (Object) this).getRotationState(); + var facing = rot == RotationState.NONE ? Direction.NORTH : st.getValue(rot.property); + + // Example footprint: extend one cell to +X relative to facing (adjust as + // needed) + BlockPos partPos = pos.relative(facing.getCounterClockWise()); + + if (lvl.getBlockState(partPos).canBeReplaced()) { + lvl.setBlockAndUpdate(partPos, ModBlocks.PART.get().defaultBlockState()); + var be = lvl.getBlockEntity(partPos); + if (be instanceof PartBE part) + part.setMaster(pos); + } else { + // Optional: rollback place if you require free space + // lvl.removeBlock(pos, false); + } + } + + @Inject(method = "onRemove", at = @At("HEAD")) + private void ibg$removePart(BlockState st, Level lvl, BlockPos pos, BlockState newState, boolean moving, + CallbackInfo ci) { + if (lvl.isClientSide || st.getBlock() == newState.getBlock()) + return; + var def = getDefinition().getId(); + if (!("gtceu".equals(def.getNamespace()) && "lv_lathe".equals(def.getPath()))) + return; + + var rot = ((IMachineBlock) (Object) this).getRotationState(); + var facing = rot == RotationState.NONE ? Direction.NORTH : st.getValue(rot.property); + BlockPos partPos = pos.relative(facing.getCounterClockWise()); + + if (lvl.getBlockState(partPos).is(ModBlocks.PART.get())) { + lvl.removeBlock(partPos, false); + } + } } diff --git a/src/main/resources/assets/ibg/animations/animated_block.animation.json b/src/main/resources/assets/ibg/animations/animated_block.animation.json index 1de5aff..865bdb2 100644 --- a/src/main/resources/assets/ibg/animations/animated_block.animation.json +++ b/src/main/resources/assets/ibg/animations/animated_block.animation.json @@ -3,29 +3,18 @@ "animations": { "idle": { "loop": true, - "animation_length": 2, + "animation_length": 0.25, "bones": { "bone2": { "rotation": { "0.0": { "vector": [0, 0, 0] }, - "1.0": { - "vector": [0, 180, 0] + "0.125": { + "vector": [180, 0, 0] }, - "2.0": { - "vector": [0, 360, 0] - } - }, - "position": { - "0.0": { - "vector": [0, 0, 0] - }, - "1.0": { - "vector": [0, 2, 0] - }, - "2.0": { - "vector": [0, 0, 0] + "0.25": { + "vector": [360, 0, 0] } } } diff --git a/src/main/resources/assets/ibg/geo/animated_block.geo.json b/src/main/resources/assets/ibg/geo/animated_block.geo.json index de3e9ad..7a2fccc 100644 --- a/src/main/resources/assets/ibg/geo/animated_block.geo.json +++ b/src/main/resources/assets/ibg/geo/animated_block.geo.json @@ -4,26 +4,26 @@ { "description": { "identifier": "geometry.unknown", - "texture_width": 64, - "texture_height": 64, - "visible_bounds_width": 2, - "visible_bounds_height": 2.5, - "visible_bounds_offset": [0, 0.75, 0] + "texture_width": 128, + "texture_height": 128, + "visible_bounds_width": 4, + "visible_bounds_height": 3.5, + "visible_bounds_offset": [0, 1.25, 0] }, "bones": [ { "name": "bone", - "pivot": [0, 1, 0], + "pivot": [17, 18, 0], "cubes": [ - {"origin": [-7, 1, -7], "size": [14, 5, 14], "inflate": 1, "uv": [0, 0]} + {"origin": [-7, 1, -7], "size": [30, 12, 14], "inflate": 1, "uv": [0, 0]} ] }, { "name": "bone2", "parent": "bone", - "pivot": [0, 14, 0], + "pivot": [17, 18, 0], "cubes": [ - {"origin": [-1, 9, -1], "size": [2, 11, 2], "uv": [0, 19]} + {"origin": [-8, 7, -1], "size": [2, 11, 2], "pivot": [-7, 18, 0], "rotation": [-90, -90, 0], "uv": [0, 26]} ] } ] diff --git a/src/main/resources/assets/ibg/models/block/animated_block.json b/src/main/resources/assets/ibg/models/block/animated_block.json index c61ebf3..14894c5 100644 --- a/src/main/resources/assets/ibg/models/block/animated_block.json +++ b/src/main/resources/assets/ibg/models/block/animated_block.json @@ -1,9 +1,8 @@ { "credit": "Made with Blockbench", - "parent": "builtin/entity", "texture_size": [ - 64, - 64 + 128, + 128 ], "display": { "thirdperson_righthand": { @@ -15,9 +14,9 @@ }, "gui": { "rotation": [ - 10.81, - -60.3, - -1.32 + 10.809999999999945, + -60.30000000000018, + -1.3200000000001637 ], "translation": [ -0.25, @@ -32,6 +31,6 @@ } }, "textures": { - "particle": "ibg:block/animated_block" + "particle": "ibg:item/animated_block" } -} +} \ No newline at end of file diff --git a/src/main/resources/assets/ibg/textures/block/animated_block.png b/src/main/resources/assets/ibg/textures/block/animated_block.png index edac85a..4f4350f 100644 Binary files a/src/main/resources/assets/ibg/textures/block/animated_block.png and b/src/main/resources/assets/ibg/textures/block/animated_block.png differ