package com.tiedup.remake.blocks; import com.tiedup.remake.blocks.entity.IronBarDoorBlockEntity; import com.tiedup.remake.core.SystemMessageManager; import com.tiedup.remake.items.ItemCellKey; import com.tiedup.remake.items.ItemKey; import com.tiedup.remake.items.ItemMasterKey; import java.util.UUID; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.*; import net.minecraft.world.level.material.MapColor; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.Shapes; import net.minecraft.world.phys.shapes.VoxelShape; import org.jetbrains.annotations.Nullable; /** * Iron Bar Door - A door made of iron bars that can be locked. * * Phase: Kidnapper Revamp - Cell System * * Features: * - Lockable with ItemKey (stores key UUID) * - Can be unlocked with matching key, ItemCellKey, or ItemMasterKey * - When locked, cannot be opened by redstone or by hand * - Visually connects to iron bars (like vanilla iron bars) * - Uses DoorBlock mechanics for open/close state */ public class BlockIronBarDoor extends DoorBlock implements EntityBlock { // VoxelShapes for different orientations - centered like iron bars (7-9 = 2 pixels thick) protected static final VoxelShape SOUTH_AABB = Block.box( 0.0, 0.0, 7.0, 16.0, 16.0, 9.0 ); protected static final VoxelShape NORTH_AABB = Block.box( 0.0, 0.0, 7.0, 16.0, 16.0, 9.0 ); protected static final VoxelShape WEST_AABB = Block.box( 7.0, 0.0, 0.0, 9.0, 16.0, 16.0 ); protected static final VoxelShape EAST_AABB = Block.box( 7.0, 0.0, 0.0, 9.0, 16.0, 16.0 ); public BlockIronBarDoor() { super( BlockBehaviour.Properties.of() .mapColor(MapColor.METAL) .strength(5.0f, 45.0f) .sound(SoundType.METAL) .requiresCorrectToolForDrops() .noOcclusion(), BlockSetType.IRON ); } // ==================== SHAPE ==================== @Override public VoxelShape getShape( BlockState state, BlockGetter level, BlockPos pos, CollisionContext context ) { Direction facing = state.getValue(FACING); boolean open = state.getValue(OPEN); boolean hingeRight = state.getValue(HINGE) == DoorHingeSide.RIGHT; Direction effectiveDirection; if (open) { effectiveDirection = hingeRight ? facing.getCounterClockWise() : facing.getClockWise(); } else { effectiveDirection = facing; } return switch (effectiveDirection) { case NORTH -> NORTH_AABB; case SOUTH -> SOUTH_AABB; case WEST -> WEST_AABB; case EAST -> EAST_AABB; default -> SOUTH_AABB; }; } // ==================== INTERACTION ==================== @Override public InteractionResult use( BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit ) { // Get the BlockEntity BlockEntity be = level.getBlockEntity( getBlockEntityPos(state, pos, level) ); if (!(be instanceof IronBarDoorBlockEntity doorEntity)) { return InteractionResult.PASS; } ItemStack heldItem = player.getItemInHand(hand); // Handle key interactions if (heldItem.getItem() instanceof ItemMasterKey) { // Master key can always unlock if (doorEntity.isLocked()) { if (!level.isClientSide) { doorEntity.setLocked(false); SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.INFO, "Door unlocked with master key" ); } return InteractionResult.sidedSuccess(level.isClientSide); } } else if (heldItem.getItem() instanceof ItemCellKey) { // Cell key can unlock any iron bar door if (doorEntity.isLocked()) { if (!level.isClientSide) { doorEntity.setLocked(false); SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.INFO, "Door unlocked with cell key" ); } return InteractionResult.sidedSuccess(level.isClientSide); } } else if (heldItem.getItem() instanceof ItemKey key) { UUID keyUUID = key.getKeyUUID(heldItem); if (doorEntity.isLocked()) { // Try to unlock if (doorEntity.matchesKey(keyUUID)) { if (!level.isClientSide) { doorEntity.setLocked(false); SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.INFO, "Door unlocked" ); } return InteractionResult.sidedSuccess(level.isClientSide); } else { if (!level.isClientSide) { SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.ERROR, "This key doesn't fit this lock" ); } return InteractionResult.FAIL; } } else { // Lock the door with this key if (!level.isClientSide) { doorEntity.setLockedByKeyUUID(keyUUID); SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.INFO, "Door locked" ); } return InteractionResult.sidedSuccess(level.isClientSide); } } // If locked and no valid key, deny access if (doorEntity.isLocked()) { if (!level.isClientSide) { SystemMessageManager.sendToPlayer( player, SystemMessageManager.MessageCategory.ERROR, "This door is locked" ); } return InteractionResult.FAIL; } // Iron doors don't open by hand normally, but we allow it if unlocked // Toggle the door state if (!level.isClientSide) { boolean newOpen = !state.getValue(OPEN); setOpen(player, level, state, pos, newOpen); } return InteractionResult.sidedSuccess(level.isClientSide); } /** * Get the BlockEntity position (handles double-height doors). */ private BlockPos getBlockEntityPos( BlockState state, BlockPos pos, Level level ) { // LOW FIX: Check if HALF property exists before accessing (WorldEdit/SetBlock safety) if (!state.hasProperty(HALF)) { // Invalid state - assume lower half return pos; } // BlockEntity is always on the lower half if (state.getValue(HALF) == DoubleBlockHalf.UPPER) { return pos.below(); } return pos; } // ==================== REDSTONE ==================== @Override public void neighborChanged( BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, boolean movedByPiston ) { // Check if locked before allowing redstone control BlockEntity be = level.getBlockEntity( getBlockEntityPos(state, pos, level) ); if ( be instanceof IronBarDoorBlockEntity doorEntity && doorEntity.isLocked() ) { // Door is locked, ignore redstone return; } // Allow normal redstone behavior if unlocked super.neighborChanged( state, level, pos, neighborBlock, neighborPos, movedByPiston ); } // ==================== BLOCK ENTITY ==================== @Nullable @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { // Only create block entity for the lower half if (state.getValue(HALF) == DoubleBlockHalf.LOWER) { return new IronBarDoorBlockEntity(pos, state); } return null; } // ==================== PLACEMENT ==================== @Override public void setPlacedBy( Level level, BlockPos pos, BlockState state, @Nullable net.minecraft.world.entity.LivingEntity placer, ItemStack stack ) { super.setPlacedBy(level, pos, state, placer, stack); // BlockEntity is automatically created for lower half } @Override public void onRemove( BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston ) { if (!state.is(newState.getBlock())) { // Block entity is removed automatically } super.onRemove(state, level, pos, newState, movedByPiston); } // ==================== VISUAL ==================== @Override public float getShadeBrightness( BlockState state, BlockGetter level, BlockPos pos ) { // Allow some light through like iron bars return 1.0f; } @Override public boolean propagatesSkylightDown( BlockState state, BlockGetter level, BlockPos pos ) { return true; } }