Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
347 lines
11 KiB
Java
347 lines
11 KiB
Java
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;
|
|
}
|
|
}
|