diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerRenderer.java index 91064b937..83679ee68 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/chassis/StickerRenderer.java @@ -7,7 +7,6 @@ import com.simibubi.create.foundation.render.backend.FastRenderDispatcher; import com.simibubi.create.foundation.tileEntity.renderer.SafeTileEntityRenderer; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AnimationTickHolder; -import com.simibubi.create.foundation.utility.MatrixStacker; import net.minecraft.block.BlockState; import net.minecraft.client.Minecraft; @@ -30,9 +29,9 @@ public class StickerRenderer extends SafeTileEntityRenderer { BlockState state = te.getBlockState(); SuperByteBuffer head = AllBlockPartials.STICKER_HEAD.renderOn(state); - float offset = te.piston.getValue(AnimationTickHolder.getPartialTicks()); + float offset = te.piston.getValue(AnimationTickHolder.getPartialTicks(te.getWorld())); - if (te.getWorld() != Minecraft.getInstance().world) + if (te.getWorld() != Minecraft.getInstance().world && !te.isVirtual()) offset = state.get(StickerBlock.EXTENDED) ? 1 : 0; Direction facing = state.get(StickerBlock.FACING); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java index 21af0fd87..7627845d2 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java @@ -1,5 +1,9 @@ package com.simibubi.create.content.contraptions.components.structureMovement.glue; +import javax.annotation.Nullable; + +import org.apache.commons.lang3.Validate; + import com.simibubi.create.AllBlocks; import com.simibubi.create.AllEntityTypes; import com.simibubi.create.AllItems; @@ -14,13 +18,19 @@ import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType; import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.BlockFace; +import com.simibubi.create.foundation.utility.worldWrappers.WrappedWorld; + import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.block.DirectionalBlock; import net.minecraft.client.Minecraft; import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.*; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntitySize; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.MoverType; +import net.minecraft.entity.Pose; import net.minecraft.entity.effect.LightningBoltEntity; import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.player.PlayerEntity; @@ -29,10 +39,21 @@ import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.IPacket; import net.minecraft.network.PacketBuffer; import net.minecraft.state.BooleanProperty; -import net.minecraft.util.*; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.DamageSource; +import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; -import net.minecraft.util.math.*; +import net.minecraft.util.Hand; +import net.minecraft.util.Mirror; +import net.minecraft.util.Rotation; +import net.minecraft.util.SoundEvents; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.RayTraceResult.Type; +import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -40,9 +61,6 @@ import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData; import net.minecraftforge.fml.network.NetworkHooks; import net.minecraftforge.fml.network.PacketDistributor; -import org.apache.commons.lang3.Validate; - -import javax.annotation.Nullable; public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnData, ISpecialEntityItemRequirement { @@ -149,6 +167,9 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat public boolean isVisible() { if (!isAlive()) return false; + if (world instanceof WrappedWorld) + return true; + BlockPos pos = hangingPosition; BlockPos pos2 = pos.offset(getFacingDirection().getOpposite()); return isValidFace(world, pos2, getFacingDirection()) != isValidFace(world, pos, diff --git a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java index 8856abb0b..ce90a339d 100644 --- a/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java +++ b/src/main/java/com/simibubi/create/foundation/command/ConfigureConfigPacket.java @@ -1,5 +1,10 @@ package com.simibubi.create.foundation.command; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.apache.logging.log4j.LogManager; + import com.simibubi.create.Create; import com.simibubi.create.content.contraptions.goggles.GoggleConfigScreen; import com.simibubi.create.foundation.config.AllConfigs; @@ -9,6 +14,8 @@ import com.simibubi.create.foundation.ponder.PonderRegistry; import com.simibubi.create.foundation.ponder.PonderUI; import com.simibubi.create.foundation.ponder.content.PonderIndexScreen; import com.simibubi.create.foundation.render.backend.FastRenderDispatcher; +import com.simibubi.create.foundation.render.backend.OptifineHandler; + import net.minecraft.client.Minecraft; import net.minecraft.client.entity.player.ClientPlayerEntity; import net.minecraft.network.PacketBuffer; @@ -21,10 +28,6 @@ import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.common.ForgeConfig; import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.network.NetworkEvent; -import org.apache.logging.log4j.LogManager; - -import java.util.function.Consumer; -import java.util.function.Supplier; public class ConfigureConfigPacket extends SimplePacketBase { @@ -112,10 +115,17 @@ public class ConfigureConfigPacket extends SimplePacketBase { return; } - AllConfigs.CLIENT.experimentalRendering.set(Boolean.parseBoolean(value)); - ITextComponent text = boolToText(AllConfigs.CLIENT.experimentalRendering.get()).appendSibling(new StringTextComponent(" Experimental Rendering").applyTextStyle(TextFormatting.WHITE)); - player.sendStatusMessage(text, false); + boolean parsedBoolean = Boolean.parseBoolean(value); + boolean cannotUseER = OptifineHandler.usingShaders() && parsedBoolean; + + AllConfigs.CLIENT.experimentalRendering.set(parsedBoolean); + + ITextComponent text = boolToText(AllConfigs.CLIENT.experimentalRendering.get()) + .appendSibling(new StringTextComponent(" Experimental Rendering").applyTextStyle(TextFormatting.WHITE)); + ITextComponent error = new StringTextComponent("Experimental Rendering does not support Optifine Shaders") + .applyTextStyle(TextFormatting.RED); + player.sendStatusMessage(cannotUseER ? error : text, false); FastRenderDispatcher.refresh(); } diff --git a/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java b/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java index 52fd625c5..4f4d8323c 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/PonderUI.java @@ -1,14 +1,37 @@ package com.simibubi.create.foundation.ponder; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; + +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.lwjgl.opengl.GL11; + import com.mojang.blaze3d.matrix.MatrixStack; import com.mojang.blaze3d.systems.RenderSystem; -import com.simibubi.create.foundation.gui.*; +import com.simibubi.create.foundation.gui.AbstractSimiScreen; +import com.simibubi.create.foundation.gui.AllGuiTextures; +import com.simibubi.create.foundation.gui.AllIcons; +import com.simibubi.create.foundation.gui.GuiGameElement; +import com.simibubi.create.foundation.gui.ScreenOpener; +import com.simibubi.create.foundation.gui.UIRenderHelper; import com.simibubi.create.foundation.ponder.PonderScene.SceneTransform; -import com.simibubi.create.foundation.ponder.content.*; +import com.simibubi.create.foundation.ponder.content.DebugScenes; +import com.simibubi.create.foundation.ponder.content.PonderChapter; +import com.simibubi.create.foundation.ponder.content.PonderIndex; +import com.simibubi.create.foundation.ponder.content.PonderTag; +import com.simibubi.create.foundation.ponder.content.PonderTagScreen; import com.simibubi.create.foundation.ponder.ui.PonderButton; import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer; -import com.simibubi.create.foundation.utility.*; +import com.simibubi.create.foundation.utility.ColorHelper; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.Lang; +import com.simibubi.create.foundation.utility.LerpedFloat; import com.simibubi.create.foundation.utility.LerpedFloat.Chaser; +import com.simibubi.create.foundation.utility.Pair; +import com.simibubi.create.foundation.utility.Pointing; + import net.minecraft.client.ClipboardHelper; import net.minecraft.client.GameSettings; import net.minecraft.client.MainWindow; @@ -29,13 +52,6 @@ import net.minecraft.world.gen.feature.template.PlacementSettings; import net.minecraft.world.gen.feature.template.Template; import net.minecraftforge.fml.client.gui.GuiUtils; import net.minecraftforge.registries.ForgeRegistries; -import org.apache.commons.lang3.mutable.MutableBoolean; -import org.lwjgl.opengl.GL11; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.stream.IntStream; public class PonderUI extends AbstractSimiScreen { diff --git a/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java b/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java index 2190daabf..25e9c1cd0 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/SceneBuilder.java @@ -10,6 +10,7 @@ import java.util.function.UnaryOperator; import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel; import com.simibubi.create.content.contraptions.base.KineticBlock; import com.simibubi.create.content.contraptions.base.KineticTileEntity; +import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueItem; import com.simibubi.create.content.contraptions.particle.RotationIndicatorParticleData; import com.simibubi.create.content.contraptions.relays.belt.BeltTileEntity; @@ -532,6 +533,11 @@ public class SceneBuilder { return itemEntity; }); } + + public ElementLink createGlueEntity(BlockPos pos, Direction face) { + effects.superGlue(pos, face, false); + return createEntity(world -> new SuperGlueEntity(world, pos, face.getOpposite())); + } public void createItemOnBeltLike(BlockPos location, Direction insertionSide, ItemStack stack) { addInstruction(scene -> { diff --git a/src/main/java/com/simibubi/create/foundation/ponder/SceneBuildingUtil.java b/src/main/java/com/simibubi/create/foundation/ponder/SceneBuildingUtil.java index 58e7ebd0e..e0451e394 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/SceneBuildingUtil.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/SceneBuildingUtil.java @@ -105,8 +105,8 @@ public class SceneBuildingUtil { } public Selection layers(int y, int height) { - return cuboid(new BlockPos(0, y, 0), new Vec3i(sceneBounds.getXSize(), - Math.min(sceneBounds.getYSize() - y, height) - 1, sceneBounds.getZSize())); + return cuboid(new BlockPos(0, y, 0), new Vec3i(sceneBounds.getXSize() - 1, + Math.min(sceneBounds.getYSize() - y, height) - 1, sceneBounds.getZSize() - 1)); } public Selection cuboid(BlockPos origin, Vec3i size) { diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/ChassisScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/ChassisScenes.java new file mode 100644 index 000000000..8ce8f0b6a --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/ChassisScenes.java @@ -0,0 +1,570 @@ +package com.simibubi.create.foundation.ponder.content; + +import org.apache.commons.lang3.mutable.MutableObject; + +import com.simibubi.create.AllItems; +import com.simibubi.create.content.contraptions.components.structureMovement.chassis.LinearChassisBlock; +import com.simibubi.create.content.contraptions.components.structureMovement.chassis.RadialChassisBlock; +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.EntityElement; +import com.simibubi.create.foundation.ponder.elements.InputWindowElement; +import com.simibubi.create.foundation.ponder.elements.WorldSectionElement; +import com.simibubi.create.foundation.utility.Pointing; + +import net.minecraft.entity.Entity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.Direction; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; + +public class ChassisScenes { + + public static void linearGroup(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("linear_chassis_group", "Moving Linear Chassis in groups"); + scene.configureBasePlate(0, 0, 5); + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + scene.world.showSection(util.select.layer(1), Direction.DOWN); + scene.idle(10); + + BlockPos centralChassis = util.grid.at(2, 2, 2); + ElementLink chassis = + scene.world.showIndependentSection(util.select.position(centralChassis), Direction.DOWN); + scene.idle(10); + scene.world.showSectionAndMerge(util.select.position(centralChassis.west()), Direction.EAST, chassis); + scene.idle(5); + scene.world.showSectionAndMerge(util.select.position(centralChassis.east()), Direction.WEST, chassis); + scene.idle(4); + scene.world.showSectionAndMerge(util.select.position(centralChassis.east() + .north()), Direction.SOUTH, chassis); + scene.idle(3); + scene.world.showSectionAndMerge(util.select.position(centralChassis.up()), Direction.DOWN, chassis); + scene.idle(2); + scene.world.showSectionAndMerge(util.select.position(centralChassis.up() + .east()), Direction.DOWN, chassis); + scene.idle(10); + + scene.overlay.showText(80) + .attachKeyFrame() + .placeNearTarget() + .text("Linear Chassis connect to identical Chassis blocks next to them") + .pointAt(util.vector.topOf(util.grid.at(2, 3, 2))); + scene.idle(90); + + BlockPos bearingPos = util.grid.at(2, 1, 2); + scene.world.moveSection(chassis, util.vector.of(0, -1 / 1024f, 0), 0); + scene.world.configureCenterOfRotation(chassis, util.vector.centerOf(bearingPos)); + scene.world.rotateBearing(bearingPos, 360, 80); + scene.world.rotateSection(chassis, 0, 360, 0, 80); + + scene.idle(20); + scene.overlay.showText(80) + .placeNearTarget() + .text("When one is moved by a Contraption, the others are dragged with it") + .pointAt(util.vector.topOf(util.grid.at(2, 3, 2))); + scene.idle(90); + + Selection wrong1 = util.select.position(2, 4, 2); + Selection wrong2 = util.select.position(0, 2, 2); + + scene.addKeyframe(); + scene.world.showSection(wrong2, Direction.EAST); + scene.idle(10); + scene.world.showSection(wrong1, Direction.DOWN); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.RED, wrong2, wrong2, 80); + scene.overlay.showSelectionWithText(wrong1, 80) + .colored(PonderPalette.RED) + .placeNearTarget() + .text("Chassis of a different type or facing another direction will not attach"); + scene.idle(40); + + scene.world.rotateBearing(bearingPos, 360, 80); + scene.world.rotateSection(chassis, 0, 360, 0, 80); + } + + public static void linearAttachement(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("linear_chassis_attachment", "Attaching blocks using Linear Chassis"); + scene.configureBasePlate(0, 0, 5); + scene.setSceneOffsetY(-1); + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + + BlockPos chassisPos = util.grid.at(2, 2, 2); + Selection chassis = util.select.position(chassisPos); + + scene.world.showSection(util.select.layer(1), Direction.DOWN); + scene.world.showSection(chassis, Direction.DOWN); + scene.idle(10); + + InputWindowElement input = + new InputWindowElement(util.vector.blockSurface(chassisPos, Direction.WEST), Pointing.LEFT).rightClick() + .withItem(new ItemStack(Items.SLIME_BALL)); + scene.overlay.showControls(input, 30); + scene.idle(7); + scene.world.modifyBlock(chassisPos, s -> s.with(LinearChassisBlock.STICKY_BOTTOM, true), false); + scene.effects.superGlue(chassisPos, Direction.WEST, false); + scene.idle(30); + + scene.overlay.showText(60) + .text("The open faces of a Linear Chassis can be made Sticky") + .placeNearTarget() + .pointAt(util.vector.blockSurface(chassisPos, Direction.WEST)); + scene.idle(70); + + scene.overlay.showControls(input, 15); + scene.idle(7); + scene.world.modifyBlock(chassisPos, s -> s.with(LinearChassisBlock.STICKY_TOP, true), false); + scene.effects.superGlue(chassisPos, Direction.EAST, false); + scene.idle(15); + + scene.overlay.showText(60) + .text("Click again to make the opposite side sticky") + .placeNearTarget() + .pointAt(util.vector.topOf(chassisPos)); + scene.idle(10); + scene.rotateCameraY(60); + scene.idle(35); + scene.rotateCameraY(-60); + scene.idle(25); + + scene.overlay.showControls( + new InputWindowElement(util.vector.blockSurface(chassisPos, Direction.WEST), Pointing.LEFT).rightClick() + .whileSneaking(), + 30); + scene.idle(7); + scene.world.modifyBlock(chassisPos, s -> s.with(LinearChassisBlock.STICKY_BOTTOM, false), false); + scene.effects.superGlue(chassisPos, Direction.WEST, false); + scene.idle(30); + + scene.overlay.showText(60) + .text("Sneak and Right-Click with an empty hand to remove the slime") + .placeNearTarget() + .pointAt(util.vector.blockSurface(chassisPos, Direction.WEST)); + scene.idle(70); + + scene.world.hideSection(chassis, Direction.UP); + + scene.idle(20); + ElementLink glassSection = + scene.world.showIndependentSection(util.select.position(chassisPos.up()), Direction.DOWN); + scene.world.moveSection(glassSection, util.vector.of(0, -1, 0), 0); + scene.idle(25); + scene.addKeyframe(); + scene.world.showSectionAndMerge(util.select.fromTo(2, 4, 2, 2, 5, 2), Direction.DOWN, glassSection); + ElementLink topGlassSection = + scene.world.showIndependentSection(util.select.position(2, 6, 2), Direction.DOWN); + scene.world.moveSection(topGlassSection, util.vector.of(0, -1, 0), 0); + scene.idle(30); + + Selection column1 = util.select.fromTo(2, 3, 2, 2, 3, 2); + Selection column2 = util.select.fromTo(2, 3, 2, 2, 4, 2); + Selection column3 = util.select.fromTo(2, 3, 2, 2, 5, 2); + + scene.overlay.showSelectionWithText(column3, 80) + .colored(PonderPalette.GREEN) + .text("Stickied faces of the Linear Chassis will attach a line of blocks in front of it") + .placeNearTarget(); + scene.idle(90); + + BlockPos bearingPos = util.grid.at(2, 1, 2); + scene.world.configureCenterOfRotation(glassSection, util.vector.centerOf(bearingPos)); + scene.world.rotateBearing(bearingPos, 180, 40); + scene.world.rotateSection(glassSection, 0, 180, 0, 40); + scene.world.rotateSection(topGlassSection, 0, 180, 0, 40); + scene.idle(50); + + Vec3d blockSurface = util.vector.blockSurface(chassisPos, Direction.NORTH); + scene.overlay.showCenteredScrollInput(chassisPos, Direction.NORTH, 50); + scene.overlay.showControls(new InputWindowElement(blockSurface, Pointing.UP).scroll() + .withWrench(), 50); + + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column3, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column2, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column1, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column2, 15); + scene.idle(10); + + scene.overlay.showText(60) + .pointAt(blockSurface) + .text("Using a Wrench, a precise Range can be specified for this chassis") + .placeNearTarget(); + scene.idle(70); + + scene.world.rotateBearing(bearingPos, 180, 40); + scene.world.rotateSection(glassSection, 0, 180, 0, 40); + scene.idle(50); + + scene.world.rotateSection(topGlassSection, 0, 180, 0, 0); + scene.world.showSectionAndMerge(util.select.position(1, 3, 2), Direction.UP, glassSection); + scene.world.showSectionAndMerge(util.select.position(3, 3, 2), Direction.UP, glassSection); + scene.world.showSectionAndMerge(util.select.fromTo(1, 4, 2, 1, 6, 2), Direction.DOWN, glassSection); + scene.world.showSectionAndMerge(util.select.fromTo(3, 4, 2, 3, 6, 2), Direction.DOWN, glassSection); + scene.addKeyframe(); + scene.idle(20); + + scene.overlay.showCenteredScrollInput(chassisPos, Direction.NORTH, 50); + scene.overlay.showControls(new InputWindowElement(blockSurface, Pointing.UP).whileCTRL() + .scroll() + .withWrench(), 50); + + column1 = util.select.fromTo(1, 3, 2, 3, 3, 2); + column2 = util.select.fromTo(1, 3, 2, 3, 4, 2); + column3 = util.select.fromTo(1, 3, 2, 3, 5, 2); + + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column2, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column1, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column2, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, column3, 15); + scene.idle(10); + + scene.overlay.showText(80) + .pointAt(blockSurface) + .text("Holding CTRL and scrolling adjusts the range of all attached Chassis Blocks") + .placeNearTarget(); + scene.idle(90); + + scene.world.rotateBearing(bearingPos, 180, 40); + scene.world.rotateSection(glassSection, 0, 180, 0, 40); + scene.world.rotateSection(topGlassSection, 0, 180, 0, 40); + scene.idle(50); + + Vec3d glueSurface = util.vector.blockSurface(chassisPos.west(), Direction.NORTH); + scene.overlay.showText(80) + .attachKeyFrame() + .pointAt(glueSurface) + .text("Attaching blocks to any other side requires the use of Super Glue") + .placeNearTarget(); + scene.idle(90); + scene.overlay.showControls(new InputWindowElement(glueSurface, Pointing.DOWN).rightClick() + .withItem(AllItems.SUPER_GLUE.asStack()), 30); + scene.idle(7); + ElementLink glueEntity = scene.world.createGlueEntity(chassisPos.west(), Direction.NORTH); + scene.idle(20); + ElementLink gluedPlank = + scene.world.showIndependentSection(util.select.position(3, 3, 1), Direction.SOUTH); + scene.world.moveSection(gluedPlank, util.vector.of(-2, -1, 0), 0); + scene.idle(15); + scene.effects.superGlue(chassisPos.west(), Direction.NORTH, true); + scene.idle(20); + + scene.world.modifyEntity(glueEntity, Entity::remove); + scene.world.hideIndependentSection(glassSection, Direction.UP); + scene.world.hideIndependentSection(gluedPlank, Direction.UP); + scene.world.hideIndependentSection(topGlassSection, Direction.UP); + scene.idle(15); + + scene.addKeyframe(); + ElementLink chain = + scene.world.showIndependentSection(util.select.position(2, 7, 2), Direction.DOWN); + scene.world.configureCenterOfRotation(chain, util.vector.centerOf(bearingPos)); + scene.world.moveSection(chain, util.vector.of(0, -5, 0), 0); + scene.idle(10); + scene.world.showSectionAndMerge(util.select.fromTo(2, 8, 2, 3, 9, 2), Direction.DOWN, chain); + scene.idle(10); + scene.world.showSectionAndMerge(util.select.fromTo(3, 9, 1, 3, 9, 0), Direction.SOUTH, chain); + scene.idle(10); + scene.world.showSectionAndMerge(util.select.fromTo(2, 9, 0, 1, 9, 0), Direction.EAST, chain); + scene.idle(20); + + scene.overlay.showText(80) + .pointAt(util.vector.topOf(chassisPos.up(2))) + .text("Using these mechanics, structures of any shape can move as a Contraption") + .placeNearTarget(); + scene.idle(30); + + scene.world.rotateBearing(bearingPos, 720, 160); + scene.world.rotateSection(chain, 0, 720, 0, 160); + } + + public static void radial(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("radial_chassis", "Attaching blocks using Radial Chassis"); + scene.configureBasePlate(0, 0, 5); + scene.setSceneOffsetY(-1); + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + + util.select.position(2, 4, 2); + + BlockPos chassisPos = util.grid.at(2, 2, 2); + Selection chassis = util.select.position(chassisPos); + + scene.world.showSection(util.select.layer(1), Direction.DOWN); + scene.idle(10); + ElementLink contraption = scene.world.showIndependentSection(chassis, Direction.DOWN); + scene.idle(5); + ElementLink top = + scene.world.showIndependentSection(util.select.position(chassisPos.up()), Direction.DOWN); + scene.idle(10); + + scene.overlay.showText(50) + .attachKeyFrame() + .placeNearTarget() + .text("Radial Chassis connect to identical Chassis blocks in a row") + .pointAt(util.vector.topOf(chassisPos.up())); + scene.idle(60); + + BlockPos bearingPos = util.grid.at(2, 1, 2); + scene.world.moveSection(contraption, util.vector.of(0, -1 / 1024f, 0), 0); + scene.world.configureCenterOfRotation(contraption, util.vector.centerOf(bearingPos)); + scene.world.rotateBearing(bearingPos, 360, 80); + scene.world.rotateSection(contraption, 0, 360, 0, 80); + scene.world.rotateSection(top, 0, 360, 0, 80); + + scene.idle(20); + scene.overlay.showText(70) + .placeNearTarget() + .text("When one is moved by a Contraption, the others are dragged with it") + .pointAt(util.vector.topOf(util.grid.at(2, 3, 2))); + scene.idle(80); + + scene.world.hideIndependentSection(top, Direction.UP); + scene.idle(15); + + scene.addKeyframe(); + InputWindowElement input = + new InputWindowElement(util.vector.blockSurface(chassisPos, Direction.WEST), Pointing.LEFT).rightClick() + .withItem(new ItemStack(Items.SLIME_BALL)); + scene.overlay.showControls(input, 30); + scene.idle(7); + scene.world.modifyBlock(chassisPos, s -> s.with(RadialChassisBlock.STICKY_WEST, true), false); + scene.effects.superGlue(chassisPos, Direction.WEST, false); + scene.idle(30); + + scene.overlay.showText(60) + .text("The side faces of a Radial Chassis can be made Sticky") + .placeNearTarget() + .pointAt(util.vector.blockSurface(chassisPos, Direction.WEST)); + scene.idle(70); + + scene.overlay.showControls(input, 15); + scene.idle(7); + scene.world.modifyBlock(chassisPos, s -> s.with(RadialChassisBlock.STICKY_EAST, true) + .with(RadialChassisBlock.STICKY_NORTH, true) + .with(RadialChassisBlock.STICKY_SOUTH, true), false); + scene.effects.superGlue(chassisPos, Direction.EAST, false); + scene.effects.superGlue(chassisPos, Direction.SOUTH, false); + scene.effects.superGlue(chassisPos, Direction.NORTH, false); + scene.idle(15); + + scene.overlay.showText(60) + .text("Click again to make all other sides sticky") + .placeNearTarget() + .pointAt(util.vector.topOf(chassisPos)); + scene.idle(10); + scene.rotateCameraY(60); + scene.idle(35); + scene.rotateCameraY(-60); + scene.idle(25); + + scene.overlay.showControls( + new InputWindowElement(util.vector.blockSurface(chassisPos, Direction.WEST), Pointing.LEFT).rightClick() + .whileSneaking(), + 30); + scene.idle(7); + scene.world.modifyBlock(chassisPos, s -> s.with(RadialChassisBlock.STICKY_WEST, false), false); + scene.effects.superGlue(chassisPos, Direction.WEST, false); + scene.idle(30); + + scene.overlay.showText(60) + .text("Sneak and Right-Click with an empty hand to remove the slime") + .placeNearTarget() + .pointAt(util.vector.blockSurface(chassisPos, Direction.WEST)); + scene.idle(70); + + Selection s = util.select.position(chassisPos.north()); + Selection growing = s.copy(); + Selection r1 = util.select.fromTo(1, 2, 1, 3, 2, 3) + .substract(chassis); + Selection r2 = r1.copy() + .add(util.select.fromTo(0, 2, 1, 0, 2, 3)) + .add(util.select.fromTo(1, 2, 0, 3, 2, 0)) + .add(util.select.fromTo(1, 2, 4, 3, 2, 4)) + .add(util.select.fromTo(4, 2, 1, 4, 2, 3)); + Selection r3 = util.select.layer(2) + .add(util.select.fromTo(-1, 2, 1, 5, 2, 3)) + .add(util.select.fromTo(1, 2, -1, 3, 2, 5)) + .substract(chassis); + + scene.addKeyframe(); + scene.world.showSectionAndMerge(r1, Direction.DOWN, contraption); + ElementLink outer = scene.world.showIndependentSection(util.select.layer(2) + .substract(chassis) + .substract(r1), Direction.DOWN); + scene.world.showSection(util.select.fromTo(0, 3, 3, 1, 3, 4), Direction.DOWN); + scene.idle(10); + Vec3d blockSurface = util.vector.blockSurface(chassisPos, Direction.NORTH); + AxisAlignedBB bb = new AxisAlignedBB(blockSurface, blockSurface).grow(.501, .501, 0); + scene.overlay.chaseBoundingBoxOutline(PonderPalette.GREEN, bb, bb, 60); + scene.overlay.showOutline(PonderPalette.WHITE, s, s, 80); + scene.overlay.showText(40) + .text("Whenever a Block is next to a sticky face...") + .placeNearTarget() + .pointAt(blockSurface.add(0, .5, 0)); + scene.idle(60); + + MutableObject obj = new MutableObject<>(growing); + r2.forEach(pos -> { + scene.idle(1); + Selection add = obj.getValue() + .copy() + .add(util.select.position(pos)); + scene.overlay.showOutline(PonderPalette.WHITE, s, add, 3); + obj.setValue(add); + }); + + scene.overlay.showSelectionWithText(obj.getValue(), 60) + .colored(PonderPalette.GREEN) + .text("...it will attach all reachable blocks within a radius on that layer"); + scene.idle(70); + + scene.world.configureCenterOfRotation(outer, util.vector.centerOf(bearingPos)); + scene.world.rotateBearing(bearingPos, 360, 80); + scene.world.rotateSection(contraption, 0, 360, 0, 80); + scene.world.rotateSection(outer, 0, 360, 0, 80); + scene.idle(90); + + scene.addKeyframe(); + blockSurface = util.vector.topOf(chassisPos); + scene.overlay.showCenteredScrollInput(chassisPos, Direction.UP, 50); + scene.overlay.showControls(new InputWindowElement(blockSurface, Pointing.DOWN).scroll() + .withWrench(), 50); + + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, r2, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, r3, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, r2, 20); + scene.idle(10); + scene.overlay.showOutline(PonderPalette.WHITE, chassis, r1, 15); + scene.idle(10); + + scene.overlay.showText(60) + .pointAt(blockSurface) + .text("Using a Wrench, a precise Radius can be specified for this chassis") + .placeNearTarget(); + scene.idle(70); + + scene.world.rotateBearing(bearingPos, 360, 80); + scene.world.rotateSection(contraption, 0, 360, 0, 80); + scene.idle(90); + + scene.world.destroyBlock(util.grid.at(1, 2, 0)); + scene.idle(1); + scene.world.destroyBlock(util.grid.at(1, 2, 1)); + scene.idle(1); + scene.world.destroyBlock(util.grid.at(1, 2, 3)); + scene.idle(1); + scene.world.destroyBlock(util.grid.at(1, 2, 4)); + scene.idle(10); + + Selection ignored = util.select.fromTo(0, 2, 1, 0, 2, 3) + .add(util.select.position(1, 2, 2)); + scene.overlay.showOutline(PonderPalette.GREEN, r2, r2.copy() + .substract(util.select.fromTo(0, 2, 0, 1, 2, 4)), 80); + scene.markAsFinished(); + scene.overlay.showSelectionWithText(ignored, 80) + .colored(PonderPalette.RED) + .text("Blocks not reachable by any sticky face will not attach"); + } + + public static void superGlue(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("super_glue", "Attaching blocks using Super Glue"); + scene.configureBasePlate(0, 0, 5); + scene.world.showSection(util.select.layer(0), Direction.UP); + scene.idle(5); + scene.world.showSection(util.select.layer(1), Direction.DOWN); + scene.idle(10); + + BlockPos central = util.grid.at(2, 2, 2); + ElementLink plank = + scene.world.showIndependentSection(util.select.position(central), Direction.DOWN); + scene.idle(15); + Vec3d blockSurface = util.vector.blockSurface(central, Direction.NORTH); + scene.overlay.showControls(new InputWindowElement(blockSurface, Pointing.DOWN).rightClick() + .withItem(AllItems.SUPER_GLUE.asStack()), 40); + scene.idle(7); + ElementLink glueEntity = scene.world.createGlueEntity(central, Direction.NORTH); + scene.idle(10); + scene.overlay.showText(60) + .pointAt(blockSurface) + .placeNearTarget() + .text("Super Glue can be used between any two blocks") + .colored(PonderPalette.GREEN); + scene.idle(50); + + scene.world.glueBlockOnto(central.north(), Direction.SOUTH, plank); + scene.idle(20); + scene.world.modifyEntity(glueEntity, Entity::remove); + + BlockPos bearingPos = util.grid.at(2, 1, 2); + scene.world.configureCenterOfRotation(plank, util.vector.centerOf(bearingPos)); + scene.world.rotateBearing(bearingPos, 360, 80); + scene.world.rotateSection(plank, 0, 360, 0, 80); + scene.idle(30); + scene.overlay.showText(80) + .attachKeyFrame() + .pointAt(util.vector.topOf(central)) + .placeNearTarget() + .text("The attached blocks will move together when assembled into a Contraption"); + scene.idle(90); + + scene.overlay.showText(50) + .attachKeyFrame() + .pointAt(util.vector.topOf(central)) + .placeNearTarget() + .text("Whenever Super Glue is held in the off-hand..."); + scene.idle(60); + + scene.world.glueBlockOnto(central.south(), Direction.NORTH, plank); + scene.idle(5); + scene.world.glueBlockOnto(central.north() + .east(), Direction.WEST, plank); + scene.idle(5); + scene.world.glueBlockOnto(central.up(), Direction.DOWN, plank); + scene.idle(5); + scene.world.glueBlockOnto(central.south() + .west(), Direction.EAST, plank); + scene.idle(10); + + scene.overlay.showText(80) + .pointAt(util.vector.topOf(central) + .subtract(.5, 0, 0)) + .placeNearTarget() + .text("...added blocks will be glued to the face they were placed on automatically"); + scene.idle(90); + + scene.world.rotateBearing(bearingPos, 360, 80); + scene.world.rotateSection(plank, 0, 360, 0, 80); + scene.idle(90); + + glueEntity = scene.world.createGlueEntity(central, Direction.UP); + scene.world.destroyBlock(central.up()); + scene.idle(20); + scene.addKeyframe(); + scene.overlay.showControls(new InputWindowElement(util.vector.topOf(central), Pointing.DOWN).leftClick(), 40); + scene.idle(7); + scene.world.modifyEntity(glueEntity, Entity::remove); + scene.effects.superGlue(central, Direction.UP, false); + scene.idle(10); + scene.overlay.showText(60) + .pointAt(util.vector.topOf(central)) + .placeNearTarget() + .text("Super Glue can be removed with Left-Click"); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java index 1f37322eb..0cb80253f 100644 --- a/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/DebugScenes.java @@ -84,7 +84,6 @@ public class DebugScenes { scene.overlay.showSelectionWithText(zAxis, 20) .colored(PonderPalette.BLUE) .text("Das Z axis"); - scene.idle(10); } public static void blocksScene(SceneBuilder scene, SceneBuildingUtil util) { 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 67b6c5e68..d536dfce7 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 @@ -77,6 +77,17 @@ public class PonderIndex { .addStoryBoard("funnels/transposer", FunnelScenes::transposer); PonderRegistry.addStoryBoard(AllBlocks.ANDESITE_FUNNEL, "funnels/brass", FunnelScenes::brass); + // Chassis & Super Glue + PonderRegistry.forComponents(AllBlocks.LINEAR_CHASSIS, AllBlocks.SECONDARY_LINEAR_CHASSIS) + .addStoryBoard("chassis/linear_group", ChassisScenes::linearGroup) + .addStoryBoard("chassis/linear_attachment", ChassisScenes::linearAttachement); + PonderRegistry.forComponents(AllBlocks.RADIAL_CHASSIS) + .addStoryBoard("chassis/radial", ChassisScenes::radial); + PonderRegistry.forComponents(AllItems.SUPER_GLUE) + .addStoryBoard("super_glue", ChassisScenes::superGlue); + PonderRegistry.forComponents(AllBlocks.STICKER) + .addStoryBoard("sticker", RedstoneScenes::sticker); + // Mechanical Piston PonderRegistry.forComponents(AllBlocks.MECHANICAL_PISTON, AllBlocks.STICKY_MECHANICAL_PISTON) .addStoryBoard("mechanical_piston/anchor", PistonScenes::movement, PonderTag.KINETIC_APPLIANCES, @@ -128,6 +139,8 @@ public class PonderIndex { PonderRegistry.forComponents(AllBlocks.PORTABLE_STORAGE_INTERFACE) .addStoryBoard("portable_interface/transfer", MovementActorScenes::psiTransfer, PonderTag.CONTRAPTION_ACTOR) .addStoryBoard("portable_interface/redstone", MovementActorScenes::psiRedstone); + PonderRegistry.forComponents(AllBlocks.REDSTONE_CONTACT) + .addStoryBoard("redstone_contact", RedstoneScenes::contact); // Debug scenes, can be found in game via the Brass Hand if (EDITOR_MODE) diff --git a/src/main/java/com/simibubi/create/foundation/ponder/content/RedstoneScenes.java b/src/main/java/com/simibubi/create/foundation/ponder/content/RedstoneScenes.java new file mode 100644 index 000000000..c707e191b --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/ponder/content/RedstoneScenes.java @@ -0,0 +1,169 @@ +package com.simibubi.create.foundation.ponder.content; + +import com.simibubi.create.content.contraptions.components.structureMovement.chassis.StickerBlock; +import com.simibubi.create.content.contraptions.components.structureMovement.chassis.StickerTileEntity; +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.WorldSectionElement; + +import net.minecraft.util.Direction; +import net.minecraft.util.math.BlockPos; + +public class RedstoneScenes { + + public static void sticker(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("sticker", "Attaching blocks using the Sticker"); + scene.configureBasePlate(0, 0, 5); + scene.showBasePlate(); + scene.idle(5); + + Selection redstone = util.select.fromTo(0, 2, 2, 2, 2, 2); + BlockPos stickerPos = util.grid.at(2, 2, 2); + Selection stickerSelect = util.select.position(stickerPos); + BlockPos buttonPos = util.grid.at(0, 2, 2); + BlockPos bearingPos = util.grid.at(2, 1, 2); + + scene.world.showSection(util.select.fromTo(2, 1, 2, 0, 2, 2) + .substract(stickerSelect), Direction.DOWN); + scene.idle(10); + ElementLink sticker = scene.world.showIndependentSection(stickerSelect, Direction.DOWN); + scene.idle(10); + ElementLink plank = + scene.world.showIndependentSection(util.select.position(2, 2, 1), Direction.SOUTH); + scene.world.configureCenterOfRotation(sticker, util.vector.centerOf(stickerPos)); + scene.world.configureCenterOfRotation(plank, util.vector.centerOf(stickerPos)); + scene.overlay.showText(60) + .text("Stickers are ideal for Redstone-controlled block attachment") + .attachKeyFrame() + .pointAt(util.vector.blockSurface(stickerPos, Direction.WEST)) + .placeNearTarget(); + scene.idle(70); + + scene.world.toggleRedstonePower(redstone); + scene.world.modifyBlock(stickerPos, s -> s.with(StickerBlock.EXTENDED, true), false); + scene.effects.indicateRedstone(buttonPos); + scene.world.modifyTileNBT(stickerSelect, StickerTileEntity.class, nbt -> { + }); + scene.idle(20); + + scene.world.toggleRedstonePower(redstone); + scene.idle(20); + + scene.overlay.showText(60) + .text("Upon receiving a signal, it will toggle its state") + .pointAt(util.vector.blockSurface(stickerPos, Direction.WEST)) + .placeNearTarget(); + scene.idle(70); + + scene.world.rotateBearing(bearingPos, 180 * 3, 80); + scene.world.rotateSection(sticker, 0, 180 * 3, 0, 80); + scene.world.rotateSection(plank, 0, 180 * 3, 0, 80); + scene.overlay.showText(70) + .text("If it is now moved in a contraption, the block will move with it") + .pointAt(util.vector.topOf(stickerPos)) + .placeNearTarget(); + scene.idle(90); + scene.addKeyframe(); + + scene.world.toggleRedstonePower(redstone); + scene.world.modifyBlock(stickerPos, s -> s.with(StickerBlock.EXTENDED, false), false); + scene.effects.indicateRedstone(buttonPos); + scene.world.modifyTileNBT(stickerSelect, StickerTileEntity.class, nbt -> { + }); + scene.idle(20); + + scene.world.toggleRedstonePower(redstone); + scene.idle(20); + + scene.overlay.showText(60) + .text("Toggled once again, the block is no longer attached") + .pointAt(util.vector.blockSurface(stickerPos, Direction.WEST)) + .placeNearTarget(); + scene.idle(70); + + scene.world.rotateBearing(bearingPos, 180 * 3, 80); + scene.world.rotateSection(sticker, 0, 180 * 3, 0, 80); + } + + public static void contact(SceneBuilder scene, SceneBuildingUtil util) { + scene.title("redstone_contact", "Redstone Contacts"); + scene.configureBasePlate(0, 0, 5); + scene.showBasePlate(); + scene.idle(5); + Selection contactAndRedstone = util.select.fromTo(1, 1, 0, 1, 1, 2); + Selection topContact = util.select.position(1, 2, 2); + + scene.world.toggleRedstonePower(contactAndRedstone); + scene.world.toggleRedstonePower(topContact); + scene.world.showSection(contactAndRedstone, Direction.DOWN); + + BlockPos bearingPos = util.grid.at(3, 1, 2); + scene.idle(25); + + ElementLink contact = scene.world.showIndependentSection(topContact, Direction.DOWN); + scene.idle(10); + scene.world.toggleRedstonePower(topContact); + scene.world.toggleRedstonePower(contactAndRedstone); + scene.effects.indicateRedstone(util.grid.at(1, 1, 2)); + scene.idle(10); + scene.overlay.showText(60) + .attachKeyFrame() + .placeNearTarget() + .pointAt(util.vector.of(1, 2, 2.5)) + .text("Redstone Contacts facing each other will emit a redstone signal"); + scene.idle(70); + + scene.world.showSection(util.select.position(bearingPos), Direction.DOWN); + scene.idle(10); + scene.world.showSectionAndMerge(util.select.fromTo(2, 2, 2, 4, 2, 2), Direction.DOWN, contact); + scene.idle(10); + scene.effects.superGlue(util.grid.at(1, 2, 2), Direction.EAST, true); + scene.world.configureCenterOfRotation(contact, util.vector.centerOf(bearingPos)); + + int speed = 2; + + scene.idle(10); + scene.world.rotateBearing(bearingPos, 10, speed); + scene.world.rotateSection(contact, 0, 10, 0, speed); + scene.idle(speed); + + scene.world.toggleRedstonePower(topContact); + scene.world.toggleRedstonePower(contactAndRedstone); + scene.effects.indicateRedstone(util.grid.at(1, 1, 2)); + scene.world.rotateBearing(bearingPos, 340, 34 * speed); + scene.world.rotateSection(contact, 0, 340, 0, 34 * speed); + scene.addKeyframe(); + scene.idle(34 * speed); + + scene.overlay.showText(100) + .placeNearTarget() + .pointAt(util.vector.of(1, 1.5, 2.5)) + .text("This still applies when one of them is part of a moving Contraption"); + + for (int i = 0; i < 5; i++) { + scene.world.toggleRedstonePower(topContact); + scene.world.toggleRedstonePower(contactAndRedstone); + scene.effects.indicateRedstone(util.grid.at(1, 1, 2)); + scene.world.rotateBearing(bearingPos, 20, 2 * speed); + scene.world.rotateSection(contact, 0, 20, 0, 2 * speed); + scene.idle(2 * speed); + + scene.world.toggleRedstonePower(topContact); + scene.world.toggleRedstonePower(contactAndRedstone); + scene.world.rotateBearing(bearingPos, 340, 34 * speed); + scene.world.rotateSection(contact, 0, 340, 0, 34 * speed); + scene.idle(34 * speed); + + if (i == 0) + scene.markAsFinished(); + } + + scene.world.toggleRedstonePower(topContact); + scene.world.toggleRedstonePower(contactAndRedstone); + scene.world.rotateBearing(bearingPos, 10, speed); + scene.world.rotateSection(contact, 0, 10, 0, speed); + } + +} diff --git a/src/main/resources/ponder/chassis/linear_attachment.nbt b/src/main/resources/ponder/chassis/linear_attachment.nbt new file mode 100644 index 000000000..f7ef01bc6 Binary files /dev/null and b/src/main/resources/ponder/chassis/linear_attachment.nbt differ diff --git a/src/main/resources/ponder/chassis/linear_group.nbt b/src/main/resources/ponder/chassis/linear_group.nbt new file mode 100644 index 000000000..cfde348fb Binary files /dev/null and b/src/main/resources/ponder/chassis/linear_group.nbt differ diff --git a/src/main/resources/ponder/chassis/radial.nbt b/src/main/resources/ponder/chassis/radial.nbt new file mode 100644 index 000000000..17413d4a1 Binary files /dev/null and b/src/main/resources/ponder/chassis/radial.nbt differ diff --git a/src/main/resources/ponder/redstone_contact.nbt b/src/main/resources/ponder/redstone_contact.nbt new file mode 100644 index 000000000..73e656bba Binary files /dev/null and b/src/main/resources/ponder/redstone_contact.nbt differ diff --git a/src/main/resources/ponder/sticker.nbt b/src/main/resources/ponder/sticker.nbt new file mode 100644 index 000000000..92063f497 Binary files /dev/null and b/src/main/resources/ponder/sticker.nbt differ diff --git a/src/main/resources/ponder/super_glue.nbt b/src/main/resources/ponder/super_glue.nbt new file mode 100644 index 000000000..46f30a682 Binary files /dev/null and b/src/main/resources/ponder/super_glue.nbt differ