package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import com.mojang.serialization.codecs.RecordCodecBuilder; import java.util.List; import java.util.Map; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.ScheduledTickAccess; 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.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.Property; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.phys.shapes.CollisionContext; import net.minecraft.world.phys.shapes.VoxelShape; public class TripWireBlock extends Block { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec( instance -> instance.group(BuiltInRegistries.BLOCK.byNameCodec().fieldOf("hook").forGetter(tripWireBlock -> tripWireBlock.hook), propertiesCodec()) .apply(instance, TripWireBlock::new) ); public static final BooleanProperty POWERED = BlockStateProperties.POWERED; public static final BooleanProperty ATTACHED = BlockStateProperties.ATTACHED; public static final BooleanProperty DISARMED = BlockStateProperties.DISARMED; public static final BooleanProperty NORTH = PipeBlock.NORTH; public static final BooleanProperty EAST = PipeBlock.EAST; public static final BooleanProperty SOUTH = PipeBlock.SOUTH; public static final BooleanProperty WEST = PipeBlock.WEST; private static final Map PROPERTY_BY_DIRECTION = CrossCollisionBlock.PROPERTY_BY_DIRECTION; protected static final VoxelShape AABB = Block.box(0.0, 1.0, 0.0, 16.0, 2.5, 16.0); protected static final VoxelShape NOT_ATTACHED_AABB = Block.box(0.0, 0.0, 0.0, 16.0, 8.0, 16.0); private static final int RECHECK_PERIOD = 10; private final Block hook; @Override public MapCodec codec() { return CODEC; } public TripWireBlock(Block hook, BlockBehaviour.Properties properties) { super(properties); this.registerDefaultState( this.stateDefinition .any() .setValue(POWERED, false) .setValue(ATTACHED, false) .setValue(DISARMED, false) .setValue(NORTH, false) .setValue(EAST, false) .setValue(SOUTH, false) .setValue(WEST, false) ); this.hook = hook; } @Override protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { return state.getValue(ATTACHED) ? AABB : NOT_ATTACHED_AABB; } @Override public BlockState getStateForPlacement(BlockPlaceContext context) { BlockGetter blockGetter = context.getLevel(); BlockPos blockPos = context.getClickedPos(); return this.defaultBlockState() .setValue(NORTH, this.shouldConnectTo(blockGetter.getBlockState(blockPos.north()), Direction.NORTH)) .setValue(EAST, this.shouldConnectTo(blockGetter.getBlockState(blockPos.east()), Direction.EAST)) .setValue(SOUTH, this.shouldConnectTo(blockGetter.getBlockState(blockPos.south()), Direction.SOUTH)) .setValue(WEST, this.shouldConnectTo(blockGetter.getBlockState(blockPos.west()), Direction.WEST)); } @Override protected BlockState updateShape( BlockState blockState, LevelReader levelReader, ScheduledTickAccess scheduledTickAccess, BlockPos blockPos, Direction direction, BlockPos blockPos2, BlockState blockState2, RandomSource randomSource ) { return direction.getAxis().isHorizontal() ? blockState.setValue((Property)PROPERTY_BY_DIRECTION.get(direction), this.shouldConnectTo(blockState2, direction)) : super.updateShape(blockState, levelReader, scheduledTickAccess, blockPos, direction, blockPos2, blockState2, randomSource); } @Override protected void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean movedByPiston) { if (!oldState.is(state.getBlock())) { this.updateSource(level, pos, state); } } @Override protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { if (!movedByPiston && !state.is(newState.getBlock())) { this.updateSource(level, pos, state.setValue(POWERED, true)); } } @Override public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) { if (!level.isClientSide && !player.getMainHandItem().isEmpty() && player.getMainHandItem().is(Items.SHEARS)) { level.setBlock(pos, state.setValue(DISARMED, true), 4); level.gameEvent(player, GameEvent.SHEAR, pos); } return super.playerWillDestroy(level, pos, state, player); } private void updateSource(Level level, BlockPos pos, BlockState state) { for (Direction direction : new Direction[]{Direction.SOUTH, Direction.WEST}) { for (int i = 1; i < 42; i++) { BlockPos blockPos = pos.relative(direction, i); BlockState blockState = level.getBlockState(blockPos); if (blockState.is(this.hook)) { if (blockState.getValue(TripWireHookBlock.FACING) == direction.getOpposite()) { TripWireHookBlock.calculateState(level, blockPos, blockState, false, true, i, state); } break; } if (!blockState.is(this)) { break; } } } } @Override protected VoxelShape getEntityInsideCollisionShape(BlockState blockState, Level level, BlockPos blockPos) { return blockState.getShape(level, blockPos); } @Override protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { if (!level.isClientSide) { if (!(Boolean)state.getValue(POWERED)) { this.checkPressed(level, pos, List.of(entity)); } } } @Override protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { if ((Boolean)level.getBlockState(pos).getValue(POWERED)) { this.checkPressed(level, pos); } } private void checkPressed(Level level, BlockPos pos) { BlockState blockState = level.getBlockState(pos); List list = level.getEntities(null, blockState.getShape(level, pos).bounds().move(pos)); this.checkPressed(level, pos, list); } private void checkPressed(Level level, BlockPos blockPos, List list) { BlockState blockState = level.getBlockState(blockPos); boolean bl = (Boolean)blockState.getValue(POWERED); boolean bl2 = false; if (!list.isEmpty()) { for (Entity entity : list) { if (!entity.isIgnoringBlockTriggers()) { bl2 = true; break; } } } if (bl2 != bl) { blockState = blockState.setValue(POWERED, bl2); level.setBlock(blockPos, blockState, 3); this.updateSource(level, blockPos, blockState); } if (bl2) { level.scheduleTick(new BlockPos(blockPos), this, 10); } } public boolean shouldConnectTo(BlockState state, Direction direction) { return state.is(this.hook) ? state.getValue(TripWireHookBlock.FACING) == direction.getOpposite() : state.is(this); } @Override protected BlockState rotate(BlockState state, Rotation rotation) { switch (rotation) { case CLOCKWISE_180: return state.setValue(NORTH, (Boolean)state.getValue(SOUTH)) .setValue(EAST, (Boolean)state.getValue(WEST)) .setValue(SOUTH, (Boolean)state.getValue(NORTH)) .setValue(WEST, (Boolean)state.getValue(EAST)); case COUNTERCLOCKWISE_90: return state.setValue(NORTH, (Boolean)state.getValue(EAST)) .setValue(EAST, (Boolean)state.getValue(SOUTH)) .setValue(SOUTH, (Boolean)state.getValue(WEST)) .setValue(WEST, (Boolean)state.getValue(NORTH)); case CLOCKWISE_90: return state.setValue(NORTH, (Boolean)state.getValue(WEST)) .setValue(EAST, (Boolean)state.getValue(NORTH)) .setValue(SOUTH, (Boolean)state.getValue(EAST)) .setValue(WEST, (Boolean)state.getValue(SOUTH)); default: return state; } } @Override protected BlockState mirror(BlockState state, Mirror mirror) { switch (mirror) { case LEFT_RIGHT: return state.setValue(NORTH, (Boolean)state.getValue(SOUTH)).setValue(SOUTH, (Boolean)state.getValue(NORTH)); case FRONT_BACK: return state.setValue(EAST, (Boolean)state.getValue(WEST)).setValue(WEST, (Boolean)state.getValue(EAST)); default: return super.mirror(state, mirror); } } @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { builder.add(POWERED, ATTACHED, DISARMED, NORTH, EAST, WEST, SOUTH); } }