A final thought

- Fixed non-splitting modes on brass tunnels
- Ponder scenes for ejectors and tunnels
This commit is contained in:
simibubi 2021-03-28 18:57:50 +02:00
parent 7de922f239
commit bf2a506bb8
15 changed files with 973 additions and 10 deletions

View file

@ -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) {

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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;

View file

@ -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() {

View file

@ -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<ParrotElement> 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<WorldSectionElement> 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");
}
}
}
}

View file

@ -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)

View file

@ -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<ElementLink<WorldSectionElement>> 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<ElementLink<WorldSectionElement>> 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<BrassTunnelTileEntity> 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<WorldSectionElement> 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<BrassTunnelTileEntity> tunnelClass = BrassTunnelTileEntity.class;
ElementLink<WorldSectionElement> 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<WorldSectionElement> 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);
}
}

View file

@ -58,6 +58,11 @@ public class InputWindowElement extends AnimatedOverlayElement {
return this;
}
public InputWindowElement showing(AllIcons icon) {
this.icon = icon;
return this;
}
public InputWindowElement leftClick() {
icon = AllIcons.I_LMB;
return this;

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.