From 96a325c03b2d47a21c6a7d7c5e4b17b12c138368 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9leste=20Wouters?= Date: Fri, 23 Aug 2024 18:54:21 +0200 Subject: [PATCH] Add ComputerCraft integration to Nixie Tubes - Makes computer-controlled Nixie Tubes unable to be changed by external factors (but can still be used as a Display Link source) - Add CC setText(text[, colour]) function - Add CC setTextColour(colour) function - Add CC setSignal(first, second) function taking 2 tables describing the appearance of the first and second tube as custom train signals --- .../com/simibubi/create/AllPartialModels.java | 4 + .../implementation/ComputerBehaviour.java | 4 + .../peripherals/NixieTubePeripheral.java | 169 ++++++++++++++++++ .../peripherals/SyncedPeripheral.java | 32 +++- .../target/NixieTubeDisplayTarget.java | 7 +- .../redstone/nixieTube/NixieTubeBlock.java | 154 +++++++++++++--- .../nixieTube/NixieTubeBlockEntity.java | 104 ++++++++++- .../redstone/nixieTube/NixieTubeRenderer.java | 135 ++++++++++---- .../track_signal/computer_white_cube.json | 20 +++ .../track_signal/computer_white_glow.json | 20 +++ .../track_signal/computer_white_tube.json | 24 +++ .../computer_white_tube_base.json | 24 +++ .../create/textures/block/signal_glow_3.png | Bin 0 -> 242 bytes 13 files changed, 621 insertions(+), 76 deletions(-) create mode 100644 src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/NixieTubePeripheral.java create mode 100644 src/main/resources/assets/create/models/block/track_signal/computer_white_cube.json create mode 100644 src/main/resources/assets/create/models/block/track_signal/computer_white_glow.json create mode 100644 src/main/resources/assets/create/models/block/track_signal/computer_white_tube.json create mode 100644 src/main/resources/assets/create/models/block/track_signal/computer_white_tube_base.json create mode 100644 src/main/resources/assets/create/textures/block/signal_glow_3.png diff --git a/src/main/java/com/simibubi/create/AllPartialModels.java b/src/main/java/com/simibubi/create/AllPartialModels.java index 8fde35136..8c3439038 100644 --- a/src/main/java/com/simibubi/create/AllPartialModels.java +++ b/src/main/java/com/simibubi/create/AllPartialModels.java @@ -156,6 +156,10 @@ public class AllPartialModels { SIGNAL_RED_CUBE = block("track_signal/red_cube"), SIGNAL_RED_GLOW = block("track_signal/red_glow"), SIGNAL_RED = block("track_signal/red_tube"), SIGNAL_YELLOW_CUBE = block("track_signal/yellow_cube"), SIGNAL_YELLOW_GLOW = block("track_signal/yellow_glow"), SIGNAL_YELLOW = block("track_signal/yellow_tube"), + SIGNAL_COMPUTER_WHITE_CUBE = block("track_signal/computer_white_cube"), + SIGNAL_COMPUTER_WHITE_GLOW = block("track_signal/computer_white_glow"), + SIGNAL_COMPUTER_WHITE = block("track_signal/computer_white_tube"), + SIGNAL_COMPUTER_WHITE_BASE = block("track_signal/computer_white_tube_base"), BLAZE_INERT = block("blaze_burner/blaze/inert"), BLAZE_SUPER_ACTIVE = block("blaze_burner/blaze/super_active"), BLAZE_GOGGLES = block("blaze_burner/goggles"), BLAZE_GOGGLES_SMALL = block("blaze_burner/goggles_small"), diff --git a/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java b/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java index e33cc4a9d..a678f882e 100644 --- a/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java +++ b/src/main/java/com/simibubi/create/compat/computercraft/implementation/ComputerBehaviour.java @@ -2,6 +2,7 @@ package com.simibubi.create.compat.computercraft.implementation; import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour; import com.simibubi.create.compat.computercraft.implementation.peripherals.DisplayLinkPeripheral; +import com.simibubi.create.compat.computercraft.implementation.peripherals.NixieTubePeripheral; import com.simibubi.create.compat.computercraft.implementation.peripherals.SequencedGearshiftPeripheral; import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedControllerPeripheral; import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedGaugePeripheral; @@ -12,6 +13,7 @@ import com.simibubi.create.content.kinetics.gauge.StressGaugeBlockEntity; import com.simibubi.create.content.kinetics.speedController.SpeedControllerBlockEntity; import com.simibubi.create.content.kinetics.transmission.sequencer.SequencedGearshiftBlockEntity; import com.simibubi.create.content.redstone.displayLink.DisplayLinkBlockEntity; +import com.simibubi.create.content.redstone.nixieTube.NixieTubeBlockEntity; import com.simibubi.create.content.trains.station.StationBlockEntity; import com.simibubi.create.foundation.blockEntity.SmartBlockEntity; @@ -40,6 +42,8 @@ public class ComputerBehaviour extends AbstractComputerBehaviour { return () -> new SpeedControllerPeripheral(scbe, scbe.targetSpeed); if (be instanceof DisplayLinkBlockEntity dlbe) return () -> new DisplayLinkPeripheral(dlbe); + if (be instanceof NixieTubeBlockEntity ntbe) + return () -> new NixieTubePeripheral(ntbe); if (be instanceof SequencedGearshiftBlockEntity sgbe) return () -> new SequencedGearshiftPeripheral(sgbe); if (be instanceof SpeedGaugeBlockEntity sgbe) diff --git a/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/NixieTubePeripheral.java b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/NixieTubePeripheral.java new file mode 100644 index 000000000..aa52ad9f5 --- /dev/null +++ b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/NixieTubePeripheral.java @@ -0,0 +1,169 @@ +package com.simibubi.create.compat.computercraft.implementation.peripherals; + +import java.util.Map; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + + +import com.simibubi.create.content.redstone.nixieTube.NixieTubeBlock; +import com.simibubi.create.content.redstone.nixieTube.NixieTubeBlockEntity; +import com.simibubi.create.foundation.utility.Components; + +import dan200.computercraft.api.lua.IArguments; +import dan200.computercraft.api.lua.LuaException; +import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.lua.LuaValues; +import net.minecraft.network.chat.Component; +import net.minecraft.world.item.DyeColor; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +public class NixieTubePeripheral extends SyncedPeripheral { + + private static final String EMPTY_COMPONENT_JSON = Component.Serializer.toJson(Components.literal("")); + + public NixieTubePeripheral(NixieTubeBlockEntity blockEntity) { + super(blockEntity); + } + + @Override + protected void onFirstAttach() { + // When first attaching to a computer, clear out the entire nixie tube row. + super.onFirstAttach(); + Level world = blockEntity.getLevel(); + if (world == null) + return; + NixieTubeBlock.walkNixies(world, blockEntity.getBlockPos(), true, + (currentPos, rowPosition) -> { + if (world.getBlockEntity(currentPos) instanceof NixieTubeBlockEntity ntbe) + ntbe.displayCustomText(EMPTY_COMPONENT_JSON, rowPosition); + }); + } + + @Override + protected void onLastDetach() { + // When detaching from the last computer, reset the entire nixie tube row back to redstone display, + // except if it's still being controlled from some other tube. onLastDetach runs after the + // hasAttachedComputer flag is reset, so we can use walkNixies()'s computer control rejection for that. + super.onLastDetach(); + Level world = blockEntity.getLevel(); + if (world == null) + return; + // Check if the nixie tube block is still there; if it isn't then the nixie was removed/destroyed + // and the row reset is handled in NixieTubeBlock::remove. + BlockState state = world.getBlockState(blockEntity.getBlockPos()); + if (!(state.getBlock() instanceof NixieTubeBlock)) + return; + NixieTubeBlock.walkNixies(world, blockEntity.getBlockPos(), false, + (currentPos, rowPosition) -> { + if (world.getBlockEntity(currentPos) instanceof NixieTubeBlockEntity ntbe) { + NixieTubeBlock.updateDisplayedRedstoneValue(ntbe, true); + } + }); + } + + @LuaFunction(mainThread = true) + public void setText(IArguments arguments) throws LuaException { + Level world = blockEntity.getLevel(); + if (world == null) + return; + blockEntity.computerSignal = null; + + String tagElement = Component.Serializer.toJson(Components.literal(arguments.getString(0))); + + @Nullable String colour = arguments.optString(1, null); + BlockState state = null; + DyeColor dye = null; + if (colour != null) { + state = blockEntity.getLevel().getBlockState(blockEntity.getBlockPos()); + dye = LuaValues.checkEnum(1, DyeColor.class, colour.equals("grey") ? "gray" : colour); + } + + changeTextNixie(tagElement, state, dye); + } + + @LuaFunction(mainThread = true) + public void setTextColour(String colour) throws LuaException { + Level world = blockEntity.getLevel(); + if (world == null) + return; + BlockState state = blockEntity.getLevel().getBlockState(blockEntity.getBlockPos()); + DyeColor dye = LuaValues.checkEnum(1, DyeColor.class, colour.equals("grey") ? "gray" : colour); + changeTextNixie(null, state, dye); + } + + @LuaFunction(mainThread = true) + public void setTextColor(String color) throws LuaException { + setTextColour(color); + } + + private void changeTextNixie(@Nullable String tagElement, @Nullable BlockState state, @Nullable DyeColor dye) { + Level world = blockEntity.getLevel(); + if (world == null) + return; + NixieTubeBlock.walkNixies(world, blockEntity.getBlockPos(), true, (currentPos, rowPosition) -> { + if (tagElement != null) + ((NixieTubeBlock) blockEntity.getBlockState().getBlock()).withBlockEntityDo( + world, currentPos, be -> be.displayCustomText(tagElement, rowPosition)); + if (state != null && dye != null) + world.setBlockAndUpdate(currentPos, NixieTubeBlock.withColor(state, dye)); + }); + } + + @LuaFunction(mainThread = true) + public void setSignal(IArguments arguments) throws LuaException { + if (arguments.optTable(0).isPresent()) + setSignal(signal().first, arguments.getTable(0)); + if (arguments.optTable(1).isPresent()) + setSignal(signal().second, arguments.getTable(1)); + } + + private void setSignal(NixieTubeBlockEntity.ComputerSignal.TubeDisplay display, @NotNull Map attrs) + throws LuaException { + if (attrs.containsKey("r")) + display.r = constrainByte("r", 0, 255, attrs.get("r")); + if (attrs.containsKey("g")) + display.g = constrainByte("g", 0, 255, attrs.get("g")); + if (attrs.containsKey("b")) + display.b = constrainByte("r", 0, 255, attrs.get("b")); + if (attrs.containsKey("glowWidth")) + display.glowWidth = constrainByte("glowWidth", 1, 4, attrs.get("glowWidth")); + if (attrs.containsKey("glowHeight")) + display.glowHeight = constrainByte("glowHeight", 1, 4, attrs.get("glowHeight")); + if (attrs.containsKey("blinkPeriod")) + display.blinkPeriod = constrainByte("blinkPeriod", 0, 255, attrs.get("blinkPeriod")); + if (attrs.containsKey("blinkOffTime")) + display.blinkOffTime = constrainByte("blinkOffTime", 0, 255, attrs.get("blinkOffTime")); + if (display.r == 0 && display.g == 0 && display.b == 0) { + display.blinkPeriod = 0; + display.blinkOffTime = 0; + } else if (display.blinkPeriod == 0) { + display.blinkPeriod = 1; + display.blinkOffTime = 0; + } + blockEntity.notifyUpdate(); + } + + private byte constrainByte(String name, int min, int max, Object rawValue) throws LuaException { + if (!(rawValue instanceof Number)) + throw LuaValues.badField(name, "number", LuaValues.getType(rawValue)); + int value = ((Number) rawValue).intValue(); + if (value < min || value > max) + throw new LuaException("field " + name + " must be in range " + min + "-" + max); + return (byte) value; + } + + private NixieTubeBlockEntity.ComputerSignal signal() { + if (blockEntity.computerSignal == null) + blockEntity.computerSignal = new NixieTubeBlockEntity.ComputerSignal(); + return blockEntity.computerSignal; + } + + @NotNull + @Override + public String getType() { + return "Create_NixieTube"; + } + +} diff --git a/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java index 7c0b1f7b1..56937a3d1 100644 --- a/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java +++ b/src/main/java/com/simibubi/create/compat/computercraft/implementation/peripherals/SyncedPeripheral.java @@ -1,6 +1,9 @@ package com.simibubi.create.compat.computercraft.implementation.peripherals; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.ArrayList; +import java.util.List; + +import com.simibubi.create.compat.computercraft.events.ComputerEvent; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -17,7 +20,7 @@ import net.minecraftforge.network.PacketDistributor; public abstract class SyncedPeripheral implements IPeripheral { protected final T blockEntity; - private final AtomicInteger computers = new AtomicInteger(); + private final List<@NotNull IComputerAccess> computers = new ArrayList<>(); public SyncedPeripheral(T blockEntity) { this.blockEntity = blockEntity; @@ -25,21 +28,34 @@ public abstract class SyncedPeripheral implements IP @Override public void attach(@NotNull IComputerAccess computer) { - computers.incrementAndGet(); - updateBlockEntity(); + synchronized (computers) { + computers.add(computer); + if (computers.size() == 1) + onFirstAttach(); + updateBlockEntity(); + } } + protected void onFirstAttach() {} + @Override public void detach(@NotNull IComputerAccess computer) { - computers.decrementAndGet(); - updateBlockEntity(); + synchronized (computers) { + computers.remove(computer); + updateBlockEntity(); + if (computers.isEmpty()) + onLastDetach(); + } } + protected void onLastDetach() {} + private void updateBlockEntity() { - boolean hasAttachedComputer = computers.get() > 0; + boolean hasAttachedComputer = !computers.isEmpty(); blockEntity.getBehaviour(ComputerBehaviour.TYPE).setHasAttachedComputer(hasAttachedComputer); - AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), new AttachedComputerPacket(blockEntity.getBlockPos(), hasAttachedComputer)); + AllPackets.getChannel().send(PacketDistributor.ALL.noArg(), + new AttachedComputerPacket(blockEntity.getBlockPos(), hasAttachedComputer)); } @Override diff --git a/src/main/java/com/simibubi/create/content/redstone/displayLink/target/NixieTubeDisplayTarget.java b/src/main/java/com/simibubi/create/content/redstone/displayLink/target/NixieTubeDisplayTarget.java index 13415b9be..8853d5bac 100644 --- a/src/main/java/com/simibubi/create/content/redstone/displayLink/target/NixieTubeDisplayTarget.java +++ b/src/main/java/com/simibubi/create/content/redstone/displayLink/target/NixieTubeDisplayTarget.java @@ -22,7 +22,7 @@ public class NixieTubeDisplayTarget extends SingleLineDisplayTarget { @Override protected void acceptLine(MutableComponent text, DisplayLinkContext context) { String tagElement = Component.Serializer.toJson(text); - NixieTubeBlock.walkNixies(context.level(), context.getTargetPos(), (currentPos, rowPosition) -> { + NixieTubeBlock.walkNixies(context.level(), context.getTargetPos(), false, (currentPos, rowPosition) -> { BlockEntity blockEntity = context.level() .getBlockEntity(currentPos); if (blockEntity instanceof NixieTubeBlockEntity nixie) @@ -33,7 +33,8 @@ public class NixieTubeDisplayTarget extends SingleLineDisplayTarget { @Override protected int getWidth(DisplayLinkContext context) { MutableInt count = new MutableInt(0); - NixieTubeBlock.walkNixies(context.level(), context.getTargetPos(), (currentPos, rowPosition) -> count.add(2)); + NixieTubeBlock.walkNixies(context.level(), context.getTargetPos(), false, + (currentPos, rowPosition) -> count.add(2)); return count.intValue(); } @@ -42,7 +43,7 @@ public class NixieTubeDisplayTarget extends SingleLineDisplayTarget { public AABB getMultiblockBounds(LevelAccessor level, BlockPos pos) { MutableObject start = new MutableObject<>(null); MutableObject end = new MutableObject<>(null); - NixieTubeBlock.walkNixies(level, pos, (currentPos, rowPosition) -> { + NixieTubeBlock.walkNixies(level, pos, true, (currentPos, rowPosition) -> { end.setValue(currentPos); if (start.getValue() == null) start.setValue(currentPos); diff --git a/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlock.java b/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlock.java index 747138da8..ab98a87c3 100644 --- a/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlock.java +++ b/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlock.java @@ -6,9 +6,14 @@ import java.util.List; import java.util.Random; import java.util.function.BiConsumer; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + + import com.simibubi.create.AllBlockEntityTypes; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllShapes; +import com.simibubi.create.compat.Mods; import com.simibubi.create.content.equipment.clipboard.ClipboardEntry; import com.simibubi.create.content.equipment.wrench.IWrenchable; import com.simibubi.create.content.schematics.requirement.ISpecialBlockItemRequirement; @@ -71,6 +76,11 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock if (nixie == null) return InteractionResult.PASS; + + // Refuse interaction if nixie tube is in a computer-controlled row + if (isInComputerControlledRow(world, pos)) + return InteractionResult.PASS; + if (heldItem.isEmpty()) { if (nixie.reactsToRedstone()) return InteractionResult.PASS; @@ -101,7 +111,8 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock return InteractionResult.SUCCESS; String tagUsed = tagElement; - walkNixies(world, pos, (currentPos, rowPosition) -> { + // Skip computer check in this walk since it was already performed at the start. + walkNixies(world, pos, true, (currentPos, rowPosition) -> { if (display) withBlockEntityDo(world, currentPos, be -> be.displayCustomText(tagUsed, rowPosition)); if (dye != null) @@ -111,40 +122,98 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock return InteractionResult.SUCCESS; } - public static void walkNixies(LevelAccessor world, BlockPos start, BiConsumer callback) { - BlockState state = world.getBlockState(start); - if (!(state.getBlock() instanceof NixieTubeBlock)) - return; - - BlockPos currentPos = start; - Direction left = state.getValue(FACING) - .getOpposite(); - + public static Direction getLeftNixieDirection(@NotNull BlockState state) { + Direction left = state.getValue(FACING).getOpposite(); if (state.getValue(FACE) == DoubleAttachFace.WALL) left = Direction.UP; if (state.getValue(FACE) == DoubleAttachFace.WALL_REVERSED) left = Direction.DOWN; + return left; + } + public static Direction getRightNixieDirection(@NotNull BlockState state) { + return getLeftNixieDirection(state).getOpposite(); + } + + public static boolean isInComputerControlledRow(@NotNull LevelAccessor world, @NotNull BlockPos pos) { + return Mods.COMPUTERCRAFT.isLoaded() && !walkNixies(world, pos, false, null); + } + + /** + * Walk down a nixie tube row and execute a callback on each tube in said row. + * @param world The world the tubes are in. + * @param start Start position for the walk. + * @param allowComputerControlled Allow or disallow running callbacks if the row is computer-controlled. + * @param callback Callback to run for each tube. + * @return True if the row was walked, false if the walk was aborted because it is computer-controlled. + */ + public static boolean walkNixies(@NotNull LevelAccessor world, @NotNull BlockPos start, + boolean allowComputerControlled, + @Nullable BiConsumer callback) { + BlockState state = world.getBlockState(start); + if (!(state.getBlock() instanceof NixieTubeBlock)) + return false; + + // If ComputerCraft is not installed, ignore allowComputerControlled since + // nixies can't be computer-controlled + if (!Mods.COMPUTERCRAFT.isLoaded()) + allowComputerControlled = true; + + BlockPos currentPos = start; + Direction left = getLeftNixieDirection(state); Direction right = left.getOpposite(); while (true) { BlockPos nextPos = currentPos.relative(left); if (!areNixieBlocksEqual(world.getBlockState(nextPos), state)) break; + // If computer-controlled nixie walking is disallowed, presence of any (same-color) + // controlled nixies aborts the entire nixie walk. + if (!allowComputerControlled && world.getBlockEntity(nextPos) instanceof NixieTubeBlockEntity ntbe && + ntbe.computerBehaviour.hasAttachedComputer()) { + return false; + } currentPos = nextPos; } + // As explained above, a controlled nixie in the row aborts the walk if they are disallowed, + // and that includes those down the chain too. + if (!allowComputerControlled) { + // Check the start block itself + if (world.getBlockEntity(start) instanceof NixieTubeBlockEntity ntbe && + ntbe.computerBehaviour.hasAttachedComputer()) { + return false; + } + BlockPos leftmostPos = currentPos; + // No need to iterate over the nixies to the left again + currentPos = start; + while (true) { + BlockPos nextPos = currentPos.relative(right); + if (!areNixieBlocksEqual(world.getBlockState(nextPos), state)) + break; + if (world.getBlockEntity(nextPos) instanceof NixieTubeBlockEntity ntbe && + ntbe.computerBehaviour.hasAttachedComputer()) { + return false; + } + currentPos = nextPos; + } + currentPos = leftmostPos; + } + int index = 0; while (true) { final int rowPosition = index; - callback.accept(currentPos, rowPosition); + if (callback != null) + callback.accept(currentPos, rowPosition); BlockPos nextPos = currentPos.relative(right); if (!areNixieBlocksEqual(world.getBlockState(nextPos), state)) break; currentPos = nextPos; index++; } + + return true; } @Override @@ -153,10 +222,41 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock } @Override - public void onRemove(BlockState p_196243_1_, Level p_196243_2_, BlockPos p_196243_3_, BlockState p_196243_4_, - boolean p_196243_5_) { - if (!(p_196243_4_.getBlock() instanceof NixieTubeBlock)) - p_196243_2_.removeBlockEntity(p_196243_3_); + public void onRemove(BlockState state, Level world, BlockPos pos, BlockState newState, boolean isMoving) { + if (newState.getBlock() instanceof NixieTubeBlock) + return; + world.removeBlockEntity(pos); + if (Mods.COMPUTERCRAFT.isLoaded()) { + // A computer-controlled nixie tube row may have been broken in the middle. + Direction left = getLeftNixieDirection(state); + BlockPos leftPos = pos.relative(left); + if (areNixieBlocksEqual(world.getBlockState(leftPos), state)) { + boolean leftRowComputerControlled = isInComputerControlledRow(world, leftPos); + walkNixies(world, leftPos, true, leftRowComputerControlled ? + (currentPos, rowPosition) -> { + if (world.getBlockEntity(currentPos) instanceof NixieTubeBlockEntity ntbe) + ntbe.displayCustomText("{\"text\":\"\"}", rowPosition); + } : + (currentPos, rowPosition) -> { + if (world.getBlockEntity(currentPos) instanceof NixieTubeBlockEntity ntbe) + NixieTubeBlock.updateDisplayedRedstoneValue(ntbe, true); + }); + } + Direction right = left.getOpposite(); + BlockPos rightPos = pos.relative(right); + if (areNixieBlocksEqual(world.getBlockState(rightPos), state)) { + boolean rightRowComputerControlled = isInComputerControlledRow(world, rightPos); + walkNixies(world, rightPos, true, rightRowComputerControlled ? + (currentPos, rowPosition) -> { + if (world.getBlockEntity(currentPos) instanceof NixieTubeBlockEntity ntbe) + ntbe.displayCustomText("{\"text\":\"\"}", rowPosition); + } : + (currentPos, rowPosition) -> { + if (world.getBlockEntity(currentPos) instanceof NixieTubeBlockEntity ntbe) + NixieTubeBlock.updateDisplayedRedstoneValue(ntbe, true); + }); + } + } } @Override @@ -237,18 +337,30 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock @Override public void onPlace(BlockState state, Level worldIn, BlockPos pos, BlockState oldState, boolean isMoving) { - if (state.getBlock() == oldState.getBlock() || isMoving) + if (state.getBlock() == oldState.getBlock() || isMoving || oldState.getBlock() instanceof NixieTubeBlock) return; + if (Mods.COMPUTERCRAFT.isLoaded() && isInComputerControlledRow(worldIn, pos)) { + // The nixie tube has been placed in a computer-controlled row. + walkNixies(worldIn, pos, true, (currentPos, rowPosition) -> { + if (worldIn.getBlockEntity(currentPos) instanceof NixieTubeBlockEntity ntbe) + ntbe.displayCustomText("{\"text\":\"\"}", rowPosition); + }); + return; + } updateDisplayedRedstoneValue(state, worldIn, pos); } + public static void updateDisplayedRedstoneValue(NixieTubeBlockEntity be, boolean force) { + if (be.getLevel() == null || be.getLevel().isClientSide) + return; + if (be.reactsToRedstone() || force) + be.updateRedstoneStrength(getPower(be.getLevel(), be.getBlockPos())); + } + private void updateDisplayedRedstoneValue(BlockState state, Level worldIn, BlockPos pos) { if (worldIn.isClientSide) return; - withBlockEntityDo(worldIn, pos, be -> { - if (be.reactsToRedstone()) - be.updateRedstoneStrength(getPower(worldIn, pos)); - }); + withBlockEntityDo(worldIn, pos, be -> NixieTubeBlock.updateDisplayedRedstoneValue(be, false)); } static boolean isValidBlock(BlockGetter world, BlockPos pos, boolean above) { @@ -257,7 +369,7 @@ public class NixieTubeBlock extends DoubleFaceAttachedBlock .isEmpty(); } - private int getPower(Level worldIn, BlockPos pos) { + private static int getPower(Level worldIn, BlockPos pos) { int power = 0; for (Direction direction : Iterate.directions) power = Math.max(worldIn.getSignal(pos.relative(direction), direction), power); diff --git a/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlockEntity.java b/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlockEntity.java index b1c13be2c..90ce9f39b 100644 --- a/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeBlockEntity.java @@ -4,6 +4,14 @@ import java.lang.ref.WeakReference; import java.util.List; import java.util.Optional; +import com.simibubi.create.Create; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + + +import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour; +import com.simibubi.create.compat.computercraft.ComputerCraftProxy; import com.simibubi.create.content.redstone.displayLink.DisplayLinkBlock; import com.simibubi.create.content.trains.signal.SignalBlockEntity; import com.simibubi.create.content.trains.signal.SignalBlockEntity.SignalState; @@ -13,6 +21,8 @@ import com.simibubi.create.foundation.utility.Components; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.DynamicComponent; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; @@ -23,15 +33,62 @@ import net.minecraft.world.level.block.state.BlockState; public class NixieTubeBlockEntity extends SmartBlockEntity { + public static final class ComputerSignal { + public static final class TubeDisplay { + public static final int ENCODED_SIZE = 7; + + public byte r = 63, g = 63, b = 63; + public byte blinkPeriod = 0, blinkOffTime = 0; + public byte glowWidth = 1, glowHeight = 1; + + public void decode(byte[] data, int offset) { + r = data[offset]; + g = data[offset + 1]; + b = data[offset + 2]; + blinkPeriod = data[offset + 3]; + blinkOffTime = data[offset + 4]; + glowWidth = data[offset + 5]; + glowHeight = data[offset + 6]; + } + + public void encode(byte[] data, int offset) { + data[offset] = r; + data[offset + 1] = g; + data[offset + 2] = b; + data[offset + 3] = blinkPeriod; + data[offset + 4] = blinkOffTime; + data[offset + 5] = glowWidth; + data[offset + 6] = glowHeight; + } + } + + public @NotNull TubeDisplay first = new TubeDisplay(); + public @NotNull TubeDisplay second = new TubeDisplay(); + + public void decode(byte[] encoded) { + first.decode(encoded, 0); + second.decode(encoded, TubeDisplay.ENCODED_SIZE); + } + + public byte[] encode() { + byte[] encoded = new byte[TubeDisplay.ENCODED_SIZE * 2]; + first.encode(encoded, 0); + second.encode(encoded, TubeDisplay.ENCODED_SIZE); + return encoded; + } + } + private static final Couple EMPTY = Couple.create("", ""); private int redstoneStrength; private Optional customText; private int nixieIndex; private Couple displayedStrings; + public AbstractComputerBehaviour computerBehaviour; private WeakReference cachedSignalTE; - public SignalState signalState; + public @Nullable SignalState signalState; + public @Nullable ComputerSignal computerSignal; public NixieTubeBlockEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); @@ -47,6 +104,13 @@ public class NixieTubeBlockEntity extends SmartBlockEntity { return; signalState = null; + if (computerBehaviour.hasAttachedComputer()) { + if (level.isClientSide && cachedSignalTE.get() != null) { + cachedSignalTE = new WeakReference<>(null); + } + return; + } + SignalBlockEntity signalBlockEntity = cachedSignalTE.get(); if (signalBlockEntity == null || signalBlockEntity.isRemoved()) { @@ -71,7 +135,7 @@ public class NixieTubeBlockEntity extends SmartBlockEntity { // public boolean reactsToRedstone() { - return customText.isEmpty(); + return !computerBehaviour.hasAttachedComputer() && customText.isEmpty(); } public Couple getDisplayedStrings() { @@ -108,7 +172,7 @@ public class NixieTubeBlockEntity extends SmartBlockEntity { } public void updateDisplayedStrings() { - if (signalState != null) + if (signalState != null || computerSignal != null) return; customText.map(DynamicComponent::resolve) .ifPresentOrElse( @@ -144,12 +208,25 @@ public class NixieTubeBlockEntity extends SmartBlockEntity { customText = Optional.empty(); nixieIndex = 0; } + } else { + customText = Optional.empty(); + nixieIndex = 0; } if (customText.isEmpty()) redstoneStrength = nbt.getInt("RedstoneStrength"); - if (clientPacket) + if (clientPacket) { + if (nbt.contains("ComputerSignal")) { + byte[] encodedComputerSignal = nbt.getByteArray("ComputerSignal"); + if (computerSignal == null) + computerSignal = new ComputerSignal(); + computerSignal.decode(encodedComputerSignal); + } else { + computerSignal = null; + } + updateDisplayedStrings(); + } } @Override @@ -162,6 +239,8 @@ public class NixieTubeBlockEntity extends SmartBlockEntity { .write(nbt); } else nbt.putInt("RedstoneStrength", redstoneStrength); + if (clientPacket && computerSignal != null) + nbt.putByteArray("ComputerSignal", computerSignal.encode()); } private String charOrEmpty(String string, int index) { @@ -169,6 +248,21 @@ public class NixieTubeBlockEntity extends SmartBlockEntity { } @Override - public void addBehaviours(List behaviours) {} + public void addBehaviours(List behaviours) { + behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this)); + } + + @Override + public @NotNull LazyOptional getCapability(@NotNull Capability cap, Direction side) { + if (computerBehaviour.isPeripheralCap(cap)) + return computerBehaviour.getPeripheralCapability(); + return super.getCapability(cap, side); + } + + @Override + public void invalidateCaps() { + super.invalidateCaps(); + computerBehaviour.removePeripheral(); + } } diff --git a/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeRenderer.java b/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeRenderer.java index e1cf7edfc..d55c00f72 100644 --- a/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeRenderer.java +++ b/src/main/java/com/simibubi/create/content/redstone/nixieTube/NixieTubeRenderer.java @@ -32,6 +32,7 @@ import net.minecraft.world.phys.Vec3; public class NixieTubeRenderer extends SafeBlockEntityRenderer { + private static final int GLOW_VIEW_DISTANCE = 96; private static Random r = new Random(); public NixieTubeRenderer(BlockEntityRendererProvider.Context context) {} @@ -52,7 +53,7 @@ public class NixieTubeRenderer extends SafeBlockEntityRenderer 1 && renderTime % tubeDisplay.blinkPeriod < tubeDisplay.blinkOffTime) + continue; - CachedBufferer - .partial(first ? AllPartialModels.SIGNAL_RED - : yellow ? AllPartialModels.SIGNAL_YELLOW : AllPartialModels.SIGNAL_WHITE, blockState) - .light(0xF000F0) - .disableDiffuse() - .scale(1 + 1 / 16f) - .renderInto(ms, buffer.getBuffer(RenderTypes.getAdditive())); + boolean flip = first == invertTubes; - ms.popPose(); + ms.pushPose(); + ms.translate(flip ? 4 / 16f : -4 / 16f, 0, 0); + + if (diff.lengthSqr() < GLOW_VIEW_DISTANCE * GLOW_VIEW_DISTANCE) { + boolean horiz = facing.getAxis().isHorizontal(); + float width = horiz ? tubeDisplay.glowWidth : tubeDisplay.glowHeight; + float height = horiz ? tubeDisplay.glowHeight : tubeDisplay.glowWidth; + + CachedBufferer.partial(AllPartialModels.SIGNAL_COMPUTER_WHITE_CUBE, blockState) + .light(0xf000f0) + .disableDiffuse() + .scale(width, height, 1) + .renderInto(ms, buffer.getBuffer(RenderType.translucent())); + + CachedBufferer + .partial(AllPartialModels.SIGNAL_COMPUTER_WHITE_GLOW, blockState) + .light(0xf000f0) + .color( + Math.min(((tubeDisplay.r & 0xFF) * 6 + 256) >> 3, 255), + Math.min(((tubeDisplay.g & 0xFF) * 6 + 256) >> 3, 255), + Math.min(((tubeDisplay.b & 0xFF) * 6 + 256) >> 3, 255), + 255) + .disableDiffuse() + .scale(width + 1.125f, height + 1.125f, 2) + .renderInto(ms, buffer.getBuffer(RenderTypes.getAdditive())); + } + + CachedBufferer + .partial(AllPartialModels.SIGNAL_COMPUTER_WHITE_BASE, blockState) + .light(0xF000F0) + .color(12, 12, 12, 255) + .disableDiffuse() + .scale(1 + 1.25f / 16f) + .renderInto(ms, buffer.getBuffer(RenderTypes.getAdditive())); + + CachedBufferer + .partial(AllPartialModels.SIGNAL_COMPUTER_WHITE, blockState) + .light(0xF000F0) + .color(tubeDisplay.r, tubeDisplay.g, tubeDisplay.b, 255) + .disableDiffuse() + .scale(1 + 1 / 16f) + .renderInto(ms, buffer.getBuffer(RenderTypes.getAdditive())); + + ms.popPose(); + } } + ms.popPose(); } - + @Override public int getViewDistance() { return 128; diff --git a/src/main/resources/assets/create/models/block/track_signal/computer_white_cube.json b/src/main/resources/assets/create/models/block/track_signal/computer_white_cube.json new file mode 100644 index 000000000..c843c78f3 --- /dev/null +++ b/src/main/resources/assets/create/models/block/track_signal/computer_white_cube.json @@ -0,0 +1,20 @@ +{ + "textures": { + "0": "create:block/signal_glow_3", + "particle": "create:block/signal_glow_3" + }, + "elements": [ + { + "from": [-0.5, -0.5, -0.5], + "to": [0.5, 0.5, 0.5], + "faces": { + "north": {"uv": [1, 1, 2, 2], "texture": "#0"}, + "east": {"uv": [1, 1, 2, 2], "texture": "#0"}, + "south": {"uv": [1, 1, 2, 2], "texture": "#0"}, + "west": {"uv": [1, 1, 2, 2], "texture": "#0"}, + "up": {"uv": [1, 1, 2, 2], "texture": "#0"}, + "down": {"uv": [1, 1, 2, 2], "texture": "#0"} + } + } + ] +} diff --git a/src/main/resources/assets/create/models/block/track_signal/computer_white_glow.json b/src/main/resources/assets/create/models/block/track_signal/computer_white_glow.json new file mode 100644 index 000000000..29817b2b0 --- /dev/null +++ b/src/main/resources/assets/create/models/block/track_signal/computer_white_glow.json @@ -0,0 +1,20 @@ +{ + "textures": { + "0": "create:block/signal_glow_3", + "particle": "create:block/signal_glow_3" + }, + "elements": [ + { + "from": [-0.5, -0.5, -0.5], + "to": [0.5, 0.5, 0.5], + "faces": { + "north": {"uv": [1, 0, 2, 1], "texture": "#0"}, + "east": {"uv": [1, 0, 2, 1], "texture": "#0"}, + "south": {"uv": [1, 0, 2, 1], "texture": "#0"}, + "west": {"uv": [1, 0, 2, 1], "texture": "#0"}, + "up": {"uv": [1, 0, 2, 1], "texture": "#0"}, + "down": {"uv": [1, 0, 2, 1], "texture": "#0"} + } + } + ] +} diff --git a/src/main/resources/assets/create/models/block/track_signal/computer_white_tube.json b/src/main/resources/assets/create/models/block/track_signal/computer_white_tube.json new file mode 100644 index 000000000..57eef33a1 --- /dev/null +++ b/src/main/resources/assets/create/models/block/track_signal/computer_white_tube.json @@ -0,0 +1,24 @@ +{ + "parent": "block/block", + "ambientocclusion": false, + "textures": { + "1": "create:block/signal_glow_3", + "particle": "create:block/signal_glow_3" + }, + "elements": [ + { + "name": "tube3", + "from": [-3, -4.5, -3], + "to": [3, 4.5, 3], + "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [10, 7, 16, 16], "texture": "#1"}, + "east": {"uv": [10, 7, 16, 16], "texture": "#1"}, + "south": {"uv": [10, 7, 16, 16], "texture": "#1"}, + "west": {"uv": [10, 7, 16, 16], "texture": "#1"}, + "up": {"uv": [10, 0, 16, 6], "rotation": 90, "texture": "#1"}, + "down": {"uv": [10, 0, 16, 6], "rotation": 90, "texture": "#1"} + } + } + ] +} diff --git a/src/main/resources/assets/create/models/block/track_signal/computer_white_tube_base.json b/src/main/resources/assets/create/models/block/track_signal/computer_white_tube_base.json new file mode 100644 index 000000000..381483ba4 --- /dev/null +++ b/src/main/resources/assets/create/models/block/track_signal/computer_white_tube_base.json @@ -0,0 +1,24 @@ +{ + "parent": "block/block", + "ambientocclusion": false, + "textures": { + "1": "create:block/signal_glow_3", + "particle": "create:block/signal_glow_3" + }, + "elements": [ + { + "name": "tube3", + "from": [-3, -4.5, -3], + "to": [3, 4.5, 3], + "rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]}, + "faces": { + "north": {"uv": [3, 7, 9, 16], "texture": "#1"}, + "east": {"uv": [3, 7, 9, 16], "texture": "#1"}, + "south": {"uv": [3, 7, 9, 16], "texture": "#1"}, + "west": {"uv": [3, 7, 9, 16], "texture": "#1"}, + "up": {"uv": [3, 0, 9, 6], "rotation": 90, "texture": "#1"}, + "down": {"uv": [3, 0, 9, 6], "rotation": 90, "texture": "#1"} + } + } + ] +} diff --git a/src/main/resources/assets/create/textures/block/signal_glow_3.png b/src/main/resources/assets/create/textures/block/signal_glow_3.png new file mode 100644 index 0000000000000000000000000000000000000000..34dd3d81d46b4fe31e23bca1a7cc60974c903f7d GIT binary patch literal 242 zcmV7zCmyySVjE9)OCTl7^N6*n!D10U~+Dw~mA+5=J_G3csiG9npQx zaU5hM&7E}K`*>YfB<j_}2;_POK-vJ3?=5HpNS12^0=5zffPk%J5?HvkHg#a( sob&S?kn0W}oq^T}M6I4O5T&>923_+#ZN!|ANdN!<07*qoM6N<$f)gTMXaE2J literal 0 HcmV?d00001