HexCasting/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockSlate.java
2023-05-15 12:19:53 -05:00

239 lines
9.8 KiB
Java

package at.petrak.hexcasting.common.blocks.circles;
import at.petrak.hexcasting.annotations.SoftImplement;
import at.petrak.hexcasting.api.block.circle.BlockCircleComponent;
import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv;
import at.petrak.hexcasting.api.casting.eval.vm.CastingImage;
import at.petrak.hexcasting.api.casting.eval.vm.CastingVM;
import at.petrak.hexcasting.api.casting.iota.PatternIota;
import at.petrak.hexcasting.api.casting.math.HexPattern;
import at.petrak.hexcasting.common.lib.HexItems;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.FluidTags;
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.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
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.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.material.PushReaction;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import java.util.EnumSet;
// When on the floor or ceiling FACING is the direction the *bottom* of the pattern points
// (or which way is "down").
// When on the wall FACING is the direction of the *front* of the block
public class BlockSlate extends BlockCircleComponent implements EntityBlock, SimpleWaterloggedBlock {
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final EnumProperty<AttachFace> ATTACH_FACE = BlockStateProperties.ATTACH_FACE;
public static final double THICKNESS = 1;
public static final VoxelShape AABB_FLOOR = Block.box(0, 0, 0, 16, THICKNESS, 16);
public static final VoxelShape AABB_CEILING = Block.box(0, 16 - THICKNESS, 0, 16, 16, 16);
public static final VoxelShape AABB_EAST_WALL = Block.box(0, 0, 0, THICKNESS, 16, 16);
public static final VoxelShape AABB_WEST_WALL = Block.box(16 - THICKNESS, 0, 0, 16, 16, 16);
public static final VoxelShape AABB_SOUTH_WALL = Block.box(0, 0, 0, 16, 16, THICKNESS);
public static final VoxelShape AABB_NORTH_WALL = Block.box(0, 0, 16 - THICKNESS, 16, 16, 16);
public BlockSlate(Properties p_53182_) {
super(p_53182_);
this.registerDefaultState(
this.stateDefinition.any()
.setValue(ENERGIZED, false)
.setValue(FACING, Direction.NORTH)
.setValue(WATERLOGGED, false));
}
@Override
public boolean propagatesSkylightDown(BlockState state, @Nonnull BlockGetter reader, @Nonnull BlockPos pos) {
return !state.getValue(WATERLOGGED);
}
@Nonnull
@Override
public FluidState getFluidState(BlockState state) {
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
}
@Override
public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Direction enterDir, BlockPos pos,
BlockState bs, ServerLevel world) {
HexPattern pattern;
if (world.getBlockEntity(pos) instanceof BlockEntitySlate tile) {
pattern = tile.pattern;
} else {
return new ControlFlow.Stop();
}
var exitDirsSet = this.possibleExitDirections(pos, bs, world);
exitDirsSet.remove(enterDir.getOpposite());
var exitDirs = exitDirsSet.stream().map((dir) -> this.exitPositionFromDirection(pos, dir));
if (pattern == null)
return new ControlFlow.Continue(imageIn, exitDirs.toList());
var vm = new CastingVM(imageIn, env);
var result = vm.queueExecuteAndWrapIota(new PatternIota(pattern), world);
if (result.getResolutionType().getSuccess()) {
return new ControlFlow.Continue(vm.getImage(), exitDirs.toList());
} else {
return new ControlFlow.Stop();
}
}
@Override
public boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) {
var thisNormal = this.normalDir(pos, bs, world);
return enterDir != thisNormal && enterDir != thisNormal.getOpposite();
}
@Override
public EnumSet<Direction> possibleExitDirections(BlockPos pos, BlockState bs, Level world) {
var allDirs = EnumSet.allOf(Direction.class);
var normal = this.normalDir(pos, bs, world);
allDirs.remove(normal);
allDirs.remove(normal.getOpposite());
return allDirs;
}
@SoftImplement("forge")
public ItemStack getCloneItemStack(BlockState state, HitResult target, BlockGetter level, BlockPos pos,
Player player) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof BlockEntitySlate slate) {
ItemStack stack = new ItemStack(HexItems.SLATE);
if (slate.pattern != null) {
HexItems.SLATE.writeDatum(stack, new PatternIota(slate.pattern));
}
return stack;
}
return new ItemStack(this);
}
@Override
public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft) {
return switch (bs.getValue(ATTACH_FACE)) {
case FLOOR -> Direction.UP;
case CEILING -> Direction.DOWN;
case WALL -> bs.getValue(FACING);
};
}
@Override
public float particleHeight(BlockPos pos, BlockState bs, Level world) {
return 0.5f - 15f / 16f;
}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pPos, BlockState pState) {
return new BlockEntitySlate(pPos, pState);
}
@Override
public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) {
return switch (pState.getValue(ATTACH_FACE)) {
case FLOOR -> AABB_FLOOR;
case CEILING -> AABB_CEILING;
case WALL -> switch (pState.getValue(FACING)) {
case NORTH -> AABB_NORTH_WALL;
case EAST -> AABB_EAST_WALL;
case SOUTH -> AABB_SOUTH_WALL;
// NORTH; up and down don't happen (but we need branches for them)
default -> AABB_WEST_WALL;
};
};
}
@Override
public PushReaction getPistonPushReaction(BlockState pState) {
return PushReaction.DESTROY;
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
super.createBlockStateDefinition(builder);
builder.add(FACING, ATTACH_FACE, WATERLOGGED);
}
@Override
@Nullable
public BlockState getStateForPlacement(BlockPlaceContext pContext) {
FluidState fluidState = pContext.getLevel().getFluidState(pContext.getClickedPos());
for (Direction direction : pContext.getNearestLookingDirections()) {
BlockState blockstate;
if (direction.getAxis() == Direction.Axis.Y) {
blockstate = this.defaultBlockState()
.setValue(ATTACH_FACE, direction == Direction.UP ? AttachFace.CEILING : AttachFace.FLOOR)
.setValue(FACING, pContext.getHorizontalDirection().getOpposite());
} else {
blockstate = this.defaultBlockState()
.setValue(ATTACH_FACE, AttachFace.WALL)
.setValue(FACING, direction.getOpposite());
}
blockstate = blockstate.setValue(WATERLOGGED,
fluidState.is(FluidTags.WATER) && fluidState.getAmount() == 8);
if (blockstate.canSurvive(pContext.getLevel(), pContext.getClickedPos())) {
return blockstate;
}
}
return null;
}
// i do as the FaceAttachedHorizontalDirectionBlock.java guides
@Override
public boolean canSurvive(BlockState pState, LevelReader pLevel, BlockPos pPos) {
return canAttach(pLevel, pPos, getConnectedDirection(pState).getOpposite());
}
@Override
public BlockState updateShape(BlockState pState, Direction pFacing, BlockState pFacingState, LevelAccessor pLevel,
BlockPos pCurrentPos, BlockPos pFacingPos) {
if (pState.getValue(WATERLOGGED)) {
pLevel.scheduleTick(pCurrentPos, Fluids.WATER, Fluids.WATER.getTickDelay(pLevel));
}
return getConnectedDirection(pState).getOpposite() == pFacing
&& !pState.canSurvive(pLevel, pCurrentPos) ?
pState.getFluidState().createLegacyBlock()
: super.updateShape(pState, pFacing, pFacingState, pLevel, pCurrentPos, pFacingPos);
}
public static boolean canAttach(LevelReader pReader, BlockPos pPos, Direction pDirection) {
BlockPos blockpos = pPos.relative(pDirection);
return pReader.getBlockState(blockpos).isFaceSturdy(pReader, blockpos, pDirection.getOpposite());
}
protected static Direction getConnectedDirection(BlockState pState) {
return switch (pState.getValue(ATTACH_FACE)) {
case CEILING -> Direction.DOWN;
case FLOOR -> Direction.UP;
default -> pState.getValue(FACING);
};
}
}