package net.shadowfacts.phycon.block import net.minecraft.block.Block import net.minecraft.block.BlockState import net.minecraft.block.ShapeContext import net.minecraft.item.ItemPlacementContext import net.minecraft.state.StateManager import net.minecraft.state.property.EnumProperty import net.minecraft.state.property.Properties import net.minecraft.util.StringIdentifiable import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.shape.VoxelShape import net.minecraft.util.shape.VoxelShapes import net.minecraft.world.BlockView import net.minecraft.world.World import net.minecraft.world.WorldAccess import net.shadowfacts.phycon.api.Interface import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.block.cable.CableBlock import java.util.* /** * @author shadowfacts */ abstract class FaceDeviceBlock(settings: Settings): DeviceBlock(settings) { companion object { val FACING = Properties.FACING val CABLE_CONNECTION = EnumProperty.of("cable_connection", FaceCableConnection::class.java) } enum class FaceCableConnection : StringIdentifiable { NONE, DOWN, UP, NORTH, SOUTH, WEST, EAST; companion object { fun from(dir: Direction?) = when (dir) { null -> NONE Direction.DOWN -> DOWN Direction.UP -> UP Direction.NORTH -> NORTH Direction.SOUTH -> SOUTH Direction.WEST -> WEST Direction.EAST -> EAST } } val direction: Direction? get() = when (this) { NONE -> null DOWN -> Direction.DOWN UP -> Direction.UP NORTH -> Direction.NORTH SOUTH -> Direction.SOUTH WEST -> Direction.WEST EAST -> Direction.EAST } override fun asString() = name.toLowerCase() } protected abstract val faceThickness: Double abstract val faceShapes: Map private val centerShapes: Map by lazy { mapOf( Direction.DOWN to createCuboidShape(6.0, faceThickness, 6.0, 10.0, 10.0, 10.0), Direction.UP to createCuboidShape(6.0, 6.0, 6.0, 10.0, 16.0 - faceThickness, 10.0), Direction.NORTH to createCuboidShape(6.0, 6.0, faceThickness, 10.0, 10.0, 10.0), Direction.SOUTH to createCuboidShape(6.0, 6.0, 6.0, 10.0, 10.0, 16.0 - faceThickness), Direction.WEST to createCuboidShape(faceThickness, 6.0, 6.0, 10.0, 10.0, 10.0), Direction.EAST to createCuboidShape(6.0, 6.0, 6.0, 16.0 - faceThickness, 10.0, 10.0) ) } private val shapeCache = mutableMapOf, VoxelShape>() private fun getShape(facing: Direction, cableConnection: FaceCableConnection): VoxelShape { return shapeCache.getOrPut(facing to cableConnection) { if (cableConnection == FaceCableConnection.NONE) { VoxelShapes.union(faceShapes[facing], centerShapes[facing]) } else { VoxelShapes.union(faceShapes[facing], centerShapes[facing], CableBlock.SIDE_SHAPES[cableConnection.direction]) } } } override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection { val direction = state[CABLE_CONNECTION].direction return if (direction != null) EnumSet.of(direction) else setOf() } override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? { return if (side == state[FACING]) { null } else { getBlockEntity(world, pos) } } override fun appendProperties(builder: StateManager.Builder) { super.appendProperties(builder) builder.add(FACING) builder.add(CABLE_CONNECTION) } override fun getPlacementState(context: ItemPlacementContext): BlockState { val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite val cableConnection = FaceCableConnection.from(getCableConnectedSide(context.world, context.blockPos, facing)) return defaultState .with(FACING, facing) .with(CABLE_CONNECTION, cableConnection) } private fun getCableConnectedSide(world: WorldAccess, pos: BlockPos, facing: Direction): Direction? { for (side in Direction.values()) { if (side == facing) { continue } val offsetPos = pos.offset(side) val state = world.getBlockState(offsetPos) val block = state.block if (block is NetworkComponentBlock && block.getNetworkConnectedSides(state, world, offsetPos).contains(side.opposite)) { return side } } return null } override fun getStateForNeighborUpdate(state: BlockState, side: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState { val current = state[CABLE_CONNECTION] var newConnection = current if (current == FaceCableConnection.NONE) { if (neighborState.block is NetworkComponentBlock) { newConnection = FaceCableConnection.from(side) } } else { val currentConnectedPos = pos.offset(current.direction) if (neighborPos == currentConnectedPos && neighborState.block !is NetworkComponentBlock) { // the old cable connection is no longer correct, try to find another newConnection = FaceCableConnection.from(getCableConnectedSide(world, pos, state[FACING])) } } return state.with(CABLE_CONNECTION, newConnection) } override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape { return getShape(state[FACING], state[CABLE_CONNECTION]) } }