diff --git a/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c b/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c index 1817b6197c..a464cf4561 100644 --- a/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c +++ b/src/generated/resources/.cache/2d64935085b86659cb7857bad9701dbf9bab6e4c @@ -1,4 +1,4 @@ -// 1.20.1 2025-01-08T09:46:32.7401287 Registrate Provider for create [Recipes, Advancements, Loot Tables, Tags (blocks), Tags (items), Tags (fluids), Tags (entity_types), Blockstates, Item models, Lang (en_us/en_ud)] +// 1.20.1 2025-01-08T12:42:08.2186069 Registrate Provider for create [Recipes, Advancements, Loot Tables, Tags (blocks), Tags (items), Tags (fluids), Tags (entity_types), Blockstates, Item models, Lang (en_us/en_ud)] 60bbdf92d2ac9824ea6144955c74043a6005f79d assets/create/blockstates/acacia_window.json 6a67703c2697d81b7dc83e9d72a66f9c9ff08383 assets/create/blockstates/acacia_window_pane.json c3ae87b62e81d8e9476eccd793bb1548d74c66a1 assets/create/blockstates/adjustable_chain_gearshift.json @@ -642,8 +642,8 @@ b0d8f08968763a5f74e5cd5644377a76a9f39753 assets/create/blockstates/yellow_toolbo fe8c497aacc641c2f01cec90bba9f19e59cc2ed2 assets/create/blockstates/yellow_valve_handle.json e819e93fdcbe9fd9c050a052d2718ff3b3539365 assets/create/blockstates/zinc_block.json 64121dcb216381c83b4fe28aa361ea07c24c9ad0 assets/create/blockstates/zinc_ore.json -739f9cbd71716d56d1561002e8668261cddf5b0c assets/create/lang/en_ud.json -20c3b2a59090bf209090579c2f37db818a06a26b assets/create/lang/en_us.json +c1d7b1f496afa2f971078caf90f20255292d760b assets/create/lang/en_ud.json +ce3c58e8401db1c42f3e1187bec1343fbf15009e assets/create/lang/en_us.json a97e1060e00ae701a02e39cd4ef8054cf345fac4 assets/create/models/block/acacia_window.json 103e032c0b1a0a6a27c67da8c91179a564bd281c assets/create/models/block/acacia_window_pane_noside.json fb00b627abda76ad4fea867ca57dbfadd24fffa3 assets/create/models/block/acacia_window_pane_noside_alt.json diff --git a/src/generated/resources/assets/create/lang/en_ud.json b/src/generated/resources/assets/create/lang/en_ud.json index 1c783c293c..403ebcb5bc 100644 --- a/src/generated/resources/assets/create/lang/en_ud.json +++ b/src/generated/resources/assets/create/lang/en_ud.json @@ -2167,6 +2167,12 @@ "create.ponder.pulse_repeater.text_1": "ʎɐןǝp ɐ ɹǝʇɟɐ ǝsןnd ʇɹoɥs ɐ ʇıɯǝ sɹǝʇɐǝdǝᴚ ǝsןnԀ", "create.ponder.pulse_repeater.text_2": "pǝɹnbıɟuoɔ ǝq uɐɔ ǝɯıʇ ǝbɹɐɥɔ ǝɥʇ 'ןǝuɐd ǝnןɐʌ ǝɥʇ buıs∩", "create.ponder.pulse_repeater.text_3": "ɹnoɥ uɐ oʇ dn ǝbuɐɹ uɐɔ sʎɐןǝp pǝɹnbıɟuoƆ", + "create.ponder.pulse_timer.header": "ɹǝɯı⟘ ǝsןnԀ ǝɥʇ ɟo ʇndʇno ǝuoʇspǝᴚ", + "create.ponder.pulse_timer.text_1": "sǝsןnd ʇɹoɥs ʇıɯǝ ʎןpǝʇɐǝdǝɹ sɹǝɯı⟘ ǝsןnԀ", + "create.ponder.pulse_timer.text_2": "pǝɹnbıɟuoɔ ǝq uɐɔ ןɐʌɹǝʇuı ǝɯıʇ ǝɥʇ 'ןǝuɐd ǝnןɐʌ ǝɥʇ buıs∩", + "create.ponder.pulse_timer.text_3": "ɯǝɥʇ ʇǝsǝɹ puɐ ǝsnɐd ןןıʍ ǝpıs ʇnduı ǝɥʇ buıɹǝʍoԀ", + "create.ponder.pulse_timer.text_4": "ʇndʇno ǝɥʇ ʇɹǝʌuı oʇ ǝsɐq ʇınɔɹıɔ ǝɥʇ ʞɔıןɔ-ʇɥbıᴚ", + "create.ponder.pulse_timer.text_5": "ןɐubıs ǝuoʇspǝɹ ɐ ʇnoɥʇıʍ ʎןuo ǝʇɐʌıʇɔɐ ʇɐɥʇ sɯsıuɐɥɔǝɯ ɹǝbbıɹʇ sdןǝɥ sıɥ⟘", "create.ponder.radial_chassis.header": "sıssɐɥƆ ןɐıpɐᴚ buısn sʞɔoןq buıɥɔɐʇʇⱯ", "create.ponder.radial_chassis.text_1": "ʍoɹ ɐ uı sʞɔoןq sıssɐɥƆ ןɐɔıʇuǝpı oʇ ʇɔǝuuoɔ sıssɐɥƆ ןɐıpɐᴚ", "create.ponder.radial_chassis.text_2": "ʇı ɥʇıʍ pǝbbɐɹp ǝɹɐ sɹǝɥʇo ǝɥʇ 'uoıʇdɐɹʇuoƆ ɐ ʎq pǝʌoɯ sı ǝuo uǝɥM", @@ -2322,6 +2328,8 @@ "create.ponder.tag.display_targets.description": "ʞuıꞀ ʎɐןdsıᗡ ɐ ɯoɹɟ pǝʌıǝɔǝɹ ɐʇɐp ǝɥʇ ʎɐןdsıp puɐ ssǝɔoɹd uɐɔ ɥɔıɥʍ sʞɔoןᗺ ɹo sʇuǝuodɯoƆ", "create.ponder.tag.fluids": "sɹoʇɐןndıuɐW pınןℲ", "create.ponder.tag.fluids.description": "spınןℲ ɟo ǝsn buıʞɐɯ puɐ buıʎɐןǝɹ dןǝɥ ɥɔıɥʍ sʇuǝuodɯoƆ", + "create.ponder.tag.high_logistics": "sɔıʇsıboꞀ ɥbıH", + "create.ponder.tag.high_logistics.description": "ʎɹoʇɔɐɟ ɹnoʎ punoɹɐ sʇsǝnbǝɹ pǝʇɐɯoʇnɐ puɐ ǝbɐɹoʇs ɯǝʇı pǝʇnqıɹʇsıp buıbɐuɐɯ dןǝɥ ɥɔıɥʍ sʇuǝuodɯoƆ", "create.ponder.tag.kinetic_appliances": "sǝɔuɐıןddⱯ ɔıʇǝuıʞ", "create.ponder.tag.kinetic_appliances.description": "ǝɔɹoℲ ןɐuoıʇɐʇoᴚ ɟo ǝsn ǝʞɐɯ ɥɔıɥʍ sʇuǝuodɯoƆ", "create.ponder.tag.kinetic_relays": "sʞɔoןᗺ ɔıʇǝuıʞ", @@ -2332,8 +2340,6 @@ "create.ponder.tag.logistics.description": "punoɹɐ sɯǝʇı buıʌoɯ dןǝɥ ɥɔıɥʍ sʇuǝuodɯoƆ", "create.ponder.tag.movement_anchor": "sɹoɥɔuⱯ ʇuǝɯǝʌoW", "create.ponder.tag.movement_anchor.description": "sʎɐʍ ɟo ʎʇǝıɹɐʌ ɐ uı ǝɹnʇɔnɹʇs pǝɥɔɐʇʇɐ uɐ buıʇɐɯıuɐ 'suoıʇdɐɹʇuoɔ buıʌoɯ ɟo uoıʇɐǝɹɔ ǝɥʇ ʍoןןɐ ɥɔıɥʍ sʇuǝuodɯoƆ", - "create.ponder.tag.recently_updated": "sǝbuɐɥƆ ʇuǝɔǝᴚ", - "create.ponder.tag.recently_updated.description": "ǝʇɐǝɹƆ ɟo suoısɹǝʌ ʇsǝʇɐן ǝɥʇ uı ʎןʇuɐɔıɟıubıs pǝbuɐɥɔ ɹo pǝppɐ uǝǝq ǝʌɐɥ ʇɐɥʇ sʇuǝuodɯoƆ", "create.ponder.tag.redstone": "sʇuǝuodɯoƆ ɔıboꞀ", "create.ponder.tag.redstone.description": "buıɹǝǝuıbuǝ ǝuoʇspǝɹ ɥʇıʍ dןǝɥ ɥɔıɥʍ sʇuǝuodɯoƆ", "create.ponder.tag.threshold_switch_targets": "sǝɥɔʇıʍS pןoɥsǝɹɥ⟘ ɹoɟ sʇǝbɹɐ⟘", diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index 3f854462f5..af4a9663c2 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -2167,6 +2167,12 @@ "create.ponder.pulse_repeater.text_1": "Pulse Repeaters emit a short pulse after a delay", "create.ponder.pulse_repeater.text_2": "Using the value panel, the charge time can be configured", "create.ponder.pulse_repeater.text_3": "Configured delays can range up to an hour", + "create.ponder.pulse_timer.header": "Redstone output of the Pulse Timer", + "create.ponder.pulse_timer.text_1": "Pulse Timers repeatedly emit short pulses", + "create.ponder.pulse_timer.text_2": "Using the value panel, the time interval can be configured", + "create.ponder.pulse_timer.text_3": "Powering the input side will pause and reset them", + "create.ponder.pulse_timer.text_4": "Right-click the circuit base to invert the output", + "create.ponder.pulse_timer.text_5": "This helps trigger mechanisms that activate only without a redstone signal", "create.ponder.radial_chassis.header": "Attaching blocks using Radial Chassis", "create.ponder.radial_chassis.text_1": "Radial Chassis connect to identical Chassis blocks in a row", "create.ponder.radial_chassis.text_2": "When one is moved by a Contraption, the others are dragged with it", @@ -2322,6 +2328,8 @@ "create.ponder.tag.display_targets.description": "Components or Blocks which can process and display the data received from a Display Link", "create.ponder.tag.fluids": "Fluid Manipulators", "create.ponder.tag.fluids.description": "Components which help relaying and making use of Fluids", + "create.ponder.tag.high_logistics": "High Logistics", + "create.ponder.tag.high_logistics.description": "Components which help managing distributed item storage and automated requests around your factory", "create.ponder.tag.kinetic_appliances": "Kinetic Appliances", "create.ponder.tag.kinetic_appliances.description": "Components which make use of Rotational Force", "create.ponder.tag.kinetic_relays": "Kinetic Blocks", @@ -2332,8 +2340,6 @@ "create.ponder.tag.logistics.description": "Components which help moving items around", "create.ponder.tag.movement_anchor": "Movement Anchors", "create.ponder.tag.movement_anchor.description": "Components which allow the creation of moving contraptions, animating an attached structure in a variety of ways", - "create.ponder.tag.recently_updated": "Recent Changes", - "create.ponder.tag.recently_updated.description": "Components that have been added or changed significantly in the latest versions of Create", "create.ponder.tag.redstone": "Logic Components", "create.ponder.tag.redstone.description": "Components which help with redstone engineering", "create.ponder.tag.threshold_switch_targets": "Targets for Threshold Switches", diff --git a/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorBlockEntity.java b/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorBlockEntity.java index 6aedd39d2e..6d01c3059c 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorBlockEntity.java +++ b/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorBlockEntity.java @@ -26,7 +26,7 @@ import com.simibubi.create.content.schematics.requirement.ItemRequirement; import com.simibubi.create.foundation.utility.ServerSpeedProvider; import com.simibubi.create.infrastructure.config.AllConfigs; -import dev.engine_room.flywheel.api.backend.BackendManager; +import dev.engine_room.flywheel.api.visualization.VisualizationManager; import net.createmod.catnip.utility.Iterate; import net.createmod.catnip.utility.NBTHelper; import net.createmod.catnip.utility.VecHelper; @@ -146,9 +146,8 @@ public class ChainConveyorBlockEntity extends KineticBlockEntity implements ITra if (level.isClientSide()) { // We can use TickableVisuals if flywheel is enabled - if (!BackendManager.isBackendOn()) { + if (!VisualizationManager.supportsVisualization(level)) tickBoxVisuals(); - } } if (!level.isClientSide()) { @@ -207,7 +206,7 @@ public class ChainConveyorBlockEntity extends KineticBlockEntity implements ITra anticipatePosition += serverSpeed * distancePerTick * 4; anticipatePosition = Math.min(stats.chainLength, anticipatePosition); - if (level.isClientSide()) + if (level.isClientSide() && !isVirtual()) continue; for (Entry portEntry : travelPorts.entrySet()) { @@ -376,6 +375,8 @@ public class ChainConveyorBlockEntity extends KineticBlockEntity implements ITra return false; travellingPackages.computeIfAbsent(connection, $ -> new ArrayList<>()) .add(box); + if (level.isClientSide) + return true; notifyUpdate(); return true; } @@ -450,7 +451,7 @@ public class ChainConveyorBlockEntity extends KineticBlockEntity implements ITra ChainConveyorPackagePhysicsData physicsData = box.physicsData(level); physicsData.setBE(this); - if (!physicsData.shouldTick()) + if (!physicsData.shouldTick() && !isVirtual()) return; physicsData.prevTargetPos = physicsData.targetPos; @@ -652,7 +653,7 @@ public class ChainConveyorBlockEntity extends KineticBlockEntity implements ITra boolean connectedViaAxes, boolean connectedViaCogs) { if (connections.contains(target.getBlockPos() .subtract(worldPosition))) { - if (!(target instanceof ChainConveyorBlockEntity clbe)) + if (!(target instanceof ChainConveyorBlockEntity)) return 0; return 1; } diff --git a/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorRenderer.java b/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorRenderer.java index e87224acf7..eee6d6f037 100644 --- a/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorRenderer.java +++ b/src/main/java/com/simibubi/create/content/kinetics/chainConveyor/ChainConveyorRenderer.java @@ -15,7 +15,7 @@ import com.simibubi.create.content.kinetics.chainConveyor.ChainConveyorPackage.C import com.simibubi.create.content.logistics.box.PackageItem; import com.simibubi.create.foundation.render.RenderTypes; -import dev.engine_room.flywheel.api.backend.BackendManager; +import dev.engine_room.flywheel.api.visualization.VisualizationManager; import dev.engine_room.flywheel.lib.transform.TransformStack; import net.createmod.catnip.render.CachedBuffers; import net.createmod.catnip.render.SuperByteBuffer; @@ -55,13 +55,14 @@ public class ChainConveyorRenderer extends KineticBlockEntityRenderer> entry : be.travellingPackages.entrySet()) + for (ChainConveyorPackage box : entry.getValue()) renderBox(be, ms, buffer, overlay, pos, box, partialTicks); - for (Entry> entry : be.travellingPackages.entrySet()) - for (ChainConveyorPackage box : entry.getValue()) - renderBox(be, ms, buffer, overlay, pos, box, partialTicks); - } } private void renderBox(ChainConveyorBlockEntity be, PoseStack ms, MultiBufferSource buffer, int overlay, @@ -151,10 +152,9 @@ public class ChainConveyorRenderer extends KineticBlockEntityRenderer 0.5 && animatedPackage != null) { if (target == null || !target.depositImmediately() && !target.export(level, worldPosition, animatedPackage, false)) diff --git a/src/main/java/com/simibubi/create/foundation/blockEntity/renderer/SmartBlockEntityRenderer.java b/src/main/java/com/simibubi/create/foundation/blockEntity/renderer/SmartBlockEntityRenderer.java index e7c3903207..80a4f5ecc8 100644 --- a/src/main/java/com/simibubi/create/foundation/blockEntity/renderer/SmartBlockEntityRenderer.java +++ b/src/main/java/com/simibubi/create/foundation/blockEntity/renderer/SmartBlockEntityRenderer.java @@ -32,6 +32,8 @@ public class SmartBlockEntityRenderer extends SafeBl protected void renderNameplateOnHover(T blockEntity, Component tag, float yOffset, PoseStack ms, MultiBufferSource buffer, int light) { Minecraft mc = Minecraft.getInstance(); + if (blockEntity.isVirtual()) + return; if (mc.player.distanceToSqr(Vec3.atCenterOf(blockEntity.getBlockPos())) > 4096.0f) return; HitResult hitResult = mc.hitResult; diff --git a/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderScenes.java b/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderScenes.java index c2d3e655a6..4e82b92d44 100644 --- a/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderScenes.java +++ b/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderScenes.java @@ -19,6 +19,7 @@ import com.simibubi.create.infrastructure.ponder.scenes.DisplayScenes; import com.simibubi.create.infrastructure.ponder.scenes.EjectorScenes; import com.simibubi.create.infrastructure.ponder.scenes.ElevatorScenes; import com.simibubi.create.infrastructure.ponder.scenes.FanScenes; +import com.simibubi.create.infrastructure.ponder.scenes.FrogAndConveyorScenes; import com.simibubi.create.infrastructure.ponder.scenes.FunnelScenes; import com.simibubi.create.infrastructure.ponder.scenes.GantryScenes; import com.simibubi.create.infrastructure.ponder.scenes.ItemVaultScenes; @@ -339,12 +340,20 @@ public class AllCreatePonderScenes { .addStoryBoard("redstone_link", RedstoneScenes::redstoneLink); HELPER.forComponents(AllBlocks.ROSE_QUARTZ_LAMP) .addStoryBoard("rose_quartz_lamp", RedstoneScenes2::roseQuartzLamp); + HELPER.forComponents(AllBlocks.PULSE_TIMER) + .addStoryBoard("pulse_timer", RedstoneScenes2::pulseTimer); HELPER.forComponents(AllBlocks.SMART_OBSERVER) .addStoryBoard("smart_observer", DetectorScenes::smartObserver); HELPER.forComponents(AllBlocks.THRESHOLD_SWITCH) .addStoryBoard("threshold_switch", DetectorScenes::thresholdSwitch); + // Hilo + HELPER.forComponents(AllBlocks.CHAIN_CONVEYOR) + .addStoryBoard("chain_conveyor", FrogAndConveyorScenes::conveyor); + HELPER.forComponents(AllBlocks.PACKAGE_FROGPORT) + .addStoryBoard("package_frogport", FrogAndConveyorScenes::frogPort); + // Trains HELPER.forComponents(TrackMaterial.allBlocks() .stream() diff --git a/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderTags.java b/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderTags.java index 1d3ea02916..1c21c061a2 100644 --- a/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderTags.java +++ b/src/main/java/com/simibubi/create/infrastructure/ponder/AllCreatePonderTags.java @@ -25,6 +25,7 @@ public class AllCreatePonderTags { KINETIC_APPLIANCES = loc("kinetic_appliances"), FLUIDS = loc("fluids"), LOGISTICS = loc("logistics"), + HIGH_LOGISTICS = loc("high_logistics"), REDSTONE = loc("redstone"), DECORATION = loc("decoration"), CREATIVE = loc("creative"), @@ -34,73 +35,11 @@ public class AllCreatePonderTags { SAILS = loc("windmill_sails"), ARM_TARGETS = loc("arm_targets"), TRAIN_RELATED = loc("train_related"), - RECENTLY_UPDATED = loc("recently_updated"), +// RECENTLY_UPDATED = loc("recently_updated"), DISPLAY_SOURCES = loc("display_sources"), DISPLAY_TARGETS = loc("display_targets"), THRESHOLD_SWITCH_TARGETS = loc("threshold_switch_targets"); - /*public static final PonderTag - - KINETIC_RELAYS = create("kinetic_relays").item(AllBlocks.COGWHEEL.get()) - .defaultLang("Kinetic Blocks", "Components which help relaying Rotational Force elsewhere"), - - KINETIC_SOURCES = create("kinetic_sources").item(AllBlocks.WATER_WHEEL.get()) - .defaultLang("Kinetic Sources", "Components which generate Rotational Force"), - - KINETIC_APPLIANCES = create("kinetic_appliances").item(AllBlocks.MECHANICAL_PRESS.get()) - .defaultLang("Kinetic Appliances", "Components which make use of Rotational Force"), - - FLUIDS = create("fluids").item(AllBlocks.FLUID_PIPE.get()) - .defaultLang("Fluid Manipulators", "Components which help relaying and making use of Fluids"), - - LOGISTICS = create("logistics").item(Blocks.CHEST) - .defaultLang("Item Transportation", "Components which help moving items around"), - - REDSTONE = create("redstone").item(Items.REDSTONE) - .defaultLang("Logic Components", "Components which help with redstone engineering"), - - DECORATION = create("decoration").item(Items.ROSE_BUSH) - .defaultLang("Aesthetics", "Components used mostly for decorative purposes"), - - CREATIVE = create("creative").item(AllBlocks.CREATIVE_CRATE.get()) - .defaultLang("Creative Mode", "Components not usually available for Survival Mode"), - - MOVEMENT_ANCHOR = create("movement_anchor").item(AllBlocks.MECHANICAL_PISTON.get()) - .defaultLang("Movement Anchors", - "Components which allow the creation of moving contraptions, animating an attached structure in a variety of ways"), - - CONTRAPTION_ACTOR = create("contraption_actor").item(AllBlocks.MECHANICAL_HARVESTER.get()) - .defaultLang("Contraption Actors", - "Components which expose special behaviour when attached to a moving contraption"), - - CONTRAPTION_ASSEMBLY = create("contraption_assembly").item(AllItems.SUPER_GLUE.get()) - .defaultLang("Block Attachment Utility", - "Tools and Components used to assemble structures moved as an animated Contraption"), - - SAILS = create("windmill_sails").item(AllBlocks.WINDMILL_BEARING.get(), true, true) - .defaultLang("Sails for Windmill Bearings", - "Blocks that count towards the strength of a Windmill Contraption when assembled. Each of these have equal efficiency in doing so."), - - ARM_TARGETS = create("arm_targets").item(AllBlocks.MECHANICAL_ARM.get(), true, true) - .defaultLang("Targets for Mechanical Arms", - "Components which can be selected as inputs or outputs to the Mechanical Arm"), - - TRAIN_RELATED = create("train_related").item(AllBlocks.TRACK.get()) - .defaultLang("Railway Equipment", - "Components used in the construction or management of Train Contraptions"), - - RECENTLY_UPDATED = create("recently_updated").item(AllBlocks.CLIPBOARD.get()) - .defaultLang("Recent Changes", - "Components that have been added or changed significantly in the latest versions of Create"), - - DISPLAY_SOURCES = create("display_sources").item(AllBlocks.DISPLAY_LINK.get(), true, true) - .defaultLang("Sources for Display Links", - "Components or Blocks which offer some data that can be read with a Display Link"), - - DISPLAY_TARGETS = create("display_targets").item(AllBlocks.DISPLAY_LINK.get(), true, true) - .defaultLang("Targets for Display Links", - "Components or Blocks which can process and display the data received from a Display Link");*/ - private static ResourceLocation loc(String id) { return Create.asResource(id); } @@ -146,6 +85,13 @@ public class AllCreatePonderTags { .title("Item Transportation") .description("Components which help moving items around") .register(); + + helper.registerTag(HIGH_LOGISTICS) + .addToIndex() + .item(AllBlocks.STOCK_TICKER.get()) + .title("High Logistics") + .description("Components which help managing distributed item storage and automated requests around your factory") + .register(); helper.registerTag(REDSTONE) .addToIndex() @@ -208,12 +154,12 @@ public class AllCreatePonderTags { .description("Components used in the construction or management of Train Contraptions") .register(); - helper.registerTag(RECENTLY_UPDATED) - .addToIndex() - .item(AllBlocks.CLIPBOARD.get()) - .title("Recent Changes") - .description("Components that have been added or changed significantly in the latest versions of Create") - .register(); +// helper.registerTag(RECENTLY_UPDATED) +// .addToIndex() +// .item(AllBlocks.CLIPBOARD.get()) +// .title("Recent Changes") +// .description("Components that have been added or changed significantly in the latest versions of Create") +// .register(); helper.registerTag(DISPLAY_SOURCES) .item(AllBlocks.DISPLAY_LINK.get()) @@ -233,19 +179,7 @@ public class AllCreatePonderTags { .description("Threshold Switches can read from these blocks, as well as most item and fluid containers.") .register(); - HELPER.addToTag(RECENTLY_UPDATED) - .add(AllBlocks.WATER_WHEEL) - .add(AllBlocks.LARGE_WATER_WHEEL) - .add(AllBlocks.COPPER_VALVE_HANDLE) - .add(AllBlocks.ELEVATOR_PULLEY) - .add(AllBlocks.CONTRAPTION_CONTROLS) - .add(AllBlocks.MECHANICAL_ROLLER) - .add(AllBlocks.MECHANICAL_PUMP) - .add(AllBlocks.SMART_OBSERVER) - .add(AllBlocks.THRESHOLD_SWITCH) - .add(AllItems.NETHERITE_BACKTANK) - .add(AllBlocks.COPYCAT_PANEL) - .add(AllBlocks.COPYCAT_STEP); +// HELPER.addToTag(RECENTLY_UPDATED); HELPER.addToTag(KINETIC_RELAYS) .add(AllBlocks.SHAFT) @@ -253,10 +187,12 @@ public class AllCreatePonderTags { .add(AllBlocks.LARGE_COGWHEEL) .add(AllItems.BELT_CONNECTOR) .add(AllBlocks.GEARBOX) + .add(AllItems.VERTICAL_GEARBOX) .add(AllBlocks.CLUTCH) .add(AllBlocks.GEARSHIFT) .add(AllBlocks.ENCASED_CHAIN_DRIVE) .add(AllBlocks.ADJUSTABLE_CHAIN_GEARSHIFT) + .add(AllBlocks.CHAIN_CONVEYOR) .add(AllBlocks.SEQUENCED_GEARSHIFT) .add(AllBlocks.ROTATION_SPEED_CONTROLLER); @@ -391,6 +327,7 @@ public class AllCreatePonderTags { .add(AllBlocks.REDSTONE_LINK) .add(AllBlocks.PULSE_EXTENDER) .add(AllBlocks.PULSE_REPEATER) + .add(AllBlocks.PULSE_TIMER) .add(AllBlocks.POWERED_LATCH) .add(AllBlocks.POWERED_TOGGLE_LATCH) .add(AllBlocks.ROSE_QUARTZ_LAMP); @@ -417,6 +354,18 @@ public class AllCreatePonderTags { .add(Blocks.SLIME_BLOCK) .add(Blocks.HONEY_BLOCK); + HELPER.addToTag(HIGH_LOGISTICS) + .add(AllBlocks.PACKAGER) + .add(AllBlocks.PACKAGE_FROGPORT) + .add(AllBlocks.PACKAGE_POSTBOXES.get(DyeColor.WHITE)) + .add(AllBlocks.STOCK_LINK) + .add(AllBlocks.STOCK_TICKER) + .add(AllBlocks.REDSTONE_REQUESTER) + .add(AllBlocks.TABLE_CLOTHS.get(DyeColor.WHITE)) + .add(AllBlocks.FACTORY_GAUGE) + .add(AllBlocks.REPACKAGER) + .add(AllItems.PACKAGE_FILTER); + HELPER.addToTag(CONTRAPTION_ACTOR) .add(AllBlocks.MECHANICAL_HARVESTER) .add(AllBlocks.MECHANICAL_PLOUGH) @@ -452,6 +401,7 @@ public class AllCreatePonderTags { .add(AllBlocks.STRESSOMETER) .add(AllBlocks.SPEEDOMETER) .add(AllBlocks.FLUID_TANK) + .add(AllBlocks.FACTORY_GAUGE) .add(AllItems.BELT_CONNECTOR); itemHelper.addToTag(DISPLAY_SOURCES) diff --git a/src/main/java/com/simibubi/create/infrastructure/ponder/scenes/FrogAndConveyorScenes.java b/src/main/java/com/simibubi/create/infrastructure/ponder/scenes/FrogAndConveyorScenes.java new file mode 100644 index 0000000000..53cd2921a3 --- /dev/null +++ b/src/main/java/com/simibubi/create/infrastructure/ponder/scenes/FrogAndConveyorScenes.java @@ -0,0 +1,935 @@ +package com.simibubi.create.infrastructure.ponder.scenes; + +import java.util.Iterator; +import java.util.function.Supplier; + +import com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.math.Axis; +import com.simibubi.create.AllBlocks; +import com.simibubi.create.AllItems; +import com.simibubi.create.content.kinetics.chainConveyor.ChainConveyorBlockEntity; +import com.simibubi.create.content.kinetics.chainConveyor.ChainConveyorPackage; +import com.simibubi.create.content.logistics.box.PackageItem; +import com.simibubi.create.content.logistics.box.PackageStyles; +import com.simibubi.create.content.logistics.packagePort.frogport.FrogportBlockEntity; +import com.simibubi.create.content.logistics.packager.PackagerBlockEntity; +import com.simibubi.create.foundation.ponder.CreateSceneBuilder; + +import net.createmod.catnip.utility.Pointing; +import net.createmod.catnip.utility.math.AngleHelper; +import net.createmod.ponder.api.PonderPalette; +import net.createmod.ponder.api.element.ElementLink; +import net.createmod.ponder.api.element.ParrotElement; +import net.createmod.ponder.api.element.ParrotPose; +import net.createmod.ponder.api.element.WorldSectionElement; +import net.createmod.ponder.api.level.PonderLevel; +import net.createmod.ponder.api.scene.SceneBuilder; +import net.createmod.ponder.api.scene.SceneBuildingUtil; +import net.createmod.ponder.api.scene.Selection; +import net.createmod.ponder.foundation.element.ElementLinkImpl; +import net.createmod.ponder.foundation.element.ParrotElementImpl; +import net.createmod.ponder.foundation.instruction.CreateParrotInstruction; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiGraphics; +import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.entity.EntityRenderDispatcher; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.client.resources.model.BakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemDisplayContext; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +public class FrogAndConveyorScenes { + + public static void conveyor(SceneBuilder builder, SceneBuildingUtil util) { + CreateSceneBuilder scene = new CreateSceneBuilder(builder); + scene.title("chain_conveyor", "Relaying rotational force using Chain Conveyors"); + scene.configureBasePlate(0, 0, 9); + scene.scaleSceneView(.75f); + scene.setSceneOffsetY(-1); + scene.world() + .showSection(util.select() + .layer(0), Direction.UP); + + Selection pole = util.select() + .fromTo(1, 1, 6, 1, 3, 6); + Selection cogs = util.select() + .position(8, 1, 2); + Selection cogs2 = util.select() + .fromTo(7, 1, 1, 7, 3, 1); + + BlockPos conv1 = util.grid() + .at(7, 4, 1); + BlockPos conv2 = util.grid() + .at(1, 4, 7); + BlockPos conv3 = util.grid() + .at(1, 2, 4); + BlockPos conv4 = util.grid() + .at(7, 4, 7); + + connection(builder, conv1, conv2, false); + connection(builder, conv1, conv3, false); + connection(builder, conv1, conv4, false); + + Selection pole3 = util.select() + .position(1, 1, 4); + Selection pole4 = util.select() + .fromTo(7, 1, 7, 7, 3, 7); + Selection cogsBelow = util.select() + .fromTo(1, 2, 7, 1, 3, 7); + Selection cogsAbove = util.select() + .position(1, 5, 7); + + Selection conv1S = util.select() + .position(conv1); + Selection conv2S = util.select() + .position(conv2); + Selection conv3S = util.select() + .position(conv3); + Selection conv4S = util.select() + .position(conv4); + + scene.world() + .setKineticSpeed(conv2S, 0); + + scene.idle(5); + scene.world() + .showSection(cogs, Direction.EAST); + scene.idle(5); + scene.world() + .showSection(cogs2, Direction.DOWN); + scene.idle(5); + scene.world() + .showSection(conv1S, Direction.DOWN); + ElementLink poleE = scene.world() + .showIndependentSection(pole, Direction.DOWN); + scene.world() + .moveSection(poleE, util.vector() + .of(0, 0, 1), 0); + scene.idle(5); + scene.world() + .showSection(conv2S, Direction.DOWN); + scene.idle(20); + + ItemStack chainItem = new ItemStack(Items.CHAIN); + scene.overlay() + .showControls(util.vector() + .topOf(conv1), Pointing.DOWN, 117) + .rightClick() + .withItem(chainItem); + + Vec3 c1 = util.vector() + .centerOf(conv1); + AABB bb1 = new AABB(c1, c1); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, conv1, bb1, 10); + scene.idle(1); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, conv1, bb1.inflate(1, 0.5, 1), 117); + scene.idle(16); + + scene.overlay() + .showControls(util.vector() + .topOf(conv2), Pointing.DOWN, 100) + .rightClick() + .withItem(chainItem); + + Vec3 c2 = util.vector() + .centerOf(conv2); + AABB bb2 = new AABB(c2, c2); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, conv2, bb2, 10); + scene.idle(1); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, conv2, bb2.inflate(1, 0.5, 1), 100); + scene.idle(10); + + connection(builder, conv1, conv2, true); + scene.world() + .setKineticSpeed(conv2S, -32); + + scene.overlay() + .showText(80) + .text("Right-click two conveyors with chains to connect them") + .attachKeyFrame() + .placeNearTarget() + .pointAt(util.vector() + .topOf(conv1.offset(-1, 0, -1))); + scene.idle(90); + + scene.world() + .showSection(pole3, Direction.DOWN); + scene.idle(3); + scene.world() + .showSection(pole4, Direction.DOWN); + scene.idle(6); + scene.world() + .showSection(conv3S, Direction.DOWN); + scene.idle(3); + scene.world() + .showSection(conv4S, Direction.DOWN); + scene.idle(12); + connection(builder, conv1, conv3, true); + scene.idle(3); + connection(builder, conv1, conv4, true); + scene.idle(20); + + scene.overlay() + .showText(60) + .text("These relay rotational power to each other very flexibly..") + .attachKeyFrame() + .placeNearTarget() + .pointAt(util.vector() + .topOf(conv3.offset(-1, 0, -1))); + scene.idle(50); + + scene.world() + .hideIndependentSection(poleE, Direction.SOUTH); + scene.idle(20); + scene.world() + .showSection(cogsAbove, Direction.DOWN); + scene.idle(3); + scene.world() + .showSection(cogsBelow, Direction.UP); + scene.idle(12); + + scene.effects() + .rotationDirectionIndicator(conv2.above()); + scene.idle(3); + scene.effects() + .rotationDirectionIndicator(conv2.below(2)); + scene.idle(10); + + scene.overlay() + .showText(60) + .text("..and connect to shafts above or below them") + .attachKeyFrame() + .placeNearTarget() + .pointAt(util.vector() + .centerOf(util.grid() + .at(1, 2, 7))); + scene.idle(60); + scene.world() + .hideSection(cogsBelow, Direction.SOUTH); + scene.idle(15); + ElementLink poleE2 = scene.world() + .showIndependentSection(pole, Direction.EAST); + scene.world() + .moveSection(poleE2, util.vector() + .of(0, 0, 1), 0); + scene.idle(10); + + scene.overlay() + .showText(80) + .text("Right-click holding a wrench to start travelling on the chain") + .attachKeyFrame() + .independent(30); + + scene.idle(40); + ElementLink parrot = new ElementLinkImpl<>(ParrotElement.class); + Vec3 parrotStart = util.vector() + .centerOf(conv2) + .add(0, -1.45, 1); + ChainConveyorParrotElement element = + new ChainConveyorParrotElement(parrotStart, ParrotPose.FacePointOfInterestPose::new); + scene.addInstruction(new CreateParrotInstruction(0, Direction.DOWN, element)); + scene.addInstruction(s -> s.linkElement(element, parrot)); + scene.special() + .movePointOfInterest(util.grid() + .at(0, 3, 2)); + + scene.idle(20); + scene.special() + .moveParrot(parrot, util.vector() + .of(-1, 0, -1), 14); + scene.idle(14); + scene.special() + .movePointOfInterest(util.grid() + .at(7, 3, 0)); + scene.special() + .moveParrot(parrot, util.vector() + .of(5.75, 0, -5.75), 90); + scene.idle(65); + + scene.overlay() + .showText(60) + .text("At a junction, face towards a chain to follow it") + .attachKeyFrame() + .placeNearTarget() + .pointAt(util.vector() + .topOf(conv1)); + + scene.idle(25); + scene.special() + .movePointOfInterest(util.grid() + .at(9, 3, 1)); + scene.special() + .moveParrot(parrot, util.vector() + .of(1, 0, -1), 14); + scene.idle(14); + scene.special() + .movePointOfInterest(util.grid() + .at(9, 3, 3)); + scene.special() + .moveParrot(parrot, util.vector() + .of(0.5, 0, 0), 6); + scene.idle(6); + scene.special() + .movePointOfInterest(util.grid() + .at(8, 3, 10)); + scene.special() + .moveParrot(parrot, util.vector() + .of(0.5, 0, 0.5), 14); + scene.idle(14); + scene.special() + .moveParrot(parrot, util.vector() + .of(0, 0, 7), 78); + scene.idle(78); + scene.special() + .hideElement(parrot, Direction.SOUTH); + } + + private static void connection(SceneBuilder builder, BlockPos p1, BlockPos p2, boolean connect) { + builder.world() + .modifyBlockEntity(p1, ChainConveyorBlockEntity.class, be -> { + if (connect) + be.connections.add(p2.subtract(p1)); + else + be.connections.remove(p2.subtract(p1)); + }); + builder.world() + .modifyBlockEntity(p2, ChainConveyorBlockEntity.class, be -> { + if (connect) + be.connections.add(p1.subtract(p2)); + else + be.connections.remove(p1.subtract(p2)); + }); + } + + public static class ChainConveyorParrotElement extends ParrotElementImpl { + + private ItemEntity wrench; + + public ChainConveyorParrotElement(Vec3 location, Supplier pose) { + super(location, pose); + } + + @Override + protected void renderLast(PonderLevel world, MultiBufferSource buffer, GuiGraphics graphics, float fade, + float pt) { + PoseStack poseStack = graphics.pose(); + EntityRenderDispatcher entityrenderermanager = Minecraft.getInstance() + .getEntityRenderDispatcher(); + + if (entity == null) { + entity = pose.create(world); + entity.setYRot(entity.yRotO = 180); + } + + if (wrench == null) { + wrench = new ItemEntity(world, 0, 0, 0, AllItems.WRENCH.asStack()); + wrench.setYRot(wrench.yRotO = 180); + } + + double lx = Mth.lerp(pt, entity.xo, entity.getX()); + double ly = Mth.lerp(pt, entity.yo, entity.getY()); + double lz = Mth.lerp(pt, entity.zo, entity.getZ()); + float angle = AngleHelper.angleLerp(pt, entity.yRotO, entity.getYRot()); + + poseStack.pushPose(); + poseStack.translate(location.x, location.y, location.z); + poseStack.translate(lx, ly, lz); + poseStack.mulPose(Axis.YP.rotationDegrees(angle)); + + poseStack.translate(0, 1.5f, 0); + poseStack.mulPose(Axis.ZP.rotationDegrees(Mth.sin((world.scene.getCurrentTime() + pt) * 0.2f) * 10)); + poseStack.translate(0, -1.5f, 0); + + poseStack.pushPose(); + poseStack.mulPose(Axis.YP.rotationDegrees(90)); + poseStack.mulPose(Axis.XP.rotationDegrees(90)); + poseStack.mulPose(Axis.ZP.rotationDegrees(90)); + poseStack.scale(1.5f, 1.5f, 1.5f); + poseStack.translate(-0.1, 0.2, -0.6); + BakedModel bakedmodel = Minecraft.getInstance() + .getItemRenderer() + .getModel(wrench.getItem(), world, (LivingEntity) null, 0); + Minecraft.getInstance() + .getItemRenderer() + .render(wrench.getItem(), ItemDisplayContext.GROUND, false, poseStack, buffer, + lightCoordsFromFade(fade), OverlayTexture.NO_OVERLAY, bakedmodel); + poseStack.popPose(); + + entity.flapSpeed = 2; + entityrenderermanager.render(entity, 0, 0, 0, 0, pt, poseStack, buffer, lightCoordsFromFade(fade)); + poseStack.popPose(); + } + + } + + public static void frogPort(SceneBuilder builder, SceneBuildingUtil util) { + CreateSceneBuilder scene = new CreateSceneBuilder(builder); + scene.title("package_frogport", "Transporting packages with Frogports"); + scene.configureBasePlate(0, 0, 9); + scene.scaleSceneView(.75f); + scene.setSceneOffsetY(-1); + + BlockPos conv1 = util.grid() + .at(1, 4, 7); + BlockPos conv2 = util.grid() + .at(7, 4, 7); + BlockPos conv3 = util.grid() + .at(7, 4, 1); + Selection conv1S = util.select() + .position(conv1); + Selection conv2S = util.select() + .position(conv2); + Selection conv3S = util.select() + .position(conv3); + Selection largeCog = util.select() + .position(2, 1, 8); + Selection shaftPole = util.select() + .fromTo(1, 1, 7, 1, 3, 7); + Selection pole2 = util.select() + .fromTo(7, 1, 7, 7, 3, 7); + Selection pole3 = util.select() + .fromTo(7, 1, 1, 7, 3, 1); + Selection largeCog2 = util.select() + .position(9, 0, 1); + Selection fromBelt = util.select() + .fromTo(9, 1, 0, 6, 1, 0) + .add(util.select() + .fromTo(5, 1, 0, 5, 1, 1)); + BlockPos fromFunnel = util.grid() + .at(5, 2, 1); + Selection logistics = util.select() + .fromTo(2, 1, 2, 1, 1, 2); + Selection toBelt = util.select() + .fromTo(1, 1, 9, 0, 1, 9) + .add(util.select() + .fromTo(0, 1, 8, 0, 1, 6)) + .add(util.select() + .fromTo(1, 1, 5, 0, 1, 5)); + BlockPos toFunnel = util.grid() + .at(1, 2, 5); + + BlockPos fromFrog = util.grid() + .at(5, 2, 2); + BlockPos toFrog = util.grid() + .at(2, 2, 5); + Selection fromFrogS = util.select() + .position(fromFrog); + Selection toFrogS = util.select() + .position(toFrog); + + Selection casing = util.select() + .position(5, 1, 4); + Selection fromBarrel = util.select() + .position(5, 1, 3); + Selection toBarrel = util.select() + .position(3, 1, 5); + Selection fromPackager = util.select() + .fromTo(7, 1, 2, 5, 1, 2); + Selection sign = util.select() + .position(4, 1, 2); + BlockPos lever = util.grid() + .at(6, 1, 1); + Selection toPackager = util.select() + .fromTo(2, 1, 4, 0, 1, 4); + + scene.world() + .showSection(util.select() + .layer(0) + .substract(largeCog2), Direction.UP); + scene.idle(10); + scene.world() + .showSection(largeCog, Direction.SOUTH); + scene.idle(2); + scene.world() + .showSection(shaftPole, Direction.DOWN); + scene.idle(2); + scene.world() + .showSection(pole2, Direction.DOWN); + scene.idle(2); + scene.world() + .showSection(pole3, Direction.DOWN); + scene.idle(5); + scene.world() + .showSection(conv1S, Direction.DOWN); + scene.world() + .showSection(conv2S, Direction.DOWN); + scene.world() + .showSection(conv3S, Direction.DOWN); + scene.idle(25); + + Vec3 fromTarget = util.vector() + .of(6.78, 4.37, 3.5); + + ItemStack frogItem = AllBlocks.PACKAGE_FROGPORT.asStack(); + scene.overlay() + .showControls(fromTarget, Pointing.UP, 50) + .rightClick() + .withItem(frogItem); + scene.idle(5); + + AABB bb1 = new AABB(fromTarget, fromTarget); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.WHITE, conv1, bb1, 10); + scene.idle(1); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.WHITE, conv1, bb1.inflate(0.025, 0.025, 0.025), 50); + scene.idle(26); + + scene.overlay() + .showText(80) + .text("Right-click a Chain Conveyor and place the Frogport nearby") + .attachKeyFrame() + .placeNearTarget() + .pointAt(fromTarget); + + scene.idle(40); + + ElementLink fromFrogE = scene.world() + .showIndependentSection(fromFrogS, Direction.DOWN); + scene.world() + .moveSection(fromFrogE, util.vector() + .of(0, -1, 0), 0); + + scene.idle(15); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, conv1, bb1.inflate(0.025, 0.025, 0.025), 50); + + AABB bb2 = new AABB(fromFrog.below()).contract(0, 0.75, 0); + + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, conv2, bb2, 50); + scene.idle(10); + scene.overlay() + .showLine(PonderPalette.GREEN, util.vector() + .topOf(fromFrog.below()), fromTarget, 40); + scene.idle(45); + + scene.overlay() + .showControls(util.vector() + .topOf(fromFrog.below()), Pointing.DOWN, 40) + .rightClick(); + scene.idle(7); + scene.overlay() + .showOutlineWithText(util.select() + .position(fromFrog.below()), 70) + .attachKeyFrame() + .colored(PonderPalette.BLUE) + .text("They can be given a name in their inventory UI") + .pointAt(util.vector() + .topOf(fromFrog.below())) + .placeNearTarget(); + scene.idle(80); + + scene.world() + .moveSection(fromFrogE, util.vector() + .of(0, 1, 0), 10); + scene.idle(10); + ElementLink casingE = scene.world() + .showIndependentSection(casing, Direction.NORTH); + scene.world() + .moveSection(casingE, util.vector() + .of(0, 0, -2), 0); + scene.idle(5); + scene.world() + .showSection(largeCog2, Direction.UP); + scene.world() + .showSection(fromBelt, Direction.SOUTH); + scene.idle(5); + scene.world() + .showSection(util.select() + .position(fromFunnel), Direction.DOWN); + scene.idle(10); + + ItemStack box = PackageStyles.getDefaultBox() + .copy(); + PackageItem.addAddress(box, "Peter"); + + scene.world() + .createItemOnBelt(util.grid() + .at(5, 1, 0), Direction.NORTH, box); + scene.idle(5); + + scene.world() + .multiplyKineticSpeed(util.select() + .fromTo(9, 0, 1, 5, 1, 0), 1 / 32f); + scene.idle(5); + + scene.overlay() + .showText(75) + .colored(PonderPalette.BLUE) + .text("Albert") + .pointAt(util.vector() + .topOf(fromFrog)) + .placeNearTarget(); + scene.idle(5); + scene.overlay() + .showText(70) + .colored(PonderPalette.OUTPUT) + .text("Peter") + .pointAt(util.vector() + .centerOf(util.grid() + .at(5, 2, 0))) + .placeNearTarget(); + scene.idle(30); + + scene.overlay() + .showText(80) + .attachKeyFrame() + .text("If a package is addressed to a different name..") + .pointAt(util.vector() + .topOf(5, 0, 3)) + .placeNearTarget(); + + scene.idle(60); + + scene.world() + .multiplyKineticSpeed(util.select() + .fromTo(9, 0, 1, 5, 1, 0), 32f); + scene.idle(13); + scene.world() + .removeItemsFromBelt(util.grid() + .at(5, 1, 1)); + scene.world() + .flapFunnel(fromFunnel, false); + scene.idle(15); + scene.world() + .modifyBlockEntity(fromFrog, FrogportBlockEntity.class, be -> be.startAnimation(box, true)); + scene.idle(15); + + scene.overlay() + .showText(60) + .text("..the Frogport will place the package on the conveyor") + .pointAt(fromTarget.add(0, 0, 1.5)) + .placeNearTarget(); + scene.idle(95); + + scene.overlay() + .showText(60) + .attachKeyFrame() + .colored(PonderPalette.RED) + .text("Packages spin in place if they have no valid destination") + .pointAt(util.vector() + .of(6.5, 4.25, 7.5)) + .placeNearTarget(); + scene.idle(60); + + scene.world() + .showSection(util.select() + .position(toFrog.below()), Direction.SOUTH); + scene.idle(5); + scene.world() + .showSection(toFrogS, Direction.DOWN); + scene.idle(15); + + Vec3 toTarget = util.vector() + .of(3.5, 4.37, 6.78); + AABB bb3 = new AABB(toTarget, toTarget); + AABB bb4 = new AABB(toFrog).contract(0, 0.75, 0); + + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, conv3, bb3.inflate(0.025, 0.025, 0.025), 80); + scene.overlay() + .chaseBoundingBoxOutline(PonderPalette.GREEN, lever, bb4, 80); + scene.overlay() + .showLine(PonderPalette.GREEN, util.vector() + .topOf(toFrog), toTarget, 80); + + scene.idle(10); + + scene.overlay() + .showText(70) + .text("More Frogports can be added anywhere on the chain network") + .attachKeyFrame() + .placeNearTarget() + .pointAt(toTarget); + + scene.idle(75); + + scene.overlay() + .showText(70) + .colored(PonderPalette.BLUE) + .text("Peter") + .pointAt(util.vector() + .topOf(toFrog)) + .placeNearTarget(); + scene.idle(30); + + scene.world() + .modifyBlockEntity(conv2, ChainConveyorBlockEntity.class, be -> boxTransfer(conv1, conv2, be)); + + scene.idle(50); + scene.overlay() + .showText(70) + .attachKeyFrame() + .text("Packages find their path to a matching frog on the chain network") + .pointAt(util.vector() + .topOf(toFrog)) + .placeNearTarget(); + scene.idle(40); + + scene.world() + .showSection(toBelt, Direction.SOUTH); + scene.idle(10); + scene.world() + .showSection(util.select() + .position(toFunnel), Direction.DOWN); + scene.idle(15); + + scene.world() + .createItemOnBelt(util.grid() + .at(1, 1, 5), Direction.EAST, box); + scene.idle(20); + scene.world() + .hideSection(util.select() + .fromTo(0, 1, 6, 0, 1, 9) + .add(util.select() + .position(1, 1, 9)), + Direction.SOUTH); + scene.world() + .setKineticSpeed(util.select() + .fromTo(1, 1, 5, 0, 1, 5), 0); + + scene.overlay() + .showText(50) + .colored(PonderPalette.BLUE) + .text("Peter") + .pointAt(util.vector() + .topOf(toFrog)) + .placeNearTarget(); + scene.idle(5); + scene.overlay() + .showText(55) + .colored(PonderPalette.OUTPUT) + .text("Peter") + .pointAt(util.vector() + .centerOf(util.grid() + .at(0, 2, 5))) + .placeNearTarget(); + + scene.idle(60); + + scene.world() + .hideSection(util.select() + .fromTo(1, 2, 5, 0, 1, 5), Direction.WEST); + scene.world() + .hideSection(util.select() + .fromTo(5, 2, 1, 5, 1, 0) + .add(util.select() + .fromTo(6, 1, 0, 9, 1, 0)), + Direction.NORTH); + scene.world() + .hideSection(util.select() + .position(9, 0, 1), Direction.DOWN); + + scene.idle(15); + + scene.world() + .hideIndependentSection(casingE, Direction.WEST); + scene.world() + .hideSection(util.select() + .position(2, 1, 5), Direction.WEST); + scene.idle(15); + ElementLink fromBarrelE = scene.world() + .showIndependentSection(fromBarrel, Direction.WEST); + scene.world() + .moveSection(fromBarrelE, util.vector() + .of(0, 0, -1), 0); + ElementLink toBarrelE = scene.world() + .showIndependentSection(toBarrel, Direction.WEST); + scene.world() + .moveSection(toBarrelE, util.vector() + .of(-1, 0, 0), 0); + scene.idle(20); + + scene.overlay() + .showOutlineWithText(util.select() + .position(fromFrog.below()) + .add(fromFrogS), 70) + .attachKeyFrame() + .colored(PonderPalette.BLUE) + .text("Frogports can directly interface with inventories below them") + .pointAt(util.vector() + .topOf(fromFrog.below())) + .placeNearTarget(); + + scene.idle(70); + + scene.world() + .hideIndependentSection(fromBarrelE, Direction.WEST); + scene.world() + .hideIndependentSection(toBarrelE, Direction.EAST); + scene.idle(15); + + scene.world() + .showIndependentSection(fromPackager, Direction.WEST); + ElementLink toPackagerE = scene.world() + .showIndependentSection(toPackager, Direction.WEST); + scene.world() + .moveSection(toPackagerE, util.vector() + .of(0, 0, 1), 0); + ElementLink leverE = scene.world() + .showIndependentSection(util.select() + .position(lever), Direction.DOWN); + scene.world() + .moveSection(leverE, util.vector() + .of(-1, 0, 0), 0); + scene.idle(15); + + scene.overlay() + .showText(90) + .attachKeyFrame() + .text("This works with packagers, items can be packed and shipped directly") + .pointAt(util.vector() + .blockSurface(fromFrog.below(), Direction.WEST)) + .placeNearTarget(); + scene.idle(100); + + scene.world() + .showSection(sign, Direction.EAST); + scene.idle(10); + + scene.overlay() + .showText(80) + .colored(PonderPalette.BLUE) + .text("Albert") + .pointAt(util.vector() + .topOf(fromFrog)) + .placeNearTarget(); + scene.overlay() + .showText(80) + .colored(PonderPalette.BLUE) + .text("Peter") + .pointAt(util.vector() + .topOf(toFrog)) + .placeNearTarget(); + scene.idle(20); + + scene.overlay() + .showOutlineWithText(util.select() + .position(fromFrog.below()) + .add(util.select() + .position(fromFrog.below() + .west())), + 70) + .colored(PonderPalette.OUTPUT) + .text("Addresses packages to 'Peter'") + .pointAt(util.vector() + .blockSurface(fromFrog.below() + .west(), Direction.NORTH)) + .placeNearTarget(); + scene.idle(80); + + scene.overlay() + .showControls(util.vector() + .blockSurface(util.grid() + .at(6, 1, 2), Direction.UP) + .add(0.5, 0, 0), Pointing.DOWN, 40) + .withItem(new ItemStack(Items.DIAMOND)); + scene.idle(25); + + scene.addKeyframe(); + + scene.effects() + .indicateRedstone(util.grid() + .at(5, 1, 1)); + scene.world() + .toggleRedstonePower(util.select() + .fromTo(5, 1, 2, 6, 1, 1)); + scene.idle(5); + + scene.world() + .modifyBlockEntity(util.grid() + .at(5, 1, 2), PackagerBlockEntity.class, be -> { + be.animationTicks = PackagerBlockEntity.CYCLE; + be.animationInward = false; + be.heldBox = box; + }); + + scene.idle(30); + + scene.world() + .modifyBlockEntity(util.grid() + .at(5, 1, 2), PackagerBlockEntity.class, be -> { + be.heldBox = ItemStack.EMPTY; + }); + scene.world() + .modifyBlockEntity(fromFrog, FrogportBlockEntity.class, be -> be.startAnimation(box, true)); + + scene.idle(40); + + scene.world() + .modifyBlockEntity(conv2, ChainConveyorBlockEntity.class, be -> boxTransfer(conv1, conv2, be)); + scene.idle(50); + + scene.world() + .modifyBlockEntity(util.grid() + .at(2, 1, 4), PackagerBlockEntity.class, be -> { + be.animationTicks = PackagerBlockEntity.CYCLE; + be.animationInward = true; + be.previouslyUnwrapped = box; + }); + + scene.idle(20); + scene.overlay() + .showControls(util.vector() + .blockSurface(util.grid() + .at(0, 1, 5), Direction.UP) + .add(0.5, 0, 0), Pointing.DOWN, 40) + .withItem(new ItemStack(Items.DIAMOND)); + scene.idle(60); + + scene.overlay() + .showControls(util.vector() + .centerOf(util.grid() + .at(2, 2, 5)), + Pointing.RIGHT, 40) + .rightClick() + .withItem(AllBlocks.CLIPBOARD.asStack()); + scene.idle(10); + + scene.overlay() + .showText(90) + .attachKeyFrame() + .text("Right-click Frogports with a clipboard to collect their address") + .pointAt(util.vector() + .blockSurface(toFrog, Direction.WEST)) + .placeNearTarget(); + scene.idle(70); + + scene.world() + .showSection(logistics, Direction.DOWN); + scene.idle(30); + + scene.overlay() + .showText(120) + .text("Clipboards with collected names can help auto-complete address inputs in other UIs") + .pointAt(util.vector() + .topOf(util.grid() + .at(2, 1, 2))) + .placeNearTarget(); + scene.idle(70); + } + + public static void boxTransfer(BlockPos to, BlockPos from, ChainConveyorBlockEntity be) { + for (Iterator iterator = be.getLoopingPackages() + .iterator(); iterator.hasNext();) { + ChainConveyorPackage chainConveyorPackage = iterator.next(); + chainConveyorPackage.chainPosition = 0; + be.addTravellingPackage(chainConveyorPackage, to.subtract(from)); + iterator.remove(); + } + } + +} diff --git a/src/main/java/com/simibubi/create/infrastructure/ponder/scenes/RedstoneScenes2.java b/src/main/java/com/simibubi/create/infrastructure/ponder/scenes/RedstoneScenes2.java index e500120a8c..1fd3bd01b3 100644 --- a/src/main/java/com/simibubi/create/infrastructure/ponder/scenes/RedstoneScenes2.java +++ b/src/main/java/com/simibubi/create/infrastructure/ponder/scenes/RedstoneScenes2.java @@ -2,8 +2,11 @@ package com.simibubi.create.infrastructure.ponder.scenes; import com.simibubi.create.AllItems; import com.simibubi.create.content.redstone.RoseQuartzLampBlock; +import com.simibubi.create.content.redstone.diodes.BrassDiodeBlock; +import com.simibubi.create.content.redstone.diodes.PulseTimerBlockEntity; import com.simibubi.create.content.redstone.nixieTube.NixieTubeBlockEntity; import com.simibubi.create.foundation.ponder.CreateSceneBuilder; +import com.simibubi.create.foundation.ponder.CreateSceneBuilder.WorldInstructions; import net.createmod.catnip.utility.Pointing; import net.createmod.ponder.api.element.ElementLink; @@ -11,8 +14,10 @@ import net.createmod.ponder.api.element.WorldSectionElement; import net.createmod.ponder.api.scene.SceneBuilder; import net.createmod.ponder.api.scene.SceneBuildingUtil; import net.createmod.ponder.api.scene.Selection; +import net.createmod.ponder.api.scene.SelectionUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.phys.Vec3; public class RedstoneScenes2 { @@ -21,120 +26,184 @@ public class RedstoneScenes2 { scene.title("rose_quartz_lamp", "Rose Quartz Lamps"); scene.configureBasePlate(0, 0, 7); - BlockPos centerLamp = util.grid().at(3, 1, 3); - Selection input = util.select().fromTo(3, 1, 1, 3, 1, 2); - Selection button = util.select().position(3, 1, 1); - Selection wire = util.select().position(3, 1, 2); - Selection output = util.select().fromTo(5, 1, 2, 5, 1, 1); - Selection comparator = util.select().fromTo(1, 1, 3, 0, 1, 3); + BlockPos centerLamp = util.grid() + .at(3, 1, 3); + Selection input = util.select() + .fromTo(3, 1, 1, 3, 1, 2); + Selection button = util.select() + .position(3, 1, 1); + Selection wire = util.select() + .position(3, 1, 2); + Selection output = util.select() + .fromTo(5, 1, 2, 5, 1, 1); + Selection comparator = util.select() + .fromTo(1, 1, 3, 0, 1, 3); scene.showBasePlate(); scene.idle(15); - ElementLink rowElement = - scene.world().showIndependentSection(util.select().position(centerLamp), Direction.DOWN); + ElementLink rowElement = scene.world() + .showIndependentSection(util.select() + .position(centerLamp), Direction.DOWN); scene.idle(5); - scene.world().showSection(input, Direction.SOUTH); + scene.world() + .showSection(input, Direction.SOUTH); scene.idle(15); - scene.world().toggleRedstonePower(input); - scene.effects().indicateRedstone(util.grid().at(3, 1, 1)); - scene.world().cycleBlockProperty(centerLamp, RoseQuartzLampBlock.POWERING); + scene.world() + .toggleRedstonePower(input); + scene.effects() + .indicateRedstone(util.grid() + .at(3, 1, 1)); + scene.world() + .cycleBlockProperty(centerLamp, RoseQuartzLampBlock.POWERING); scene.idle(15); - scene.overlay().showText(70) - .pointAt(util.vector().blockSurface(centerLamp, Direction.WEST)) + scene.overlay() + .showText(70) + .pointAt(util.vector() + .blockSurface(centerLamp, Direction.WEST)) .placeNearTarget() .attachKeyFrame() .text("Rose Quartz Lamps activate on a Redstone signal"); scene.idle(5); - scene.world().toggleRedstonePower(button); + scene.world() + .toggleRedstonePower(button); scene.idle(55); - scene.world().hideSection(input, Direction.EAST); + scene.world() + .hideSection(input, Direction.EAST); scene.idle(10); - ElementLink outputElement = scene.world().showIndependentSection(output, Direction.EAST); - scene.world().moveSection(outputElement, util.vector().of(-2, 0, 0), 0); + ElementLink outputElement = scene.world() + .showIndependentSection(output, Direction.EAST); + scene.world() + .moveSection(outputElement, util.vector() + .of(-2, 0, 0), 0); scene.idle(10); - scene.world().toggleRedstonePower(wire); - scene.world().toggleRedstonePower(output); + scene.world() + .toggleRedstonePower(wire); + scene.world() + .toggleRedstonePower(output); scene.idle(5); - scene.overlay().showText(70) - .pointAt(util.vector().blockSurface(centerLamp, Direction.WEST)) + scene.overlay() + .showText(70) + .pointAt(util.vector() + .blockSurface(centerLamp, Direction.WEST)) .placeNearTarget() .text("They will continue to emit redstone power afterwards"); scene.idle(60); - scene.world().hideIndependentSection(outputElement, Direction.NORTH); - scene.world().showSectionAndMerge(util.select().position(centerLamp.west()), Direction.EAST, rowElement); + scene.world() + .hideIndependentSection(outputElement, Direction.NORTH); + scene.world() + .showSectionAndMerge(util.select() + .position(centerLamp.west()), Direction.EAST, rowElement); scene.idle(3); - scene.world().showSectionAndMerge(util.select().position(centerLamp.east()), Direction.WEST, rowElement); + scene.world() + .showSectionAndMerge(util.select() + .position(centerLamp.east()), Direction.WEST, rowElement); scene.idle(25); - scene.overlay().showText(50) - .pointAt(util.vector().blockSurface(util.grid().at(2, 1, 3), Direction.WEST)) + scene.overlay() + .showText(50) + .pointAt(util.vector() + .blockSurface(util.grid() + .at(2, 1, 3), Direction.WEST)) .placeNearTarget() .attachKeyFrame() .text("When multiple lamps are arranged in a group..."); scene.idle(40); - ElementLink inputElement = scene.world().showIndependentSection(input, Direction.SOUTH); - scene.world().moveSection(inputElement, util.vector().of(1, 0, 0), 0); + ElementLink inputElement = scene.world() + .showIndependentSection(input, Direction.SOUTH); + scene.world() + .moveSection(inputElement, util.vector() + .of(1, 0, 0), 0); scene.idle(15); - scene.world().toggleRedstonePower(input); - scene.effects().indicateRedstone(util.grid().at(4, 1, 1)); - scene.world().cycleBlockProperty(centerLamp, RoseQuartzLampBlock.POWERING); - scene.world().cycleBlockProperty(centerLamp.east(), RoseQuartzLampBlock.POWERING); + scene.world() + .toggleRedstonePower(input); + scene.effects() + .indicateRedstone(util.grid() + .at(4, 1, 1)); + scene.world() + .cycleBlockProperty(centerLamp, RoseQuartzLampBlock.POWERING); + scene.world() + .cycleBlockProperty(centerLamp.east(), RoseQuartzLampBlock.POWERING); scene.idle(15); - scene.overlay().showText(80) - .pointAt(util.vector().blockSurface(util.grid().at(4, 1, 3), Direction.UP)) + scene.overlay() + .showText(80) + .pointAt(util.vector() + .blockSurface(util.grid() + .at(4, 1, 3), Direction.UP)) .placeNearTarget() .text("...activating a Lamp will focus the signal to it, deactivating all others"); scene.idle(5); - scene.world().toggleRedstonePower(button); + scene.world() + .toggleRedstonePower(button); scene.idle(60); - scene.world().hideIndependentSection(inputElement, Direction.NORTH); - scene.world().moveSection(rowElement, util.vector().of(1, 0, 0), 10); - scene.idle(15); - scene.world().showSectionAndMerge(comparator, Direction.EAST, rowElement); - scene.idle(15); - scene.world().toggleRedstonePower(comparator); scene.world() - .modifyBlockEntityNBT(comparator, NixieTubeBlockEntity.class, nbt -> nbt.putInt("RedstoneStrength", 13)); + .hideIndependentSection(inputElement, Direction.NORTH); + scene.world() + .moveSection(rowElement, util.vector() + .of(1, 0, 0), 10); + scene.idle(15); + scene.world() + .showSectionAndMerge(comparator, Direction.EAST, rowElement); + scene.idle(15); + scene.world() + .toggleRedstonePower(comparator); + scene.world() + .modifyBlockEntityNBT(comparator, NixieTubeBlockEntity.class, nbt -> nbt.putInt("RedstoneStrength", 13)); scene.idle(25); - scene.overlay().showText(80) - .pointAt(util.vector().blockSurface(util.grid().at(1, 1, 3), Direction.WEST)) + scene.overlay() + .showText(80) + .pointAt(util.vector() + .blockSurface(util.grid() + .at(1, 1, 3), Direction.WEST)) .placeNearTarget() .attachKeyFrame() .text("Comparators output based on the distance to a powered lamp"); scene.idle(90); - scene.overlay().showControls(util.vector().topOf(centerLamp.east(2)), Pointing.DOWN, 20).rightClick() - .withItem(AllItems.WRENCH.asStack()); - scene.idle(6); - scene.world().cycleBlockProperty(centerLamp.east(), RoseQuartzLampBlock.POWERING); - scene.world().toggleRedstonePower(comparator); - scene.world() - .modifyBlockEntityNBT(comparator, NixieTubeBlockEntity.class, nbt -> nbt.putInt("RedstoneStrength", 0)); - scene.idle(20); - - scene.overlay().showControls(util.vector().topOf(centerLamp), Pointing.DOWN, 20).rightClick() + scene.overlay() + .showControls(util.vector() + .topOf(centerLamp.east(2)), Pointing.DOWN, 20) + .rightClick() .withItem(AllItems.WRENCH.asStack()); scene.idle(6); - scene.world().cycleBlockProperty(centerLamp.west(), RoseQuartzLampBlock.POWERING); - scene.world().toggleRedstonePower(comparator); scene.world() - .modifyBlockEntityNBT(comparator, NixieTubeBlockEntity.class, nbt -> nbt.putInt("RedstoneStrength", 15)); + .cycleBlockProperty(centerLamp.east(), RoseQuartzLampBlock.POWERING); + scene.world() + .toggleRedstonePower(comparator); + scene.world() + .modifyBlockEntityNBT(comparator, NixieTubeBlockEntity.class, nbt -> nbt.putInt("RedstoneStrength", 0)); scene.idle(20); - scene.overlay().showText(80) - .pointAt(util.vector().blockSurface(util.grid().at(3, 1, 3), Direction.UP)) + scene.overlay() + .showControls(util.vector() + .topOf(centerLamp), Pointing.DOWN, 20) + .rightClick() + .withItem(AllItems.WRENCH.asStack()); + scene.idle(6); + scene.world() + .cycleBlockProperty(centerLamp.west(), RoseQuartzLampBlock.POWERING); + scene.world() + .toggleRedstonePower(comparator); + scene.world() + .modifyBlockEntityNBT(comparator, NixieTubeBlockEntity.class, nbt -> nbt.putInt("RedstoneStrength", 15)); + scene.idle(20); + + scene.overlay() + .showText(80) + .pointAt(util.vector() + .blockSurface(util.grid() + .at(3, 1, 3), Direction.UP)) .placeNearTarget() .attachKeyFrame() .text("The Lamps can also be toggled manually using a Wrench"); @@ -142,4 +211,166 @@ public class RedstoneScenes2 { } + public static void pulseTimer(SceneBuilder builder, SceneBuildingUtil util) { + CreateSceneBuilder scene = new CreateSceneBuilder(builder); + scene.title("pulse_timer", "Redstone output of the Pulse Timer"); + scene.configureBasePlate(0, 0, 5); + WorldInstructions world = scene.world(); + SelectionUtil select = util.select(); + world.showSection(select.layer(0), Direction.UP); + + BlockPos circuitPos = util.grid() + .at(2, 1, 2); + BlockPos leverPos = util.grid() + .at(4, 1, 2); + Vec3 circuitTop = util.vector() + .blockSurface(circuitPos, Direction.DOWN) + .add(0, 3 / 16f, 0); + + world.modifyBlockEntityNBT(select.position(circuitPos), PulseTimerBlockEntity.class, + nbt -> nbt.putInt("ScrollValue", 30)); + world.showSection(select.fromTo(1, 1, 2, 0, 1, 2), Direction.UP); + scene.idle(10); + world.showSection(select.position(circuitPos), Direction.DOWN); + scene.idle(8); + + for (int i = 0; i < 1; i++) { + scene.idle(12); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.fromTo(1, 1, 2, 0, 1, 2)); + scene.idle(2); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.position(1, 1, 2)); + scene.idle(1); + world.toggleRedstonePower(select.position(0, 1, 2)); + scene.idle(15); + } + + scene.overlay() + .showText(60) + .text("Pulse Timers repeatedly emit short pulses") + .attachKeyFrame() + .placeNearTarget() + .pointAt(circuitTop); + scene.idle(13); + + for (int i = 0; i < 3; i++) { + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.fromTo(1, 1, 2, 0, 1, 2)); + scene.idle(2); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.position(1, 1, 2)); + scene.idle(1); + world.toggleRedstonePower(select.position(0, 1, 2)); + scene.idle(27); + } + + scene.overlay() + .showRepeaterScrollInput(circuitPos, 60); + scene.overlay() + .showControls(circuitTop, Pointing.DOWN, 60) + .rightClick(); + scene.idle(10); + scene.overlay() + .showText(60) + .text("Using the value panel, the time interval can be configured") + .attachKeyFrame() + .placeNearTarget() + .pointAt(circuitTop); + + world.modifyBlockEntityNBT(select.position(circuitPos), PulseTimerBlockEntity.class, + nbt -> nbt.putInt("ScrollValue", 100)); + scene.idle(70); + + world.showSection(select.fromTo(3, 1, 2, 4, 1, 2), Direction.WEST); + + scene.idle(20); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.fromTo(1, 1, 2, 0, 1, 2)); + scene.idle(2); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.position(1, 1, 2)); + scene.idle(1); + world.toggleRedstonePower(select.position(0, 1, 2)); + scene.idle(10); + + scene.effects() + .indicateRedstone(leverPos); + scene.world() + .toggleRedstonePower(util.select() + .fromTo(4, 1, 2, 2, 1, 2)); + scene.idle(30); + + scene.overlay() + .showText(60) + .text("Powering the input side will pause and reset them") + .attachKeyFrame() + .placeNearTarget() + .pointAt(util.vector() + .topOf(4, 0, 2)); + scene.idle(70); + + world.hideSection(select.fromTo(3, 1, 2, 4, 1, 2), Direction.EAST); + scene.idle(5); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERED); + world.hideSection(select.position(0, 1, 2), Direction.WEST); + scene.idle(10); + + scene.overlay() + .showControls(circuitTop.add(-.375, 0, .375), Pointing.DOWN, 60) + .rightClick(); + scene.idle(10); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.INVERTED); + world.toggleRedstonePower(select.position(1, 1, 2)); + scene.overlay() + .showText(60) + .text("Right-click the circuit base to invert the output") + .attachKeyFrame() + .placeNearTarget() + .pointAt(circuitTop.add(-.375, 0, .375)); + + scene.idle(70); + ElementLink link = world.showIndependentSection(select.position(0, 1, 4), Direction.EAST); + world.moveSection(link, util.vector() + .of(0, 0, -2), 0); + scene.idle(10); + + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.fromTo(1, 1, 2, 0, 1, 2)); + scene.idle(3); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.position(1, 1, 2)); + scene.idle(1); + world.toggleRedstonePower(select.position(0, 1, 2)); + scene.idle(10); + + scene.overlay() + .showText(80) + .text("This helps trigger mechanisms that activate only without a redstone signal") + .placeNearTarget() + .pointAt(util.vector() + .centerOf(0, 1, 2)); + + scene.idle(86); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.fromTo(1, 1, 2, 0, 1, 2)); + scene.idle(3); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.position(1, 1, 2)); + scene.idle(1); + world.toggleRedstonePower(select.position(0, 1, 2)); + scene.idle(10); + + scene.markAsFinished(); + + scene.idle(86); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.fromTo(1, 1, 2, 0, 1, 2)); + scene.idle(3); + world.cycleBlockProperty(circuitPos, BrassDiodeBlock.POWERING); + world.toggleRedstonePower(select.position(1, 1, 2)); + scene.idle(1); + world.toggleRedstonePower(select.position(0, 1, 2)); + } + } diff --git a/src/main/resources/assets/create/ponder/chain_conveyor.nbt b/src/main/resources/assets/create/ponder/chain_conveyor.nbt new file mode 100644 index 0000000000..75c7bec3f5 Binary files /dev/null and b/src/main/resources/assets/create/ponder/chain_conveyor.nbt differ diff --git a/src/main/resources/assets/create/ponder/package_frogport.nbt b/src/main/resources/assets/create/ponder/package_frogport.nbt new file mode 100644 index 0000000000..ef2c54a3a3 Binary files /dev/null and b/src/main/resources/assets/create/ponder/package_frogport.nbt differ diff --git a/src/main/resources/assets/create/ponder/pulse_timer.nbt b/src/main/resources/assets/create/ponder/pulse_timer.nbt new file mode 100644 index 0000000000..3532642240 Binary files /dev/null and b/src/main/resources/assets/create/ponder/pulse_timer.nbt differ