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.item.ItemStack import net.minecraft.server.world.ServerWorld import net.minecraft.state.StateManager import net.minecraft.state.property.EnumProperty import net.minecraft.state.property.Properties import net.minecraft.util.DyeColor 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.WorldAccess import net.shadowfacts.phycon.api.Interface import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.block.cable.CableBlock import net.shadowfacts.phycon.init.PhyItems 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) val COLOR = EnumProperty.of("color", DyeColor::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) builder.add(COLOR) } override fun getPlacementState(context: ItemPlacementContext): BlockState { val facing = if (context.player?.isSneaking == true) context.side.opposite else context.playerLookDirection.opposite // todo: this should never be called val cableConnection = FaceCableConnection.from(getCableConnectedSide(context.world, context.blockPos, facing, DyeColor.BLUE)) return defaultState .with(FACING, facing) .with(CABLE_CONNECTION, cableConnection) } private fun getCableConnectedSide(world: WorldAccess, pos: BlockPos, facing: Direction, color: DyeColor): Direction? { for (side in Direction.values()) { if (side == facing) { continue } val offsetPos = pos.offset(side) val state = world.getBlockState(offsetPos) if (canConnectTo(world, side, state, offsetPos, color)) { return side } } return null } private fun canConnectTo(world: WorldAccess, side: Direction, candidateState: BlockState, candidatePos: BlockPos, myColor: DyeColor): Boolean { val block = candidateState.block return if (block is FaceDeviceBlock<*> && candidateState[COLOR] == myColor) { true } else if (block is CableBlock && block.color == myColor) { true } else { block is NetworkComponentBlock && block.getNetworkConnectedSides(candidateState, world, candidatePos).contains(side.opposite) } } 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 (canConnectTo(world, side, neighborState, neighborPos, state[COLOR])) { 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], state[COLOR])) } } 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]) } override fun onStacksDropped(state: BlockState, world: ServerWorld, pos: BlockPos, stack: ItemStack) { super.onStacksDropped(state, world, pos, stack) val cableStack = ItemStack(PhyItems.CABLES[state[COLOR]]) dropStack(world, pos, cableStack) } }