Big lathe

This commit is contained in:
2025-08-02 13:58:38 +02:00
parent ac8912443b
commit f1a7ebb4f1
10 changed files with 259 additions and 48 deletions

View File

@@ -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<BlockEntity> provider = (BlockEntityRendererProvider) (ctx -> new AnimatedBlockRenderer<>(
ctx));

View File

@@ -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<Block> BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, IBG.MOD_ID);
public static final RegistryObject<Block> 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 <T extends Block> RegistryObject<T> registerBlock(String name, Supplier<T> block) {
RegistryObject<T> toReturn = BLOCKS.register(name, block);
registerBlockItem(name, toReturn);
return toReturn;
}
public static final RegistryObject<Block> PART = BLOCKS.register(
"part",
() -> new PartBlock(BlockBehaviour.Properties.of().noOcclusion().strength(2.0F)));
private static <T extends Block> RegistryObject<Item> registerBlockItem(String name, RegistryObject<T> block) {
return ModItems.ITEMS_REGISTER.register(name, () -> new BlockItem(block.get(), new Item.Properties()));

View File

@@ -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<net.minecraftforge.client.extensions.common.IClientBlockExtensions> 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;
}
});
}
}

View File

@@ -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<BlockEntityType<PartBE>> 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);
}

View File

@@ -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);
}
}

View File

@@ -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<RenderShape> 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<BlockState> 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);
}
}
}

View File

@@ -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]
}
}
}

View File

@@ -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]}
]
}
]

View File

@@ -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"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

After

Width:  |  Height:  |  Size: 499 B