diff --git a/src/generated/resources/assets/little_big_redstone/blockstates/channel.json b/src/generated/resources/assets/little_big_redstone/blockstates/channel.json new file mode 100644 index 00000000..351242cb --- /dev/null +++ b/src/generated/resources/assets/little_big_redstone/blockstates/channel.json @@ -0,0 +1,388 @@ +{ + "variants": { + "origin_direction=down,power=0": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=1": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=10": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=11": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=12": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=13": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=14": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=15": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=2": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=3": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=4": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=5": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=6": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=7": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=8": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=down,power=9": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=east,power=0": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=1": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=10": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=11": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=12": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=13": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=14": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=15": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=2": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=3": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=4": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=5": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=6": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=7": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=8": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=east,power=9": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=north,power=0": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=1": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=10": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=11": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=12": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=13": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=14": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=15": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=2": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=3": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=4": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=5": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=6": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=7": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=8": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=north,power=9": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=0": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=1": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=10": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=11": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=12": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=13": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=14": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=15": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=2": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=3": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=4": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=5": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=6": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=7": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=8": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=south,power=9": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90 + }, + "origin_direction=up,power=0": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=1": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=10": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=11": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=12": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=13": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=14": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=15": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=2": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=3": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=4": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=5": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=6": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=7": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=8": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=up,power=9": { + "model": "little_big_redstone:block/channel_vertical" + }, + "origin_direction=west,power=0": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=1": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=10": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=11": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=12": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=13": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=14": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=15": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=2": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=3": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=4": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=5": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=6": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=7": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=8": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + }, + "origin_direction=west,power=9": { + "model": "little_big_redstone:block/channel_horizontal", + "x": 90, + "y": 90 + } + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/little_big_redstone/lang/en_us.json b/src/generated/resources/assets/little_big_redstone/lang/en_us.json index 72cb4f33..a4b0ecc3 100644 --- a/src/generated/resources/assets/little_big_redstone/lang/en_us.json +++ b/src/generated/resources/assets/little_big_redstone/lang/en_us.json @@ -2,6 +2,7 @@ "block.little_big_redstone.black_microchip": "Black Microchip", "block.little_big_redstone.blue_microchip": "Blue Microchip", "block.little_big_redstone.brown_microchip": "Brown Microchip", + "block.little_big_redstone.channel": "Channel", "block.little_big_redstone.cyan_microchip": "Cyan Microchip", "block.little_big_redstone.gray_microchip": "Gray Microchip", "block.little_big_redstone.green_microchip": "Green Microchip", @@ -126,6 +127,7 @@ "text.little_big_redstone.logic_config_button_label_direction": "Direction", "text.little_big_redstone.logic_config_button_label_duration": "Duration: ", "text.little_big_redstone.logic_config_button_label_inputs": "Inputs: ", + "text.little_big_redstone.logic_config_button_label_io_channel": "Channel: ", "text.little_big_redstone.logic_config_button_label_io_signal_strength": "Signal Strength: ", "text.little_big_redstone.logic_config_button_label_mode": "Mode", "text.little_big_redstone.logic_config_button_label_outputs": "Outputs: ", @@ -138,6 +140,7 @@ "text.little_big_redstone.logic_config_button_label_ticks_and_seconds_singular": "%s tick (%ss)", "text.little_big_redstone.logic_config_button_tooltip_duration": "The time for the output to be on.", "text.little_big_redstone.logic_config_button_tooltip_inputs": "The number of inputs that this component can accept.", + "text.little_big_redstone.logic_config_button_tooltip_io_channel": "What channel this port should link to.\nWhen the channel is set to 0, the microchip block itself is used.", "text.little_big_redstone.logic_config_button_tooltip_io_direction": "The direction this port should interact with redstone power on.", "text.little_big_redstone.logic_config_button_tooltip_io_mode": "Whether this port should input or output redstone power.", "text.little_big_redstone.logic_config_button_tooltip_io_signal_comparison_mode_equal_to": "The input signal must be equal to %s.", @@ -173,6 +176,7 @@ "text.little_big_redstone.logic_config_tooltip_direction": " Direction: %s", "text.little_big_redstone.logic_config_tooltip_duration": " Duration: %s", "text.little_big_redstone.logic_config_tooltip_inputs": " Inputs: %s", + "text.little_big_redstone.logic_config_tooltip_io_channel": " Channel: %s", "text.little_big_redstone.logic_config_tooltip_io_signal": " Signal: %s", "text.little_big_redstone.logic_config_tooltip_io_signal_comparison": " Signal: %s %s", "text.little_big_redstone.logic_config_tooltip_mode": " Mode: %s", diff --git a/src/generated/resources/assets/little_big_redstone/models/block/channel_horizontal.json b/src/generated/resources/assets/little_big_redstone/models/block/channel_horizontal.json new file mode 100644 index 00000000..743096da --- /dev/null +++ b/src/generated/resources/assets/little_big_redstone/models/block/channel_horizontal.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_column_horizontal", + "textures": { + "end": "little_big_redstone:block/channel_end", + "side": "little_big_redstone:block/channel_side" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/little_big_redstone/models/block/channel_vertical.json b/src/generated/resources/assets/little_big_redstone/models/block/channel_vertical.json new file mode 100644 index 00000000..12ef86da --- /dev/null +++ b/src/generated/resources/assets/little_big_redstone/models/block/channel_vertical.json @@ -0,0 +1,7 @@ +{ + "parent": "minecraft:block/cube_column", + "textures": { + "end": "little_big_redstone:block/channel_end", + "side": "little_big_redstone:block/channel_side" + } +} \ No newline at end of file diff --git a/src/generated/resources/assets/little_big_redstone/models/item/channel.json b/src/generated/resources/assets/little_big_redstone/models/item/channel.json new file mode 100644 index 00000000..c8b27baa --- /dev/null +++ b/src/generated/resources/assets/little_big_redstone/models/item/channel.json @@ -0,0 +1,3 @@ +{ + "parent": "little_big_redstone:block/channel_vertical" +} \ No newline at end of file diff --git a/src/generated/resources/data/little_big_redstone/loot_table/blocks/channel.json b/src/generated/resources/data/little_big_redstone/loot_table/blocks/channel.json new file mode 100644 index 00000000..be7f4d71 --- /dev/null +++ b/src/generated/resources/data/little_big_redstone/loot_table/blocks/channel.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "bonus_rolls": 0.0, + "conditions": [ + { + "condition": "minecraft:survives_explosion" + } + ], + "entries": [ + { + "type": "minecraft:item", + "name": "little_big_redstone:channel" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "little_big_redstone:blocks/channel" +} \ No newline at end of file diff --git a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json index 8b68e43c..fdc2c7e4 100644 --- a/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json +++ b/src/generated/resources/data/minecraft/tags/block/mineable/pickaxe.json @@ -3,6 +3,7 @@ "little_big_redstone:black_microchip", "little_big_redstone:blue_microchip", "little_big_redstone:brown_microchip", + "little_big_redstone:channel", "little_big_redstone:cyan_microchip", "little_big_redstone:gray_microchip", "little_big_redstone:green_microchip", diff --git a/src/main/java/net/swedz/little_big_redstone/LBRBlocks.java b/src/main/java/net/swedz/little_big_redstone/LBRBlocks.java index e1dae3f2..12e6778f 100644 --- a/src/main/java/net/swedz/little_big_redstone/LBRBlocks.java +++ b/src/main/java/net/swedz/little_big_redstone/LBRBlocks.java @@ -2,7 +2,9 @@ import com.google.common.collect.Maps; import com.google.common.collect.Sets; +import net.minecraft.core.Direction; import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.BlockTags; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.DyeColor; @@ -12,8 +14,10 @@ import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.material.MapColor; import net.neoforged.bus.api.IEventBus; +import net.neoforged.neoforge.client.model.generators.BlockStateProvider; import net.neoforged.neoforge.client.model.generators.ModelFile; import net.neoforged.neoforge.registries.DeferredRegister; +import net.swedz.little_big_redstone.block.channel.ChannelBlock; import net.swedz.little_big_redstone.block.microchip.MicrochipBlock; import net.swedz.little_big_redstone.block.microchip.MicrochipBlockEntity; import net.swedz.tesseract.neoforge.api.Assert; @@ -27,6 +31,7 @@ import java.util.Map; import java.util.Set; import java.util.function.BiFunction; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Supplier; @@ -65,6 +70,48 @@ public static void init(IEventBus bus) MICROCHIPS = Collections.unmodifiableMap(microchips); } + public static final BlockHolder CHANNEL = create("channel", "Channel", ChannelBlock::new, BlockItem::new, LBRSortOrder.CHANNEL) + .withProperties((p) -> p + .mapColor(MapColor.STONE) + .destroyTime(4f) + .requiresCorrectToolForDrops()) + .tag(BlockTags.MINEABLE_WITH_PICKAXE) + .withLootTable(CommonLootTableBuilders::self) + .withModel(LBRBlocks::channelModel) + .register(); + + private static Consumer channelModel(BlockHolder block) + { + return (builder) -> + { + var id = block.identifier().id(); + var side = ResourceLocation.fromNamespaceAndPath(block.identifier().modId(), "block/%s_side".formatted(id)); + var end = ResourceLocation.fromNamespaceAndPath(block.identifier().modId(), "block/%s_end".formatted(id)); + var vertical = builder.models().cubeColumn("%s_vertical".formatted(id), side, end); + var horizontal = builder.models().cubeColumnHorizontal("%s_horizontal".formatted(id), side, end); + + var blockStatesBuilder = builder.getVariantBuilder(block.get()); + for(var originDirection : Direction.values()) + { + var axis = originDirection.getAxis(); + for(int power = 0; power <= 15; power++) + { + boolean powered = power > 0; + blockStatesBuilder.partialState() + .with(ChannelBlock.ORIGIN_DIRECTION, originDirection) + .with(ChannelBlock.POWER, power) + .modelForState() + .modelFile(axis.isVertical() ? vertical : horizontal) + .rotationX(axis.isHorizontal() ? 90 : 0) + .rotationY(axis == Direction.Axis.X ? 90 : 0) + .addModel(); + } + } + + builder.simpleBlockItem(block.get(), new ModelFile.UncheckedModelFile(LBR.id("block/%s_vertical".formatted(id)))); + }; + } + public static BlockHolder microchip(DyeColor color) { Assert.notNull(color); diff --git a/src/main/java/net/swedz/little_big_redstone/LBRSortOrder.java b/src/main/java/net/swedz/little_big_redstone/LBRSortOrder.java index 7aea1d89..5f9018b3 100644 --- a/src/main/java/net/swedz/little_big_redstone/LBRSortOrder.java +++ b/src/main/java/net/swedz/little_big_redstone/LBRSortOrder.java @@ -5,9 +5,10 @@ public interface LBRSortOrder { SortOrder MICROCHIP = new SortOrder(0); - SortOrder RESOURCES = new SortOrder(1); - SortOrder LOGIC = new SortOrder(2); - SortOrder LOGIC_ARRAYS = new SortOrder(3); - SortOrder FLOPPY_DISKS = new SortOrder(4); - SortOrder STICKY_NOTES = new SortOrder(5); + SortOrder CHANNEL = new SortOrder(1); + SortOrder RESOURCES = new SortOrder(2); + SortOrder LOGIC = new SortOrder(3); + SortOrder LOGIC_ARRAYS = new SortOrder(4); + SortOrder FLOPPY_DISKS = new SortOrder(5); + SortOrder STICKY_NOTES = new SortOrder(6); } diff --git a/src/main/java/net/swedz/little_big_redstone/LBRText.java b/src/main/java/net/swedz/little_big_redstone/LBRText.java index 0788bbf4..7db139c1 100644 --- a/src/main/java/net/swedz/little_big_redstone/LBRText.java +++ b/src/main/java/net/swedz/little_big_redstone/LBRText.java @@ -172,6 +172,9 @@ MutableComponent floppyDiskHelp2( @LangKey(text = "Inputs: ") MutableComponent logicConfigButtonLabelInputs(); + @LangKey(text = "Channel: ") + MutableComponent logicConfigButtonLabelIOChannel(); + @LangKey(text = "Signal Strength: ") MutableComponent logicConfigButtonLabelIoSignalStrength(); @@ -208,6 +211,10 @@ MutableComponent floppyDiskHelp2( @LangKey(text = "The number of inputs that this component can accept.") MutableComponent logicConfigButtonTooltipInputs(); + @LangKey(text = "What channel this port should link to." + + "\nWhen the channel is set to 0, the microchip block itself is used.") + MutableComponent logicConfigButtonTooltipIOChannel(); + @LangKey(text = "The direction this port should interact with redstone power on.") MutableComponent logicConfigButtonTooltipIoDirection(); @@ -319,6 +326,10 @@ MutableComponent floppyDiskHelp2( @WithStyle("tooltip") MutableComponent logicConfigTooltipInputs(@WithStyle("highlighted") int inputs); + @LangKey(text = " Channel: %s") + @WithStyle("tooltip") + MutableComponent logicConfigTooltipIOChannel(@WithStyle("highlighted") int channel); + @LangKey(text = " Signal: %s") @WithStyle("tooltip") MutableComponent logicConfigTooltipIoSignal(@WithStyle("highlighted") int signal); diff --git a/src/main/java/net/swedz/little_big_redstone/block/channel/ChannelBlock.java b/src/main/java/net/swedz/little_big_redstone/block/channel/ChannelBlock.java new file mode 100644 index 00000000..103fafd0 --- /dev/null +++ b/src/main/java/net/swedz/little_big_redstone/block/channel/ChannelBlock.java @@ -0,0 +1,178 @@ +package net.swedz.little_big_redstone.block.channel; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RotatedPillarBlock; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.swedz.little_big_redstone.LBRBlocks; +import net.swedz.little_big_redstone.block.microchip.MicrochipBlockEntity; +import net.swedz.little_big_redstone.microchip.awareness.AwarenessTypes; +import net.swedz.little_big_redstone.microchip.awareness.types.RedstoneAwareness; +import net.swedz.tesseract.neoforge.api.Assert; + +public final class ChannelBlock extends Block +{ + public static final DirectionProperty ORIGIN_DIRECTION = DirectionProperty.create("origin_direction"); + public static final BooleanProperty INPUT = BooleanProperty.create("input"); + public static final IntegerProperty CHANNEL = IntegerProperty.create("channel", 1, 15); + public static final IntegerProperty POWER = IntegerProperty.create("power", 0, 15); + + public static boolean is(BlockState state, Direction originDirection) + { + return state.is(LBRBlocks.CHANNEL.get()) && + state.getValue(ORIGIN_DIRECTION) == originDirection; + } + + public static boolean is(BlockState state, Direction originDirection, int channel) + { + return is(state, originDirection) && + state.getValue(CHANNEL) == channel; + } + + private static MicrochipBlockEntity getOrigin(Level level, BlockPos pos, BlockState state) + { + Assert.that(state.is(LBRBlocks.CHANNEL.get()), "Cannot get origin direction for non-channel block"); + + var originDirection = state.getValue(ORIGIN_DIRECTION); + var channelIndex = state.getValue(CHANNEL); + var originPosition = pos.relative(originDirection, channelIndex); + if(level.getBlockEntity(originPosition) instanceof MicrochipBlockEntity microchip) + { + return microchip; + } + return null; + } + + private static RedstoneAwareness getOriginRedstone(Level level, BlockPos pos, BlockState state) + { + var microchip = getOrigin(level, pos, state); + return microchip != null ? microchip.microchip().awarenesses().get(AwarenessTypes.REDSTONE) : null; + } + + public ChannelBlock(Properties properties) + { + super(properties.isRedstoneConductor(Blocks::never)); + + this.registerDefaultState(stateDefinition.any() + .setValue(ORIGIN_DIRECTION, Direction.NORTH) + .setValue(INPUT, true) + .setValue(CHANNEL, 1) + .setValue(POWER, 0)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) + { + builder.add(ORIGIN_DIRECTION); + builder.add(INPUT); + builder.add(CHANNEL); + builder.add(POWER); + } + + private static int calculateChannelIndex(Level level, BlockPos channelPos, Direction originDirection) + { + var adjacentPos = channelPos.relative(originDirection); + var adjacentBlock = level.getBlockState(adjacentPos); + if(is(adjacentBlock, originDirection)) + { + int previousChannelIndex = adjacentBlock.getValue(CHANNEL); + return Math.min(previousChannelIndex + 1, 15); + } + return 1; + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) + { + var face = context.getClickedFace(); + var originDirection = face.getOpposite(); + var channelPos = context.getClickedPos(); + return this.defaultBlockState() + .setValue(ORIGIN_DIRECTION, originDirection) + .setValue(CHANNEL, calculateChannelIndex(context.getLevel(), channelPos, originDirection)); + } + + @Override + protected BlockState rotate(BlockState state, Rotation rotation) + { + // TODO recalculate channel index, but we cant get level or pos here??? + return RotatedPillarBlock.rotatePillar(state, rotation); + } + + @Override + protected boolean isSignalSource(BlockState state) + { + return true; + } + + @Override + protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direction direction) + { + return state.getValue(ORIGIN_DIRECTION).getAxis() == direction.getAxis() || state.getValue(INPUT) ? 0 : state.getValue(POWER); + } + + @Override + protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, boolean movedByPiston) + { + if(level.isClientSide()) + { + return; + } + + var originalState = state; + + var delta = neighborPos.subtract(pos); + Direction neighborDirection = Direction.fromDelta(delta.getX(), delta.getY(), delta.getZ()); + + // The block that updated is the block in the direction the channel is pointing + if(is(state, neighborDirection)) + { + int channel = calculateChannelIndex(level, pos, neighborDirection); + state = state.setValue(CHANNEL, channel); + + var channelDirection = state.getValue(ORIGIN_DIRECTION).getOpposite(); + + boolean input = true; + int power = 0; + var redstone = getOriginRedstone(level, pos, state); + if(redstone != null) + { + input = !redstone.isOutput(channelDirection, channel); + power = redstone.getOutputPower(channelDirection, channel); + } + state = state + .setValue(INPUT, input) + .setValue(POWER, power); + } + // The block that updated is not on either end of the channel + else if(!is(state, neighborDirection.getOpposite())) + { + var redstone = getOriginRedstone(level, pos, state); + if(redstone != null) + { + state = redstone.updateSignal(level, pos, state, neighborPos, neighborDirection, state.getValue(CHANNEL), state.getValue(ORIGIN_DIRECTION).getOpposite()); + } + else + { + int signal = level.getSignal(neighborPos, neighborDirection); + state = state.setValue(ChannelBlock.POWER, signal); + } + } + + if(state != originalState) + { + level.setBlock(pos, state, Block.UPDATE_ALL); + } + super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston); + } +} diff --git a/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlock.java b/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlock.java index 851810a8..f43ef313 100644 --- a/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlock.java +++ b/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlock.java @@ -108,7 +108,7 @@ protected int getSignal(BlockState state, BlockGetter level, BlockPos pos, Direc } var redstone = blockEntity.microchip().awarenesses().get(AwarenessTypes.REDSTONE); - return redstone != null ? redstone.outputRedstoneSignal(state, direction) : 0; + return redstone != null ? redstone.outputRedstoneSignal(state, direction, 0) : 0; } @Override diff --git a/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlockEntity.java b/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlockEntity.java index 8af9bb6a..d022ff83 100644 --- a/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlockEntity.java +++ b/src/main/java/net/swedz/little_big_redstone/block/microchip/MicrochipBlockEntity.java @@ -87,7 +87,7 @@ private MicrochipModelData createModelData() var redstone = microchip.awarenesses().get(AwarenessTypes.REDSTONE); if(redstone != null) { - data.sides(redstone.getSides()); + data.sides(redstone.getSides(0)); } return data; } diff --git a/src/main/java/net/swedz/little_big_redstone/guide/tags/microchip/TimedRedstoneSignals.java b/src/main/java/net/swedz/little_big_redstone/guide/tags/microchip/TimedRedstoneSignals.java index ad4fb69d..a4eb59e7 100644 --- a/src/main/java/net/swedz/little_big_redstone/guide/tags/microchip/TimedRedstoneSignals.java +++ b/src/main/java/net/swedz/little_big_redstone/guide/tags/microchip/TimedRedstoneSignals.java @@ -28,7 +28,7 @@ public void applySignals(RedstoneAwareness redstone) for(var direction : Direction.values()) { int signal = signals[direction.ordinal()]; - redstone.setInputPowered(direction, signal); + redstone.setInputPowered(direction, 0, signal); } } diff --git a/src/main/java/net/swedz/little_big_redstone/microchip/awareness/types/RedstoneAwareness.java b/src/main/java/net/swedz/little_big_redstone/microchip/awareness/types/RedstoneAwareness.java index f0800104..bf2f7df4 100644 --- a/src/main/java/net/swedz/little_big_redstone/microchip/awareness/types/RedstoneAwareness.java +++ b/src/main/java/net/swedz/little_big_redstone/microchip/awareness/types/RedstoneAwareness.java @@ -2,8 +2,11 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.state.BlockState; +import net.swedz.little_big_redstone.LBRBlocks; +import net.swedz.little_big_redstone.block.channel.ChannelBlock; import net.swedz.little_big_redstone.block.microchip.MicrochipBlock; import net.swedz.little_big_redstone.microchip.Microchip; import net.swedz.little_big_redstone.microchip.awareness.AwarenessContext; @@ -16,65 +19,75 @@ public final class RedstoneAwareness extends MicrochipAwareness 0) { - outputPower[index] = 0; - inputPower[index] = signal; + outputPower[channel][index] = 0; + inputPower[channel][index] = signal; } else { - inputPower[index] = 0; + inputPower[channel][index] = 0; } } - public boolean setOutputPowered(Direction direction, int signal) + public boolean setOutputPowered(Direction direction, int channel, int signal) { int index = direction.ordinal(); - if(signal > outputPowerEvaluated[index]) + if(signal > outputPowerEvaluated[channel][index]) { - inputPower[index] = 0; - outputPowerEvaluated[index] = signal; + inputPower[channel][index] = 0; + outputPowerEvaluated[channel][index] = signal; return true; } return false; } - public int outputRedstoneSignal(BlockState state, Direction direction) + public int outputRedstoneSignal(BlockState state, Direction direction, int channel) { direction = direction.getOpposite(); int index = direction.ordinal(); - boolean powered = outputSides[index] && state.getValue(MicrochipBlock.getDirectionalState(direction)); - return powered ? outputPower[index] : 0; + boolean powered = outputSides[channel][index] && state.getValue(MicrochipBlock.getDirectionalState(direction)); + return powered ? outputPower[channel][index] : 0; } @Override @@ -86,24 +99,25 @@ public AwarenessType type() @Override public void load(Microchip microchip) { - boolean[] inputSides = new boolean[6]; - boolean[] outputSides = new boolean[6]; + boolean[][] inputSides = new boolean[16][6]; + boolean[][] outputSides = new boolean[16][6]; for(var entry : microchip.components()) { if(entry.component() instanceof LogicIO io) { + int channel = io.config().channel; var direction = io.config().direction.ordinal(); - if(inputSides[direction] || outputSides[direction]) + if(inputSides[channel][direction] || outputSides[channel][direction]) { continue; } if(io.config().input) { - inputSides[direction] = true; + inputSides[channel][direction] = true; } else { - outputSides[direction] = true; + outputSides[channel][direction] = true; } } } @@ -114,17 +128,38 @@ public void load(Microchip microchip) @Override public void neighborChanged(AwarenessContext context, Block neighborBlock, BlockPos neighborPos, Direction neighborDirection, boolean movedByPiston) { - var level = context.level(); - var pos = context.pos(); + var state = this.updateSignal(context.level(), context.pos(), context.state(), neighborPos, neighborDirection, 0, neighborDirection); + context.level().setBlock(context.pos(), state, Block.UPDATE_ALL); + } + + public BlockState updateSignal(Level level, BlockPos pos, BlockState state, BlockPos neighborPos, Direction neighborDirection, int channel, Direction channelDirection) + { + boolean isMicrochip = state.getBlock() instanceof MicrochipBlock; + boolean isChannel = state.is(LBRBlocks.CHANNEL.get()); - int neighborDirectionIndex = neighborDirection.ordinal(); - if(inputSides[neighborDirectionIndex] && !outputSides[neighborDirectionIndex]) + if(!isMicrochip && !isChannel) + { + throw new IllegalStateException("Cannot update signal at block (" + pos.toShortString() + ") " + state.getBlockHolder().getKey().location()); + } + + int channelDirectionIndex = channelDirection.ordinal(); + if(inputSides[channel][channelDirectionIndex] && !outputSides[channel][channelDirectionIndex]) { int signal = level.getSignal(neighborPos, neighborDirection); - boolean powered = signal > 0; - this.setInputPowered(neighborDirection, signal); - level.setBlock(pos, context.state().setValue(MicrochipBlock.getDirectionalState(neighborDirection), powered), Block.UPDATE_ALL); + + this.setInputPowered(channelDirection, channel, signal); + + if(isMicrochip) + { + state = state.setValue(MicrochipBlock.getDirectionalState(neighborDirection), signal > 0); + } + else if(isChannel) + { + state = state.setValue(ChannelBlock.POWER, signal); + } } + + return state; } @Override @@ -135,17 +170,18 @@ public void preTick(AwarenessContext context) if(!initialized) { - int[] inputPower = new int[6]; + int[][] inputPower = new int[16][6]; for(var entry : context.microchip().components()) { if(entry.component() instanceof LogicIO io) { + int channel = io.config().channel; var direction = io.config().direction; int directionIndex = direction.ordinal(); - if(inputSides[directionIndex]) + if(inputSides[channel][directionIndex]) { int signal = level.getSignal(pos.relative(direction), direction); - inputPower[directionIndex] = signal; + inputPower[channel][directionIndex] = signal; } } } @@ -153,7 +189,7 @@ public void preTick(AwarenessContext context) initialized = true; } - outputPowerEvaluated = new int[6]; + outputPowerEvaluated = new int[16][6]; } @Override @@ -161,30 +197,76 @@ public void postTick(AwarenessContext context, boolean microchipDirty, boolean c { var level = context.level(); var pos = context.pos(); - var state = context.state(); - boolean powerChanged = false; - for(int index = 0; index < outputPowerEvaluated.length; index++) + boolean[] powerChanged = new boolean[16]; + for(int channel = 0; channel < outputPowerEvaluated.length; channel++) { - int signal = outputPowerEvaluated[index]; - if(outputPower[index] != signal) + int[] signals = outputPowerEvaluated[channel]; + for(int index = 0; index < signals.length; index++) { - powerChanged = true; - break; + int signal = signals[index]; + if(outputPower[channel][index] != signal) + { + powerChanged[channel] = true; + break; + } } } outputPower = outputPowerEvaluated; - var newState = state; - for(var direction : Direction.values()) + // Update the microchip block itself { - int index = direction.ordinal(); - newState = newState.setValue(MicrochipBlock.getDirectionalState(direction), inputPower[index] > 0 || outputPower[index] > 0); + var state = context.state(); + var newState = state; + for(var direction : Direction.values()) + { + int index = direction.ordinal(); + newState = newState.setValue(MicrochipBlock.getDirectionalState(direction), inputPower[0][index] > 0 || outputPower[0][index] > 0); + } + if(powerChanged[0] || newState != state) + { + level.setBlock(pos, newState, Block.UPDATE_ALL); + level.updateNeighborsAt(pos, state.getBlock()); + } } - if(powerChanged || newState != state) + + // Update the channel blocks { - level.setBlock(pos, newState, Block.UPDATE_ALL); - level.updateNeighborsAt(pos, state.getBlock()); + for(int channel = 1; channel < 16; channel++) + { + for(var direction : Direction.values()) + { + var relative = pos.relative(direction, channel); + // TODO cache the BlockStates using LocalizedListeners + var state = level.getBlockState(relative); + if(!ChannelBlock.is(state, direction.getOpposite(), channel)) + { + continue; + } + + var newState = state; + int power = 0; + boolean input = true; + if(this.isInput(direction, channel)) + { + power = this.getInputPower(direction, channel); + } + else if(this.isOutput(direction, channel)) + { + power = this.getOutputPower(direction, channel); + input = false; + } + newState = newState + .setValue(ChannelBlock.INPUT, input) + .setValue(ChannelBlock.POWER, power); + + if(powerChanged[channel] || newState != state) + { + level.setBlock(relative, newState, Block.UPDATE_ALL); + level.updateNeighborsAt(relative, state.getBlock()); + } + } + } } } } diff --git a/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIO.java b/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIO.java index a6dcd758..ad384856 100644 --- a/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIO.java +++ b/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIO.java @@ -78,7 +78,7 @@ protected void processTickInternal(LogicContext context, boolean[] inputs) boolean originalOutputState = outputState; if(config.input) { - int signal = context.awareness(AwarenessTypes.REDSTONE).getInputPower(config.direction); + int signal = context.awareness(AwarenessTypes.REDSTONE).getInputPower(config.direction, config.channel); outputState = config.signalComparison.test(signal, config.signalStrength); } else @@ -86,7 +86,7 @@ protected void processTickInternal(LogicContext context, boolean[] inputs) outputState = inputs[0]; var redstone = context.awareness(AwarenessTypes.REDSTONE); int signal = outputState ? config.signalStrength : 0; - if(redstone.setOutputPowered(config.direction, signal)) + if(redstone.setOutputPowered(config.direction, config.channel, signal)) { powerChanged = true; } diff --git a/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIOConfig.java b/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIOConfig.java index 9603d1d2..db750bcb 100644 --- a/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIOConfig.java +++ b/src/main/java/net/swedz/little_big_redstone/microchip/object/logic/io/LogicIOConfig.java @@ -30,32 +30,34 @@ public final class LogicIOConfig extends LogicConfig public static final MapCodec CODEC = RecordCodecBuilder.mapCodec((instance) -> instance .group( Codec.BOOL.optionalFieldOf("input", true).forGetter((config) -> config.input), + Codec.intRange(0, 15).optionalFieldOf("channel", 0).forGetter((config) -> config.channel), Direction.CODEC.optionalFieldOf("direction", Direction.NORTH).forGetter((config) -> config.direction), Codec.intRange(1, 15).optionalFieldOf("signal_strength", 1).forGetter((config) -> config.signalStrength), CodecHelper.forLowercaseEnum(LogicComparisonMode.class).optionalFieldOf("signal_comparison", LogicComparisonMode.GREATER_THAN_OR_EQUAL_TO).forGetter((config) -> config.signalComparison) ) - .apply(instance, (input, direction, signalStrength, precise) -> new LogicIOConfig(true, input, direction, signalStrength, precise))); + .apply(instance, (input, channel, direction, signalStrength, precise) -> new LogicIOConfig(true, input, channel, direction, signalStrength, precise))); public static final StreamCodec STREAM_CODEC = StreamCodec.composite( ByteBufCodecs.BOOL, (config) -> config.valid, ByteBufCodecs.BOOL, (config) -> config.input, + ByteBufCodecs.INT, (config) -> config.channel, Direction.STREAM_CODEC, (config) -> config.direction, ByteBufCodecs.INT, (config) -> config.signalStrength, CodecHelper.forEnumStream(LogicComparisonMode.class), (config) -> config.signalComparison, LogicIOConfig::new ); - public boolean input; - - public Direction direction; - - public int signalStrength; + public boolean input; + public int channel; + public Direction direction; + public int signalStrength; public LogicComparisonMode signalComparison; - private LogicIOConfig(boolean valid, boolean input, Direction direction, int signalStrength, LogicComparisonMode signalComparison) + private LogicIOConfig(boolean valid, boolean input, int channel, Direction direction, int signalStrength, LogicComparisonMode signalComparison) { this.valid = valid; this.input = input; + this.channel = channel; this.direction = direction; this.signalStrength = Mth.clamp(signalStrength, 1, 15); this.signalComparison = signalComparison; @@ -63,7 +65,7 @@ private LogicIOConfig(boolean valid, boolean input, Direction direction, int sig public LogicIOConfig() { - this(true, true, Direction.NORTH, 1, LogicComparisonMode.GREATER_THAN_OR_EQUAL_TO); + this(true, true, 0, Direction.NORTH, 1, LogicComparisonMode.GREATER_THAN_OR_EQUAL_TO); } @Override @@ -72,7 +74,9 @@ protected boolean calculateValidity(LogicComponents components) for(var entry : components) { if(entry.component().config() != this && entry.component().config() instanceof LogicIOConfig entryConfig && - input != entryConfig.input && direction == entryConfig.direction) + input != entryConfig.input && + channel == entryConfig.channel && + direction == entryConfig.direction) { return false; } @@ -108,6 +112,7 @@ public int outputs() public void appendHoverText(List lines) { lines.add(LBR.text().logicConfigTooltipMode(input ? LogicMode.input() : LogicMode.output())); + lines.add(LBR.text().logicConfigTooltipIOChannel(channel)); lines.add(LBR.text().logicConfigTooltipDirection(direction)); if(input) { @@ -170,15 +175,17 @@ public void buildMenu(LogicConfigMenuBuilder builder, int width, int height) updateComparisonButtonTooltip.run(); }); - builder.addCycleButton(LBR.text().logicConfigButtonLabelDirection(), LBR.text().logicConfigButtonTooltipIoDirection(), 0, 22, width, 18, false, direction, Arrays.asList(Direction.values()), LBRTooltips.DIRECTION_PARSER::parse, (value) -> direction = value); + builder.addSlider(LBR.text().logicConfigButtonLabelIOChannel(), Component.empty(), LBR.text().logicConfigButtonTooltipIOChannel(), 0, 22, width, 18, 0, 15, channel, 1, 0, (value) -> channel = value.intValue()); + + builder.addCycleButton(LBR.text().logicConfigButtonLabelDirection(), LBR.text().logicConfigButtonTooltipIoDirection(), 0, 22 * 2, width, 18, false, direction, Arrays.asList(Direction.values()), LBRTooltips.DIRECTION_PARSER::parse, (value) -> direction = value); - signalStrengthSlider.set(builder.addSlider(LBR.text().logicConfigButtonLabelIoSignalStrength(), Component.empty(), input ? LBR.text().logicConfigButtonTooltipIoSignalStrengthInput() : LBR.text().logicConfigButtonTooltipIoSignalStrengthOutput(), 18 + 4, 22 * 2, width - 18 - 4, 18, 1, 15, signalStrength, 1, 0, (value) -> + signalStrengthSlider.set(builder.addSlider(LBR.text().logicConfigButtonLabelIoSignalStrength(), Component.empty(), input ? LBR.text().logicConfigButtonTooltipIoSignalStrengthInput() : LBR.text().logicConfigButtonTooltipIoSignalStrengthOutput(), 18 + 4, 22 * 3, width - 18 - 4, 18, 1, 15, signalStrength, 1, 0, (value) -> { signalStrength = value.intValue(); updateComparisonButtonTooltip.run(); })); - comparisonButton.set(builder.addCycleButton(this.signalComparisonTooltip(signalStrength), 0, 22 * 2, LBR.id("textures/gui/slot_atlas.png"), signalComparison, Arrays.asList(LogicComparisonMode.values()), (value) -> + comparisonButton.set(builder.addCycleButton(this.signalComparisonTooltip(signalStrength), 0, 22 * 3, LBR.id("textures/gui/slot_atlas.png"), signalComparison, Arrays.asList(LogicComparisonMode.values()), (value) -> { signalComparison = value; updateComparisonButtonTooltip.run(); @@ -191,6 +198,7 @@ public void buildMenu(LogicConfigMenuBuilder builder, int width, int height) protected void internalLoadFrom(LogicIOConfig other) { input = other.input; + channel = other.channel; direction = other.direction; signalStrength = other.signalStrength; signalComparison = other.signalComparison; @@ -205,13 +213,13 @@ public void resetForPickup() @Override public int hashCode() { - return Objects.hash(input, direction, signalStrength, signalComparison); + return Objects.hash(input, channel, direction, signalStrength, signalComparison); } @Override public boolean equals(Object o) { return this == o || - (o instanceof LogicIOConfig other && input == other.input && direction == other.direction && signalStrength == other.signalStrength && signalComparison == other.signalComparison); + (o instanceof LogicIOConfig other && input == other.input && channel == other.channel && direction == other.direction && signalStrength == other.signalStrength && signalComparison == other.signalComparison); } } \ No newline at end of file diff --git a/src/main/resources/assets/little_big_redstone/textures/block/channel_end.png b/src/main/resources/assets/little_big_redstone/textures/block/channel_end.png new file mode 100644 index 00000000..002ad40e Binary files /dev/null and b/src/main/resources/assets/little_big_redstone/textures/block/channel_end.png differ diff --git a/src/main/resources/assets/little_big_redstone/textures/block/channel_side.png b/src/main/resources/assets/little_big_redstone/textures/block/channel_side.png new file mode 100644 index 00000000..98ac33a2 Binary files /dev/null and b/src/main/resources/assets/little_big_redstone/textures/block/channel_side.png differ