Add Redstone Emitter

This commit is contained in:
Shadowfacts 2021-03-02 22:20:25 -05:00
parent 4fe6391e69
commit 3cd4a7aa0d
Signed by: shadowfacts
GPG Key ID: 94A5AB95422746E5
22 changed files with 782 additions and 16 deletions

View File

@ -7,6 +7,7 @@ import net.minecraft.screen.ScreenHandler
import net.minecraft.sound.SoundEvents
import net.minecraft.text.Text
import net.shadowfacts.cacao.geometry.Point
import net.shadowfacts.cacao.util.KeyModifiers
import net.shadowfacts.cacao.util.MouseButton
import net.shadowfacts.cacao.util.RenderHelper
import net.shadowfacts.cacao.window.ScreenHandlerWindow
@ -57,15 +58,16 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
renderBackground(matrixStack)
}
override fun drawForeground(matrixStack: MatrixStack, mouseX: Int, mouseY: Int) {
// no-op
}
override fun render(matrixStack: MatrixStack, mouseX: Int, mouseY: Int, delta: Float) {
renderBackground(matrixStack)
val mouse = Point(mouseX, mouseY)
windows.forEachIndexed { index, it ->
it.draw(matrixStack, mouse, delta)
if (it is ScreenHandlerWindow) {
if (index == windows.size - 1) {
super.render(matrixStack, mouseX, mouseY, delta)
@ -74,7 +76,10 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
super.render(matrixStack, -1, -1, delta)
}
}
it.draw(matrixStack, mouse, delta)
}
drawMouseoverTooltip(matrixStack, mouseX, mouseY)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
@ -90,4 +95,20 @@ open class CacaoHandledScreen<Handler: ScreenHandler>(
}
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.keyPressed(keyCode, modifiersSet) }) {
return true
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
override fun charTyped(char: Char, modifiers: Int): Boolean {
val modifiersSet by lazy { KeyModifiers(modifiers) }
if (findResponder { it.charTyped(char, modifiersSet) }) {
return true
}
return super.charTyped(char, modifiers)
}
}

View File

@ -139,15 +139,15 @@ open class CacaoScreen(title: Text = LiteralText("CacaoScreen")): Screen(title),
return super.charTyped(char, modifiers)
}
private fun findResponder(fn: (Responder) -> Boolean): Boolean {
var responder = windows.lastOrNull()?.firstResponder
while (responder != null) {
if (fn(responder)) {
return true
}
responder = responder.nextResponder
}
return false
}
}
fun AbstractCacaoScreen.findResponder(fn: (Responder) -> Boolean): Boolean {
var responder = windows.lastOrNull()?.firstResponder
while (responder != null) {
if (fn(responder)) {
return true
}
responder = responder.nextResponder
}
return false
}

View File

@ -29,6 +29,7 @@ data class Color(val red: Int, val green: Int, val blue: Int, val alpha: Int = 2
val RED = Color(0xff0000)
val GREEN = Color(0x00ff00)
val BLUE = Color(0x0000ff)
val MAGENTA = Color(0xfc46e4)
val TEXT = Color(0x404040)
}

View File

@ -94,7 +94,7 @@ class Label(
for (i in 0 until lines.size) {
val x = when (textAlignment) {
TextAlignment.LEFT -> 0.0
TextAlignment.CENTER -> (bounds.width + textRenderer.getWidth(lines[i])) / 2
TextAlignment.CENTER -> (bounds.width - textRenderer.getWidth(lines[i])) / 2
TextAlignment.RIGHT -> bounds.width - textRenderer.getWidth(lines[i])
}
val y = i * textRenderer.fontHeight

View File

@ -62,12 +62,22 @@ abstract class AbstractTextField<Impl: AbstractTextField<Impl>>(
minecraftWidget.setMaxLength(value)
}
/**
* Whether the Minecraft builtin black background and border are drawn. Defaults to true.
*/
var drawBackground = true
set(value) {
field = value
minecraftWidget.setHasBorder(value)
}
private lateinit var originInWindow: Point
private var minecraftWidget = ProxyWidget()
init {
minecraftWidget.text = initialText
minecraftWidget.setTextPredicate { this.validate(it) }
minecraftWidget.setHasBorder(drawBackground)
}
/**

View File

@ -0,0 +1,42 @@
package net.shadowfacts.cacao.view.textfield
/**
* @author shadowfacts
*/
class NumberField(
initialValue: Int,
handler: ((NumberField) -> Unit)? = null,
): AbstractTextField<NumberField>(initialValue.toString()) {
var number: Int?
get() {
return if (text.isEmpty()) {
null
} else {
try {
Integer.parseInt(text)
} catch (e: NumberFormatException) {
null
}
}
}
set(value) {
text = value?.toString() ?: ""
}
var validator: ((Int) -> Boolean)? = null
init {
this.handler = handler
}
override fun validate(proposedText: String): Boolean {
return proposedText.isEmpty() || try {
val value = Integer.parseInt(proposedText)
validator?.invoke(value) ?: true
} catch (e: NumberFormatException) {
false
}
}
}

View File

@ -29,6 +29,7 @@ object PhysicalConnectivity: ModInitializer {
registerGlobalReceiver(C2SConfigureActivationMode)
registerGlobalReceiver(C2SConfigureRedstoneController)
registerGlobalReceiver(C2SConfigureInserterAmount)
registerGlobalReceiver(C2SConfigureRedstoneEmitterAmount)
}
private fun registerGlobalReceiver(receiver: ServerReceiver) {

View File

@ -4,6 +4,7 @@ import net.fabricmc.api.ClientModInitializer
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry
import net.shadowfacts.phycon.block.inserter.InserterScreen
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreen
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.block.terminal.TerminalScreen
import net.shadowfacts.phycon.networking.ClientReceiver
@ -17,6 +18,7 @@ object PhysicalConnectivityClient: ClientModInitializer {
override fun onInitializeClient() {
ScreenRegistry.register(PhyScreens.TERMINAL, ::TerminalScreen)
ScreenRegistry.register(PhyScreens.INSERTER, ::InserterScreen)
ScreenRegistry.register(PhyScreens.REDSTONE_EMITTER, ::RedstoneEmitterScreen)
registerGlobalReceiver(S2CTerminalUpdateDisplayedItems)
}

View File

@ -0,0 +1,88 @@
package net.shadowfacts.phycon.block.redstone_emitter
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory
import net.minecraft.block.BlockState
import net.minecraft.block.Material
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
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.world.BlockView
import net.minecraft.world.World
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.FaceDeviceBlock
/**
* @author shadowfacts
*/
class RedstoneEmitterBlock: FaceDeviceBlock<RedstoneEmitterBlockEntity>(
Settings.of(Material.METAL)
.strength(1.5f)
.sounds(BlockSoundGroup.METAL)
) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "redstone_emitter")
}
// todo: don't just copy the redstone controller
override val faceThickness = 3.0
override val faceShapes = mapOf(
Direction.DOWN to createCuboidShape(0.0, 0.0, 0.0, 16.0, 3.0, 16.0),
Direction.UP to createCuboidShape(0.0, 13.0, 0.0, 16.0, 16.0, 16.0),
Direction.NORTH to createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 3.0),
Direction.SOUTH to createCuboidShape(0.0, 0.0, 13.0, 16.0, 16.0, 16.0),
Direction.WEST to createCuboidShape(0.0, 0.0, 0.0, 3.0, 16.0, 16.0),
Direction.EAST to createCuboidShape(13.0, 0.0, 0.0, 16.0, 16.0, 16.0)
)
override fun createBlockEntity(world: BlockView) = RedstoneEmitterBlockEntity()
override fun emitsRedstonePower(state: BlockState): Boolean {
return true
}
override fun getStrongRedstonePower(state: BlockState, world: BlockView, pos: BlockPos, receivingSide: Direction): Int {
return if (receivingSide.opposite == state[FACING]) {
getBlockEntity(world, pos)!!.cachedEmittedPower
} else {
0
}
}
override fun getWeakRedstonePower(state: BlockState, world: BlockView, pos: BlockPos, receivingSide: Direction): Int {
return getStrongRedstonePower(state, world, pos, receivingSide)
}
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, result: BlockHitResult): ActionResult {
if (!world.isClient) {
val be = getBlockEntity(world, pos)!!
be.sync()
val factory = object: ExtendedScreenHandlerFactory {
override fun createMenu(syncId: Int, playerInv: PlayerInventory, player: PlayerEntity): ScreenHandler {
return RedstoneEmitterScreenHandler(syncId, playerInv, be)
}
override fun getDisplayName() = this@RedstoneEmitterBlock.name
override fun writeScreenOpeningData(player: ServerPlayerEntity, buf: PacketByteBuf) {
buf.writeBlockPos(be.pos)
}
}
player.openHandledScreen(factory)
}
return ActionResult.SUCCESS
}
}

View File

@ -0,0 +1,97 @@
package net.shadowfacts.phycon.block.redstone_emitter
import alexiil.mc.lib.attributes.item.GroupedItemInvView
import net.minecraft.item.ItemStack
import net.minecraft.nbt.CompoundTag
import net.shadowfacts.phycon.api.packet.Packet
import net.shadowfacts.phycon.api.util.IPAddress
import net.shadowfacts.phycon.block.DeviceBlockEntity
import net.shadowfacts.phycon.block.FaceDeviceBlock
import net.shadowfacts.phycon.init.PhyBlockEntities
import net.shadowfacts.phycon.packet.DeviceRemovedPacket
import net.shadowfacts.phycon.packet.ReadInventoryPacket
import net.shadowfacts.phycon.packet.RequestInventoryPacket
import kotlin.math.round
/**
* @author shadowfacts
*/
class RedstoneEmitterBlockEntity: DeviceBlockEntity(PhyBlockEntities.REDSTONE_EMITTER) {
private val inventoryCache = mutableMapOf<IPAddress, GroupedItemInvView>()
var cachedEmittedPower: Int = 0
private set
var stackToMonitor: ItemStack = ItemStack.EMPTY
var maxAmount = 64
override fun handle(packet: Packet) {
when (packet) {
is ReadInventoryPacket -> handleReadInventory(packet)
is DeviceRemovedPacket -> handleDeviceRemoved(packet)
}
}
private fun handleReadInventory(packet: ReadInventoryPacket) {
inventoryCache[packet.source] = packet.inventory
recalculateRedstone()
}
private fun handleDeviceRemoved(packet: DeviceRemovedPacket) {
inventoryCache.remove(packet.source)
recalculateRedstone()
}
override fun tick() {
super.tick()
if (!world!!.isClient && counter % 20 == 0L) {
if (counter % 80 == 0L) {
updateInventories()
} else if (counter % 20 == 0L) {
recalculateRedstone()
}
}
}
private fun updateInventories() {
sendPacket(RequestInventoryPacket(ipAddress))
}
private fun recalculateRedstone() {
if (stackToMonitor.isEmpty) {
cachedEmittedPower = 0
updateWorld()
return
}
val networkAmount = inventoryCache.values.fold(0) { acc, inv ->
acc + inv.getAmount(stackToMonitor)
}
cachedEmittedPower =
if (networkAmount == 0) {
0
} else {
1 + round(networkAmount / maxAmount.toDouble() * 14).toInt()
}
updateWorld()
}
private fun updateWorld() {
world!!.updateNeighborsAlways(pos, cachedState.block)
world!!.updateNeighborsAlways(pos.offset(cachedState[FaceDeviceBlock.FACING]), cachedState.block)
}
override fun toCommonTag(tag: CompoundTag) {
super.toCommonTag(tag)
tag.putInt("CachedEmittedPower", cachedEmittedPower)
tag.put("StackToMonitor", stackToMonitor.toTag(CompoundTag()))
tag.putInt("MaxAmount", maxAmount)
}
override fun fromCommonTag(tag: CompoundTag) {
super.fromCommonTag(tag)
cachedEmittedPower = tag.getInt("CachedEmittedPower")
stackToMonitor = ItemStack.fromTag(tag.getCompound("StackToMonitor"))
maxAmount = tag.getInt("MaxAmount")
}
}

View File

@ -0,0 +1,155 @@
package net.shadowfacts.phycon.block.redstone_emitter
import com.mojang.blaze3d.systems.RenderSystem
import net.minecraft.client.MinecraftClient
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.text.Text
import net.minecraft.text.TranslatableText
import net.minecraft.util.Identifier
import net.shadowfacts.cacao.CacaoHandledScreen
import net.shadowfacts.cacao.geometry.Axis
import net.shadowfacts.cacao.util.Color
import net.shadowfacts.cacao.view.Label
import net.shadowfacts.cacao.view.StackView
import net.shadowfacts.cacao.view.View
import net.shadowfacts.cacao.view.textfield.NumberField
import net.shadowfacts.cacao.window.ScreenHandlerWindow
import net.shadowfacts.kiwidsl.dsl
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.networking.C2SConfigureRedstoneEmitterAmount
import no.birkett.kiwi.Variable
import kotlin.math.ceil
/**
* @author shadowfacts
*/
class RedstoneEmitterScreen(
handler: RedstoneEmitterScreenHandler,
playerInv: PlayerInventory,
title: Text
): CacaoHandledScreen<RedstoneEmitterScreenHandler>(
handler,
playerInv,
title
) {
companion object {
val BACKGROUND = Identifier(PhysicalConnectivity.MODID, "textures/gui/redstone_emitter.png")
}
init {
backgroundWidth = 176
backgroundHeight = 166
addWindow(ScreenHandlerWindow(handler, ViewController(handler.emitter)))
}
override fun drawBackground(matrixStack: MatrixStack, delta: Float, mouseX: Int, mouseY: Int) {
super.drawBackground(matrixStack, delta, mouseX, mouseY)
RenderSystem.color4f(1f, 1f, 1f, 1f)
client!!.textureManager.bindTexture(BACKGROUND)
val x = (width - backgroundWidth) / 2
val y = (height - backgroundHeight) / 2
drawTexture(matrixStack, x, y, 0, 0, backgroundWidth, backgroundHeight)
}
class ViewController(
private val emitter: RedstoneEmitterBlockEntity,
): net.shadowfacts.cacao.viewcontroller.ViewController() {
lateinit var halfLabel: Label
lateinit var fullLabel: Label
override fun viewDidLoad() {
super.viewDidLoad()
val title = Label(PhyBlocks.REDSTONE_EMITTER.name)
title.textColor = Color.TEXT
view.addSubview(title)
val inv = Label(MinecraftClient.getInstance().player!!.inventory.displayName)
inv.textColor = Color.TEXT
view.addSubview(inv)
val field = NumberField(emitter.maxAmount) {
if (it.number != null) {
emitter.maxAmount = it.number!!
MinecraftClient.getInstance().player!!.networkHandler.sendPacket(C2SConfigureRedstoneEmitterAmount(emitter))
}
updateLabelTexts()
}
field.validator = { it >= 0 }
field.drawBackground = false
view.addSubview(field)
val hStack = StackView(Axis.HORIZONTAL, StackView.Distribution.FILL, spacing = 2.0)
view.addSubview(hStack)
val zeroStack = hStack.addArrangedSubview(StackView(Axis.VERTICAL))
zeroStack.addArrangedSubview(Label(TranslatableText("gui.phycon.emitter.count", 0), textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.TEXT
}
zeroStack.addArrangedSubview(View())
zeroStack.addArrangedSubview(Label("0", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.RED
}
val halfStack = hStack.addArrangedSubview(StackView(Axis.VERTICAL))
halfLabel = halfStack.addArrangedSubview(Label("half", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.TEXT
}
halfStack.addArrangedSubview(View())
halfStack.addArrangedSubview(Label("8", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.RED
}
val fullStack = hStack.addArrangedSubview(StackView(Axis.VERTICAL))
fullLabel = fullStack.addArrangedSubview(Label("full", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.TEXT
}
fullStack.addArrangedSubview(View())
fullStack.addArrangedSubview(Label("15", textAlignment = Label.TextAlignment.CENTER)).apply {
textColor = Color.RED
}
updateLabelTexts()
view.solver.dsl {
val minX = Variable("minX")
val minY = Variable("minY")
minX equalTo ((view.widthAnchor - 176) / 2)
minY equalTo ((view.heightAnchor - 166) / 2)
title.leftAnchor equalTo (minX + 8)
title.topAnchor equalTo (minY + 6)
inv.leftAnchor equalTo (minX + 8)
inv.topAnchor equalTo (minY + 72)
// offset by 1 on each edge to account for MC border
field.widthAnchor equalTo 82
field.heightAnchor equalTo 11
field.leftAnchor equalTo (minX + 56)
field.topAnchor equalTo (minY + 23)
hStack.centerXAnchor equalTo view.centerXAnchor
hStack.widthAnchor equalTo (176 - 4)
hStack.topAnchor equalTo (field.bottomAnchor + 8)
hStack.bottomAnchor equalTo inv.topAnchor
zeroStack.widthAnchor equalTo halfStack.widthAnchor
halfStack.widthAnchor equalTo fullStack.widthAnchor
}
}
private fun updateLabelTexts() {
halfLabel.text = TranslatableText("gui.phycon.emitter.count", ceil(emitter.maxAmount / 2.0).toInt())
fullLabel.text = TranslatableText("gui.phycon.emitter.count", emitter.maxAmount)
window!!.layout()
}
}
}

View File

@ -0,0 +1,76 @@
package net.shadowfacts.phycon.block.redstone_emitter
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
import net.minecraft.network.PacketByteBuf
import net.minecraft.screen.ScreenHandler
import net.minecraft.screen.slot.Slot
import net.minecraft.screen.slot.SlotActionType
import net.minecraft.util.Identifier
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.init.PhyBlocks
import net.shadowfacts.phycon.init.PhyScreens
import net.shadowfacts.phycon.util.GhostSlot
import net.shadowfacts.phycon.util.copyWithCount
/**
* @author shadowfacts
*/
class RedstoneEmitterScreenHandler(
syncId: Int,
playerInv: PlayerInventory,
val emitter: RedstoneEmitterBlockEntity,
): ScreenHandler(PhyScreens.REDSTONE_EMITTER, syncId) {
companion object {
val ID = Identifier(PhysicalConnectivity.MODID, "redstone_emitter")
}
constructor(syncId: Int, playerInv: PlayerInventory, buf: PacketByteBuf):
this(
syncId,
playerInv,
PhyBlocks.REDSTONE_EMITTER.getBlockEntity(playerInv.player.world, buf.readBlockPos())!!
)
init {
// fake slot
addSlot(GhostSlot(emitter::stackToMonitor, 31, 20))
// player inv
for (y in 0 until 3) {
for (x in 0 until 9) {
addSlot(Slot(playerInv, x + y * 9 + 9, 8 + x * 18, 84 + y * 18))
}
}
// hotbar
for (x in 0 until 9) {
addSlot(Slot(playerInv, x, 8 + x * 18, 142))
}
}
override fun canUse(player: PlayerEntity): Boolean {
return true
}
override fun onSlotClick(slotId: Int, clickData: Int, slotActionType: SlotActionType, player: PlayerEntity): ItemStack {
// fake slot
if (slotId == 0) {
if (player.inventory.cursorStack.isEmpty) {
emitter.stackToMonitor = ItemStack.EMPTY
} else {
emitter.stackToMonitor = player.inventory.cursorStack.copyWithCount(1)
}
}
return super.onSlotClick(slotId, clickData, slotActionType, player)
}
override fun transferSlot(player: PlayerEntity, slotId: Int): ItemStack {
val slot = slots[slotId]
emitter.stackToMonitor = slot.stack.copyWithCount(1)
return ItemStack.EMPTY
}
}

View File

@ -17,6 +17,8 @@ import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlockEntity
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlockEntity
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity
import net.shadowfacts.phycon.block.terminal.TerminalBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlockEntity
@ -32,6 +34,7 @@ object PhyBlockEntities {
val INSERTER = create(::InserterBlockEntity, PhyBlocks.INSERTER)
val MINER = create(::MinerBlockEntity, PhyBlocks.MINER)
val REDSTONE_CONTROLLER = create(::RedstoneControllerBlockEntity, PhyBlocks.REDSTONE_CONTROLLER)
val REDSTONE_EMITTER = create(::RedstoneEmitterBlockEntity, PhyBlocks.REDSTONE_EMITTER)
private fun <T: BlockEntity> create(builder: () -> T, block: Block): BlockEntityType<T> {
return BlockEntityType.Builder.create(builder, block).build(null)
@ -45,6 +48,7 @@ object PhyBlockEntities {
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
}
private fun register(id: Identifier, type: BlockEntityType<*>) {

View File

@ -10,6 +10,7 @@ import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlock
/**
@ -25,6 +26,7 @@ object PhyBlocks {
val INSERTER = InserterBlock()
val MINER = MinerBlock()
val REDSTONE_CONTROLLER = RedstoneControllerBlock()
val REDSTONE_EMITTER = RedstoneEmitterBlock()
fun init() {
register(InterfaceBlock.ID, INTERFACE)
@ -35,6 +37,7 @@ object PhyBlocks {
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
}
private fun register(id: Identifier, block: Block) {

View File

@ -13,6 +13,7 @@ import net.shadowfacts.phycon.block.miner.MinerBlock
import net.shadowfacts.phycon.block.netinterface.InterfaceBlock
import net.shadowfacts.phycon.block.netswitch.SwitchBlock
import net.shadowfacts.phycon.block.redstone_controller.RedstoneControllerBlock
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlock
import net.shadowfacts.phycon.block.terminal.TerminalBlock
/**
@ -28,6 +29,7 @@ object PhyItems {
val INSERTER = BlockItem(PhyBlocks.INSERTER, Item.Settings())
val MINER = BlockItem(PhyBlocks.MINER, Item.Settings())
val REDSTONE_CONTROLLER = BlockItem(PhyBlocks.REDSTONE_CONTROLLER, Item.Settings())
val REDSTONE_EMITTER = BlockItem(PhyBlocks.REDSTONE_EMITTER, Item.Settings())
val SCREWDRIVER = ScrewdriverItem()
val CONSOLE = ConsoleItem()
@ -41,6 +43,7 @@ object PhyItems {
register(InserterBlock.ID, INSERTER)
register(MinerBlock.ID, MINER)
register(RedstoneControllerBlock.ID, REDSTONE_CONTROLLER)
register(RedstoneEmitterBlock.ID, REDSTONE_EMITTER)
register(ScrewdriverItem.ID, SCREWDRIVER)
register(ConsoleItem.ID, CONSOLE)

View File

@ -3,6 +3,7 @@ package net.shadowfacts.phycon.init
import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry
import net.minecraft.screen.ScreenHandlerType
import net.shadowfacts.phycon.block.inserter.InserterScreenHandler
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterScreenHandler
import net.shadowfacts.phycon.block.terminal.TerminalScreenHandler
object PhyScreens {
@ -11,10 +12,13 @@ object PhyScreens {
private set
lateinit var INSERTER: ScreenHandlerType<InserterScreenHandler>
private set
lateinit var REDSTONE_EMITTER: ScreenHandlerType<RedstoneEmitterScreenHandler>
private set
fun init() {
TERMINAL = ScreenHandlerRegistry.registerExtended(TerminalScreenHandler.ID, ::TerminalScreenHandler)
INSERTER = ScreenHandlerRegistry.registerExtended(InserterScreenHandler.ID, ::InserterScreenHandler)
REDSTONE_EMITTER = ScreenHandlerRegistry.registerExtended(RedstoneEmitterScreenHandler.ID, ::RedstoneEmitterScreenHandler)
}
}

View File

@ -0,0 +1,46 @@
package net.shadowfacts.phycon.networking
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
import net.fabricmc.fabric.api.networking.v1.PacketSender
import net.minecraft.network.Packet
import net.minecraft.network.PacketByteBuf
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayNetworkHandler
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.registry.Registry
import net.minecraft.util.registry.RegistryKey
import net.shadowfacts.phycon.PhysicalConnectivity
import net.shadowfacts.phycon.block.inserter.InserterBlockEntity
import net.shadowfacts.phycon.block.redstone_emitter.RedstoneEmitterBlockEntity
/**
* @author shadowfacts
*/
object C2SConfigureRedstoneEmitterAmount: ServerReceiver {
override val CHANNEL = Identifier(PhysicalConnectivity.MODID, "configure_redstone_emitter_amount")
operator fun invoke(be: RedstoneEmitterBlockEntity): Packet<*> {
val buf = PacketByteBufs.create()
buf.writeIdentifier(be.world!!.registryKey.value)
buf.writeBlockPos(be.pos)
buf.writeVarInt(be.maxAmount)
return createPacket(buf)
}
override fun receive(server: MinecraftServer, player: ServerPlayerEntity, handler: ServerPlayNetworkHandler, buf: PacketByteBuf, responseSender: PacketSender) {
val dimID = buf.readIdentifier()
val pos = buf.readBlockPos()
val amount = buf.readVarInt()
server.execute {
val key = RegistryKey.of(Registry.DIMENSION, dimID)
val world = server.getWorld(key) ?: return@execute
val be = world.getBlockEntity(pos) as? RedstoneEmitterBlockEntity ?: return@execute
be.maxAmount = amount
be.markDirty()
}
}
}

View File

@ -0,0 +1,159 @@
{
"multipart": [
{
"apply": { "model": "phycon:block/cable_center" }
},
{
"when": { "facing": "down" },
"apply": { "model": "phycon:block/redstone_emitter_side" }
},
{
"when": { "facing": "up" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 180 }
},
{
"when": { "facing": "north" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 270 }
},
{
"when": { "facing": "south" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 90 }
},
{
"when": { "facing": "west" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 90, "y": 90 }
},
{
"when": { "facing": "east" },
"apply": { "model": "phycon:block/redstone_emitter_side", "x": 90, "y": 270 }
},
{
"when": { "cable_connection": "up", "facing": "down" },
"apply": { "model": "phycon:block/interface_cable_straight" }
},
{
"when": { "cable_connection": "down", "facing": "up" },
"apply": { "model": "phycon:block/interface_cable_straight", "x": 180 }
},
{
"when": { "cable_connection": "north", "facing": "south" },
"apply": { "model": "phycon:block/interface_cable_straight", "x": 90 }
},
{
"when": { "cable_connection": "south", "facing": "north" },
"apply": { "model": "phycon:block/interface_cable_straight", "x": 270 }
},
{
"when": { "cable_connection": "west", "facing": "east" },
"apply": { "model": "phycon:block/interface_cable_straight", "x": 90, "y": 270 }
},
{
"when": { "cable_connection": "east", "facing": "west" },
"apply": { "model": "phycon:block/interface_cable_straight", "x": 90, "y": 90 }
},
{
"when": {"cable_connection": "north", "facing": "down"},
"apply": { "model": "phycon:block/interface_cable_corner" }
},
{
"when": {"cable_connection": "east", "facing": "down"},
"apply": { "model": "phycon:block/interface_cable_corner", "y": 90 }
},
{
"when": {"cable_connection": "south", "facing": "down"},
"apply": { "model": "phycon:block/interface_cable_corner", "y": 180 }
},
{
"when": {"cable_connection": "west", "facing": "down"},
"apply": { "model": "phycon:block/interface_cable_corner", "y": 270 }
},
{
"when": {"cable_connection": "north", "facing": "up"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180, "y": 180 }
},
{
"when": {"cable_connection": "east", "facing": "up"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180, "y": 270 }
},
{
"when": {"cable_connection": "south", "facing": "up"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180 }
},
{
"when": {"cable_connection": "west", "facing": "up"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 180, "y": 90 }
},
{
"when": {"cable_connection": "down", "facing": "north"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90, "y": 180 }
},
{
"when": {"cable_connection": "up", "facing": "north"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270 }
},
{
"when": {"cable_connection": "west", "facing": "north"},
"apply": { "model": "phycon:block/interface_cable_corner_2" }
},
{
"when": {"cable_connection": "east", "facing": "north"},
"apply": { "model": "phycon:block/interface_cable_corner_2", "x": 180, "y": 180 }
},
{
"when": {"cable_connection": "down", "facing": "south"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90 }
},
{
"when": {"cable_connection": "up", "facing": "south"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270, "y": 180 }
},
{
"when": {"cable_connection": "west", "facing": "south"},
"apply": { "model": "phycon:block/interface_cable_corner_3" }
},
{
"when": {"cable_connection": "east", "facing": "south"},
"apply": { "model": "phycon:block/interface_cable_corner_3", "x": 180, "y": 180 }
},
{
"when": {"cable_connection": "down", "facing": "west"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90, "y": 90 }
},
{
"when": {"cable_connection": "up", "facing": "west"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270, "y": 270 }
},
{
"when": {"cable_connection": "north", "facing": "west"},
"apply": { "model": "phycon:block/interface_cable_corner_3", "y": 90 }
},
{
"when": {"cable_connection": "south", "facing": "west"},
"apply": { "model": "phycon:block/interface_cable_corner_3", "x": 180, "y": 270 }
},
{
"when": {"cable_connection": "down", "facing": "east"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 90, "y": 270 }
},
{
"when": {"cable_connection": "up", "facing": "east"},
"apply": { "model": "phycon:block/interface_cable_corner", "x": 270, "y": 90 }
},
{
"when": {"cable_connection": "north", "facing": "east"},
"apply": { "model": "phycon:block/interface_cable_corner_2", "y": 90 }
},
{
"when": {"cable_connection": "south", "facing": "east"},
"apply": { "model": "phycon:block/interface_cable_corner_2", "x": 180, "y": 270 }
}
]
}

View File

@ -7,6 +7,7 @@
"block.phycon.inserter": "Inventory Inserter",
"block.phycon.miner": "Block Miner",
"block.phycon.redstone_controller": "Redstone Controller",
"block.phycon.redstone_emitter": "Redstone Emitter",
"item.phycon.screwdriver": "Screwdriver",
"item.phycon.console": "Console",
@ -25,5 +26,6 @@
"gui.phycon.redstone_mode.rising_edge": "Rising Edge",
"gui.phycon.redstone_mode.falling_edge": "Falling Edge",
"gui.phycon.activation_mode.automatic": "Automatic",
"gui.phycon.activation_mode.managed": "Managed"
"gui.phycon.activation_mode.managed": "Managed",
"gui.phycon.emitter.count": "%d Item(s)"
}

View File

@ -0,0 +1,33 @@
{
"parent": "block/block",
"textures": {
"cable": "phycon:block/cable_straight",
"front": "phycon:block/redstone_emitter_front",
"back": "phycon:block/redstone_emitter_back",
"side": "phycon:block/redstone_emitter_back"
},
"elements": [
{
"from": [0, 0, 0],
"to": [16, 3, 16],
"faces": {
"down": { "texture": "#front" },
"up": { "texture": "#back" },
"north": { "texture": "#side" },
"south": { "texture": "#side" },
"west": { "texture": "#side" },
"east": { "texture": "#side" }
}
},
{
"from": [6, 3, 6],
"to": [10, 6, 10],
"faces": {
"north": { "texture": "#cable" },
"south": { "texture": "#cable" },
"west": { "texture": "#cable" },
"east": { "texture": "#cable" }
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,19 @@
{
"type": "minecraft:block",
"pools": [
{
"rolls": 1,
"entries": [
{
"type": "minecraft:item",
"name": "phycon:redstone_emitter"
}
],
"conditions": [
{
"condition": "minecraft:survives_explosion"
}
]
}
]
}