From bf2a506bb87d4527eed8e41c3ceadc7d78762b09 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Sun, 28 Mar 2021 18:57:50 +0200 Subject: [PATCH] A final thought - Fixed non-splitting modes on brass tunnels - Ponder scenes for ejectors and tunnels --- .../BeltTunnelInteractionHandler.java | 4 +- .../belts/tunnel/BeltTunnelTileEntity.java | 2 +- .../belts/tunnel/BrassTunnelTileEntity.java | 7 +- .../logistics/block/depot/DepotBehaviour.java | 2 +- .../block/depot/EjectorTileEntity.java | 16 +- .../ponder/content/EjectorScenes.java | 373 ++++++++++++ .../ponder/content/PonderIndex.java | 10 + .../ponder/content/TunnelScenes.java | 564 ++++++++++++++++++ .../ponder/elements/InputWindowElement.java | 5 + .../resources/ponder/tunnels/andesite.nbt | Bin 0 -> 1062 bytes src/main/resources/ponder/tunnels/brass.nbt | Bin 0 -> 1669 bytes .../resources/ponder/tunnels/brass_modes.nbt | Bin 0 -> 1288 bytes .../ponder/weighted_ejector/eject.nbt | Bin 0 -> 902 bytes .../ponder/weighted_ejector/redstone.nbt | Bin 0 -> 907 bytes .../ponder/weighted_ejector/split.nbt | Bin 0 -> 1217 bytes 15 files changed, 973 insertions(+), 10 deletions(-) create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/EjectorScenes.java create mode 100644 src/main/java/com/simibubi/create/foundation/ponder/content/TunnelScenes.java create mode 100644 src/main/resources/ponder/tunnels/andesite.nbt create mode 100644 src/main/resources/ponder/tunnels/brass.nbt create mode 100644 src/main/resources/ponder/tunnels/brass_modes.nbt create mode 100644 src/main/resources/ponder/weighted_ejector/eject.nbt create mode 100644 src/main/resources/ponder/weighted_ejector/redstone.nbt create mode 100644 src/main/resources/ponder/weighted_ejector/split.nbt diff --git a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java index 08f3dcb0a..b239b65b9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/relays/belt/transport/BeltTunnelInteractionHandler.java @@ -42,7 +42,7 @@ public class BeltTunnelInteractionHandler { } World world = beltInventory.belt.getWorld(); - boolean onServer = !world.isRemote; + boolean onServer = !world.isRemote || beltInventory.belt.isVirtual(); boolean removed = false; BeltTunnelTileEntity nextTunnel = getTunnelOnSegement(beltInventory, upcomingSegment); @@ -128,7 +128,7 @@ public class BeltTunnelInteractionHandler { BeltTunnelTileEntity te = getTunnelOnSegement(beltInventory, offset); if (te == null) return; - te.flap(side, inward ^ side.getAxis() == Axis.Z); + te.flap(side, inward); } protected static BeltTunnelTileEntity getTunnelOnSegement(BeltInventory beltInventory, int offset) { diff --git a/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BeltTunnelTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BeltTunnelTileEntity.java index 4bc4cf3b6..aaf96c230 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BeltTunnelTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BeltTunnelTileEntity.java @@ -147,7 +147,7 @@ public class BeltTunnelTileEntity extends SmartTileEntity implements IInstanceRe if (world.isRemote) { if (flaps.containsKey(side)) flaps.get(side) - .set(inward ? -1 : 1); + .set(inward ^ side.getAxis() == Axis.Z ? -1 : 1); return; } diff --git a/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BrassTunnelTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BrassTunnelTileEntity.java index f24f86018..cca38928c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BrassTunnelTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/belts/tunnel/BrassTunnelTileEntity.java @@ -115,7 +115,7 @@ public class BrassTunnelTileEntity extends BeltTunnelTileEntity { return; if (stackToDistribute.isEmpty() && !syncedOutputActive) return; - if (world.isRemote) + if (world.isRemote && !isVirtual()) return; if (distributionProgress == -1) { @@ -206,6 +206,7 @@ public class BrassTunnelTileEntity extends BeltTunnelTileEntity { SelectionMode mode = selectionMode.get(); boolean force = mode == SelectionMode.FORCED_ROUND_ROBIN || mode == SelectionMode.FORCED_SPLIT; boolean split = mode == SelectionMode.FORCED_SPLIT || mode == SelectionMode.SPLIT; + boolean robin = mode == SelectionMode.FORCED_ROUND_ROBIN || mode == SelectionMode.ROUND_ROBIN; if (mode == SelectionMode.RANDOMIZE) indexStart = rand.nextInt(amountTargets); @@ -265,6 +266,8 @@ public class BrassTunnelTileEntity extends BeltTunnelTileEntity { remainingOutputs--; if (!simulate) full.add(pair); + if (robin) + break; continue; } else if (!remainder.isEmpty() && !simulate) { full.add(pair); @@ -336,7 +339,7 @@ public class BrassTunnelTileEntity extends BeltTunnelTileEntity { return null; ItemStack result = sideOutput.handleInsertion(stack, side, simulate); if (result.isEmpty() && !simulate) - tunnel.flap(side, true); + tunnel.flap(side, false); return result; } diff --git a/src/main/java/com/simibubi/create/content/logistics/block/depot/DepotBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/block/depot/DepotBehaviour.java index f79ef2fe2..ce9bf9820 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/depot/DepotBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/depot/DepotBehaviour.java @@ -80,7 +80,7 @@ public class DepotBehaviour extends TileEntityBehaviour { TransportedItemStack ts = iterator.next(); if (!tick(ts)) continue; - if (world.isRemote) + if (world.isRemote && !tileEntity.isVirtual()) continue; if (heldItem == null) { heldItem = ts; diff --git a/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTileEntity.java index 69cc84372..a5d0bf6e1 100644 --- a/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/block/depot/EjectorTileEntity.java @@ -135,7 +135,8 @@ public class EjectorTileEntity extends KineticTileEntity { world.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(pos).grow(-1 / 16f, 0, -1 / 16f)); // Launch Items - if (!world.isRemote) + boolean doLogic = !world.isRemote || isVirtual(); + if (doLogic) launchItems(); // Launch Entities @@ -169,11 +170,13 @@ public class EjectorTileEntity extends KineticTileEntity { AllPackets.channel.sendToServer(new EjectorElytraPacket(pos)); } - if (!world.isRemote) { + if (doLogic) { lidProgress.chase(1, .8f, Chaser.EXP); state = State.LAUNCHING; - world.playSound(null, pos, SoundEvents.BLOCK_WOODEN_TRAPDOOR_CLOSE, SoundCategory.BLOCKS, .35f, 1f); - world.playSound(null, pos, SoundEvents.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, .1f, 1.4f); + if (!world.isRemote) { + world.playSound(null, pos, SoundEvents.BLOCK_WOODEN_TRAPDOOR_CLOSE, SoundCategory.BLOCKS, .35f, 1f); + world.playSound(null, pos, SoundEvents.BLOCK_CHEST_OPEN, SoundCategory.BLOCKS, .1f, 1.4f); + } } } @@ -369,6 +372,8 @@ public class EjectorTileEntity extends KineticTileEntity { return; if (presentStackSize < maxStackSize.getValue()) return; + if (depotBehaviour.heldItem != null && depotBehaviour.heldItem.beltPosition < .49f) + return; Direction funnelFacing = getFacing().getOpposite(); ItemStack held = depotBehaviour.getHeldItemStack(); @@ -502,6 +507,9 @@ public class EjectorTileEntity extends KineticTileEntity { NBTUtil.readBlockPos(compound.getCompound("EarlyTargetPos"))); earlyTargetTime = compound.getFloat("EarlyTargetTime"); } + + if (compound.contains("ForceAngle")) + lidProgress.startWithValue(compound.getFloat("ForceAngle")); } public void updateSignal() { diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/EjectorScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/EjectorScenes.java new file mode 100644 index 000000000..22718cf47 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/EjectorScenes.java @@ -0,0 +1,373 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; +import com.simibubi.create.content.logistics.block.depot.EjectorTileEntity; +import com.simibubi.create.foundation.gui.AllIcons; +import com.simibubi.create.foundation.ponder.ElementLink; +import com.simibubi.create.foundation.ponder.SceneBuilder; +import com.simibubi.create.foundation.ponder.SceneBuildingUtil; +import com.simibubi.create.foundation.ponder.Selection; +import com.simibubi.create.foundation.ponder.elements.InputWindowElement; +import com.simibubi.create.foundation.ponder.elements.ParrotElement; +import com.simibubi.create.foundation.ponder.elements.WorldSectionElement; +import com.simibubi.create.foundation.utility.NBTHelper; +import com.simibubi.create.foundation.utility.Pointing; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Direction; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraftforge.items.ItemHandlerHelper; + +public class EjectorScenes { + + public static void ejector(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("weighted_ejector", "Using Weighted Ejectors"); + scene.configureBasePlate(0, 0, 5); + scene.showBasePlate(); + + BlockPos ejectorPos = util.grid.at(4, 1, 2); + Selection ejectorS = util.select.position(ejectorPos); + BlockPos targetPos = util.grid.at(0, 1, 2); + Selection targetS = util.select.position(targetPos); + + scene.world.setBlock(targetPos, AllBlocks.ANDESITE_CASING.getDefaultState(), false); + scene.idle(5); + scene.world.showSection(targetS, Direction.DOWN); + + scene.idle(10); + ItemStack asStack = AllBlocks.WEIGHTED_EJECTOR.asStack(); + scene.overlay.showControls(new InputWindowElement(util.vector.topOf(targetPos), Pointing.DOWN).rightClick() + .whileSneaking() + .withItem(asStack), 50); + scene.idle(7); + Object slot = new Object(); + scene.overlay.chaseBoundingBoxOutline(PonderPalette.OUTPUT, slot, new AxisAlignedBB(targetPos), 160); + + scene.overlay.showText(70) + .attachKeyFrame() + .colored(PonderPalette.OUTPUT) + .text("Sneak and Right-Click holding an Ejector to select its target location") + .pointAt(util.vector.blockSurface(targetPos, Direction.WEST)) + .placeNearTarget(); + scene.idle(80); + scene.overlay.showControls(new InputWindowElement(util.vector.topOf(ejectorPos), Pointing.DOWN).rightClick() + .withItem(asStack), 50); + scene.idle(7); + scene.world.setKineticSpeed(ejectorS, 0); + scene.world.modifyTileNBT(ejectorS, EjectorTileEntity.class, nbt -> { + NBTHelper.writeEnum(nbt, "State", EjectorTileEntity.State.RETRACTING); + nbt.putFloat("ForceAngle", 1); + }); + scene.world.showSection(ejectorS, Direction.DOWN); + scene.idle(10); + + scene.overlay.showText(60) + .colored(PonderPalette.OUTPUT) + .text("The placed ejector will now launch objects to the marked location") + .pointAt(util.vector.blockSurface(ejectorPos, Direction.WEST)) + .placeNearTarget(); + scene.idle(70); + + slot = new Object(); + AxisAlignedBB bb = new AxisAlignedBB(ejectorPos.west()); + scene.overlay.chaseBoundingBoxOutline(PonderPalette.OUTPUT, slot, bb, 20); + scene.idle(10); + scene.overlay.chaseBoundingBoxOutline(PonderPalette.GREEN, slot, bb.expand(-15, 15, 0), 100); + scene.idle(10); + + scene.overlay.showText(60) + .attachKeyFrame() + .colored(PonderPalette.GREEN) + .text("A valid target can be at any height or distance within range") + .pointAt(util.vector.blockSurface(targetPos, Direction.WEST)) + .placeNearTarget(); + scene.idle(70); + scene.overlay.chaseBoundingBoxOutline(PonderPalette.RED, new Object(), bb.offset(-2, 0, -1), 60); + scene.idle(10); + scene.overlay.showText(50) + .colored(PonderPalette.RED) + .text("They cannot however be off to a side") + .pointAt(util.vector.blockSurface(targetPos.north() + .east(), Direction.WEST)) + .placeNearTarget(); + scene.idle(70); + scene.overlay.showSelectionWithText(util.select.position(ejectorPos.west()), 70) + .colored(PonderPalette.OUTPUT) + .text("If no valid Target was selected, it will simply target the block directly in front") + .placeNearTarget(); + scene.idle(80); + + scene.world.showSection(util.select.position(3, 0, 5), Direction.UP); + scene.world.showSection(util.select.fromTo(4, 1, 5, 4, 1, 3), Direction.DOWN); + scene.idle(12); + scene.world.setKineticSpeed(ejectorS, 32); + scene.idle(10); + scene.overlay.showText(50) + .attachKeyFrame() + .text("Supply Rotational Force in order to charge it up") + .pointAt(util.vector.topOf(4, 1, 3)) + .placeNearTarget(); + scene.idle(60); + + ItemStack copperBlock = AllBlocks.COPPER_BLOCK.asStack(); + ItemStack copperIngot = AllItems.COPPER_INGOT.asStack(); + scene.overlay.showControls(new InputWindowElement(util.vector.topOf(ejectorPos) + .add(0.5, 0, 0), Pointing.RIGHT).withItem(copperBlock), 30); + scene.idle(7); + scene.world.createItemOnBeltLike(ejectorPos, Direction.NORTH, copperBlock); + scene.idle(20); + scene.overlay.showText(50) + .text("Items placed on the ejector cause it to trigger") + .pointAt(util.vector.topOf(ejectorPos)) + .placeNearTarget(); + scene.idle(60); + + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.world.hideSection(targetS, Direction.SOUTH); + scene.idle(15); + scene.world.restoreBlocks(targetS); + scene.world.showSection(targetS, Direction.SOUTH); + scene.idle(10); + scene.world.createItemOnBeltLike(targetPos, Direction.SOUTH, copperIngot); + scene.idle(20); + scene.world.createItemOnBeltLike(ejectorPos, Direction.SOUTH, copperBlock); + scene.overlay.showText(60) + .attachKeyFrame() + .text("If Inventories are targeted, the ejector will wait until there is space") + .pointAt(util.vector.topOf(targetPos)) + .placeNearTarget(); + scene.idle(70); + scene.effects.indicateSuccess(targetPos); + scene.world.removeItemsFromBelt(targetPos); + scene.idle(40); + scene.world.hideSection(targetS, Direction.NORTH); + scene.idle(15); + scene.world.setBlock(targetPos, AllBlocks.ANDESITE_CASING.getDefaultState(), false); + scene.world.showSection(targetS, Direction.NORTH); + + Vec3d input = util.vector.of(4.8, 1 + 12 / 16f, 2.5); + Vec3d topOfSlot = input.add(0, 2 / 16f, 0); + scene.overlay.showControls(new InputWindowElement(topOfSlot, Pointing.DOWN).scroll() + .withWrench(), 60); + scene.overlay.showFilterSlotInput(input, 80); + scene.idle(10); + scene.overlay.showText(80) + .attachKeyFrame() + .text("Using the Wrench, a required Stack Size can be configured") + .pointAt(topOfSlot) + .placeNearTarget(); + scene.world.modifyTileNBT(ejectorS, EjectorTileEntity.class, nbt -> { + nbt.putInt("ScrollValue", 10); + }); + scene.idle(90); + + scene.world.showSection(util.select.fromTo(5, 1, 0, 4, 1, 1), Direction.DOWN); + scene.world.showSection(util.select.position(5, 0, 1), Direction.UP); + scene.idle(15); + + BlockPos beltPos = util.grid.at(4, 1, 0); + scene.world.createItemOnBeltLike(beltPos, Direction.UP, copperBlock); + scene.overlay.showText(100) + .text("It is now limited to this stack size, and only activates when its held stack reaches this amount") + .pointAt(util.vector.topOf(ejectorPos)) + .placeNearTarget(); + for (int i = 0; i < 4; i++) { + scene.idle(20); + scene.world.createItemOnBeltLike(beltPos, Direction.UP, copperBlock); + } + scene.idle(20); + scene.world.createItemOnBeltLike(beltPos, Direction.UP, ItemHandlerHelper.copyStackWithSize(copperBlock, 15)); + scene.idle(80); + + scene.world.hideSection(util.select.fromTo(5, 1, 0, 4, 1, 1), Direction.UP); + scene.world.hideSection(util.select.position(5, 0, 1), Direction.DOWN); + scene.idle(30); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + + scene.addKeyframe(); + ElementLink birb = scene.special.createBirb(util.vector.topOf(ejectorPos) + .add(0, -3 / 16f, 0), ParrotElement.FlappyPose::new); + scene.idle(15); + scene.world.modifyTileEntity(ejectorPos, EjectorTileEntity.class, ejector -> ejector.activateDeferred()); + scene.special.moveParrot(birb, util.vector.of(-2, 3, 0), 5); + scene.special.rotateParrot(birb, 0, 360 * 2, 0, 21); + scene.idle(5); + scene.special.moveParrot(birb, util.vector.of(-1, 0, 0), 3); + scene.idle(3); + scene.special.moveParrot(birb, util.vector.of(-0.75, -1, 0), 6); + scene.idle(6); + scene.special.moveParrot(birb, util.vector.of(-0.25, -2 + 3 / 16f, 0), 12); + scene.idle(15); + scene.special.changeBirbPose(birb, ParrotElement.FaceCursorPose::new); + scene.overlay.showText(80) + .text("Other Entities will always trigger an Ejector when stepping on it") + .pointAt(util.vector.topOf(targetPos)) + .placeNearTarget(); + + } + + public static void splitY(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("weighted_ejector_tunnel", "Splitting item stacks using Weighted Ejectors"); + scene.configureBasePlate(0, 0, 5); + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + scene.world.showSection(util.select.fromTo(4, 1, 5, 0, 1, 3), Direction.DOWN); + scene.idle(5); + scene.world.showSection(util.select.position(2, 2, 3), Direction.DOWN); + scene.idle(10); + scene.world.showSection(util.select.position(2, 1, 2), Direction.SOUTH); + scene.idle(5); + scene.world.showSection(util.select.fromTo(4, 1, 2, 3, 1, 1), Direction.SOUTH); + scene.world.showSection(util.select.fromTo(2, 1, 1, 2, 1, 0), Direction.SOUTH); + scene.idle(10); + + BlockPos ejectorPos = util.grid.at(2, 1, 2); + + scene.overlay.showText(80) + .attachKeyFrame() + .text("Combined with Brass Tunnels, Ejectors can split item stacks by specific amounts") + .pointAt(util.vector.topOf(ejectorPos)) + .placeNearTarget(); + scene.idle(90); + + BlockPos tunnel = util.grid.at(2, 2, 3); + scene.overlay.showControls(new InputWindowElement(util.vector.topOf(tunnel), Pointing.DOWN).scroll() + .withWrench(), 70); + scene.idle(10); + scene.overlay.showControls( + new InputWindowElement(util.vector.topOf(tunnel), Pointing.UP).showing(AllIcons.I_TUNNEL_PREFER_NEAREST), + 60); + scene.overlay.showCenteredScrollInput(tunnel, Direction.UP, 100); + scene.idle(10); + scene.overlay.showText(100) + .attachKeyFrame() + .colored(PonderPalette.BLUE) + .text("First, configure the Brass Tunnel to 'Prefer Nearest', in order to prioritize its side output") + .pointAt(util.vector.topOf(tunnel)) + .placeNearTarget(); + scene.idle(110); + + Vec3d input = util.vector.of(2.5, 1 + 12 / 16f, 2.8); + Vec3d topOfSlot = input.add(0, 2 / 16f, 0); + scene.overlay.showControls(new InputWindowElement(topOfSlot, Pointing.DOWN).scroll() + .withWrench(), 60); + scene.overlay.showFilterSlotInput(input, 80); + scene.idle(10); + scene.overlay.showText(80) + .attachKeyFrame() + .text("The Stack Size set on the Ejector now determines the amount to be split off") + .pointAt(topOfSlot) + .placeNearTarget(); + scene.world.modifyTileNBT(util.select.position(2, 1, 2), EjectorTileEntity.class, nbt -> { + nbt.putInt("ScrollValue", 10); + }); + scene.idle(90); + + scene.overlay.showControls(new InputWindowElement(util.vector.topOf(util.grid.at(4, 1, 3)), Pointing.DOWN) + .withItem(AllItems.COPPER_INGOT.asStack()), 20); + scene.idle(7); + scene.world.createItemOnBelt(util.grid.at(4, 1, 3), Direction.UP, AllItems.COPPER_INGOT.asStack(64)); + scene.idle(40); + scene.world.multiplyKineticSpeed(util.select.everywhere(), 1 / 16f); + scene.overlay.showText(80) + .attachKeyFrame() + .text("While a new stack of the configured size exits the side output...") + .pointAt(util.vector.blockSurface(util.grid.at(2, 1, 1), Direction.WEST)) + .placeNearTarget(); + scene.idle(90); + scene.overlay.showText(80) + .text("...the remainder will continue on its path") + .pointAt(util.vector.blockSurface(util.grid.at(0, 1, 3), Direction.UP)) + .placeNearTarget(); + scene.idle(90); + scene.world.multiplyKineticSpeed(util.select.everywhere(), 16f); + } + + public static void redstone(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("weighted_ejector_redstone", "Controlling Weighted Ejectors with Redstone"); + scene.configureBasePlate(0, 0, 5); + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + scene.world.showSection(util.select.fromTo(4, 1, 3, 4, 1, 5), Direction.DOWN); + scene.idle(5); + scene.world.showSection(util.select.fromTo(0, 1, 2, 0, 2, 2), Direction.DOWN); + scene.idle(10); + scene.world.showSection(util.select.position(4, 1, 2), Direction.SOUTH); + scene.idle(5); + Selection redstone = util.select.fromTo(3, 1, 2, 2, 1, 2); + scene.world.showSection(redstone, Direction.EAST); + + BlockPos ejectorPos = util.grid.at(4, 1, 2); + Vec3d topOf = util.vector.topOf(ejectorPos.up(2)); + ItemStack copper = AllItems.COPPER_INGOT.asStack(); + + for (int i = 0; i < 3; i++) { + scene.world.createItemEntity(topOf, util.vector.of(0, 0.1, 0), copper); + scene.idle(12); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.world.createItemOnBeltLike(ejectorPos, Direction.UP, copper); + scene.idle(20); + if (i == 1) { + scene.world.toggleRedstonePower(redstone); + scene.effects.indicateRedstone(util.grid.at(2, 1, 2)); + scene.world.modifyTileNBT(util.select.position(4, 1, 2), EjectorTileEntity.class, + nbt -> nbt.putBoolean("Powered", true)); + } + } + + scene.idle(10); + scene.overlay.showText(60) + .colored(PonderPalette.RED) + .attachKeyFrame() + .pointAt(util.vector.topOf(ejectorPos)) + .placeNearTarget() + .text("When powered by Redstone, Ejectors will not activate"); + scene.idle(70); + + scene.world.toggleRedstonePower(redstone); + scene.idle(2); + scene.world.modifyTileNBT(util.select.position(4, 1, 2), EjectorTileEntity.class, + nbt -> nbt.putBoolean("Powered", false)); + scene.idle(5); + scene.world.hideSection(redstone, Direction.WEST); + scene.idle(10); + ElementLink observer = + scene.world.showIndependentSection(util.select.position(4, 1, 1), Direction.SOUTH); + scene.world.moveSection(observer, util.vector.of(0.5, 1.5, -0.5), 0); + scene.world.rotateSection(observer, 0, 30 - 180, 0, 0); + scene.idle(20); + scene.world.moveSection(observer, util.vector.of(-0.5, -1.5, 0.5), 10); + scene.world.rotateSection(observer, 0, -30 + 180, 0, 10); + scene.world.showSection(util.select.position(4, 1, 0), Direction.SOUTH); + + Selection observerRedstone = util.select.fromTo(4, 1, 1, 4, 1, 0); + for (int i = 0; i < 6; i++) { + scene.world.createItemEntity(topOf, util.vector.of(0, 0.1, 0), copper); + scene.idle(12); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.world.createItemOnBeltLike(ejectorPos, Direction.UP, copper); + scene.idle(1); + scene.world.toggleRedstonePower(observerRedstone); + scene.effects.indicateRedstone(util.grid.at(4, 1, 1)); + scene.idle(3); + scene.world.toggleRedstonePower(observerRedstone); + scene.idle(16); + if (i == 3) + scene.markAsFinished(); + if (i == 1) { + scene.overlay.showText(60) + .attachKeyFrame() + .pointAt(util.vector.blockSurface(util.grid.at(4, 1, 1), Direction.NORTH)) + .placeNearTarget() + .text("Furthermore, Observers can detect when Ejectors activate"); + } + } + + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java index 3ade7e53e..942d0f754 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/PonderIndex.java @@ -96,6 +96,10 @@ public class PonderIndex { ProcessingScenes::emptyBlazeBurner); PonderRegistry.addStoryBoard(AllBlocks.BLAZE_BURNER, "blaze_burner", ProcessingScenes::blazeBurner); PonderRegistry.addStoryBoard(AllBlocks.DEPOT, "depot", BeltScenes::depot); + PonderRegistry.forComponents(AllBlocks.WEIGHTED_EJECTOR) + .addStoryBoard("weighted_ejector/eject", EjectorScenes::ejector) + .addStoryBoard("weighted_ejector/split", EjectorScenes::splitY) + .addStoryBoard("weighted_ejector/redstone", EjectorScenes::redstone); // Crafters PonderRegistry.forComponents(AllBlocks.MECHANICAL_CRAFTER) @@ -121,6 +125,12 @@ public class PonderIndex { .addStoryBoard("funnels/transposer", FunnelScenes::transposer); PonderRegistry.addStoryBoard(AllBlocks.ANDESITE_FUNNEL, "funnels/brass", FunnelScenes::brass); + // Tunnels + PonderRegistry.addStoryBoard(AllBlocks.ANDESITE_TUNNEL, "tunnels/andesite", TunnelScenes::andesite); + PonderRegistry.forComponents(AllBlocks.BRASS_TUNNEL) + .addStoryBoard("tunnels/brass", TunnelScenes::brass) + .addStoryBoard("tunnels/brass_modes", TunnelScenes::brassModes); + // Chassis & Super Glue PonderRegistry.forComponents(AllBlocks.LINEAR_CHASSIS, AllBlocks.SECONDARY_LINEAR_CHASSIS) .addStoryBoard("chassis/linear_group", ChassisScenes::linearGroup, PonderTag.CONTRAPTION_ASSEMBLY) diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/TunnelScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/TunnelScenes.java new file mode 100644 index 000000000..01af4b41f --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/TunnelScenes.java @@ -0,0 +1,564 @@ +package com.simibubi.create.foundation.ponder.content; + +import java.util.Vector; + +import com.simibubi.create.AllItems; +import com.simibubi.create.content.contraptions.relays.belt.BeltBlock; +import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity; +import com.simibubi.create.content.logistics.block.belts.tunnel.BrassTunnelTileEntity; +import com.simibubi.create.foundation.gui.AllIcons; +import com.simibubi.create.foundation.ponder.ElementLink; +import com.simibubi.create.foundation.ponder.SceneBuilder; +import com.simibubi.create.foundation.ponder.SceneBuildingUtil; +import com.simibubi.create.foundation.ponder.elements.InputWindowElement; +import com.simibubi.create.foundation.ponder.elements.WorldSectionElement; +import com.simibubi.create.foundation.tileEntity.behaviour.filtering.SidedFilteringBehaviour; +import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollOptionBehaviour; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.NBTHelper; +import com.simibubi.create.foundation.utility.Pointing; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.ItemEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +public class TunnelScenes { + + public static void andesite(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("andesite_tunnel", "Using Andesite Tunnels"); + scene.configureBasePlate(0, 0, 5); + + scene.world.cycleBlockProperty(util.grid.at(2, 1, 2), BeltBlock.CASING); + + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + scene.world.showSection(util.select.fromTo(4, 1, 5, 4, 1, 3), Direction.DOWN); + scene.idle(5); + scene.world.showSection(util.select.fromTo(4, 1, 2, 0, 1, 2), Direction.SOUTH); + scene.idle(10); + + Vector> tunnels = new Vector<>(3); + for (int i = 0; i < 3; i++) { + tunnels.add(scene.world.showIndependentSection(util.select.position(1 + i, 2, 4), Direction.DOWN)); + scene.world.moveSection(tunnels.get(i), util.vector.of(0, 0, -2), 0); + scene.idle(4); + } + + for (int i = 0; i < 3; i++) { + scene.world.cycleBlockProperty(util.grid.at(1 + i, 1, 2), BeltBlock.CASING); + scene.world.modifyTileNBT(util.select.position(1 + i, 1, 2), BeltTileEntity.class, + nbt -> NBTHelper.writeEnum(nbt, "Casing", BeltTileEntity.CasingType.ANDESITE), true); + scene.idle(4); + } + + scene.overlay.showText(60) + .attachKeyFrame() + .pointAt(util.vector.topOf(util.grid.at(1, 2, 2))) + .placeNearTarget() + .text("Andesite Tunnels can be used to cover up your belts"); + scene.idle(70); + + for (int i = 0; i < 3; i++) { + scene.world.cycleBlockProperty(util.grid.at(1 + i, 1, 2), BeltBlock.CASING); + scene.world.hideIndependentSection(tunnels.get(i), Direction.UP); + scene.idle(4); + } + scene.idle(10); + scene.world.showSection(util.select.fromTo(2, 1, 0, 0, 1, 1), Direction.SOUTH); + scene.idle(10); + scene.world.showSection(util.select.position(2, 2, 2), Direction.DOWN); + scene.idle(10); + scene.world.cycleBlockProperty(util.grid.at(2, 1, 2), BeltBlock.CASING); + + scene.overlay.showText(60) + .attachKeyFrame() + .pointAt(util.vector.blockSurface(util.grid.at(2, 2, 2), Direction.NORTH)) + .placeNearTarget() + .text("Whenever an Andesite Tunnel has connections to the sides..."); + scene.idle(70); + + scene.overlay.showControls(new InputWindowElement(util.vector.topOf(util.grid.at(4, 1, 2)), Pointing.DOWN) + .withItem(AllItems.COPPER_INGOT.asStack()), 20); + scene.idle(7); + scene.world.createItemOnBelt(util.grid.at(4, 1, 2), Direction.UP, AllItems.COPPER_INGOT.asStack(64)); + scene.idle(40); + scene.world.multiplyKineticSpeed(util.select.everywhere(), 1 / 16f); + scene.overlay.showText(80) + .attachKeyFrame() + .text("...they will split exactly one item off of any passing stacks") + .pointAt(util.vector.blockSurface(util.grid.at(2, 1, 0), Direction.WEST)) + .placeNearTarget(); + scene.idle(90); + scene.overlay.showText(80) + .text("The remainder will continue on its path") + .pointAt(util.vector.blockSurface(util.grid.at(0, 1, 2), Direction.UP)) + .placeNearTarget(); + scene.idle(90); + scene.world.multiplyKineticSpeed(util.select.everywhere(), 16f); + } + + public static void brass(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("brass_tunnel", "Using Brass Tunnels"); + scene.configureBasePlate(1, 0, 5); + scene.world.cycleBlockProperty(util.grid.at(3, 1, 2), BeltBlock.CASING); + + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + scene.world.showSection(util.select.fromTo(5, 1, 5, 5, 1, 3), Direction.DOWN); + scene.idle(5); + scene.world.showSection(util.select.fromTo(5, 1, 2, 1, 1, 2), Direction.SOUTH); + scene.idle(10); + + Vector> tunnels = new Vector<>(3); + for (int i = 0; i < 3; i++) { + tunnels.add(scene.world.showIndependentSection(util.select.position(2 + i, 2, 4), Direction.DOWN)); + scene.world.moveSection(tunnels.get(i), util.vector.of(0, 0, -2), 0); + scene.idle(4); + } + + for (int i = 0; i < 3; i++) { + scene.world.cycleBlockProperty(util.grid.at(2 + i, 1, 2), BeltBlock.CASING); + scene.world.modifyTileNBT(util.select.position(2 + i, 1, 2), BeltTileEntity.class, + nbt -> NBTHelper.writeEnum(nbt, "Casing", BeltTileEntity.CasingType.BRASS), true); + scene.idle(4); + } + + scene.overlay.showText(60) + .attachKeyFrame() + .pointAt(util.vector.topOf(util.grid.at(2, 2, 2))) + .placeNearTarget() + .text("Brass Tunnels can be used to cover up your belts"); + scene.idle(70); + + for (int i = 0; i < 3; i++) { + scene.world.cycleBlockProperty(util.grid.at(2 + i, 1, 2), BeltBlock.CASING); + scene.world.hideIndependentSection(tunnels.get(i), Direction.UP); + scene.idle(4); + } + scene.idle(10); + scene.world.showSection(util.select.fromTo(3, 1, 0, 1, 1, 1), Direction.SOUTH); + scene.idle(10); + scene.world.showSection(util.select.position(3, 2, 2), Direction.DOWN); + scene.idle(10); + scene.world.cycleBlockProperty(util.grid.at(3, 1, 2), BeltBlock.CASING); + scene.idle(10); + + BlockPos tunnelPos = util.grid.at(3, 2, 2); + for (Direction d : Iterate.horizontalDirections) { + if (d == Direction.SOUTH) + continue; + Vec3d filter = getTunnelFilterVec(tunnelPos, d); + scene.overlay.showFilterSlotInput(filter, 40); + scene.idle(3); + } + + scene.overlay.showText(60) + .attachKeyFrame() + .pointAt(getTunnelFilterVec(tunnelPos, Direction.WEST)) + .placeNearTarget() + .text("Brass Tunnels have filter slots on each open side"); + scene.idle(70); + + scene.rotateCameraY(70); + + scene.idle(20); + Vec3d tunnelFilterVec = getTunnelFilterVec(tunnelPos, Direction.EAST); + scene.overlay.showFilterSlotInput(tunnelFilterVec, 40); + scene.overlay.showText(60) + .attachKeyFrame() + .pointAt(tunnelFilterVec) + .placeNearTarget() + .text("Filters on inbound connections simply block non-matching items"); + ItemStack copper = AllItems.COPPER_INGOT.asStack(); + Class tunnelClass = BrassTunnelTileEntity.class; + scene.world.modifyTileEntity(tunnelPos, tunnelClass, te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.EAST, copper)); + scene.overlay.showControls(new InputWindowElement(tunnelFilterVec, Pointing.DOWN).withItem(copper), 30); + ItemStack zinc = AllItems.ZINC_INGOT.asStack(); + scene.world.createItemOnBelt(util.grid.at(5, 1, 2), Direction.EAST, zinc); + scene.idle(70); + scene.world.multiplyKineticSpeed(util.select.everywhere(), -2); + scene.idle(20); + scene.rotateCameraY(-70); + scene.world.multiplyKineticSpeed(util.select.everywhere(), -.5f); + scene.idle(20); + scene.world.modifyTileEntity(tunnelPos, tunnelClass, te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.EAST, ItemStack.EMPTY)); + + tunnelFilterVec = getTunnelFilterVec(tunnelPos, Direction.NORTH); + scene.overlay.showFilterSlotInput(tunnelFilterVec, 40); + tunnelFilterVec = getTunnelFilterVec(tunnelPos, Direction.WEST); + scene.overlay.showFilterSlotInput(tunnelFilterVec, 40); + scene.overlay.showText(60) + .attachKeyFrame() + .pointAt(tunnelFilterVec) + .placeNearTarget() + .text("Filters on outbound connections can be used to sort items by type"); + scene.idle(70); + + scene.overlay.showControls(new InputWindowElement(tunnelFilterVec, Pointing.LEFT).withItem(copper), 30); + scene.world.modifyTileEntity(tunnelPos, tunnelClass, te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.WEST, copper)); + scene.idle(4); + tunnelFilterVec = getTunnelFilterVec(tunnelPos, Direction.NORTH); + scene.overlay.showControls(new InputWindowElement(tunnelFilterVec, Pointing.RIGHT).withItem(zinc), 30); + scene.world.modifyTileEntity(tunnelPos, tunnelClass, te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.NORTH, zinc)); + + scene.world.multiplyKineticSpeed(util.select.everywhere(), 1.5f); + for (int i = 0; i < 6; i++) { + scene.world.createItemOnBelt(util.grid.at(5, 1, 2), Direction.EAST, i % 2 == 0 ? zinc : copper); + scene.idle(12); + } + + scene.idle(30); + scene.world.modifyTileEntity(tunnelPos, tunnelClass, te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.NORTH, ItemStack.EMPTY)); + scene.world.modifyTileEntity(tunnelPos, tunnelClass, te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.WEST, ItemStack.EMPTY)); + scene.idle(10); + + Vec3d tunnelTop = util.vector.topOf(tunnelPos); + scene.overlay.showControls(new InputWindowElement(tunnelTop, Pointing.DOWN).scroll() + .withWrench(), 80); + scene.idle(7); + scene.overlay.showCenteredScrollInput(tunnelPos, Direction.UP, 120); + scene.overlay.showText(120) + .attachKeyFrame() + .pointAt(tunnelTop) + .placeNearTarget() + .text( + "Whenever a passing item has multiple valid exits, the distribution mode will decide how to handle it"); + for (int i = 0; i < 3; i++) { + scene.idle(40); + scene.world.createItemOnBelt(util.grid.at(5, 1, 2), Direction.EAST, AllItems.BRASS_INGOT.asStack(63)); + } + scene.idle(30); + + scene.world.hideSection(util.select.position(3, 2, 2), Direction.UP); + scene.idle(5); + scene.world.hideSection(util.select.fromTo(5, 1, 2, 1, 1, 0), Direction.UP); + scene.idle(15); + + ElementLink newBelt = + scene.world.showIndependentSection(util.select.fromTo(3, 3, 2, 0, 3, 4) + .add(util.select.fromTo(5, 3, 3, 4, 3, 3)), Direction.DOWN); + scene.world.moveSection(newBelt, util.vector.of(0, -2, -1), 0); + scene.idle(15); + for (int i = 0; i < 3; i++) { + scene.idle(4); + scene.world.showSectionAndMerge(util.select.position(3, 4, 2 + i), Direction.DOWN, newBelt); + } + + scene.overlay.showSelectionWithText(util.select.fromTo(3, 1, 1, 3, 2, 3), 80) + .attachKeyFrame() + .placeNearTarget() + .text("Brass Tunnels on parallel belts will form a group"); + scene.idle(90); + + ItemStack item1 = new ItemStack(Items.CARROT); + ItemStack item2 = new ItemStack(Items.field_226638_pX_); + ItemStack item3 = new ItemStack(Items.SWEET_BERRIES); + + tunnelFilterVec = getTunnelFilterVec(tunnelPos, Direction.WEST); + BlockPos newTunnelPos = tunnelPos.up(2) + .south(); + scene.overlay + .showControls(new InputWindowElement(tunnelFilterVec.add(0, 0, -1), Pointing.RIGHT).withItem(item1), 20); + scene.world.modifyTileEntity(newTunnelPos.north(), tunnelClass, + te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.WEST, item1)); + scene.idle(4); + scene.overlay.showControls(new InputWindowElement(tunnelFilterVec, Pointing.DOWN).withItem(item2), 20); + scene.world.modifyTileEntity(newTunnelPos, tunnelClass, te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.WEST, item2)); + scene.idle(4); + scene.overlay.showControls(new InputWindowElement(tunnelFilterVec.add(0, 0, 1), Pointing.LEFT).withItem(item3), + 20); + scene.world.modifyTileEntity(newTunnelPos.south(), tunnelClass, + te -> te.getBehaviour(SidedFilteringBehaviour.TYPE) + .setFilter(Direction.WEST, item3)); + scene.idle(30); + + scene.overlay.showText(80) + .pointAt(tunnelTop) + .placeNearTarget() + .text("Incoming Items will now be distributed across all connected exits"); + scene.idle(90); + + BlockPos beltPos = util.grid.at(5, 3, 3); + Vec3d m = util.vector.of(0, 0.1, 0); + Vec3d spawn = util.vector.centerOf(util.grid.at(5, 3, 2)); + scene.world.createItemEntity(spawn, m, item1); + scene.idle(12); + scene.world.createItemOnBelt(beltPos, Direction.UP, item1); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.world.createItemEntity(spawn, m, item2); + scene.idle(12); + scene.world.createItemOnBelt(beltPos, Direction.UP, item2); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.world.createItemEntity(spawn, m, item3); + scene.idle(12); + scene.world.createItemOnBelt(beltPos, Direction.UP, item3); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.idle(50); + + scene.world.showSectionAndMerge(util.select.position(3, 5, 2), Direction.DOWN, newBelt); + + scene.overlay.showText(80) + .pointAt(util.vector.blockSurface(tunnelPos.up() + .north(), Direction.WEST)) + .placeNearTarget() + .text("For this, items can also be inserted into the Tunnel block directly"); + scene.idle(20); + + beltPos = util.grid.at(3, 3, 3); + spawn = util.vector.centerOf(util.grid.at(3, 5, 1)); + scene.world.createItemEntity(spawn, m, item1); + scene.idle(12); + scene.world.createItemOnBelt(beltPos, Direction.EAST, item1); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.world.createItemEntity(spawn, m, item2); + scene.idle(12); + scene.world.createItemOnBelt(beltPos, Direction.EAST, item2); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.world.createItemEntity(spawn, m, item3); + scene.idle(12); + scene.world.createItemOnBelt(beltPos, Direction.EAST, item3); + scene.world.modifyEntities(ItemEntity.class, Entity::remove); + scene.idle(30); + + } + + protected static Vec3d getTunnelFilterVec(BlockPos pos, Direction d) { + return VecHelper.getCenterOf(pos) + .add(new Vec3d(d.getDirectionVec()).scale(.5)) + .add(0, 0.3, 0); + } + + public static void brassModes(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("brass_tunnel_modes", "Distribution Modes of the Brass Tunnel"); + scene.configureBasePlate(0, 1, 5); + BlockState barrier = Blocks.BARRIER.getDefaultState(); + scene.world.setBlock(util.grid.at(1, 1, 0), barrier, false); + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + scene.world.showSection(util.select.fromTo(1, 1, 1, 5, 1, 5) + .add(util.select.fromTo(3, 2, 5, 1, 2, 5)), Direction.DOWN); + scene.idle(10); + for (int i = 0; i < 3; i++) { + scene.world.showSection(util.select.position(3 - i, 2, 3), Direction.DOWN); + scene.idle(4); + } + + Vec3d tunnelTop = util.vector.topOf(util.grid.at(2, 2, 3)); + scene.overlay.showControls(new InputWindowElement(tunnelTop, Pointing.DOWN).scroll() + .withWrench(), 80); + scene.idle(7); + scene.overlay.showCenteredScrollInput(util.grid.at(2, 2, 3), Direction.UP, 120); + scene.overlay.showText(120) + .attachKeyFrame() + .pointAt(tunnelTop) + .placeNearTarget() + .text("Using a Wrench, the distribution behaviour of Brass Tunnels can be configured"); + scene.idle(130); + + Class tunnelClass = BrassTunnelTileEntity.class; + ElementLink blockage = + scene.world.showIndependentSection(util.select.position(4, 1, 0), Direction.UP); + scene.world.moveSection(blockage, util.vector.of(-3, 0, 0), 0); + + Vec3d modeVec = util.vector.of(4, 2.5, 3); + scene.overlay.showControls(new InputWindowElement(modeVec, Pointing.RIGHT).showing(AllIcons.I_TUNNEL_SPLIT), + 140); + + ElementLink blockage2 = null; + + for (int i = 0; i < 32; i++) { + if (i < 30) + scene.world.createItemOnBelt(util.grid.at(1, 1, 5), Direction.EAST, new ItemStack(Items.SNOWBALL, 12)); + scene.idle(i > 8 ? 30 : 40); + + if (i == 0) { + scene.overlay.showText(80) + .attachKeyFrame() + .pointAt(tunnelTop) + .placeNearTarget() + .text("'Split' will attempt to distribute the stack evenly between available outputs"); + } + + if (i == 2) { + scene.overlay.showText(60) + .text("If an output is unable to take more items, it will be skipped") + .pointAt(util.vector.blockSurface(util.grid.at(1, 1, 2), Direction.UP)) + .placeNearTarget() + .colored(PonderPalette.GREEN); + } + + if (i == 4) { + scene.overlay.showControls( + new InputWindowElement(modeVec, Pointing.RIGHT).showing(AllIcons.I_TUNNEL_FORCED_SPLIT), 140); + scene.world.modifyTileEntity(util.grid.at(1, 2, 3), tunnelClass, + te -> te.getBehaviour(ScrollOptionBehaviour.TYPE) + .setValue(BrassTunnelTileEntity.SelectionMode.FORCED_SPLIT.ordinal())); + } + + if (i == 5) { + scene.overlay.showText(80) + .attachKeyFrame() + .text("'Forced Split' will never skip outputs, and instead wait until they are free") + .pointAt(util.vector.blockSurface(util.grid.at(1, 1, 2), Direction.UP)) + .placeNearTarget() + .colored(PonderPalette.RED); + scene.idle(60); + scene.world.moveSection(blockage, util.vector.of(-1, 0, 0), 10); + scene.world.setBlock(util.grid.at(1, 1, 0), Blocks.AIR.getDefaultState(), false); + scene.world.multiplyKineticSpeed(util.select.everywhere(), 1.5f); + } + + if (i == 7) { + scene.world.modifyTileEntity(util.grid.at(1, 2, 3), tunnelClass, + te -> te.getBehaviour(ScrollOptionBehaviour.TYPE) + .setValue(BrassTunnelTileEntity.SelectionMode.ROUND_ROBIN.ordinal())); + scene.overlay.showControls( + new InputWindowElement(modeVec, Pointing.RIGHT).showing(AllIcons.I_TUNNEL_ROUND_ROBIN), 140); + scene.overlay.showText(80) + .attachKeyFrame() + .pointAt(tunnelTop) + .placeNearTarget() + .text("'Round Robin' keeps stacks whole, and cycles through outputs iteratively"); + } + + if (i == 7) { + scene.world.moveSection(blockage, util.vector.of(1, 0, 0), 10); + scene.world.setBlock(util.grid.at(1, 1, 0), barrier, false); + } + + if (i == 13) { + scene.overlay.showText(60) + .text("Once Again, if an output is unable to take more items, it will be skipped") + .placeNearTarget() + .pointAt(util.vector.blockSurface(util.grid.at(1, 1, 2), Direction.UP)) + .colored(PonderPalette.GREEN); + } + + if (i == 15) { + scene.overlay.showControls( + new InputWindowElement(modeVec, Pointing.RIGHT).showing(AllIcons.I_TUNNEL_FORCED_ROUND_ROBIN), 140); + scene.world.modifyTileEntity(util.grid.at(1, 2, 3), tunnelClass, + te -> te.getBehaviour(ScrollOptionBehaviour.TYPE) + .setValue(BrassTunnelTileEntity.SelectionMode.FORCED_ROUND_ROBIN.ordinal())); + } + + if (i == 16) { + scene.overlay.showText(50) + .attachKeyFrame() + .placeNearTarget() + .text("'Forced Round Robin' never skips outputs") + .pointAt(util.vector.blockSurface(util.grid.at(1, 1, 2), Direction.UP)) + .colored(PonderPalette.RED); + scene.idle(30); + scene.world.moveSection(blockage, util.vector.of(-1, 0, 0), 10); + scene.world.setBlock(util.grid.at(1, 1, 0), Blocks.AIR.getDefaultState(), false); + } + + if (i == 19) { + scene.overlay.showControls( + new InputWindowElement(modeVec, Pointing.RIGHT).showing(AllIcons.I_TUNNEL_PREFER_NEAREST), 140); + scene.world.modifyTileEntity(util.grid.at(1, 2, 3), tunnelClass, + te -> te.getBehaviour(ScrollOptionBehaviour.TYPE) + .setValue(BrassTunnelTileEntity.SelectionMode.PREFER_NEAREST.ordinal())); + scene.world.moveSection(blockage, util.vector.of(1, 0, 0), 10); + scene.world.setBlock(util.grid.at(1, 1, 0), barrier, false); + scene.overlay.showText(70) + .attachKeyFrame() + .text("'Prefer Nearest' prioritizes the outputs closest to the items' input location") + .pointAt(util.vector.blockSurface(util.grid.at(1, 1, 2), Direction.UP)) + .placeNearTarget() + .colored(PonderPalette.GREEN); + } + + if (i == 21) { + scene.world.setBlock(util.grid.at(2, 1, 0), Blocks.BARRIER.getDefaultState(), false); + blockage2 = scene.world.showIndependentSection(util.select.position(4, 1, 0), Direction.UP); + scene.world.moveSection(blockage2, util.vector.of(-2, 0, 0), 0); + } + + if (i == 25) { + scene.world.hideIndependentSection(blockage, Direction.DOWN); + scene.world.setBlock(util.grid.at(1, 1, 0), Blocks.AIR.getDefaultState(), false); + scene.world.hideIndependentSection(blockage2, Direction.DOWN); + scene.world.setBlock(util.grid.at(2, 1, 0), Blocks.AIR.getDefaultState(), false); + } + + if (i == 26) { + scene.overlay.showControls( + new InputWindowElement(modeVec, Pointing.RIGHT).showing(AllIcons.I_TUNNEL_RANDOMIZE), 140); + scene.world.modifyTileEntity(util.grid.at(1, 2, 3), tunnelClass, + te -> te.getBehaviour(ScrollOptionBehaviour.TYPE) + .setValue(BrassTunnelTileEntity.SelectionMode.RANDOMIZE.ordinal())); + } + + if (i == 27) { + scene.overlay.showText(70) + .attachKeyFrame() + .text("'Randomize' will distribute whole stacks to randomly picked outputs") + .pointAt(tunnelTop) + .placeNearTarget(); + } + } + + scene.world.hideSection(util.select.fromTo(3, 2, 5, 1, 2, 5), Direction.UP); + scene.idle(10); + scene.overlay + .showControls(new InputWindowElement(modeVec, Pointing.RIGHT).showing(AllIcons.I_TUNNEL_SYNCHRONIZE), 140); + scene.world.modifyTileEntity(util.grid.at(1, 2, 3), tunnelClass, + te -> te.getBehaviour(ScrollOptionBehaviour.TYPE) + .setValue(BrassTunnelTileEntity.SelectionMode.SYNCHRONIZE.ordinal())); + scene.idle(30); + scene.overlay.showText(70) + .attachKeyFrame() + .text("'Synchronize Inputs' is a unique setting for Brass Tunnels") + .pointAt(tunnelTop) + .placeNearTarget(); + + ItemStack item1 = new ItemStack(Items.CARROT); + ItemStack item2 = new ItemStack(Items.field_226638_pX_); + ItemStack item3 = AllItems.POLISHED_ROSE_QUARTZ.asStack(); + + scene.world.createItemOnBelt(util.grid.at(3, 1, 4), Direction.UP, item1); + scene.world.createItemOnBelt(util.grid.at(2, 1, 4), Direction.UP, item2); + scene.world.createItemOnBelt(util.grid.at(3, 1, 5), Direction.SOUTH, item1); + scene.world.createItemOnBelt(util.grid.at(2, 1, 5), Direction.SOUTH, item2); + + scene.idle(80); + scene.world.createItemOnBelt(util.grid.at(2, 1, 5), Direction.SOUTH, item2); + scene.rotateCameraY(-90); + scene.idle(20); + scene.world.multiplyKineticSpeed(util.select.everywhere(), .5f); + + scene.overlay.showText(70) + .text("Items are only allowed past if every tunnel in the group has one waiting") + .pointAt(util.vector.blockSurface(util.grid.at(2, 1, 4), Direction.UP)) + .placeNearTarget() + .colored(PonderPalette.OUTPUT); + scene.idle(60); + scene.world.createItemOnBelt(util.grid.at(1, 1, 5), Direction.SOUTH, item3); + scene.idle(90); + scene.rotateCameraY(90); + + scene.overlay.showText(100) + .text("This ensures that all affected belts supply items at the same rate") + .pointAt(util.vector.blockSurface(util.grid.at(1, 2, 3), Direction.WEST)) + .placeNearTarget() + .colored(PonderPalette.GREEN); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/elements/InputWindowElement.java b/src/main/java/com/simibubi/create/foundation/ponder/elements/InputWindowElement.java index dfa195ead..7791bfb25 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/elements/InputWindowElement.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/elements/InputWindowElement.java @@ -57,6 +57,11 @@ public class InputWindowElement extends AnimatedOverlayElement { icon = AllIcons.I_RMB; return this; } + + public InputWindowElement showing(AllIcons icon) { + this.icon = icon; + return this; + } public InputWindowElement leftClick() { icon = AllIcons.I_LMB; diff --git a/src/main/resources/ponder/tunnels/andesite.nbt b/src/main/resources/ponder/tunnels/andesite.nbt new file mode 100644 index 0000000000000000000000000000000000000000..b98a5975abe3b2c47f518fddbd513307844467c2 GIT binary patch literal 1062 zcmV+>1lju^iwFP!000000L@t2Zrer>9g<63D928(=uh+m;+HmYQdB^p1W;SFeKFSJ zS|Uu5#O^wj)IRvD`rP)DdV4F>p+w5mYAG)x3Jw86vN(HY=FFMdH9deX5D_ex0|43W z;!W^wb)mx|8OVSM$gVE5uEKb9BRu@$^Da0^EQ&>&%K=wNDp|gwY-4F`EXBq)z;O(4 zhyhMW(gr6yNpmjk-5RHKhUJqV*&>F_Ke8egjSa8x8_rMr(>l+r0 zSQ>8H^|`U@a?^P}c=>AB|8|F;e;Vhfk)5N=Pp3XV z+FiA_c6Jv$s&<@aw4)v72XiyuJIsx^d9-0pG{kmuViD=9kS1FK&FtNE0H5XoP$U;! zuuFLxMJ&Ws!vKHTS*4M?t$6mDJ19LYH?wnwTI@v?gXIH!Psw9litYZ0( zO1`UPb)YSDQ?Ozxm(TJi4ToFN95FF#lH z3(@pTdE%^Wj3-Uil(C9uC2wbnH@L6y0iJA)r>7NpnNQFvZ_b{)3N`$G_vmUUdg58G zhF_{)H@KxdK6w9Bt>#gw>T(_*qD?!YA(N72Zz(_O;CX^h8GJTHFC$K`<#{^E>OP7i z3*3;F(@-#6nousmVUdr|u91!GSrl_Q z>0nSHE(C89^I(CKi-sFtTgNWN1);M*fHmJ3D{mK6`(BC(@u!wx5xIezaEGAa8U`ktSv8jFrG+_WGY5w z9sMop>2HhEV<=<0Xvr-eCFV!^Mgyi(FqUA`fG>H5-J$CVnfge&n&L;0Tov?Ss gI?J&(C=T>qQc1sOTm*51d(5x!Ki{wv8`K&A03MqEE&u=k literal 0 HcmV?d00001 diff --git a/src/main/resources/ponder/tunnels/brass.nbt b/src/main/resources/ponder/tunnels/brass.nbt new file mode 100644 index 0000000000000000000000000000000000000000..065010581b8c9a6bff060bf5821a6674ebbfa002 GIT binary patch literal 1669 zcmV;02737)iwFP!000000PS1bZrer>U6M;(Z6|3iXg?)EfF^Nb)Ii__Q5v*;5omHH z5vI7rauvyHAN(o(iaw|PlD?*wFX)3iyh#+bq?a;fF@OyiO3azFGiPV#va~x0wGsC1 zInfY8!FMD1Zle})xy^0jqu{HJ8mA65&-@Pj=UE$R9`zF?1;t+)(tU1l5^JPoRDq0= zktb`Rf6Xt94vvOOW-6nYZ5$b5(crL=h5;(dLr-L;2ll%46Qb!d2oY4h{%E&|7J4-yZ^9AKm#TbGyrA=Lcz zScmmEBfkFvguocpwoun(1e|$tN*pfWINYxkH)@8P7dhNiV%tG%+mIOSl+Lnrea_G| z6CIH7JLP~Txw(}%@Gy43x5u7C`e(LFxNTnL$5eD6vVP$5L5eB~(!Z_>Ql)f|>ZtqN zvWOM&_w7aCU5T*mp!Tu*5lSj$=WTG8lW|m76}11B`cP;;k~ho(%S4$kORO##;VELV zh}eJEqO&Biv%=G_Z~OEohbLWu8s)jsD&Zy@r90+E6>!rm&y8A^8~w$*=e=H*oPX*? zjwT?dRh}GSNi7{X>k2#N23Hd}!c2Z`Zq5rcxfC}$%jCL%*mg{;hW1|C@I%)mugQR; z@X?(k+u?)}>%GB-gh$Kh=s_UK?9&Oisr#1EVPI`!!Cvni5Ue*7?oEPyZKgG0jR^4h zm`+?CX&eBRE-^W=-r2)Zprao-hPRSNf{4%oEYc1~KrJNHB2i^&L{B2J_Q%jdn9LaP zCx#6y=!em2g5}W?MXv-!y#`7L-Rp6~JbOqldoMZ5&rtK%r^~baNhb3t2|w3sCkh8GX>QgPcFK(^;HIjoP{fV6 zoyckwmjx+u^2>r0UDs>yv&?pDRu+_WS;#2~YMn~Lmzm7VCUTq!o4rNY+Ahj*d+Z%JURs(L98&$wf)$_K9o8o!9Cgf-Wa_&?-Z%YTxy24Jm!PNv#;k>;u zH}ehV?YcBpx`5ca&D*z)Rhh<0xyi;#6Es%Id5m{|C25GyXKD&bXL(VauOHv+t?7I% z@8?|lre77_t=M*DOSWqQ+p~AF0^5}`whQyiCbmmwrM2+F0yn$$?~{Md>yxpdPp(UL zUAfH+jlIpRI~-ZnL|!&wPZdBIpt~s_JI+i>>Dt2u(2?5uC zM$YcV&W z>WnC}=TKG*XR+2wmCP)GjhXEu<$NKW`R(Vde0K^DnL~#|VlC~8KxPY&B=`Cwa8S;x zfK1I`LYlLuLSP@+IpF@JBJY0VT)}GRHuOUvv!T<&NQMXn7Op zM$fkzzEvK=c)J=?+qLL))391eISbXKNQEEJ4fmr?a-*F{u$-U?glaIHj>x9FdMB|f z2L+D|XMh@~lyf@H0nLricsnq_xzV;Q*8L_`vOG31NgsmPvnUNFyY5Sa8y}!8g+>^v P{BP)gqZ-j;iZ}oO5yLN5 literal 0 HcmV?d00001 diff --git a/src/main/resources/ponder/tunnels/brass_modes.nbt b/src/main/resources/ponder/tunnels/brass_modes.nbt new file mode 100644 index 0000000000000000000000000000000000000000..e57c00dfb56689dd5ce1918e7385d4a81fdbcb0f GIT binary patch literal 1288 zcmV+j1^4JzLNufuxcW0qSZ&XJ?yC@ z&<;FurC@BZcUd4g^L@e%(7wOByZa5FKVaiCeEx{fH_(K}t|MsB1M~3caNmYTheg~a zslo3BgP+jA*c%ZrPWJaS*l3Hf4~)KXK!Tp^tLSZ5J0x<*_=N?{T_<#0Do5YMV>COs zPT{A=Cg^SN?(Uyopt=3jLf4WLA)bGYZB5Xr2kR~;IId^s#Fr&g65kUv88y*V9QDjJ zz0PQA9G&bOzTAJOOzBHx>W^8ODjMWWnFsA}-z`r@JvFtxDoZdOlt3-YATDm?4?$xf&9Hs!ruIps~Bz^s~hnx?#Ro{AQ?t0=u?P&G|@ z_7sDa%@Q4M*EmHtClwTEPPCx4e@WV8o9zzNzS)ikQ*E>TyUJ#(uF2Pft=UhB z+EOb1o0Y3O%*E=nQkoX7_}{Ez*|7|!KE#TDL7reK!n>U!JZ)y0ELh+1JmMwve08G+ zIsZ-dCajyj8tXc~z=N)*(i2?g)2zH-wdX0(d__?opyGbP%jREyMpVaQflsp-C5? z9*=WH1!z|}!bW!f?IZW|L2RWEF6#~Z1T(yVVj(uh-^2>WQ8h^pah*eyl@#*@lbz>% zRU4i?6jJ##t0d>Lwh34Z*-#a@xtElPwJ;KW#2-5{he~`I<`*fZgA2M)YEj^4-Rh(i y<2U69-I~5C>heh|N`HP}i^rAbgUDD%?m5zVg%vi|^tkr?4F3Y-_Lux+Bme+tSb7Tp literal 0 HcmV?d00001 diff --git a/src/main/resources/ponder/weighted_ejector/eject.nbt b/src/main/resources/ponder/weighted_ejector/eject.nbt new file mode 100644 index 0000000000000000000000000000000000000000..fd7bfbea82d085a594c0aa7baafe8e93678c1d81 GIT binary patch literal 902 zcmV;119|)(iwFP!000000L@oTZ__{!9mgMuogyj(lsmtIUie50tyHxYfeN?;*PbM% zuGiY#G-CvoBo|BSI?Cyrx>O%(}NiZ-!l-tK!dej7UgHW(t9q7DGb$D&UonQdrc zBm)_sfMnQUU53oN5)S=%Zi5~(ktfZE4yKT<#4Mv2qgBLcrI>~Sj;?@XDB$EnTZ5xj z#FTLw3OKp~j-h~)kA{NI4F#Ro;phrDh62vK6bd@)3OcXD(G_qE1)O{s3OXAKIyA790&C1Qkr0PV?_T=nzwsOUqyQ1W9(C8(oV;bkCR!3(_xY1EDVwBpnaliPiX#3 z^FGaAXdXcetd1*!XaL6P+3BGJR+o*qhgrhYl>`lTj>Q6)mb6YV8pvTHfdkD`lw-!P zOlWo7*!2Q=dvuo&GrD9v6l0pue(n3{cO^$5Ufv~)8^=D~`323dFHG7&dMDvX+iL_q zv^|bQZ~qcQ>41G4-H=-`erpq5iPtmF(%`{46Xanxc*cF&SyeNng)9i$P!v{$*k5Tp z$!Lh+Iu5aS6+|cluTJt8ndCQR9%cm(y~N}9-9P8Cr@|vGT!DwNd82DvY4p{EQlrDh zjlNM^*Ys*p8#B{pmb{ZC`xrBND0a?9G9Jk{qkcaTA>Tx6=@F^2>tvOprCSvh(BaV$ z;96M1s=}-SC2A}+IpBvePu&4c0-gBlQ)@mi_G7>MtF8j zt9ab)%l+{%kl6EBMBAXGJmT>pLPTubOJ9c)Va_>=6Q@E!CyF;aQOv#@3Z&H73GwfNVXhj5TpQfH5&5$4Igf zddQO09;%z@Zie=(=2Te+xj=lE96%}lB$}q=hBq+*fgt(%RlRz}VrvR80cOM<1^`h0 z!sa}hK!nl?E6_lBOki}As`%DS>Ehc71er4JqTLZdXe_tbtmtCA0gTtf_zpaQ0}pfH zX%FuRk2iqn^Y{)tfddb7;9*TpU5tM~m+#<(@8HETp1^^JIq>Z4a_}N>@ZuOx;K0Ki zc-n(G)R{Td`4~^&z{3W4rVz**JAv@Z;--F)Bk)B64>m$#tohQO$7?ipK%LifEoj(6 z2be|6W8faxy^F>BwsF32f*eu#Y%baiKUcBbCnZo0N$_L z?e-m=f1~^7bpD;ruc;p4yiz!xJ-hh%#~&`IFuq1xD1943G~*eMg?)QfrP2l&g>Z6_ zB$&*tMq|FeFYE7LCEM-4f5GV8W71VWncY|GPUf*zX}VKYd+>FvkpiC=C{}BW$pT+v zY?bz4{6ZC|sYVkxd(Lx779|}~!&9N_6>Ger>b=xl8YF!`A17D8*dp{khSs!ykm~qBav=+)ou8GaQ%@9V0?xp)wKls4)s9P3Rln>igheooqMnBIYnBp6&d2FjJE5I(mS4{ve z6EtOwn-S_FobflpfOoeeJ8c_sO)@`?qV4Il{0x)ft746xn$ClU>ktb=PNim2Nm|`B z1O53(%B#xD_F3&o3#>{F-gkSG_STXG#u98%2#p`aBTtahO_kE_ZP{s@6bE{8P(^b! hq@oe%u7b7;m)vsN^%$Y(a~=H&{{hKB$mpmI001}p!@K|h literal 0 HcmV?d00001 diff --git a/src/main/resources/ponder/weighted_ejector/split.nbt b/src/main/resources/ponder/weighted_ejector/split.nbt new file mode 100644 index 0000000000000000000000000000000000000000..82cad69807e102a7d5c8b8f04bc766a050d37d63 GIT binary patch literal 1217 zcmV;y1U~y8iwFP!000000L@riPwPey9^Y~?0Z!XfwNfw7{R@Ye0^v}Q5Fm1j+P*}_ z-Xt!LH`-m3gud{T`qcOKmvz=R9LGsE5R_vHIb?UfnfYdRW;QlJ6HM+u5(5C_pOwCA zO=yr%_`)X~zf0RN&cE z;CUI2p@3sn;@Dt>cftazGa|(MFP3|Ei643V#jhg|cgl1+4?QxG%Qo0;8VW{(fG`8J z-|)-t@%=l#5Agj5z8|0gwYJ0kun*?t)#XPUY8@Iemn;;}pmollfzjmJ1qu6NAStk+ zeo4fbvU>{}ZD-`TzL=gpNyp3%79QNQZ-RdA0n`tn{`Ut9^$A8eZy(}X19abm9hVVw z=HQM5qJrZ%76oqf^|^tKaPz||ZnU+zv0&%O^N5#l^7E6-hBer-p?MxYU?rz)+C+B+ z87B2ygWYS&vA#c$E9RkPRR>NITP#S1BC#6@`q!x&+#ikt(!KXXB7Aoh<>RUYR`$az z`EeZY`|^x3{}E&51SdXj4c+)KRy$~1_+BP1`9u#sIjm1aLaud6(MAn-+&V#&99RS1 z-4cdA%;2Lbt*c0kB5@q`dNTJiiW)NkgVmll(4N@Cdi(6?=9iBr>nGUZ@&q$rr{l_0 z&b>-v^-UV9G4cBYLA);clen038q}}pm@wq13A-0g6uJZA6`ZQhxne`HDq^+eocmEV z=kygh_hx?VrGE5@!|v#0l>?KiE?IV-Wdl$+uN zQD2`MVIXu0>|~rt)L8Id?qvR5 zBzziPGuoHdCKj|hQ$+BRAqElZMM>0=03Ho#6sFVB9#R?NfIz$(IWM4$bs9LMnUf+7 zw-79mj(>JAVnRKVbe3AOl907K#8&MxrzZ{^A6c;4iM9&$@Qh+@1Vw)ZiVa`j$mZUE zqB*~070PZFw1m7EMack6mYn!F;ETq&ud>u*4(DAFp-g}=lK#%e2o1VsYHo92S0i_1&MH^+pF4Rc=``koh% zO)D`4i{4tt@+5kl4vP;Q6swCalX0%dpQ=u>P9{F)NUf}74Dfuu3?i#Kft*jei&9z* fYwloRFFkRDbBnVIPDohj{(=7iQ=S<_Ul{-Zel|>% literal 0 HcmV?d00001