package net.shadowfacts.phycon.block.cable import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings import net.minecraft.block.* import net.minecraft.entity.player.PlayerEntity import net.minecraft.item.ItemPlacementContext import net.minecraft.state.StateManager import net.minecraft.state.property.EnumProperty import net.minecraft.util.ActionResult import net.minecraft.util.DyeColor import net.minecraft.util.Hand import net.minecraft.util.Identifier import net.minecraft.util.hit.BlockHitResult import net.minecraft.util.math.BlockPos import net.minecraft.util.math.Direction import net.minecraft.util.math.Vec3d 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.PhysicalConnectivity import net.shadowfacts.phycon.api.Interface import net.shadowfacts.phycon.api.NetworkCableBlock import net.shadowfacts.phycon.api.NetworkComponentBlock import net.shadowfacts.phycon.block.DeviceBlockEntity import net.shadowfacts.phycon.init.PhyItems import net.shadowfacts.phycon.item.FaceDeviceBlockItem import net.shadowfacts.phycon.util.CableConnection import net.shadowfacts.phycon.util.NetworkUtil import net.shadowfacts.phycon.util.containsInclusive import java.util.* /** * @author shadowfacts */ class CableBlock( val color: DyeColor, ): Block( FabricBlockSettings.of(CABLE_MATERIAL) .strength(0.3f) .nonOpaque() ), NetworkCableBlock { companion object { val ID = Identifier(PhysicalConnectivity.MODID, "cable") val CABLE_MATERIAL = Material.Builder(MapColor.BLUE).build() val CENTER_SHAPE = createCuboidShape(6.0, 6.0, 6.0, 10.0, 10.0, 10.0) val SIDE_SHAPES = mapOf( Direction.DOWN to createCuboidShape(6.0, 0.0, 6.0, 10.0, 6.0, 10.0), Direction.UP to createCuboidShape(6.0, 10.0, 6.0, 10.0, 16.0, 10.0), Direction.NORTH to createCuboidShape(6.0, 6.0, 0.0, 10.0, 10.0, 6.0), Direction.SOUTH to createCuboidShape(6.0, 6.0, 10.0, 10.0, 10.0, 16.0), Direction.WEST to createCuboidShape(0.0, 6.0, 6.0, 6.0, 10.0, 10.0), Direction.EAST to createCuboidShape(10.0, 6.0, 6.0, 16.0, 10.0, 10.0) ) private val SHAPE_CACHE = Array(64) { key -> val connectedSides = Direction.values().filterIndexed { index, _ -> ((key shr index) and 1) == 1 } connectedSides.fold(CENTER_SHAPE) { acc, side -> VoxelShapes.union(acc, SIDE_SHAPES[side]) } } val CONNECTIONS: Map> = Direction.values().associate { it to EnumProperty.of(it.name.toLowerCase(), CableConnection::class.java) } fun getShape(state: BlockState): VoxelShape { val key = Direction.values().foldIndexed(0) { i, acc, dir -> if (state[CONNECTIONS[dir]] == CableConnection.ON) { acc or (1 shl i) } else { acc } } return SHAPE_CACHE[key] } } init { defaultState = CONNECTIONS.values.fold(stateManager.defaultState) { acc, prop -> acc.with(prop, CableConnection.OFF) } } override fun getNetworkConnectedSides(state: BlockState, world: WorldAccess, pos: BlockPos): Collection { val set = EnumSet.noneOf(Direction::class.java) for ((side, prop) in CONNECTIONS) { if (state[prop] == CableConnection.ON) { set.add(side) } } return set } override fun appendProperties(builder: StateManager.Builder) { super.appendProperties(builder) CONNECTIONS.values.forEach { builder.add(it) } } fun getInitialState(world: World, pos: BlockPos): BlockState { return CONNECTIONS.entries.fold(defaultState, { acc, (dir, prop) -> acc.with(prop, getConnectionStateInDirection(world, pos, dir)) }) } override fun getPlacementState(context: ItemPlacementContext): BlockState { return getInitialState(context.world, context.blockPos) } override fun getStateForNeighborUpdate(state: BlockState, side: Direction, neighborState: BlockState, world: WorldAccess, blockPos_1: BlockPos, blockPos_2: BlockPos): BlockState { val prop = CONNECTIONS[side] val current = state[prop] return when (current) { CableConnection.DISABLED -> state else -> state.with(prop, getConnectionStateInDirection(world, blockPos_1, side)) } } private fun getConnectionStateInDirection(world: WorldAccess, pos: BlockPos, direction: Direction): CableConnection { val offsetPos = pos.offset(direction) val state = world.getBlockState(offsetPos) val block = state.block return if (block == this) { val prop = CONNECTIONS[direction.opposite] when (state[prop]) { CableConnection.DISABLED -> CableConnection.DISABLED else -> CableConnection.ON } } else if (block is NetworkComponentBlock && block !is CableBlock) { if (block.getNetworkConnectedSides(state, world, offsetPos).contains(direction.opposite)) { CableConnection.ON } else { CableConnection.OFF } } else { CableConnection.OFF } } override fun getNetworkInterfaceForSide(side: Direction, state: BlockState, world: WorldAccess, pos: BlockPos): Interface? { // cables don't have network interfaces return null } override fun onUse( state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult ): ActionResult { if (player.getStackInHand(hand).item == PhyItems.SCREWDRIVER) { val hitPos = Vec3d(hitResult.pos.x - pos.x, hitResult.pos.y - pos.y, hitResult.pos.z - pos.z) val hitConnection = SIDE_SHAPES.entries.firstOrNull { (_, shape) -> shape.boundingBox.containsInclusive(hitPos) } if (hitConnection != null) { val side = hitConnection.key val prop = CONNECTIONS[side] val newState = when (state[prop]) { CableConnection.DISABLED -> { // if the block this cable is connecting to on the side that will be re-enabled is a cable that // is disabled on the side that connects it to us, we also re-enable that cable to make sure both // "halves" of the connection are in the same state val connectedToPos = pos.offset(side) val connectedTo = world.getBlockState(connectedToPos) if (connectedTo.block == this && connectedTo[CONNECTIONS[side.opposite]] == CableConnection.DISABLED) { world.setBlockState(connectedToPos, connectedTo.with(CONNECTIONS[side.opposite], CableConnection.ON)) } state.with(prop, if (connectedTo.block is NetworkComponentBlock) CableConnection.ON else CableConnection.OFF) } else -> state.with(prop, CableConnection.DISABLED) } world.setBlockState(pos, newState) } return ActionResult.SUCCESS } return ActionResult.PASS } override fun canReplace(state: BlockState, context: ItemPlacementContext): Boolean { return context.stack.item is FaceDeviceBlockItem } override fun isTranslucent(blockState_1: BlockState?, blockView_1: BlockView?, blockPos_1: BlockPos?): Boolean { return true } // override fun isSimpleFullBlock(blockState_1: BlockState?, blockView_1: BlockView?, blockPos_1: BlockPos?): Boolean { // return false // } override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape { return getShape(state) } override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) { super.onBreak(world, pos, state, player) if (!world.isClient) { // notify devices on either end that the connection was broken (i.e., unset the cached receivers) val connectedSides = getNetworkConnectedSides(state, world, pos) for (side in connectedSides) { val dest = NetworkUtil.findConnectedInterface(world, pos, side) dest?.cableDisconnected() } } } }