Compare commits

...

5 Commits

14 changed files with 311 additions and 7 deletions

View File

@ -1,7 +1,17 @@
package net.shadowfacts.phycon.api; package net.shadowfacts.phycon.api;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
import java.util.Set;
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
public interface NetworkCable { public interface NetworkCable extends NetworkComponent {
Set<Direction> getNetworkConnectedSides(BlockState state, World world, BlockPos pos);
} }

View File

@ -0,0 +1,7 @@
package net.shadowfacts.phycon.api;
/**
* @author shadowfacts
*/
public interface NetworkComponent {
}

View File

@ -3,6 +3,7 @@ package net.shadowfacts.phycon.init
import net.minecraft.block.Block import net.minecraft.block.Block
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.network.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
@ -15,11 +16,13 @@ object PhyBlocks {
val INTERFACE = InterfaceBlock() val INTERFACE = InterfaceBlock()
val TERMINAL = TerminalBlock() val TERMINAL = TerminalBlock()
val SWITCH = SwitchBlock() val SWITCH = SwitchBlock()
val CABLE = CableBlock()
fun init() { fun init() {
register(InterfaceBlock.ID, INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE)
} }
private fun register(id: Identifier, block: Block) { private fun register(id: Identifier, block: Block) {

View File

@ -4,6 +4,8 @@ import net.minecraft.item.BlockItem
import net.minecraft.item.Item import net.minecraft.item.Item
import net.minecraft.util.Identifier import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry import net.minecraft.util.registry.Registry
import net.shadowfacts.phycon.item.ScrewdriverItem
import net.shadowfacts.phycon.network.block.cable.CableBlock
import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock import net.shadowfacts.phycon.network.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock import net.shadowfacts.phycon.network.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.network.block.terminal.TerminalBlock import net.shadowfacts.phycon.network.block.terminal.TerminalBlock
@ -16,11 +18,17 @@ object PhyItems {
val INTERFACE = BlockItem(PhyBlocks.INTERFACE, Item.Settings()) val INTERFACE = BlockItem(PhyBlocks.INTERFACE, Item.Settings())
val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings()) val TERMINAL = BlockItem(PhyBlocks.TERMINAL, Item.Settings())
val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings()) val SWITCH = BlockItem(PhyBlocks.SWITCH, Item.Settings())
val CABLE = BlockItem(PhyBlocks.CABLE, Item.Settings())
val SCREWDRIVER = ScrewdriverItem()
fun init() { fun init() {
register(InterfaceBlock.ID, INTERFACE) register(InterfaceBlock.ID, INTERFACE)
register(TerminalBlock.ID, TERMINAL) register(TerminalBlock.ID, TERMINAL)
register(SwitchBlock.ID, SWITCH) register(SwitchBlock.ID, SWITCH)
register(CableBlock.ID, CABLE)
register(ScrewdriverItem.ID, SCREWDRIVER)
} }
private fun register(id: Identifier, item: Item) { private fun register(id: Identifier, item: Item) {

View File

@ -0,0 +1,14 @@
package net.shadowfacts.phycon.item
import net.minecraft.item.Item
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
/**
* @author shadowfacts
*/
class ScrewdriverItem: Item(Settings()) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "screwdriver")
}
}

View File

@ -31,9 +31,7 @@ object NetworkUtil {
results.add(sink) results.add(sink)
} }
if (world.getBlockState(pos).block is NetworkCable) { findEdges(queue, visited, world, pos)
addAdjacent(queue, visited, pos)
}
visited.add(pos) visited.add(pos)
} }
@ -41,6 +39,20 @@ object NetworkUtil {
return results return results
} }
private fun findEdges(queue: MutableList<BlockPos>, visited: Set<BlockPos>, world: World, pos: BlockPos) {
val state = world.getBlockState(pos)
val block = state.block
if (block is NetworkCable) {
val connections = block.getNetworkConnectedSides(state, world, pos)
for (side in connections) {
val newPos = pos.offset(side)
if (newPos !in visited) {
queue.add(newPos)
}
}
}
}
private fun addAdjacent(queue: MutableList<BlockPos>, visited: Set<BlockPos>, pos: BlockPos) { private fun addAdjacent(queue: MutableList<BlockPos>, visited: Set<BlockPos>, pos: BlockPos) {
for (dir in Direction.values()) { for (dir in Direction.values()) {
val newPos = pos.offset(dir) val newPos = pos.offset(dir)

View File

@ -0,0 +1,168 @@
package net.shadowfacts.phycon.network.block.cable
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.block.*
import net.minecraft.block.piston.PistonBehavior
import net.minecraft.entity.EntityContext
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateFactory
import net.minecraft.state.property.EnumProperty
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.IWorld
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkCable
import net.shadowfacts.phycon.api.NetworkComponent
import net.shadowfacts.phycon.init.PhyItems
import net.shadowfacts.phycon.util.CableConnection
import java.util.*
/**
* @author shadowfacts
*/
class CableBlock: Block(Settings.of(CABLE_MATERIAL)), NetworkCable {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "cable")
val CABLE_MATERIAL = Material(MaterialColor.IRON, false, false, true, false, true, false, false, PistonBehavior.NORMAL)
val CENTER_SHAPE = createCuboidShape(6.0, 6.0, 6.0, 10.0, 10.0, 10.0)
val SIDE_SHAPES = mapOf<Direction, VoxelShape>(
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 = mutableMapOf<BlockState, VoxelShape>()
val CONNECTIONS: Map<Direction, EnumProperty<CableConnection>> = Direction.values().associate { it to EnumProperty.of(it.name.toLowerCase(), CableConnection::class.java) }
fun getShape(state: BlockState): VoxelShape {
return SHAPE_CACHE.getOrPut(state) {
var shape = CENTER_SHAPE
for ((side, prop) in CONNECTIONS) {
if (state[prop] == CableConnection.ON) {
shape = VoxelShapes.union(shape, SIDE_SHAPES[side])
}
}
return shape
}
}
}
init {
defaultState = CONNECTIONS.values.fold(stateFactory.defaultState) { acc, prop ->
acc.with(prop, CableConnection.OFF)
}
}
override fun getNetworkConnectedSides(state: BlockState, world: World, pos: BlockPos): Set<Direction> {
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: StateFactory.Builder<Block, BlockState>) {
super.appendProperties(builder)
CONNECTIONS.values.forEach {
builder.add(it)
}
}
override fun getPlacementState(context: ItemPlacementContext): BlockState {
return CONNECTIONS.entries.fold(defaultState, { acc, (dir, prop) ->
acc.with(prop, getConnectionStateInDirection(context.world, context.blockPos, dir))
})
}
override fun getStateForNeighborUpdate(state: BlockState, side: Direction, neighborState: BlockState, world: IWorld, 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: IWorld, pos: BlockPos, direction: Direction): CableConnection {
val state = world.getBlockState(pos.offset(direction))
return when (state.block) {
this -> {
val prop = CONNECTIONS[direction.opposite]
when (state[prop]) {
CableConnection.DISABLED -> CableConnection.DISABLED
else -> CableConnection.ON
}
}
is NetworkComponent -> CableConnection.ON
else -> CableConnection.OFF
}
}
override fun activate(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hitResult: BlockHitResult): Boolean {
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) ->
val box = shape.boundingBox
hitPos.x >= box.minX && hitPos.x <= box.maxX && hitPos.y >= box.minY && hitPos.y <= box.maxY && hitPos.z >= box.minZ && hitPos.z <= box.maxZ
}
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 NetworkComponent) CableConnection.ON else CableConnection.OFF)
}
else -> state.with(prop, CableConnection.DISABLED)
}
world.setBlockState(pos, newState)
}
return true
}
return false
}
@Environment(EnvType.CLIENT)
override fun getRenderLayer(): BlockRenderLayer {
return BlockRenderLayer.TRANSLUCENT
}
override fun isOpaque(blockState_1: BlockState?): Boolean {
return false
}
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: EntityContext): VoxelShape {
return getShape(state)
}
}

View File

@ -15,12 +15,13 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView import net.minecraft.world.BlockView
import net.minecraft.world.World import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponent
import net.shadowfacts.phycon.block.BlockWithEntity import net.shadowfacts.phycon.block.BlockWithEntity
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class InterfaceBlock: BlockWithEntity<InterfaceBlockEntity>(Settings.of(Material.METAL)), AttributeProvider { class InterfaceBlock: BlockWithEntity<InterfaceBlockEntity>(Settings.of(Material.METAL)), NetworkComponent, AttributeProvider {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "network_interface") val ID = Identifier(PhysicalConnectivity.MODID, "network_interface")
val FACING = Properties.FACING val FACING = Properties.FACING

View File

@ -9,12 +9,13 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView import net.minecraft.world.BlockView
import net.minecraft.world.World import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponent
import net.shadowfacts.phycon.block.BlockWithEntity import net.shadowfacts.phycon.block.BlockWithEntity
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class SwitchBlock: BlockWithEntity<SwitchBlockEntity>(Settings.of(Material.METAL)), AttributeProvider { class SwitchBlock: BlockWithEntity<SwitchBlockEntity>(Settings.of(Material.METAL)), NetworkComponent, AttributeProvider {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "switch") val ID = Identifier(PhysicalConnectivity.MODID, "switch")
} }

View File

@ -12,12 +12,13 @@ import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView import net.minecraft.world.BlockView
import net.minecraft.world.World import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.api.NetworkComponent
import net.shadowfacts.phycon.block.BlockWithEntity import net.shadowfacts.phycon.block.BlockWithEntity
/** /**
* @author shadowfacts * @author shadowfacts
*/ */
class TerminalBlock: BlockWithEntity<TerminalBlockEntity>(Settings.of(Material.METAL)), AttributeProvider { class TerminalBlock: BlockWithEntity<TerminalBlockEntity>(Settings.of(Material.METAL)), NetworkComponent, AttributeProvider {
companion object { companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "terminal") val ID = Identifier(PhysicalConnectivity.MODID, "terminal")
} }

View File

@ -0,0 +1,14 @@
package net.shadowfacts.phycon.util
import net.minecraft.util.StringIdentifiable
/**
* @author shadowfacts
*/
enum class CableConnection: StringIdentifiable {
ON,
OFF,
DISABLED;
override fun asString() = name.toLowerCase()
}

View File

@ -0,0 +1,31 @@
{
"multipart": [
{
"apply": { "model": "phycon:block/cable_center" }
},
{
"when": { "down": "on" },
"apply": { "model": "phycon:block/cable_side" }
},
{
"when": { "up": "on" },
"apply": { "model": "phycon:block/cable_side", "x": 180 }
},
{
"when": { "north": "on" },
"apply": { "model": "phycon:block/cable_side", "x": 270 }
},
{
"when": { "south": "on" },
"apply": { "model": "phycon:block/cable_side", "x": 90 }
},
{
"when": { "west": "on" },
"apply": { "model": "phycon:block/cable_side", "x": 90, "y": 90 }
},
{
"when": { "east": "on" },
"apply": { "model": "phycon:block/cable_side", "x": 90, "y": 270 }
}
]
}

View File

@ -0,0 +1,17 @@
{
"parent": "block/block",
"elements": [
{
"from": [6, 6, 6],
"to": [10, 10, 10],
"faces": {
"down": { "texture": "phycon:block/cable_center" },
"up": { "texture": "phycon:block/cable_center" },
"north": { "texture": "phycon:block/cable_center" },
"south": { "texture": "phycon:block/cable_center" },
"west": { "texture": "phycon:block/cable_center" },
"east": { "texture": "phycon:block/cable_center" }
}
}
]
}

View File

@ -0,0 +1,17 @@
{
"parent": "block/block",
"elements": [
{
"from": [6, 0, 6],
"to": [10, 6, 10],
"faces": {
"down": { "texture": "phycon:block/cable_side" },
"up": { "texture": "phycon:block/cable_side" },
"north": { "texture": "phycon:block/cable_side" },
"south": { "texture": "phycon:block/cable_side" },
"west": { "texture": "phycon:block/cable_side" },
"east": { "texture": "phycon:block/cable_side" }
}
}
]
}