diff --git a/CHANGELOG.md b/CHANGELOG.md index 188c4b5..bb0dfa4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ Written for Consumers: what a mod building against Pipeworks can use, or will se ## Unreleased +- The Gallery pack, **Pipeworks Gallery**: a datapack in the jar, off by default, whose `/function pipeworks:gallery/build` builds a working scene of each README feature, in rows of three north-east of the player, each with a sign naming it that rebuilds the scene when clicked; `pipeworks:gallery/` builds one. It needs libworks-runtime 0.1.9, which the jar nests (libworks ADR 0010, #26). + ## 0.3.1 ### Fixed diff --git a/GLOSSARY.md b/GLOSSARY.md index 71ae191..bac009d 100644 --- a/GLOSSARY.md +++ b/GLOSSARY.md @@ -73,6 +73,12 @@ _Avoid_: dismantle group The creative tab the suite's mods share, `libworks:works`, registered by the libworks runtime the Module nests. Every Pipeworks item shows there; Pipeworks registers no tab of its own (FactoryWorks ADR-0128). _Avoid_: Pipeworks tab, creative tab (alone) +**Gallery pack**: +The datapack in this Module's jar, "Pipeworks Gallery", off until a player turns it on, whose +functions build a working scene of each README feature, each with a sign naming it. Functions only +(libworks ADR 0010). +_Avoid_: demo pack, showcase pack, gallery datapack + ### Parties **Pipeworks**: diff --git a/README.md b/README.md index e76861b..2947add 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ To play Pipeworks beside every other FactoryWorks mod, install the [FactoryWorks ## Features -- **Pipes and a storage tank.** A pipe holds 100 mB and a tank 25,000 mB by default, Factorio's volumes; `pipeworks-server.toml` sets both, for the pipes and tanks placed afterwards. Pipes draw an arm toward each pipe, tank or port they link to, and toward any block beside them that holds fluid. +- **Pipes and a storage tank.** A pipe holds 100 mB and a tank 25,000 mB by default, Factorio's volumes; `pipeworks-server.toml` sets both, for the pipes and tanks placed afterwards. Pipes draw an arm toward each pipe, tank or port they link to, and toward any block beside them that holds fluid. Tanks that touch share their contents as one tank, their capacities added, as pipes do. - **Buckets.** A bucket, or any mod's item that holds fluid, used on a pipe or tank fills or empties its segment. A full one empties only if the segment has room for all of it and holds that fluid or none; an empty or part-full one fills from the segment. In creative the item is left as it is. - **One fluid per segment.** A pipe or tank that would join two segments holding different fluids is refused, with a message saying so. - **Breaking splits fairly.** Breaking a pipe, tank or port splits its segment by capacity, and the broken block takes its share of the fluid. Segments are saved with the world and keep their fluid while their chunks are unloaded. @@ -26,6 +26,10 @@ To play Pipeworks beside every other FactoryWorks mod, install the [FactoryWorks Not yet: the in-line pump and pipe-to-ground. +## Gallery + +Pipeworks carries a gallery of its features: one working scene each, with a sign naming it, built in rows of three north and east of where you stand. It is a datapack in the jar, **Pipeworks Gallery**, off until you turn it on in a world's Data Packs screen or with `/datapack enable "mod/pipeworks:gallery_pack"`. Then `/function pipeworks:gallery/build` builds every scene, and `/function pipeworks:gallery/` one of them, such as `outlet`; clicking a scene's sign builds it again. A function needs cheats in single player (on at world creation, or Open to LAN with cheats) or operator rights on a server. Pipeworks registers no fluid, so the scenes use water and lava. The gallery clears the ground it builds on and changes nothing else; it adds no recipes, loot or worldgen. + ## For pack authors - **Closing a pipe's sides.** A pipe's side can be closed, to keep two runs apart or cut a pipe off from a block holding fluid. A right-click on a pipe with an item in the item tag `pipeworks:closes_sides` toggles the side clicked. The tag ships a stick; a pack replaces the tag to name other items, or adds to it and keeps the stick. Opening a side that would mix two fluids is refused. diff --git a/REUSE.toml b/REUSE.toml index 61381d5..ae94b37 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -15,6 +15,7 @@ path = [ "**/*.md", "src/main/resources/**/*.json", "src/main/resources/**/*.nbt", + "src/main/resources/gallery_pack/**", "LICENSE", "publish/summary.txt", ".github/**", diff --git a/build.gradle b/build.gradle index 3ef0691..c73f0be 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,7 @@ libworks { } // Nested by jar-in-jar, so players install nothing more; FML loads the newest nested copy in range, // which runs to the next minor. - runtime = '0.1.8' + runtime = '0.1.9' } repositories { diff --git a/src/main/java/io/github/_5thlayer/pipeworks/Pipeworks.java b/src/main/java/io/github/_5thlayer/pipeworks/Pipeworks.java index e062707..7ef380f 100644 --- a/src/main/java/io/github/_5thlayer/pipeworks/Pipeworks.java +++ b/src/main/java/io/github/_5thlayer/pipeworks/Pipeworks.java @@ -3,6 +3,7 @@ package io.github._5thlayer.pipeworks; +import io.github._5thlayer.libworks.gallery.GalleryPack; import io.github._5thlayer.pipeworks.dismantle.PipeFamily; import io.github._5thlayer.pipeworks.gametest.PipeworksGameTests; import io.github._5thlayer.pipeworks.network.CreativePipeFluidPayload; @@ -36,6 +37,7 @@ public Pipeworks(IEventBus modBus, ModContainer container) { PipeFamily.register(); PipeLegs.register(); PipeworksGameTests.register(modBus); + GalleryPack.register(modBus, MOD_ID); modBus.addListener(Pipeworks::registerPayloads); modBus.addListener(TankFluidPayload::register); NeoForge.EVENT_BUS.addListener(FluidSegments::onLevelTick); diff --git a/src/main/java/io/github/_5thlayer/pipeworks/gametest/GalleryTests.java b/src/main/java/io/github/_5thlayer/pipeworks/gametest/GalleryTests.java new file mode 100644 index 0000000..084fb86 --- /dev/null +++ b/src/main/java/io/github/_5thlayer/pipeworks/gametest/GalleryTests.java @@ -0,0 +1,249 @@ +// SPDX-FileCopyrightText: 2026 5thlayer +// SPDX-License-Identifier: MIT + +package io.github._5thlayer.pipeworks.gametest; + +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; + +import io.github._5thlayer.libworks.gallery.GalleryPack; +import io.github._5thlayer.libworks.testkit.GalleryCheck; +import io.github._5thlayer.libworks.testkit.GalleryCheck.Scene; +import io.github._5thlayer.pipeworks.Pipeworks; +import io.github._5thlayer.pipeworks.PipeworksRegistries; +import io.github._5thlayer.pipeworks.api.FluidPorts; +import net.minecraft.commands.CommandSourceStack; +import net.minecraft.core.BlockPos; +import net.minecraft.gametest.framework.GameTestHelper; +import net.minecraft.gametest.framework.GameTestInfo; +import net.minecraft.gametest.framework.GameTestListener; +import net.minecraft.gametest.framework.GameTestRunner; +import net.minecraft.gametest.framework.StructureUtils; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.SignBlockEntity; +import net.minecraft.world.level.levelgen.structure.BoundingBox; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +/** + * The Gallery pack (libworks ADR 0010), through libworks' {@link GalleryCheck}: offered and off, its + * build running every command without failing, and each scene's key block where it stands. The scenes + * stand in rows of three, each row 16 blocks north of the last, which keeps them near enough to where + * the gallery is run for their pumps to tick; {@link GalleryCheck#builds} lays one long row, so the + * test runs its parts a row at a time. Beyond the build, the Pump scene fills its tank and leaves + * its water while taking its lava, the Outlet scene sets its water down, and a scene's sign, clicked, + * builds it again. Each test turns the pack on, which reloads the server's data, and off again, + * passing or failing, and each builds where the others do, so each runs in a batch of its own. + */ +final class GalleryTests { + + private static final String PACK = GalleryPack.id(Pipeworks.MOD_ID); + + /** How many scenes a row holds, and how far north each row is of the last, as {@code gallery/build} lays them. */ + private static final int ROW = 3; + private static final int ROW_DEPTH = 16; + + /** Where the gallery is run from: the platform's south-west corner, its plots spilling north and east of it. */ + private static final BlockPos ORIGIN = new BlockPos(0, 1, 8); + /** Where a scene's sign stands, from where the scene is run. */ + private static final BlockPos SIGN = new BlockPos(14, 0, 0); + /** Where the Pump scene's water source stands, from where the scene is run. */ + private static final BlockPos PUMP_WATER = new BlockPos(6, 0, -6); + /** Where the Pump scene's water Pump fills its storage tank, from where the scene is run. */ + private static final BlockPos PUMP_WATER_TANK = new BlockPos(11, 0, -6); + /** Where the Pump scene's lava source stands, from where the scene is run. */ + private static final BlockPos PUMP_LAVA = new BlockPos(22, 0, -6); + /** Where the Outlet scene's outlet stands, from where the scene is run. */ + private static final BlockPos OUTLET = new BlockPos(15, 0, -6); + /** Where the Outlet scene's outlet sets its water down, from where the scene is run. */ + private static final BlockPos OUTLET_FRONT = new BlockPos(16, 0, -6); + + /** How many plots a test of one scene loads: a scene run alone, from where the gallery is run, fills only the first. */ + private static final int ONE_SCENE = 1; + + private GalleryTests() { + } + + /** The scenes in the order {@code build} lays them, each with a key block and where, from where the scene is run, its function places it. */ + private static List scenes() { + var tank = PipeworksRegistries.STORAGE_TANK.get(); + var pump = PipeworksRegistries.PUMP.get(); + return List.of( + new Scene("pipes", new BlockPos(18, 0, -6), tank), + new Scene("buckets", new BlockPos(18, 0, -6), tank), + new Scene("one_fluid", new BlockPos(20, 0, -6), pump), + new Scene("breaking", new BlockPos(20, 0, -6), tank), + new Scene("tank_level", new BlockPos(14, 2, -6), tank), + new Scene("creative_pipe", new BlockPos(8, 0, -6), PipeworksRegistries.CREATIVE_PIPE.get()), + new Scene("pump", new BlockPos(21, 0, -6), pump), + new Scene("outlet", OUTLET, PipeworksRegistries.OUTLET.get()), + new Scene("dismantle", new BlockPos(6, 0, -6), PipeworksRegistries.PIPE.get())); + } + + static void register(PipeworksGameTests.Registrar tests) { + tests.testOnItsOwn("gallery_builds_a_scene_of_each_feature", GalleryCheck.TICKS, GalleryTests::builds); + tests.testOnItsOwn("gallery_pump_scene_leaves_its_water_and_takes_its_lava", GalleryCheck.TICKS, GalleryTests::pumpTakesLava); + tests.testOnItsOwn("gallery_outlet_scene_sets_its_water_down", GalleryCheck.TICKS, GalleryTests::outletSetsWater); + tests.testOnItsOwn("gallery_scenes_sign_clicked_builds_the_scene_again", GalleryCheck.TICKS, GalleryTests::signReplays); + } + + private static void builds(GameTestHelper helper) { + var scenes = scenes(); + var functions = helper.getLevel().getServer().getFunctions(); + withPack(helper, scenes.size(), source -> { + GalleryCheck.assertLoads(helper, source, Pipeworks.MOD_ID); + for (var scene : scenes) { + helper.assertTrue(functions.get(GalleryPack.scene(Pipeworks.MOD_ID, scene.name())).isPresent(), + "there is no scene function " + scene.name()); + } + GalleryCheck.assertRuns(helper, source, GalleryPack.build(Pipeworks.MOD_ID)); + for (int row = 0; row * ROW < scenes.size(); row++) { + GalleryCheck.assertKeyBlocks(helper, rowOrigin(helper, row), + scenes.subList(row * ROW, Math.min(scenes.size(), (row + 1) * ROW))); + } + }); + } + + /** The Pump scene: one Pump fills its tank and leaves its water source in place, the other takes its lava source whole. */ + private static void pumpTakesLava(GameTestHelper helper) { + var level = helper.getLevel(); + var water = at(helper, PUMP_WATER); + var tank = at(helper, PUMP_WATER_TANK); + var lava = at(helper, PUMP_LAVA); + withPack(helper, ONE_SCENE, source -> GalleryCheck.assertRuns(helper, source, GalleryPack.scene(Pipeworks.MOD_ID, "pump")), + () -> { + helper.assertTrue(level.getFluidState(lava).isEmpty(), "the Pump has not taken its lava source yet"); + var drawn = FluidPorts.segment(level, tank); + helper.assertTrue(drawn != null && drawn.getAmountAsLong(0) > 0, "the water Pump has drawn nothing into its tank yet"); + helper.assertTrue(level.getFluidState(water).isSourceOfType(Fluids.WATER), "the water Pump took its source"); + }); + } + + /** The Outlet scene: its Pump fills the run, and the Outlet sets a water source down in its basin. */ + private static void outletSetsWater(GameTestHelper helper) { + var front = at(helper, OUTLET_FRONT); + withPack(helper, ONE_SCENE, source -> GalleryCheck.assertRuns(helper, source, GalleryPack.scene(Pipeworks.MOD_ID, "outlet")), + () -> { + var fluid = helper.getLevel().getFluidState(front); + helper.assertTrue(fluid.isSource() && fluid.is(Fluids.WATER), "the Outlet has set no water down in its basin yet"); + }); + } + + /** A scene's sign, clicked, builds the scene again where it stands, leaving nothing loose: here the Outlet, taken away. */ + private static void signReplays(GameTestHelper helper) { + var outlet = at(helper, OUTLET); + var sign = at(helper, SIGN); + withPack(helper, ONE_SCENE, source -> { + GalleryCheck.assertRuns(helper, source, GalleryPack.scene(Pipeworks.MOD_ID, "outlet")); + helper.getLevel().removeBlock(outlet, false); + helper.assertTrue(helper.getLevel().getBlockEntity(sign) instanceof SignBlockEntity entity + && entity.executeClickCommandsIfPresent(helper.getLevel(), source.getPlayer(), sign, true), + "the scene's sign runs no command when clicked"); + helper.assertTrue(helper.getLevel().getBlockState(outlet).is(PipeworksRegistries.OUTLET.get()), + "clicking the sign did not build the Outlet again where it stood"); + var plot = GalleryCheck.plot(helper.absolutePos(ORIGIN)); + var loose = helper.getLevel().getEntitiesOfClass(ItemEntity.class, AABB.of(plot)); + helper.assertTrue(loose.isEmpty(), "building the scene again left items loose: " + loose.stream().map(ItemEntity::getItem).toList()); + helper.assertTrue(helper.getLevel().getBlockState(sign).is(Blocks.OAK_SIGN), "building the scene again left no sign"); + }); + } + + /** As {@link #withPack(GameTestHelper, int, Consumer, Runnable)}, waiting for nothing after {@code body}. */ + private static void withPack(GameTestHelper helper, int plots, Consumer body) { + withPack(helper, plots, body, () -> { + }); + } + + /** + * Turns the pack on over the first {@code plots} plots, as {@code build} lays them, loaded; runs + * {@code body} as an operator player standing where the gallery is run; waits until {@code until} + * passes; then clears the plots and turns the pack off, passing or failing. A single scene run + * from where the gallery is run stands on the first plot. + */ + private static void withPack(GameTestHelper helper, int plots, Consumer body, Runnable until) { + var server = helper.getLevel().getServer(); + GalleryCheck.assertOfferedAndOff(helper, PACK); + var run = new Run(helper, plots, GalleryCheck.operator(helper, helper.absolutePos(ORIGIN))); + var area = run.area(); + area.intersectingChunks().forEach(chunk -> helper.getLevel().setChunkForced(chunk.x(), chunk.z(), true)); + var on = GalleryCheck.select(server, PACK, true); + helper.testInfo.addListener(new ClearAwayIfFailed(run, on)); + var off = new AtomicReference>(); + helper.startSequence() + .thenWaitUntil(() -> helper.assertTrue(on.isDone(), PACK + " is not on yet")) + .thenWaitUntil(() -> helper.assertTrue(area.intersectingChunks().allMatch(chunk -> helper.getLevel().hasChunk(chunk.x(), chunk.z())), + "the gallery's plots are not loaded yet")) + .thenExecute(() -> body.accept(run.source())) + .thenWaitUntil(until) + .thenExecute(() -> off.set(run.clearAway())) + .thenWaitUntil(() -> helper.assertTrue(off.get() != null && off.get().isDone(), PACK + " is not off yet")) + .thenSucceed(); + } + + /** {@code offset}, from where the gallery is run, absolute. */ + private static BlockPos at(GameTestHelper helper, BlockPos offset) { + return helper.absolutePos(ORIGIN).offset(offset); + } + + /** Where row {@code row} of the gallery is run from, absolute. */ + private static BlockPos rowOrigin(GameTestHelper helper, int row) { + return helper.absolutePos(ORIGIN).north(ROW_DEPTH * row); + } + + /** Where plot {@code plot} of the gallery, numbered from 0 as {@code build} lays them, is run from, absolute. */ + private static BlockPos plotOrigin(GameTestHelper helper, int plot) { + return rowOrigin(helper, plot / ROW).east(GalleryCheck.PLOT_WIDTH * (plot % ROW)); + } + + /** A test's run of the gallery: its first {@code plots} plots, built by {@code source}, standing where the gallery is run. */ + private record Run(GameTestHelper helper, int plots, CommandSourceStack source) { + + /** The plots, their floors included. */ + BoundingBox area() { + int rows = Math.ceilDiv(plots, ROW); + return BoundingBox.encapsulatingBoxes(List.of( + GalleryCheck.plot(helper.absolutePos(ORIGIN)), + GalleryCheck.strip(rowOrigin(helper, rows - 1), Math.min(plots, ROW)))).orElseThrow(); + } + + /** Air where the plots stood, their entities gone, and the pack off. The runner releases the forced chunks once the batch ends. */ + CompletableFuture clearAway() { + // The pack's own clear first, which removes each pipe, tank and port as a player's world would: + // the runner's clear sets blocks with no removal, which leaves their nodes in the saved segments + // for the next test's scene to join. + var commands = helper.getLevel().getServer().getCommands(); + for (int plot = 0; plot < plots; plot++) { + var from = Vec3.atBottomCenterOf(plotOrigin(helper, plot)); + commands.performPrefixedCommand(source.withPosition(from), "function " + Pipeworks.MOD_ID + ":gallery/clear"); + } + StructureUtils.clearSpaceForStructure(area(), helper.getLevel()); + return GalleryCheck.select(helper.getLevel().getServer(), PACK, false); + } + } + + /** A failed test skips its own last steps, which would leave the pack on; it clears away once turning it on is done. */ + private record ClearAwayIfFailed(Run run, CompletableFuture on) implements GameTestListener { + + @Override + public void testStructureLoaded(GameTestInfo testInfo) { + } + + @Override + public void testPassed(GameTestInfo testInfo, GameTestRunner runner) { + } + + @Override + public void testFailed(GameTestInfo testInfo, GameTestRunner runner) { + var server = run.helper().getLevel().getServer(); + on.whenComplete((done, failed) -> server.execute(run::clearAway)); + } + + @Override + public void testAddedForRerun(GameTestInfo original, GameTestInfo copy, GameTestRunner runner) { + } + } +} diff --git a/src/main/java/io/github/_5thlayer/pipeworks/gametest/PipeworksGameTests.java b/src/main/java/io/github/_5thlayer/pipeworks/gametest/PipeworksGameTests.java index d950a74..52e6adc 100644 --- a/src/main/java/io/github/_5thlayer/pipeworks/gametest/PipeworksGameTests.java +++ b/src/main/java/io/github/_5thlayer/pipeworks/gametest/PipeworksGameTests.java @@ -63,6 +63,7 @@ private static void registerTests(RegisterGameTestsEvent event) { // Registered rather than borrowed, since the event hands out no lookup for vanilla's. var environment = event.registerEnvironment(id("default"), new TestEnvironmentDefinition.AllOf(List.of())); var tests = new Registrar(event, environment); + GalleryTests.register(tests); LoadTests.register(tests); PlaysAloneTests.register(tests); SegmentTests.register(tests); @@ -89,9 +90,21 @@ private static Identifier id(String path) { record Registrar(RegisterGameTestsEvent event, Holder> environment) { void test(String name, int maxTicks, Consumer body) { + register(id(name), environment, maxTicks, body); + } + + /** + * As {@link #test}, in a batch of its own: one that turns a pack on, which reloads the server's + * data under every test in its batch, or builds where another test would (libworks ADR 0010). + */ + void testOnItsOwn(String name, int maxTicks, Consumer body) { var id = id(name); + register(id, event.registerEnvironment(id, new TestEnvironmentDefinition.AllOf(List.of())), maxTicks, body); + } + + private void register(Identifier id, Holder> in, int maxTicks, Consumer body) { CodeGameTest.define(id, body); - event.registerTest(id, new CodeGameTest(id, new TestData<>(environment, PLATFORM, maxTicks, 0, true, Rotation.NONE))); + event.registerTest(id, new CodeGameTest(id, new TestData<>(in, PLATFORM, maxTicks, 0, true, Rotation.NONE))); } } } diff --git a/src/main/resources/assets/pipeworks/lang/en_us.json b/src/main/resources/assets/pipeworks/lang/en_us.json index 104376a..a128abf 100644 --- a/src/main/resources/assets/pipeworks/lang/en_us.json +++ b/src/main/resources/assets/pipeworks/lang/en_us.json @@ -17,6 +17,44 @@ "message.pipeworks.mixed_fluids": "Those pipes hold different fluids", "message.pipeworks.pump.no_source": "A Pump must touch a still source block.", "message.pipeworks.stretch.pipe_blocked": "Something a pipe can't replace stands in the way.", + "pipeworks.gallery.breaking": "Breaking", + "pipeworks.gallery.breaking.1": "break a pipe", + "pipeworks.gallery.breaking.2": "between tanks:", + "pipeworks.gallery.breaking.3": "split by size", + "pipeworks.gallery.buckets": "Buckets", + "pipeworks.gallery.buckets.1": "use a bucket on", + "pipeworks.gallery.buckets.2": "a pipe or tank", + "pipeworks.gallery.buckets.3": "to fill or empty", + "pipeworks.gallery.creative_pipe": "Creative", + "pipeworks.gallery.creative_pipe.1": "click the pipe,", + "pipeworks.gallery.creative_pipe.2": "then its slot", + "pipeworks.gallery.creative_pipe.3": "with a bucket", + "pipeworks.gallery.dismantle": "Dismantle", + "pipeworks.gallery.dismantle.1": "sneak-click two", + "pipeworks.gallery.dismantle.2": "with a pickaxe;", + "pipeworks.gallery.dismantle.3": "drag to lay", + "pipeworks.gallery.one_fluid": "One Fluid", + "pipeworks.gallery.one_fluid.1": "one fluid each:", + "pipeworks.gallery.one_fluid.2": "a pipe in the", + "pipeworks.gallery.one_fluid.3": "gap is refused", + "pipeworks.gallery.outlet": "Outlet", + "pipeworks.gallery.outlet.1": "sets fluid down", + "pipeworks.gallery.outlet.2": "a bucket at", + "pipeworks.gallery.outlet.3": "a time", + "pipeworks.gallery.pack": "Pipeworks Gallery", + "pipeworks.gallery.pack.description": "Working scenes: /function pipeworks:gallery/build", + "pipeworks.gallery.pipes": "Pipes", + "pipeworks.gallery.pipes.1": "a pump fills", + "pipeworks.gallery.pipes.2": "pipes and a", + "pipeworks.gallery.pipes.3": "storage tank", + "pipeworks.gallery.pump": "Pump", + "pipeworks.gallery.pump.1": "water stays,", + "pipeworks.gallery.pump.2": "lava is taken", + "pipeworks.gallery.pump.3": "whole", + "pipeworks.gallery.tank_level": "Tank Level", + "pipeworks.gallery.tank_level.1": "tanks that touch", + "pipeworks.gallery.tank_level.2": "share one level;", + "pipeworks.gallery.tank_level.3": "Jade: amounts", "screen.pipeworks.creative_pipe.fluid": "Click with a bucket to set the fluid, or with an empty cursor to clear it", "tooltip.pipeworks.jade.closed": "Closed: %s", "tooltip.pipeworks.jade.side.down": "down", diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/breaking.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/breaking.mcfunction new file mode 100644 index 0000000..13b0ff3 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/breaking.mcfunction @@ -0,0 +1,23 @@ +# A Pump on a water source filling a long run, a storage tank near it and one at the far end, and a +# pickaxe in a chest. +# A pipe broken between the tanks splits the run: the far tank keeps its share and stops rising while the near one fills. +function pipeworks:gallery/clear +execute positioned ~5 ~ ~-6 run function pipeworks:gallery/pool {fluid:"water"} +setblock ~6 ~ ~-6 pipeworks:pump[facing=east] +setblock ~7 ~ ~-6 pipeworks:pipe +setblock ~8 ~ ~-6 pipeworks:pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:pipe +setblock ~12 ~ ~-6 pipeworks:pipe +setblock ~13 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-6 pipeworks:pipe +setblock ~15 ~ ~-6 pipeworks:pipe +setblock ~16 ~ ~-6 pipeworks:pipe +setblock ~17 ~ ~-6 pipeworks:pipe +setblock ~18 ~ ~-6 pipeworks:pipe +setblock ~19 ~ ~-6 pipeworks:pipe +setblock ~9 ~1 ~-6 pipeworks:storage_tank +setblock ~20 ~ ~-6 pipeworks:storage_tank +setblock ~14 ~ ~-3 minecraft:chest[facing=south]{Items:[{Slot:0b,id:"minecraft:iron_pickaxe",count:1}]} +function pipeworks:gallery/sign {scene:"breaking"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/buckets.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/buckets.mcfunction new file mode 100644 index 0000000..11c50a9 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/buckets.mcfunction @@ -0,0 +1,17 @@ +# An empty run of pipes and a storage tank, whose glass rises a step for about each two buckets, and a +# chest of buckets to fill and empty it with. Its one lava bucket goes in only while the run is empty, +# as the segment holds one fluid. +function pipeworks:gallery/clear +setblock ~8 ~ ~-6 pipeworks:pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:pipe +setblock ~12 ~ ~-6 pipeworks:pipe +setblock ~13 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-6 pipeworks:pipe +setblock ~15 ~ ~-6 pipeworks:pipe +setblock ~16 ~ ~-6 pipeworks:pipe +setblock ~17 ~ ~-6 pipeworks:pipe +setblock ~18 ~ ~-6 pipeworks:storage_tank +setblock ~13 ~ ~-3 minecraft:chest[facing=south]{Items:[{Slot:0b,id:"minecraft:water_bucket",count:1},{Slot:1b,id:"minecraft:water_bucket",count:1},{Slot:2b,id:"minecraft:water_bucket",count:1},{Slot:6b,id:"minecraft:water_bucket",count:1},{Slot:7b,id:"minecraft:water_bucket",count:1},{Slot:8b,id:"minecraft:water_bucket",count:1},{Slot:3b,id:"minecraft:lava_bucket",count:1},{Slot:4b,id:"minecraft:bucket",count:1},{Slot:5b,id:"minecraft:bucket",count:1}]} +function pipeworks:gallery/sign {scene:"buckets"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/build.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/build.mcfunction new file mode 100644 index 0000000..f4494e9 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/build.mcfunction @@ -0,0 +1,13 @@ +# The Pipeworks Gallery: a scene of each README feature, in README order, each building and clearing a +# plot of its own, 24 blocks wide and 13 deep (libworks ADR 0010). The scenes stand in rows of three, +# read west to east and then north, each row 16 blocks north of the one before, so the whole gallery +# stays near enough to where it is run for its pumps to keep working. +function pipeworks:gallery/pipes +execute positioned ~24 ~ ~ run function pipeworks:gallery/buckets +execute positioned ~48 ~ ~ run function pipeworks:gallery/one_fluid +execute positioned ~ ~ ~-16 run function pipeworks:gallery/breaking +execute positioned ~24 ~ ~-16 run function pipeworks:gallery/tank_level +execute positioned ~48 ~ ~-16 run function pipeworks:gallery/creative_pipe +execute positioned ~ ~ ~-32 run function pipeworks:gallery/pump +execute positioned ~24 ~ ~-32 run function pipeworks:gallery/outlet +execute positioned ~48 ~ ~-32 run function pipeworks:gallery/dismantle diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/clear.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/clear.mcfunction new file mode 100644 index 0000000..8cd8e2a --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/clear.mcfunction @@ -0,0 +1,8 @@ +# Clears a scene's plot, from ~2 to ~25 east and from ~-12 to ~0 south, 16 high, onto a stone floor: +# its blocks, then the items dropped, both those left lying and those the fills knock loose, such as +# the sign, whose floor goes before it does. structure_void first, which no scene places, so each fill +# changes a block, and water and lava go with the rest. +fill ~2 ~-1 ~-12 ~25 ~15 ~ minecraft:structure_void +fill ~2 ~-1 ~-12 ~25 ~-1 ~ minecraft:stone +fill ~2 ~ ~-12 ~25 ~15 ~ minecraft:air +execute align xyz positioned ~2 ~-1 ~-12 as @e[type=minecraft:item,dx=23,dy=16,dz=12] run kill @s diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/creative_pipe.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/creative_pipe.mcfunction new file mode 100644 index 0000000..51cb7ec --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/creative_pipe.mcfunction @@ -0,0 +1,16 @@ +# A creative pipe, its fluid unset, feeding a run and two storage tanks, and a chest of buckets whose +# fluid its screen's slot takes. +function pipeworks:gallery/clear +setblock ~8 ~ ~-6 pipeworks:creative_pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:pipe +setblock ~12 ~ ~-6 pipeworks:pipe +setblock ~13 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-6 pipeworks:pipe +setblock ~15 ~ ~-6 pipeworks:pipe +setblock ~16 ~ ~-6 pipeworks:pipe +setblock ~17 ~ ~-6 pipeworks:storage_tank +setblock ~17 ~1 ~-6 pipeworks:storage_tank +setblock ~12 ~ ~-3 minecraft:chest[facing=south]{Items:[{Slot:0b,id:"minecraft:water_bucket",count:1},{Slot:1b,id:"minecraft:lava_bucket",count:1}]} +function pipeworks:gallery/sign {scene:"creative_pipe"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/dismantle.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/dismantle.mcfunction new file mode 100644 index 0000000..def5ecb --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/dismantle.mcfunction @@ -0,0 +1,21 @@ +# A run of pipes to take up, and a chest with a pickaxe and pipes to lay: sneak-click a pipe with the +# pickaxe and click another to take up the run between them, and drag a line of pipes to lay one. +function pipeworks:gallery/clear +setblock ~6 ~ ~-6 pipeworks:pipe +setblock ~7 ~ ~-6 pipeworks:pipe +setblock ~8 ~ ~-6 pipeworks:pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:pipe +setblock ~12 ~ ~-6 pipeworks:pipe +setblock ~13 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-6 pipeworks:pipe +setblock ~15 ~ ~-6 pipeworks:pipe +setblock ~16 ~ ~-6 pipeworks:pipe +setblock ~17 ~ ~-6 pipeworks:pipe +setblock ~18 ~ ~-6 pipeworks:pipe +setblock ~19 ~ ~-6 pipeworks:pipe +setblock ~20 ~ ~-6 pipeworks:pipe +setblock ~21 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-3 minecraft:chest[facing=south]{Items:[{Slot:0b,id:"minecraft:iron_pickaxe",count:1},{Slot:1b,id:"pipeworks:pipe",count:32}]} +function pipeworks:gallery/sign {scene:"dismantle"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/one_fluid.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/one_fluid.mcfunction new file mode 100644 index 0000000..b59c8f0 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/one_fluid.mcfunction @@ -0,0 +1,21 @@ +# A run of water and a run of lava, one block apart, each filled by its own Pump, and a chest of pipes: +# a pipe set in the gap would join them, so it is refused. +function pipeworks:gallery/clear +execute positioned ~5 ~ ~-6 run function pipeworks:gallery/pool {fluid:"water"} +setblock ~6 ~ ~-6 pipeworks:pump[facing=east] +setblock ~7 ~ ~-6 pipeworks:pipe +setblock ~8 ~ ~-6 pipeworks:pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:pipe +setblock ~12 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-6 pipeworks:pipe +setblock ~15 ~ ~-6 pipeworks:pipe +setblock ~16 ~ ~-6 pipeworks:pipe +setblock ~17 ~ ~-6 pipeworks:pipe +setblock ~18 ~ ~-6 pipeworks:pipe +setblock ~19 ~ ~-6 pipeworks:storage_tank +execute positioned ~21 ~ ~-6 run function pipeworks:gallery/pool {fluid:"lava"} +setblock ~20 ~ ~-6 pipeworks:pump[facing=west] +setblock ~13 ~ ~-3 minecraft:chest[facing=south]{Items:[{Slot:0b,id:"pipeworks:pipe",count:4}]} +function pipeworks:gallery/sign {scene:"one_fluid"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/outlet.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/outlet.mcfunction new file mode 100644 index 0000000..afd8357 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/outlet.mcfunction @@ -0,0 +1,19 @@ +# A Pump on a water source filling a run and a storage tank, which an Outlet empties a bucket at a time +# into the glass basin it faces. The run alone would hold less than the bucket the Outlet waits for. +function pipeworks:gallery/clear +execute positioned ~6 ~ ~-6 run function pipeworks:gallery/pool {fluid:"water"} +setblock ~7 ~ ~-6 pipeworks:pump[facing=east] +setblock ~8 ~ ~-6 pipeworks:pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:pipe +setblock ~12 ~ ~-6 pipeworks:pipe +setblock ~13 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-6 pipeworks:pipe +setblock ~11 ~1 ~-6 pipeworks:storage_tank +setblock ~15 ~ ~-6 pipeworks:outlet[facing=east] +setblock ~17 ~ ~-6 minecraft:glass +setblock ~16 ~ ~-5 minecraft:glass +setblock ~16 ~ ~-7 minecraft:glass +setblock ~16 ~1 ~-6 minecraft:glass +function pipeworks:gallery/sign {scene:"outlet"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pipes.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pipes.mcfunction new file mode 100644 index 0000000..e379ec6 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pipes.mcfunction @@ -0,0 +1,16 @@ +# A Pump on a water source filling a run of pipes and a storage tank, whose glass shows it filling. +function pipeworks:gallery/clear +execute positioned ~6 ~ ~-6 run function pipeworks:gallery/pool {fluid:"water"} +setblock ~7 ~ ~-6 pipeworks:pump[facing=east] +setblock ~8 ~ ~-6 pipeworks:pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:pipe +setblock ~12 ~ ~-6 pipeworks:pipe +setblock ~13 ~ ~-6 pipeworks:pipe +setblock ~14 ~ ~-6 pipeworks:pipe +setblock ~15 ~ ~-6 pipeworks:pipe +setblock ~16 ~ ~-6 pipeworks:pipe +setblock ~17 ~ ~-6 pipeworks:pipe +setblock ~18 ~ ~-6 pipeworks:storage_tank +function pipeworks:gallery/sign {scene:"pipes"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pool.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pool.mcfunction new file mode 100644 index 0000000..d5990b0 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pool.mcfunction @@ -0,0 +1,9 @@ +# A still source of $(fluid) where it is run, walled in glass on its four sides and over it, so it never +# flows out of its plot: a fluid outside the plot would survive the next clear. The scene then sets the +# Pump that draws it in place of one wall. No two pools share a wall, which would fail its setblock. +setblock ~1 ~ ~ minecraft:glass +setblock ~-1 ~ ~ minecraft:glass +setblock ~ ~ ~1 minecraft:glass +setblock ~ ~ ~-1 minecraft:glass +setblock ~ ~1 ~ minecraft:glass +$setblock ~ ~ ~ minecraft:$(fluid) diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pump.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pump.mcfunction new file mode 100644 index 0000000..39197ef --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/pump.mcfunction @@ -0,0 +1,19 @@ +# Two Pumps: one on a water source, which stays and keeps giving, and one on a lava source, which it +# takes whole, as a bucket would. +function pipeworks:gallery/clear +execute positioned ~6 ~ ~-6 run function pipeworks:gallery/pool {fluid:"water"} +setblock ~7 ~ ~-6 pipeworks:pump[facing=east] +setblock ~8 ~ ~-6 pipeworks:pipe +setblock ~9 ~ ~-6 pipeworks:pipe +setblock ~10 ~ ~-6 pipeworks:pipe +setblock ~11 ~ ~-6 pipeworks:storage_tank +setblock ~14 ~ ~-6 pipeworks:storage_tank +setblock ~15 ~ ~-6 pipeworks:pipe +setblock ~16 ~ ~-6 pipeworks:pipe +setblock ~17 ~ ~-6 pipeworks:pipe +setblock ~18 ~ ~-6 pipeworks:pipe +setblock ~19 ~ ~-6 pipeworks:pipe +setblock ~20 ~ ~-6 pipeworks:pipe +execute positioned ~22 ~ ~-6 run function pipeworks:gallery/pool {fluid:"lava"} +setblock ~21 ~ ~-6 pipeworks:pump[facing=west] +function pipeworks:gallery/sign {scene:"pump"} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/sign.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/sign.mcfunction new file mode 100644 index 0000000..a0722fc --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/sign.mcfunction @@ -0,0 +1,3 @@ +# A scene's sign at ~14 ~ ~0, facing south, waxed. Its name is in bold, and clicking it runs the +# scene's function again from the plot's corner, 14 blocks west of the sign, which builds it afresh. +$setblock ~14 ~ ~ minecraft:oak_sign[rotation=0]{is_waxed:1b,front_text:{messages:[{translate:"pipeworks.gallery.$(scene)",bold:true,click_event:{action:"run_command",command:"execute positioned ~-14 ~ ~ run function pipeworks:gallery/$(scene)"}},{translate:"pipeworks.gallery.$(scene).1"},{translate:"pipeworks.gallery.$(scene).2"},{translate:"pipeworks.gallery.$(scene).3"}]}} diff --git a/src/main/resources/gallery_pack/data/pipeworks/function/gallery/tank_level.mcfunction b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/tank_level.mcfunction new file mode 100644 index 0000000..6e93a25 --- /dev/null +++ b/src/main/resources/gallery_pack/data/pipeworks/function/gallery/tank_level.mcfunction @@ -0,0 +1,10 @@ +# A Pump on a water source filling a column of three storage tanks, which touch and so hold one segment +# between them: their glass rises together, step by step. +# Jade, which the gallery may not need, adds the segment's amount and capacity. +function pipeworks:gallery/clear +execute positioned ~12 ~ ~-6 run function pipeworks:gallery/pool {fluid:"water"} +setblock ~13 ~ ~-6 pipeworks:pump[facing=east] +setblock ~14 ~ ~-6 pipeworks:storage_tank +setblock ~14 ~1 ~-6 pipeworks:storage_tank +setblock ~14 ~2 ~-6 pipeworks:storage_tank +function pipeworks:gallery/sign {scene:"tank_level"} diff --git a/src/main/resources/gallery_pack/pack.mcmeta b/src/main/resources/gallery_pack/pack.mcmeta new file mode 100644 index 0000000..5619b52 --- /dev/null +++ b/src/main/resources/gallery_pack/pack.mcmeta @@ -0,0 +1,7 @@ +{ + "pack": { + "description": {"translate": "pipeworks.gallery.pack.description"}, + "min_format": [101, 1], + "max_format": [101, 1] + } +}