From 1f8a89db6ad088a86ccc496785e0551977c730c1 Mon Sep 17 00:00:00 2001
From: 44r0n7 <44r0n7+gitea@pm.me>
Date: Mon, 26 Oct 2026 18:59:13 -0400
Subject: [PATCH] Rename screw tubes to screw conveyors (t44)
Player text says Screw conveyor; code uses screw (BP_SCREW, piece id
screw, screwIntake...). No compatibility code for old saves or
blueprints. Suite outputs match before and after apart from names.
Co-Authored-By: Claude Opus 5.5
---
README.md | 2 +-
docs/DESIGN.md | 33 ++++----
docs/PROJECT_MAP.md | 34 ++++----
docs/TESTING.md | 2 +-
docs/agent-log.md | 21 +++++
docs/systems/stone-and-liquids.md | 8 +-
src/render/overlays.js | 8 +-
src/render/slope-belts.js | 4 +-
src/render/smooth-shaders.js | 12 +--
src/render/world.js | 50 +++++------
src/sim/blocks.js | 6 +-
src/sim/blueprints.js | 2 +-
src/sim/cells.js | 6 +-
src/sim/conveyors.js | 28 +++----
src/sim/items.js | 90 ++++++++++----------
src/sim/machine-tick.js | 28 +++----
src/sim/machines.js | 16 ++--
src/sim/material-tables.js | 12 +--
src/sim/materials.js | 4 +-
src/sim/pieces.js | 12 +--
src/sim/progress.js | 4 +-
src/sim/save.js | 4 +-
src/sim/vacuum.js | 2 +-
src/sim/world.js | 4 +-
src/ui/actions.js | 10 +--
src/ui/build-menu.js | 10 +--
src/ui/info.js | 2 +-
src/ui/input.js | 2 +-
src/ui/notebook.js | 8 +-
src/ui/preview-selection.js | 2 +-
src/ui/previews.js | 20 ++---
test/play.py | 16 ++--
test/t_conveyors.js | 96 +++++++++++-----------
test/t_corner_seal.js | 8 +-
test/{t_diag_tubes.js => t_diag_screws.js} | 52 ++++++------
test/t_light.js | 8 +-
test/t_racks.js | 4 +-
test/t_slope_belt_art.js | 4 +-
test/t_slope_belts.js | 6 +-
test/t_world.js | 6 +-
tools/experiments/flow.js | 14 ++--
tools/scenes/bar-lift.js | 4 +-
tools/scenes/belt-heap.js | 2 +-
tools/scenes/t35-diagonal.js | 2 +-
tools/scenes/t35-joins.js | 10 +--
45 files changed, 351 insertions(+), 327 deletions(-)
rename test/{t_diag_tubes.js => t_diag_screws.js} (80%)
diff --git a/README.md b/README.md
index 2350dcb..f6d8d64 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ A falling-sand factory game in the browser, in the style of Sandustry.
Every grain of sand, ore, water and fire is simulated. You fly a small bot over a side-view
world, dig with a resonance drill and vacuum up what breaks loose. You build furnaces from
blocks, melt ore with coal and air, and cast the metal in molds. Then you sell bars, alloys and
-other goods at a trade dock, automating with belts, screw tubes, filters, fans, pipes and
+other goods at a trade dock, automating with belts, screw conveyors, filters, fans, pipes and
machines, until the land claim is paid off and beyond.
## Play
diff --git a/docs/DESIGN.md b/docs/DESIGN.md
index fc09398..d5e1b22 100644
--- a/docs/DESIGN.md
+++ b/docs/DESIGN.md
@@ -49,7 +49,7 @@ decides something new, add it under "Decisions" with the date.
4. Bars drop out of the bottom of the mold; collect them and sell them at the trade dock, or fill buyers' orders, which pay more.
5. Better bits (copper, then iron, then steel) open harder rock: granite, then basalt. Deeper
ores give tin, zinc, silver and gold, and alloys (bronze, brass) pay more.
-6. Automate with belts, screw tubes, filters, fans, pipes and machines. Pay off the claim.
+6. Automate with belts, screw conveyors, filters, fans, pipes and machines. Pay off the claim.
## Systems in brief
@@ -74,7 +74,7 @@ Details and numbers live in the code and in `docs/systems/`.
- **Drill:** see `docs/systems/dig-tools.md`. Pulse widths, tuning to one material, hardness by
bit tier.
- **Building:** a 4×4-grain block grid.
- - Blocks: walls of any of several materials, belts, screw tubes, filters, grates, gates,
+ - Blocks: walls of any of several materials, belts, screw conveyors, filters, grates, gates,
pipes, pumps, outlets.
- Machines: fan, Refiner, Stone cutter, Silo.
- Molds: ingot, bit, tank. Ingot bars drop out of the bottom onto whatever is under the mold;
@@ -140,7 +140,7 @@ above, including:
- **Ingot molds drop bars below automatically.** The owner reported bars being thrown into the
furnace above and wants belts underneath to carry them off. Blocked bars wait in the trough;
short stubs stay for the next firing. The mold cleans itself and rakes the grate after dropping bars or clearing junk.
-- **Screw tubes carry no more than belts:** they move every other tick. For more throughput,
+- **Screw conveyors carry no more than belts:** they move every other tick. For more throughput,
build more lines.
- **A heap on a belt moves as one**, up to 96 grains deep, not just its bottom layer. If a grain is blocked,
the load above it stands still and the belt slips underneath; the top no longer slides over
@@ -177,7 +177,7 @@ above, including:
popup. The owner asked for a general description rather than the selected building.
- **Ingot molds need no F.** They push junk and mixed metal out of the bottom as loose grains with the bars, throw slag away and rake the grate. Removing any mold returns its set metal: whole bars as bars, the rest as scrap. The owner asked what F was for once bars dropped on their own, and chose this over keeping F for cleaning.
-- **Fewer, smaller messages.** Molds show only a small percentage, red when blocked or spoiled and gold when a casting waits for F; details are in the hover panel. Bars dropping, blocked molds and set castings no longer pop up cards or toasts. Fan heat is a red marker on the fan; melted tubes and machines get a short red label where they were. Status lines are one short sentence. The owner found the messages too large and too many.
+- **Fewer, smaller messages.** Molds show only a small percentage, red when blocked or spoiled and gold when a casting waits for F; details are in the hover panel. Bars dropping, blocked molds and set castings no longer pop up cards or toasts. Fan heat is a red marker on the fan; melted screw conveyors and machines get a short red label where they were. Status lines are one short sentence. The owner found the messages too large and too many.
- **World sounds are local.** Sounds from the world (water sizzling on hot metal or lava, and
any other event with a place) are heard around the bot, whatever the zoom: full volume within
@@ -190,14 +190,14 @@ above, including:
- **The silo has pixel art in the pixel look**, drawn grain by grain like the rest of the world;
the smooth look keeps the illustrated silo. The owner noticed it looked the same in both.
-- **Screw tubes use all their lanes.** Grains in an up or down tube shift sideways toward the
+- **Screw conveyors use all their lanes.** Grains in an up or down screw conveyor shift sideways toward the
emptier lanes of the block, and grains taken in from the side go to the far lanes first. A
block-wide lift fed by a belt from the side now carries about 90% of what the belt brings
(before, only the edge lane filled: the owner saw one line on either side). Belts keep their
speed.
-- **Screw tubes carry bars**, one whole bar at a time, in line with the grains: a belt pushes a
- bar into a tube's side, it rides through corners and leaves the mouth. The owner asked how bars
- should be moved and chose tubes over a separate bar lift.
+- **Screw conveyors carry bars**, one whole bar at a time, in line with the grains: a belt pushes a
+ bar into a screw conveyor's side, it rides through corners and leaves the mouth. The owner asked how bars
+ should be moved and chose screw conveyors over a separate bar lift.
- **Several worlds are kept.** Each world has its own save and autosave; the title screen's Worlds
list loads, renames and deletes them, and a new or imported world never overwrites another. An
old single save becomes 'World 1'. The owner asked for multiple saves.
@@ -214,7 +214,7 @@ above, including:
and the surface start seen. The owner chose line of sight over a plain circle around the bot.
- **Bars pile neatly, and a Bar rack stores them (t1).** Bars that come to rest snap to a tidy
brick pattern so piles have no air gaps; a Bar rack machine (the Silo for bars) takes them from
- a belt or tube, counts them and lets them out with F. The owner wanted bar storage that doesn't
+ a belt or screw conveyor, counts them and lets them out with F. The owner wanted bar storage that doesn't
waste space, like stacked ingots in real life.
As built: a resting bar snaps to a 2-grain grid, and one with fewer than 2 of its 4 bottom cells
supported tips into the gap beside it. Storage became Rack blocks rather than a machine, like
@@ -372,25 +372,25 @@ above, including:
also fills one cell past its corner so a diagonal run is sealed (grains move diagonally and
would otherwise slip through where two pieces meet). Square walls placed diagonally still
touch only at corners and leak there, like any square blocks.
- Later the same day the owner asked for the Wall piece itself to work diagonally, the way tubes
+ Later the same day the owner asked for the Wall piece itself to work diagonally, the way screw conveyors
do, so the separate Sloped wall piece left the build menu: a wall drag at about 45° lays slopes
(solid side below, T flips it during the drag), straight drags and clicks lay squares, and for a
single block T cycles square and the four corners. Slope ids are unchanged, so saves still load.
-- **Diagonal screw tubes (t35, 2026-10-25).** Tubes run in eight directions. A drag at about 45°
- lays a diagonal run pointing along the drag, and R cycles all eight for a single tube. Diagonal
- runs carry grains at the same rate as straight ones, join straight tubes and belts, and carry
+- **Diagonal screw conveyors (t35, 2026-10-25).** Screw conveyors run in eight directions. A drag at about 45°
+ lays a diagonal run pointing along the drag, and R cycles all eight for a single screw conveyor. Diagonal
+ runs carry grains at the same rate as straight ones, join straight screw conveyors and belts, and carry
bars level, a block every eight ticks. They use new part ids (11-14) because part ids are saved.
- **Sloped belts and filters (t40, 2026-10-25).** The owner asked for belts, then filters, to work
- diagonally like tubes and walls. A Belt or Filter drag at about 45° lays slope-shaped blocks
+ diagonally like screw conveyors and walls. A Belt or Filter drag at about 45° lays slope-shaped blocks
whose face is a running belt, carrying grains and bars up or down in the drag's direction at
flat-belt speed; the face grips its load, so nothing slides back. A sloped filter drops what it
passes out underneath and carries the rest. New block codes (19-22), so saves still load.
- **Square corners hold grains (t39, 2026-10-26).** The owner wants square wall staircases to
hold material instead of leaking through each touching corner. Powder, liquid and gas cannot
step diagonally between two solid neighbours; powder neighbours still allow free piles to
- slide as before. Free flyers and diagonal screw intakes obey the same solid-corner boundary.
+ slide as before. Free flyers and diagonal screw conveyor intakes obey the same solid-corner boundary.
Screw lane transport is unchanged. World generation disables cell corner sealing while
settling loose ground, preserving existing seed layouts. This supersedes the square-wall
leakage described in the t36 decision below.
@@ -400,6 +400,9 @@ above, including:
- **Distant drill aim is normal (t41, 2026-10-26).** The owner normally points beyond the
drill's reach and expects players to do the same. Keep the pulse colour and light crosshair,
mark the pulse's far edge, and omit distance warnings for the drill. Other tools still warn.
+- **Screw conveyors (t44, 2026-10-26).** The owner chose the real machine name for screw
+ conveyors; code uses `screw` because `conveyor` also covers belts and filters. Transport,
+ numeric ids and save versions stay unchanged.
- **No backward compatibility for now (2026-10-26).** The owner will start a new world after
changes: no one is playing the game for real yet. Changes may break old saves and blueprints;
don't add migration or alias code for them (first used by the t44 rename to screw conveyors).
diff --git a/docs/PROJECT_MAP.md b/docs/PROJECT_MAP.md
index cfd6837..0eb941f 100644
--- a/docs/PROJECT_MAP.md
+++ b/docs/PROJECT_MAP.md
@@ -44,7 +44,7 @@ dist/, shots/ build output and screenshots (not committed)
4. update the HUD.
- **One tick** (`simTick` in `src/sim/tick.js`), in this order:
- update the sky roof line (every 20 ticks);
- - weather, machines (including belts and tubes), pipes, drill pulses;
+ - weather, machines (including belts and screw conveyors), pipes, drill pulses;
- bars (`entTick`) and grains in flight (`flyTick`);
- falling sand (`simMove`), then heat (`heatPass`);
- every so often: molds, milestones, plants, cold, tree support;
@@ -62,7 +62,7 @@ dist/, shots/ build output and screenshots (not committed)
- `temp`: °C;
- `flags`: `F_NAT` natural rock, `F_CAST`, `F_BURN` burning;
- `stamp`: moved this tick;
- - `part`: which built part a cell belongs to (`P_*`: tube directions, grate, open gate, filter,
+ - `part`: which built part a cell belongs to (`P_*`: screw conveyor directions, grate, open gate, filter,
pipe...);
- `mach`: machine id;
- `digp`: drill damage;
@@ -81,7 +81,7 @@ dist/, shots/ build output and screenshots (not committed)
(open inside).
- `molds`;
- `ents` (bars, 4×2 grains, with their own physics);
- - `flyers` (grains in the air: blown, off belt ends, out of tube mouths, rain);
+ - `flyers` (grains in the air: blown, off belt ends, out of screw conveyor mouths, rain);
- `pipeNets`;
- `ghostBlocks`, `ghostMachines`, `ghostMolds`.
- **Player:** `bot` (position), `bag` (count per material), `player` (bit and tank tier, lens,
@@ -140,7 +140,7 @@ dist/, shots/ build output and screenshots (not committed)
- **The smooth look** (`src/render/smooth.js`) takes the buffers `drawWorld` fills (colour and
a class per cell, the colour behind, trees, fields, light, glow) and shades them with WebGL2.
- The cell classes are set in `world.js` and read in the shader: 4 is a crisp built piece,
- 6 a wall tile, 7/8 a belt, 9–12 a screw tube, 13/14 a filter.
+ 6 a wall tile, 7/8 a belt, 9–12 a screw conveyor, 13/14 a filter.
- On software GL it falls back to pixels, unless `window.__allowSoftwareGL` is set (the test
tools set it).
@@ -171,7 +171,7 @@ dist/, shots/ build output and screenshots (not committed)
| Starter furnace designs | `src/sim/progress.js` (`STARTER_DESIGNS`, `smelterBlueprint`) |
| Falling sand, liquids, gases | `src/sim/cells.js` |
| Heat, melting, fire, reactions, alloys, steel | `src/sim/heat.js` |
-| Belts, filters, screw tubes | `src/sim/conveyors.js`, `src/sim/machine-tick.js` |
+| Belts, filters, screw conveyors | `src/sim/conveyors.js`, `src/sim/machine-tick.js` |
| Fans | `src/sim/machine-tick.js` (`fanBlow`) |
| Refiner, Stone cutter, Silo | `src/sim/processors.js` |
| Pipes, pumps, outlets | `src/sim/pipes.js` |
@@ -203,12 +203,12 @@ Material definitions: ids, properties and metal families. ids, and drill/tank ti
### src/sim/material-tables.js (54 lines)
Material tables: typed properties, casting groups, flags, parts and tool tiers. Typed property tables
-- variables: PH, DENS, KC, ICAP, CASTMETAL, METAL_TIER, SOLIDMETAL, F_NAT, F_CAST, F_BURN, P_NONE, P_TU, P_TD, P_TL, P_TR, P_GRATE, P_GOPEN, P_WHEEL, P_INTAKE, P_FILTER, P_PIPE, P_TUR, P_TDR, P_TDL, P_TUL, TUBE_PARTS, TUBE_DIRS, IS_TUBE, TDX, TDY, BIT_TIERS, TANK_TIERS, REACH
+- variables: PH, DENS, KC, ICAP, CASTMETAL, METAL_TIER, SOLIDMETAL, F_NAT, F_CAST, F_BURN, P_NONE, P_SCREW_U, P_SCREW_D, P_SCREW_L, P_SCREW_R, P_GRATE, P_GOPEN, P_WHEEL, P_INTAKE, P_FILTER, P_PIPE, P_SCREW_UR, P_SCREW_DR, P_SCREW_DL, P_SCREW_UL, SCREW_PARTS, SCREW_DIRS, IS_SCREW, SCREW_DX, SCREW_DY, BIT_TIERS, TANK_TIERS, REACH
### src/sim/pieces.js (176 lines)
Everything the player can build (PIECES): blocks, machines and molds, with costs and descriptions; filter settings; wall materials and their cost.
- functions: isSlopeBelt, isFilter, isBelt, beltGrade, slopeShape, beltFaceY, beltDirection, isSlope, isWall, slopeCell, slopeCode, isPipeCode, filterable, cleanFilterCfg, filterRec, filterPassAt, filterInfo, sameFilter, wallCost
-- variables: BS, BP_WALL, BP_BELT, BP_TUBE, BP_SHAFT, BP_GRATE, BP_GATE, BP_FILTER, BP_PIPE, BP_PUMP, BP_OUTLET, BP_GRIND, BP_SAW, BP_PRESS, BP_RACK, BP_SLOPE_BL, BP_SLOPE_BR, BP_SLOPE_TL, BP_SLOPE_TR, BP_BELT_UP, BP_BELT_DOWN, BP_FILTER_UP, BP_FILTER_DOWN, filterCfg, WALL_OF, WALL_LIST, BAR_OF, GRAINS_PER_BAR, POWER_FREE, CASH, PIECES, PIECE_BY_ID, PIECE_BY_CODE, BELT_POWER, TUBE_POWER, FAN_POWER, WHEEL_K, ENGINE_K, MOLDS
+- variables: BS, BP_WALL, BP_BELT, BP_SCREW, BP_SHAFT, BP_GRATE, BP_GATE, BP_FILTER, BP_PIPE, BP_PUMP, BP_OUTLET, BP_GRIND, BP_SAW, BP_PRESS, BP_RACK, BP_SLOPE_BL, BP_SLOPE_BR, BP_SLOPE_TL, BP_SLOPE_TR, BP_BELT_UP, BP_BELT_DOWN, BP_FILTER_UP, BP_FILTER_DOWN, filterCfg, WALL_OF, WALL_LIST, BAR_OF, GRAINS_PER_BAR, POWER_FREE, CASH, PIECES, PIECE_BY_ID, PIECE_BY_CODE, BELT_POWER, SCREW_POWER, FAN_POWER, WHEEL_K, ENGINE_K, MOLDS
### src/sim/economy.js (152 lines)
Money, prices, market saturation, the trade dock, buyer and builder orders.
@@ -223,7 +223,7 @@ Starter furnace designs and the milestone list shown in the notebook.
### src/sim/world.js (104 lines)
World state: the per-cell typed arrays, alloc(), ambient temperature by depth, waking.
- functions: emit, bagCap, discover, alloc, depthU, depthY, meanSurface, computeAmbient, fillNoise, wakeXY, wakeHeatXY, wakeIdx, wakeHeatIdx
-- variables: W, H, N, CW, CH, NCH, mat, temp, life, flags, stamp, part, mach, digp, noiseB, airS, netId, isPow, occ, surfY, dirtY, graniteY, basaltY, magmaY, roofY, AMB, worldSeed, surfBase, underD, shallowD, lapse, BW, BH, NB, blockP, blockV, ents, bgm, flyers, ghostBlocks, ghostMolds, awake, hot, hotLvl, powderArea, roofDirty, loadWakeOrder, loadWakePending, loadWakeIndex, tick, tickStamp, machines, molds, nets, pcells, powerDirty, beltL, beltR, tubeLists, brokenMachines, ghostMachines, ghostBlocked, EV, bot, bag, bagTotal, player, seen, ledger, stats
+- variables: W, H, N, CW, CH, NCH, mat, temp, life, flags, stamp, part, mach, digp, noiseB, airS, netId, isPow, occ, surfY, dirtY, graniteY, basaltY, magmaY, roofY, AMB, worldSeed, surfBase, underD, shallowD, lapse, BW, BH, NB, blockP, blockV, ents, bgm, flyers, ghostBlocks, ghostMolds, awake, hot, hotLvl, powderArea, roofDirty, loadWakeOrder, loadWakePending, loadWakeIndex, tick, tickStamp, machines, molds, nets, pcells, powerDirty, beltL, beltR, screwLists, brokenMachines, ghostMachines, ghostBlocked, EV, bot, bag, bagTotal, player, seen, ledger, stats
### src/sim/fog.js (52 lines)
Exploration only: never changes grains or the random stream.
@@ -261,12 +261,12 @@ Pipes, pumps and outlets: each joined line holds one liquid as a count, not as g
- variables: PIPE_CAP, PUMP_RATE, OUTLET_RATE, PUMP_MAX_T, SIDE_V, PUMP_V, OUTLET_V, pipeNets, pipeNetAt, pipesDirty
### src/sim/machine-tick.js (154 lines)
-Per-tick machine work: steam engines, fans, belts, filters, screw tubes (machinesTick).
+Per-tick machine work: steam engines, fans, belts, filters, screw conveyors (machinesTick).
- functions: engineIntake, fanBlow, machinesTick
### src/sim/conveyors.js (202 lines)
-How belts, filters and screw tubes move what is on or in them. What rests on a belt or filter rides along: the whole stack standing on the carrying surface steps one cell toward d together (belts run front of the line first, so the way ahead is clear). If a powder grain is blocked, the load above it stands still and the belt slips underneath. A filter's own material drops through it instead, unless it is already full.
-- functions: conveyorStep, carryColumn, holdSlopeColumn, beltDrain, launchGrain, rebuildFilters, filterCarry, carryBeltsAndFilters, setFilter, filterRun, roomFor, tubeSpread, tubeIntake, diagonalTubeNext
+How belts, filters and screw conveyors move what is on or in them. What rests on a belt or filter rides along: the whole stack standing on the carrying surface steps one cell toward d together (belts run front of the line first, so the way ahead is clear). If a powder grain is blocked, the load above it stands still and the belt slips underneath. A filter's own material drops through it instead, unless it is already full.
+- functions: conveyorStep, carryColumn, holdSlopeColumn, beltDrain, launchGrain, rebuildFilters, filterCarry, carryBeltsAndFilters, setFilter, filterRun, roomFor, screwSpread, screwIntake, diagonalScrewNext
- variables: CARRY_DEPTH, filterR, filterL, filtersDirty
### src/sim/blocks.js (219 lines)
@@ -280,7 +280,7 @@ Molds: cavities for castings; ingot troughs empty themselves below. Removal retu
### src/sim/items.js (288 lines)
Bars and other loose objects (ents), and the bag: adding, taking, costs and refunds.
-- functions: entSolidCell, entSupportCount, entFreeAt, entMark, spawnBar, removeEnt, entTubeAt, entTubeFree, entTubeMove, entTubeLane, entTubeEdge, entTubeEnter, entTubeTick, entDiagonalTubeTick, entSlopeBeltTick, entTick, entsInCircle, bagAdd, bagTake, giveOrDrop, refundAt, canAfford, payCost, fmtMoney, costText, needText
+- functions: entSolidCell, entSupportCount, entFreeAt, entMark, spawnBar, removeEnt, entScrewAt, entScrewFree, entScrewMove, entScrewLane, entScrewEdge, entScrewEnter, entScrewTick, entDiagonalScrewTick, entSlopeBeltTick, entTick, entsInCircle, bagAdd, bagTake, giveOrDrop, refundAt, canAfford, payCost, fmtMoney, costText, needText
- variables: BAR_W, BAR_H, entCurM, droppedLast
### src/sim/tools.js (52 lines)
@@ -363,8 +363,8 @@ Drawing only: the face stays on its simulation cells; the band hangs below it.
### src/render/world.js (440 lines)
Scale colour without changing the smooth-look class.
-- functions: fogColor, diagonalTubeJoins, diagonalTubeShape, diagonalTubeBlock, drawWorld
-- variables: TUBE_CLASS, diagonalJoins, diagonalAcross, diagonalAlong, diagonalCap
+- functions: fogColor, diagonalScrewJoins, diagonalScrewShape, diagonalScrewBlock, drawWorld
+- variables: SCREW_CLASS, diagonalJoins, diagonalAcross, diagonalAlong, diagonalCap
### src/render/machines.js (256 lines)
Machine overlays: fans, engines, silo and the rest, drawn on top of the world.
@@ -513,9 +513,9 @@ The frame loop and boot.
- `test/t_bar_stacking.js`: Resting bars snap into pairs of grains and tip off narrow supports.
- `test/t_basics.js`: Basics: drilling to rubble, the vacuum and blowing, walls, the ingot mold and bars, belts, blueprints, save/load.
- `test/t_build.js`: Building: undo (Ctrl+Z), cut, replace a machine with Ctrl, pasting ghosts, and the fan heat warning.
-- `test/t_conveyors.js`: Screw tubes that never carry liquid (it flows through), tube and belt throughput, filters.
+- `test/t_conveyors.js`: Screw conveyors that never carry liquid (it flows through), screw and belt throughput, filters.
- `test/t_corner_seal.js`: Corner-touching square walls seal loose grains without changing free piles or worldgen.
-- `test/t_diag_tubes.js`: Diagonal screws: sealed four-lane corner transfers, bars, joins and persistence.
+- `test/t_diag_screws.js`: Diagonal screws: sealed four-lane corner transfers, bars, joins and persistence.
- `test/t_drill.js`: The resonance drill: pulses from the bot shake rock until it shatters. Widths trade area for speed, tuning shatters only one material and drains it out of the wall, built blocks are left alone, and the drill says why when nothing breaks.
- `test/t_econ.js`: Economy: the dock (instant sale), orders, prices, the market dip, money for machines, alloys.
- `test/t_fog.js`: Fog: surface, sight, range, saves and simulation independence.
@@ -546,7 +546,7 @@ The frame loop and boot.
- `tools/where.py`: Turn a line number in dist/index.html into the source file and line it came from.
- `tools/experiments/cast_sunk_mold.js`: Block-sized bit mold sunk into the ground with a stone furnace built over its funnel.
- `tools/experiments/factory_cost.js`: What does a running factory cost when the player is somewhere else? Builds K self-running modules in a generated world, far from the bot, and measures simulation time per tick. Each module: a coal + copper-ore furnace burning in a stone-block box, a spring feeding a water wheel, a shaft carrying its power to a 10-block belt that hauls sand off a hopper.
-- `tools/experiments/flow.js`: Throughput of belts and screw tubes: grains delivered per second. Whatever lands past the outlet is taken away, so the output pile never backs up into the line.
+- `tools/experiments/flow.js`: Throughput of belts and screw conveyors: grains delivered per second. Whatever lands past the outlet is taken away, so the output pile never backs up into the line.
- `tools/experiments/furnace.js`: Furnace scenarios on a flat test world: builds a chamber, charges it with ore and coal, lights it and prints temperatures, burning cells and what has melted or set at intervals.
- `tools/experiments/section.js`: ASCII cross-section of a stone furnace firing, with a temperature digit per cell (0-9), printed at intervals. Usage: node tools/experiments/section.js [WALL_MATERIAL] (default STONE).
- `tools/experiments/smelter.js`: Smelter design experiments, built with the same block pieces a player uses. Layout (block rows, 4x4 grains each), on flat ground at row 25: roof with a vent ........ row g-CH-1 chamber, CH blocks tall .. charge of coal + copper ore inside grate floor ............. row g = 23 (liquids pass, powders do not) ingot mold .............. row 24, sitting on the ground side walls run from the roof down past the mold. Usage: node t_smelter.js [key=value ...]
diff --git a/docs/TESTING.md b/docs/TESTING.md
index 70caf0c..1c084c6 100644
--- a/docs/TESTING.md
+++ b/docs/TESTING.md
@@ -130,5 +130,5 @@ python3 tools/shot.py shots/silo.png --setup tools/scenes/clear-sky.js \
- `tools/perf.py`: renderer and tick times in the browser.
- `tools/experiments/factory_cost.js`: what running factories cost the simulation.
-- `tools/experiments/flow.js`: belt and tube throughput in grains a second.
+- `tools/experiments/flow.js`: belt and screw conveyor throughput in grains a second.
- `tools/experiments/worldgen.js`: material counts and timings of world generation.
diff --git a/docs/agent-log.md b/docs/agent-log.md
index 973ac64..289ac48 100644
--- a/docs/agent-log.md
+++ b/docs/agent-log.md
@@ -4,6 +4,27 @@ What agents have done, newest first. Add an entry for every piece of work: the d
note or request it was for, what changed, the files touched, the tests run, and anything left
open or not verified. Keep each entry short; the git history has the details.
+## 2026-10-26: t44: rename screw tubes to screw conveyors (Codex)
+
+- Player text uses Screw conveyor; code uses screw/SCREW, including the piece id, part
+ constants, direction tables, transport helpers, shader variable, build icons and settings
+ access. Numeric ids and save/blueprint versions unchanged; no aliases or migrations added.
+- Updated affected files across src/sim, src/render, src/ui, test and tools, plus README,
+ AGENTS and current DESIGN/TESTING/system docs. Regenerated PROJECT_MAP and built dist.
+ Renamed test/t_diag_tubes.js to test/t_diag_screws.js with git mv; no scene filename needed
+ renaming. Past log entries, docs/history and task title/Context preserved.
+- All 26 suite outputs saved in /tmp/t44-before and /tmp/t44-after. Raw diff:
+ /tmp/t44-output.diff; name/timing-normalized diff: /tmp/t44-output-normalized.diff (empty).
+ No numeric output differences except ms timings; numeric literals unchanged in all 39
+ edited JavaScript/Python files. Only tube matches in scoped source/docs are the physical
+ casing description in pieces.js and casing/flight rendering comment in world.js.
+- python3 test/run_all.py --browser: ALL PASSED (26 headless suites, 352 play checks,
+ 15 smooth checks). Focused menu, tooltip, hover, notebook, screw blueprint-code round-trip
+ and both starter blueprint-code checks pass in pixel and smooth; no page errors.
+ Opened shots/t44-{menu,hover,notebook}-{pixel,smooth}.png and the play diagonal screenshot.
+ Temporary UI fixture initially lacked SX/SY; corrected to match shot.py before these checks.
+- Task t44 moved to Review. Owner playtest remains. No commit.
+
## 2026-10-26: t38: show silo contents at the cursor and in a wide window (Codex)
- `src/ui/info.js`: contents were confined to the bottom info plate (hidden below 720 px),
diff --git a/docs/systems/stone-and-liquids.md b/docs/systems/stone-and-liquids.md
index b8a6e95..d8b31ff 100644
--- a/docs/systems/stone-and-liquids.md
+++ b/docs/systems/stone-and-liquids.md
@@ -5,7 +5,7 @@ Built 2026-10-22; the uses for stone and sand were added 2026-10-23.
- Code:
- `src/sim/processors.js` (Refiner, Stone cutter, Silo);
- `src/sim/pipes.js`;
- - `src/sim/conveyors.js` and `src/sim/machine-tick.js` (belts and tubes);
+ - `src/sim/conveyors.js` and `src/sim/machine-tick.js` (belts and screw conveyors);
- `src/sim/economy.js` (prices, builders' orders).
- Tests: `test/t_pipes.js`, `test/t_stone.js`, `test/t_conveyors.js`.
@@ -57,12 +57,12 @@ through another refiner.
- **Water accounting.** Water in pipes counts in the world's water total, so the weather never
sends rain to replace it. Pipe contents are saved.
-## Belts and screw tubes
+## Belts and screw conveyors
- A heap on a belt moves as one: the whole stack, up to 96 grains deep, steps along together
instead of only its bottom layer. The owner reports that big piles still only move at the top
(see NOTES.md).
-- Screw tubes run at belt pace, moving every other tick.
- - A horizontal tube fed from a full hopper moves about 100–110 grains a second.
+- Screw conveyors run at belt pace, moving every other tick.
+ - A horizontal screw conveyor fed from a full hopper moves about 100–110 grains a second.
- A belt under its mouth keeps up, carrying a stream 3–5 grains deep at about the same rate.
- A vertical lift brings up about 40–60 grains a second.
- `tools/experiments/flow.js` measures these.
diff --git a/src/render/overlays.js b/src/render/overlays.js
index 4beebb3..85e5b53 100644
--- a/src/render/overlays.js
+++ b/src/render/overlays.js
@@ -27,7 +27,7 @@ function drawRain() {
}
ctx.restore();
}
-// Info overlay: a small arrow on every belt and screw tube block on screen, the way it moves things
+// Info overlay: a small arrow on every belt and screw conveyor block on screen, the way it moves things
function drawFlowArrows() {
const ctx = R.ctx, zp = R.zp, v = viewCells();
const bx0 = Math.max(0, Math.floor(R.camX / BS)), bx1 = Math.min(BW - 1, Math.floor((R.camX + v.w) / BS));
@@ -37,12 +37,12 @@ function drawFlowArrows() {
ctx.lineCap = 'round'; ctx.lineJoin = 'round';
for (let by = by0; by <= by1; by++) for (let bx = bx0; bx <= bx1; bx++) {
const b = by * BW + bx, code = blockP[b];
- if (code !== BP_BELT && code !== BP_TUBE && code !== BP_PUMP && code !== BP_OUTLET) continue;
+ if (code !== BP_BELT && code !== BP_SCREW && code !== BP_PUMP && code !== BP_OUTLET) continue;
const vv = blockV[b];
- const [dx, dy] = code === BP_BELT ? (vv ? [1, 0] : [-1, 0]) : code === BP_PUMP ? PUMP_V[vv & 3] : code === BP_OUTLET ? OUTLET_V[vv & 3] : TUBE_DIRS[vv & 7];
+ const [dx, dy] = code === BP_BELT ? (vv ? [1, 0] : [-1, 0]) : code === BP_PUMP ? PUMP_V[vv & 3] : code === BP_OUTLET ? OUTLET_V[vv & 3] : SCREW_DIRS[vv & 7];
const c = worldToScreenPx(bx * BS + BS / 2, by * BS + BS / 2);
ctx.fillStyle = 'rgba(14,12,11,0.55)'; ctx.beginPath(); ctx.arc(c.x, c.y, r * 1.35, 0, Math.PI * 2); ctx.fill();
- ctx.strokeStyle = code === BP_TUBE ? '#ffc58a' : '#e7dccb'; ctx.lineWidth = Math.max(1.2, r * 0.32);
+ ctx.strokeStyle = code === BP_SCREW ? '#ffc58a' : '#e7dccb'; ctx.lineWidth = Math.max(1.2, r * 0.32);
// a chevron pointing (dx, dy)
const px = -dy, py = dx;
ctx.beginPath();
diff --git a/src/render/slope-belts.js b/src/render/slope-belts.js
index 599199b..7e373c6 100644
--- a/src/render/slope-belts.js
+++ b/src/render/slope-belts.js
@@ -13,7 +13,7 @@ function slopeBeltConnections(bx, by, code, planned = null) {
const fy = by + (end ? grade > 0 : grade < 0), fb = fy * BW + nx;
if (nx < 0 || nx >= BW || fy < 0 || fy >= BH) continue;
const flat = planned && planned.get(fb), c = flat ? flat[0] : blockP[fb], v = flat ? flat[1] : blockV[fb];
- if (c === BP_BELT || c === BP_FILTER || (c === BP_TUBE && (v === 1 || v === 3))) joins |= 1 << end;
+ if (c === BP_BELT || c === BP_FILTER || (c === BP_SCREW && (v === 1 || v === 3))) joins |= 1 << end;
}
return joins;
}
@@ -26,7 +26,7 @@ function flatBeltCaps(b) {
if (x >= 0 && x < BW) for (let dy = -1; dy <= 0; dy++) {
if (by + dy < 0) continue;
const n = (by + dy) * BW + x, c = blockP[n], g = beltGrade(c);
- if ((!dy && (c === BP_BELT || c === BP_FILTER || (c === BP_TUBE && (blockV[n] === 1 || blockV[n] === 3 || blockV[n] >= 4)))) ||
+ if ((!dy && (c === BP_BELT || c === BP_FILTER || (c === BP_SCREW && (blockV[n] === 1 || blockV[n] === 3 || blockV[n] >= 4)))) ||
(g && dy === -(end ? g < 0 : g > 0))) joined = true;
}
if (!joined) caps |= 1 << end;
diff --git a/src/render/smooth-shaders.js b/src/render/smooth-shaders.js
index 07b3ac6..31e8dbe 100644
--- a/src/render/smooth-shaders.js
+++ b/src/render/smooth-shaders.js
@@ -250,17 +250,17 @@ ${SM_SLOPE_DRAW}
}
}
else if ((nc >= 9 && nc <= 12) || (nc >= 19 && nc <= 22)) {
- // screw tube: a copper pipe; what is inside shows through, and the screw's flights turn
+ // screw conveyor: a copper pipe; what is inside shows through, and the screw's flights turn
bool vert = nc == 9 || nc == 11;
float sp2 = nc == 9 ? -1.0 : nc == 10 ? 1.0 : nc == 11 ? 1.0 : -1.0;
float a = (vert ? fract(w.x * 0.25) : fract(w.y * 0.25)) * 4.0, along = vert ? w.y : w.x;
- float tubeSD = -1.0, capRing = 0.0;
+ float screwSD = -1.0, capRing = 0.0;
if (nc >= 19) {
vec2 d = vec2(1.0, nc == 19 || nc == 21 ? -1.0 : 1.0);
vec4 tag = cellAt(tTree, c);
vec2 local = floor(tag.gb * 255.0 + 0.5) - 8.0 + fract(w) - 2.0;
int joins = int(tag.a * 255.0 + 0.5);
- tubeSD = 100.0;
+ screwSD = 100.0;
for (int end = 0; end < 2; end++) {
int join = (joins >> (end * 3)) & 7;
float side = end == 0 ? -1.0 : 1.0;
@@ -269,8 +269,8 @@ ${SM_SLOPE_DRAW}
float t = dot(local, ray), cross2 = dot(local, vec2(-ray.y, ray.x));
float radius = length(vec2(cross2, min(0.0, t))), cap = t - (join == 0 ? 2.82842712 : 2.0);
float sd = max(radius - 2.0, join != 0 || (joins & (64 << end)) != 0 ? cap : -10.0);
- if (sd < tubeSD) {
- tubeSD = sd;
+ if (sd < screwSD) {
+ screwSD = sd;
a = 2.0 + (side * cross2 < 0.0 ? -radius : radius);
along = dot(w - local, d) * 0.70710678 + side * t;
capRing = (joins & (64 << end)) != 0 ? smoothstep(-0.55, -0.15, cap) : 0.0;
@@ -300,7 +300,7 @@ ${SM_SLOPE_DRAW}
if (nc >= 19) {
bool full = own.r + own.g + own.b > 0.0;
if (full) C = mix(C, own.rgb, 1.0 - smoothstep(0.36, 0.5, length(fract(w) - 0.5)));
- C = mix(C, tc, 1.0 - smoothstep(2.0 - px, 2.0 + px, tubeSD + 2.0));
+ C = mix(C, tc, 1.0 - smoothstep(2.0 - px, 2.0 + px, screwSD + 2.0));
} else C = tc;
}
else if (nc == 6 || (nc >= 15 && nc <= 18)) {
diff --git a/src/render/world.js b/src/render/world.js
index d8ff782..53b46c8 100644
--- a/src/render/world.js
+++ b/src/render/world.js
@@ -2,12 +2,12 @@
function fogColor(c, k) { return (c & 0xff000000) | (((c >>> 16 & 255) * k) << 16) | (((c >>> 8 & 255) * k) << 8) | ((c & 255) * k); }
// drawWorld: the per-grain pixel buffer (pixel look), also the input of the smooth look.
// ---------- World pixels ----------
-// smooth-look class of a screw tube cell by direction: 9 up, 10 right, 11 down, 12 left
-const TUBE_CLASS = new Uint8Array(15); TUBE_CLASS[1] = 9; TUBE_CLASS[4] = 10; TUBE_CLASS[2] = 11; TUBE_CLASS[3] = 12;
-for (let p = P_TUR; p <= P_TUL; p++) TUBE_CLASS[p] = 19 + p - P_TUR;
+// smooth-look class of a screw conveyor cell by direction: 9 up, 10 right, 11 down, 12 left
+const SCREW_CLASS = new Uint8Array(15); SCREW_CLASS[1] = 9; SCREW_CLASS[4] = 10; SCREW_CLASS[2] = 11; SCREW_CLASS[3] = 12;
+for (let p = P_SCREW_UR; p <= P_SCREW_UL; p++) SCREW_CLASS[p] = 19 + p - P_SCREW_UR;
// Drawing only: the four transport lanes remain in the square block.
let diagonalJoins = new Uint8Array(0);
-function diagonalTubeJoins(bx, by, v, planned = null) {
+function diagonalScrewJoins(bx, by, v, planned = null) {
// Low/high three bits select the two edge exits; bits 6/7 mark free-end collars.
const dy = v === 4 || v === 6 ? -1 : 1;
let joins = 0;
@@ -16,14 +16,14 @@ function diagonalTubeJoins(bx, by, v, planned = null) {
const nb = ny * BW + nx, next = planned && planned.get(nb);
const nc = next ? next[0] : blockP[nb], nv = next ? next[1] : blockV[nb];
if (nx >= 0 && ny >= 0 && nx < BW && ny < BH &&
- nc === BP_TUBE && nv >= 4 && (nv === 4 || nv === 6) === (dy < 0)) continue;
+ nc === BP_SCREW && nv >= 4 && (nv === 4 || nv === 6) === (dy < 0)) continue;
let join = 0;
for (const dir of [side > 0 ? 1 : 3, side * dy > 0 ? 2 : 0]) {
- const x = bx + TUBE_DIRS[dir][0], y = by + TUBE_DIRS[dir][1];
+ const x = bx + SCREW_DIRS[dir][0], y = by + SCREW_DIRS[dir][1];
if (x < 0 || y < 0 || x >= BW || y >= BH) continue;
const n = y * BW + x, next = planned && planned.get(n);
const code = next ? next[0] : blockP[n], nv = next ? next[1] : blockV[n];
- if ((code === BP_TUBE && nv < 4 && (nv & 1) === (dir & 1)) ||
+ if ((code === BP_SCREW && nv < 4 && (nv & 1) === (dir & 1)) ||
((code === BP_BELT || code === BP_FILTER) && (dir & 1))) { join = dir + 1; break; }
}
joins |= join << (end * 3);
@@ -33,13 +33,13 @@ function diagonalTubeJoins(bx, by, v, planned = null) {
}
// Scratch coordinates shared by the pixel shading; no allocations in the grain loop.
let diagonalAcross = 0, diagonalAlong = 0, diagonalCap = 0;
-function diagonalTubeShape(x, y, b, v, joins) {
+function diagonalScrewShape(x, y, b, v, joins) {
const lx = x - (b % BW) * BS - 2, ly = y - ((b / BW) | 0) * BS - 2;
let best = Infinity;
for (let end = 0; end < 2; end++) {
const join = (joins >> (end * 3)) & 7, side = end ? 1 : -1;
- const dx = join ? TUBE_DIRS[join - 1][0] : side * Math.SQRT1_2;
- const dy = join ? TUBE_DIRS[join - 1][1] : side * (v === 4 || v === 6 ? -1 : 1) * Math.SQRT1_2;
+ const dx = join ? SCREW_DIRS[join - 1][0] : side * Math.SQRT1_2;
+ const dy = join ? SCREW_DIRS[join - 1][1] : side * (v === 4 || v === 6 ? -1 : 1) * Math.SQRT1_2;
const along = lx * dx + ly * dy, across = -lx * dy + ly * dx;
const radius = Math.hypot(across, Math.min(0, along)), cap = along - (join ? 2 : 2 * Math.SQRT2);
const sd = Math.max(radius - 2, join || (joins & (64 << end)) ? cap : -10);
@@ -50,9 +50,9 @@ function diagonalTubeShape(x, y, b, v, joins) {
}
return best;
}
-function diagonalTubeBlock(x, y, smooth) {
+function diagonalScrewBlock(x, y, smooth) {
const i = y * W + x, own = (y >> 2) * BW + (x >> 2);
- if (part[i] >= P_TUR && part[i] <= P_TUL) return own;
+ if (part[i] >= P_SCREW_UR && part[i] <= P_SCREW_UL) return own;
if (mat[i] !== EMPTY || part[i] || mach[i] || occ[i] || blockP[own]) return -1;
let best = smooth ? 0.75 : 0, found = -1;
for (let k = 0; k < 4; k++) {
@@ -60,9 +60,9 @@ function diagonalTubeBlock(x, y, smooth) {
const by = (y >> 2) + (k === 2 ? -1 : k === 3 ? 1 : 0);
if (bx < 0 || by < 0 || bx >= BW || by >= BH) continue;
const b = by * BW + bx, v = blockV[b];
- if (blockP[b] !== BP_TUBE || v < 4) continue;
- const joins = diagonalJoins[b] === undefined ? diagonalTubeJoins(bx, by, v) : diagonalJoins[b];
- const sd = diagonalTubeShape(x + 0.5, y + 0.5, b, v, joins);
+ if (blockP[b] !== BP_SCREW || v < 4) continue;
+ const joins = diagonalJoins[b] === undefined ? diagonalScrewJoins(bx, by, v) : diagonalJoins[b];
+ const sd = diagonalScrewShape(x + 0.5, y + 0.5, b, v, joins);
if (sd <= best) { best = sd; found = b; }
}
return found;
@@ -89,7 +89,7 @@ function drawWorld() {
for (let by = Math.max(0, (vy0 >> 2) - 1); by <= Math.min(BH - 1, ((vy0 + bh) >> 2) + 1); by++) {
for (let bx = Math.max(0, (vx0 >> 2) - 1); bx <= Math.min(BW - 1, ((vx0 + bw) >> 2) + 1); bx++) {
const b = by * BW + bx;
- if (blockP[b] === BP_TUBE && blockV[b] >= 4) diagonalJoins[b] = diagonalTubeJoins(bx, by, blockV[b]);
+ if (blockP[b] === BP_SCREW && blockV[b] >= 4) diagonalJoins[b] = diagonalScrewJoins(bx, by, blockV[b]);
}
}
prepareSlopeBelts(vx0, vy0, vx0 + bw, vy0 + bh);
@@ -228,23 +228,23 @@ function drawWorld() {
const sb = slopeBeltAt(i, x, y, sm);
let p = part[i];
if (p === P_FILTER && (sb >= 0 || isSlopeBelt(blockP[slopeSealOwner(i)]))) p = 0;
- const db = diagonalTubeBlock(x, y, sm);
+ const db = diagonalScrewBlock(x, y, sm);
let diagonal = false;
if (db >= 0) {
const v = blockV[db], joins = diagonalJoins[db];
- const sd = diagonalTubeShape(x + 0.5, y + 0.5, db, v, joins);
- p = sm || sd <= 0 ? TUBE_PARTS[v] : 0;
+ const sd = diagonalScrewShape(x + 0.5, y + 0.5, db, v, joins);
+ p = sm || sd <= 0 ? SCREW_PARTS[v] : 0;
diagonal = !!p;
if (sm) tpx[row + bx] = (joins << 24) | ((y - ((db / BW) | 0) * BS + 8) << 16) | ((x - (db % BW) * BS + 8) << 8);
}
- if (p && sm && (IS_TUBE[p] || p === P_FILTER)) { if (m === EMPTY || PH[m] === PH_GAS) { r = 0; g = 0; b = 0; } else { r = Math.max(1, r); } }
+ if (p && sm && (IS_SCREW[p] || p === P_FILTER)) { if (m === EMPTY || PH[m] === PH_GAS) { r = 0; g = 0; b = 0; } else { r = Math.max(1, r); } }
else if (p) {
- if (IS_TUBE[p]) {
+ if (IS_SCREW[p]) {
const on = netSat(i) > 0;
- const vert = p === P_TU || p === P_TD;
+ const vert = p === P_SCREW_U || p === P_SCREW_D;
const axis = diagonal ? Math.floor(diagonalAlong) : vert ? y : x;
const across = diagonal ? diagonalAcross + 2 : vert ? x & 3 : y & 3;
- const dir = diagonal ? (blockV[db] >= 6 ? 1 : -1) : (p === P_TU || p === P_TL) ? 1 : -1;
+ const dir = diagonal ? (blockV[db] >= 6 ? 1 : -1) : (p === P_SCREW_U || p === P_SCREW_L) ? 1 : -1;
if (diagonal ? Math.abs(diagonalAcross) > 1.38 || diagonalCap > -0.55 : across === 0 || across === 3) { // copper casing on the two long sides; its load shows through
if (m === EMPTY) { r = 150; g = 96; b = 64; } else { r = r * 0.72 + 44; g = g * 0.72 + 26; b = b * 0.72 + 14; }
if (!diagonal && (axis & 3) === 0) { r -= 24; g -= 18; b -= 12; }
@@ -305,7 +305,7 @@ function drawWorld() {
// 4 built piece (drawn crisp), 5 smoke and flame, 6 built wall block (drawn as a tile)
let cl = 0;
const pt = (p === P_PIPE && !pipeBodyCell(x, y)) || (p === P_FILTER && sb >= 0) ? 0 : p; // the open parts of a pipe block show what is in them
- if (IS_TUBE[pt]) cl = TUBE_CLASS[pt];
+ if (IS_SCREW[pt]) cl = SCREW_CLASS[pt];
else if (p === P_FILTER && sb < 0) {
// filter: 13 carries left, 14 right; the tree buffer carries its colour tag (first material) and mode
const fb = (y >> 2) * BW + (x >> 2), fc = filterCfg.get(fb), fm = fc && fc.m.length ? fc.m[0] : 0;
@@ -369,7 +369,7 @@ function drawWorld() {
px[sy * bw + sx] = (sm ? 0x04000000 : 0xff000000) | ((b > 255 ? 255 : b | 0) << 16) | ((g > 255 ? 255 : g | 0) << 8) | (r > 255 ? 255 : r | 0);
}
}
- // grains in flight from the vacuum nozzle, a tube or a belt (rain and snow are drawn over the top, see drawRain)
+ // grains in flight from the vacuum nozzle, a screw or a belt (rain and snow are drawn over the top, see drawRain)
if (!lens) for (const f of flyers) {
if (f.rain || (useFog && fogSeen(f.x, f.y) < 64)) continue;
const sx = Math.floor(f.x) - vx0, sy = Math.floor(f.y) - vy0;
diff --git a/src/sim/blocks.js b/src/sim/blocks.js
index 0b2f81f..0b93eb3 100644
--- a/src/sim/blocks.js
+++ b/src/sim/blocks.js
@@ -44,7 +44,7 @@ function canPlaceBlock(bx, by, code = BP_WALL) {
}
return '';
}
-// v: wall material (loose id) for walls, direction for belts and tubes
+// v: wall material (loose id) for walls, direction for belts and screws
function placeBlock(code, v, bx, by, free, extra) {
const err = canPlaceBlock(bx, by, code); if (err) return err;
if (isWall(code) && !WALL_OF[v]) return 'Pick a wall material with R';
@@ -61,7 +61,7 @@ function placeBlock(code, v, bx, by, free, extra) {
else if (isProcCode(code)) setCell(i, code === BP_GRIND ? GRINDER : code === BP_PRESS ? PRESSB : SAWB, AMB[y], 0);
else if (code === BP_GATE) setCell(i, GATE, AMB[y], 0);
else if (code === BP_GRATE) { part[i] = P_GRATE; wakeXY(x, y); }
- else if (code === BP_TUBE) { part[i] = TUBE_PARTS[v & 7]; markPow(i, true); wakeXY(x, y); }
+ else if (code === BP_SCREW) { part[i] = SCREW_PARTS[v & 7]; markPow(i, true); wakeXY(x, y); }
else if (isFilter(code)) { part[i] = P_FILTER; wakeXY(x, y); }
else if (code === BP_PIPE) { part[i] = P_PIPE; wakeXY(x, y); }
else if (isPipeCode(code)) setCell(i, PIPE, AMB[y], 0);
@@ -99,7 +99,7 @@ function blockIntegrity(b) {
else if (isProcCode(code)) { if (m === (code === BP_GRIND ? GRINDER : code === BP_PRESS ? PRESSB : SAWB)) n++; }
else if (code === BP_GATE) { if (m === GATE || p === P_GOPEN) n++; }
else if (code === BP_GRATE) { if (p === P_GRATE) n++; }
- else if (code === BP_TUBE) { if (IS_TUBE[p]) n++; }
+ else if (code === BP_SCREW) { if (IS_SCREW[p]) n++; }
else if (isFilter(code)) { if (p === P_FILTER) n++; }
else if (isPipeCode(code)) { if (m === PIPE || p === P_PIPE) n++; }
}
diff --git a/src/sim/blueprints.js b/src/sim/blueprints.js
index ef1f061..67e9076 100644
--- a/src/sim/blueprints.js
+++ b/src/sim/blueprints.js
@@ -23,7 +23,7 @@ function copyRegion(x0, y0, x1, y1) {
}
function flipBlueprint(bp) {
const bw = bp.bw;
- const flipV = (code, v) => isBelt(code) ? 1 - v : isFilter(code) ? v ^ 128 : code === BP_TUBE ? [0, 3, 2, 1, 7, 6, 5, 4][v & 7]
+ const flipV = (code, v) => isBelt(code) ? 1 - v : isFilter(code) ? v ^ 128 : code === BP_SCREW ? [0, 3, 2, 1, 7, 6, 5, 4][v & 7]
: code === BP_PUMP || code === BP_OUTLET ? (((v & 3) === 1 ? 3 : (v & 3) === 3 ? 1 : v & 3) | (v & 4)) : v;
return {
v: 2, bw, bh: bp.bh, name: bp.name,
diff --git a/src/sim/cells.js b/src/sim/cells.js
index ba99f20..0b5c731 100644
--- a/src/sim/cells.js
+++ b/src/sim/cells.js
@@ -39,7 +39,7 @@ function enter(m, ph, j, dx, dy) {
if (occ[j]) return 0;
const p = part[j];
if (p !== 0) {
- if (IS_TUBE[p]) { if (ph === PH_POWDER && TDX[p] === -dx && TDY[p] === -dy) return 0; } // liquids and gases flow through freely
+ if (IS_SCREW[p]) { if (ph === PH_POWDER && SCREW_DX[p] === -dx && SCREW_DY[p] === -dy) return 0; } // liquids and gases flow through freely
else if (p === P_GRATE) { if (ph === PH_POWDER || m === MSLAG) return 0; } // thick slag does not run through a grate
else if (p === P_INTAKE) { if (ph !== PH_GAS) return 0; }
else if (p === P_FILTER) { if (ph === PH_POWDER && !filterPassAt(m, j)) return 0; }
@@ -93,7 +93,7 @@ function passable(m, ph, j, dx, dy) {
if (occ[j]) return false;
const p = part[j];
if (p !== 0) {
- if (IS_TUBE[p]) { if (ph === PH_POWDER && TDX[p] === -dx && TDY[p] === -dy) return false; }
+ if (IS_SCREW[p]) { if (ph === PH_POWDER && SCREW_DX[p] === -dx && SCREW_DY[p] === -dy) return false; }
else if (p === P_GRATE) { if (ph === PH_POWDER || m === MSLAG) return false; }
else if (p === P_INTAKE) { if (ph !== PH_GAS) return false; }
else if (p === P_FILTER) { if (ph === PH_POWDER && !filterPassAt(m, j)) return false; }
@@ -199,7 +199,7 @@ function simMove() {
if (stamp[i] === tickStamp) continue;
if (ph === PH_SOLID) continue;
const p = part[i];
- if (IS_TUBE[p] && ph === PH_POWDER) continue; // a screw tube moves its own grains
+ if (IS_SCREW[p] && ph === PH_POWDER) continue; // a screw conveyor moves its own grains
if (ph === PH_POWDER) powder(i, x, y, m); else if (ph === PH_LIQUID) liquid(i, x, y, m); else gas(i, x, y, m);
}
}
diff --git a/src/sim/conveyors.js b/src/sim/conveyors.js
index a9c9b88..8180bd3 100644
--- a/src/sim/conveyors.js
+++ b/src/sim/conveyors.js
@@ -1,4 +1,4 @@
-// How belts, filters and screw tubes move what is on or in them.
+// How belts, filters and screw conveyors move what is on or in them.
// What rests on a belt or filter rides along: the whole stack standing on the carrying surface
// steps one cell toward d together (belts run front of the line first, so the way ahead is
// clear). If a powder grain is blocked, the load above it stands still and the belt slips
@@ -70,7 +70,7 @@ function beltDrain(b) {
if (!passable(mat[a], PH_LIQUID, d, 0, 1)) return;
move(a, d, x, (a / W) | 0, x, (d / W) | 0);
}
-// Take a grain out of cell i and send it flying from (x, y): what leaves a belt or a tube mouth
+// Take a grain out of cell i and send it flying from (x, y): what leaves a belt or a screw mouth
function launchGrain(i, x, y, vx, vy) {
const m = mat[i], T = temp[i];
mat[i] = EMPTY; flags[i] = 0; life[i] = 0; stamp[i] = tickStamp;
@@ -145,8 +145,8 @@ function filterRun(b) {
// A cell a grain can be pushed into: empty, or liquid or gas that simply trades places with it
function roomFor(j) { const t = mat[j]; return t === EMPTY || PH[t] === PH_GAS || PH[t] === PH_LIQUID; }
-// Cross one tube block, far lanes first; corners retain their liquid/gas swaps.
-function tubeSpread(c, x, y, sx, sy, intake = false) {
+// Cross one screw block, far lanes first; corners retain their liquid/gas swaps.
+function screwSpread(c, x, y, sx, sy, intake = false) {
const p = part[c], bx = x >> 2, by = y >> 2;
for (;;) {
const nx = x + sx, ny = y + sy;
@@ -158,8 +158,8 @@ function tubeSpread(c, x, y, sx, sy, intake = false) {
return c;
}
// An empty screw cell draws in loose material from behind it or from its sides
-// (not from the cell it pushes into), so a tube end sitting in a pile feeds itself.
-function tubeIntake(i, p, dx, dy) {
+// (not from the cell it pushes into), so a screw end sitting in a pile feeds itself.
+function screwIntake(i, p, dx, dy) {
if (occ[i]) return;
const x = i % W, y = (i / W) | 0;
const start = (tick + i) % 3;
@@ -175,28 +175,28 @@ function tubeIntake(i, p, dx, dy) {
if (PH[m] !== PH_POWDER) continue; // the screw only takes loose material; liquids just flow through
if (stamp[i] === tickStamp) return;
const pj = part[j];
- if (IS_TUBE[pj]) continue; // other tube cells move their own contents
+ if (IS_SCREW[pj]) continue; // other screw cells move their own contents
if (pj === P_GRATE || pj === P_FILTER) continue;
move(j, i, nx, ny, x, y);
- if (diagonal) tubeSpread(i, x, y, 0, (y & 3) < 2 ? 1 : -1, true);
- else if (ox !== -dx || oy !== -dy) tubeSpread(i, x, y, -ox, -oy, true);
+ if (diagonal) screwSpread(i, x, y, 0, (y & 3) < 2 ? 1 : -1, true);
+ else if (ox !== -dx || oy !== -dy) screwSpread(i, x, y, -ox, -oy, true);
return;
}
}
// Four lanes cross a block in four beats; the casing carries every lane across the corner.
// Prefer the next diagonal block, then either straight join, then the open head.
-function diagonalTubeNext(i, p) {
- const x = i % W, y = (i / W) | 0, dx = TDX[p], dy = TDY[p];
+function diagonalScrewNext(i, p) {
+ const x = i % W, y = (i / W) | 0, dx = SCREW_DX[p], dy = SCREW_DY[p];
const xx = x + dx, bx = x & ~3, by = y & ~3;
if (xx < 0 || xx >= W) return -1;
if ((xx >> 2) === (x >> 2)) return i + dx;
const yy = y + dy * BS;
- if (yy >= 0 && yy < H && IS_TUBE[part[yy * W + xx]]) return yy * W + xx;
- if (IS_TUBE[part[y * W + xx]]) return y * W + xx;
+ if (yy >= 0 && yy < H && IS_SCREW[part[yy * W + xx]]) return yy * W + xx;
+ if (IS_SCREW[part[y * W + xx]]) return y * W + xx;
if (dy > 0 && yy < H && (mat[yy * W + xx] === BELTL || mat[yy * W + xx] === BELTR)) return ((yy & ~3) - 1) * W + xx;
const hy = by + (dy > 0 ? BS : -1), hx = bx + (y & 3);
- if (hy >= 0 && hy < H && IS_TUBE[part[hy * W + hx]]) return hy * W + hx;
+ if (hy >= 0 && hy < H && IS_SCREW[part[hy * W + hx]]) return hy * W + hx;
// An open mouth discharges along the diagonal, clear of the last block.
return yy >= 0 && yy < H ? yy * W + xx : -1;
}
diff --git a/src/sim/items.js b/src/sim/items.js
index 116f20e..d0e478e 100644
--- a/src/sim/items.js
+++ b/src/sim/items.js
@@ -5,7 +5,7 @@
// its cells as solid (occ).
const BAR_W = 4, BAR_H = 2;
let entCurM = 0; // the bar being moved, so a filter can let its own kind of bar through
-function entSolidCell(j) { const m = mat[j], p = part[j]; return occ[j] || PH[m] === PH_SOLID || PH[m] === PH_POWDER || (p !== 0 && (IS_TUBE[p] || p === P_GRATE || (p === P_FILTER && !filterPassAt(entCurM, j)))); }
+function entSolidCell(j) { const m = mat[j], p = part[j]; return occ[j] || PH[m] === PH_SOLID || PH[m] === PH_POWDER || (p !== 0 && (IS_SCREW[p] || p === P_GRATE || (p === P_FILTER && !filterPassAt(entCurM, j)))); }
function entSupportCount(x, y) {
if (y + BAR_H >= H) return BAR_W;
let n = 0;
@@ -38,97 +38,97 @@ function spawnBar(m, x, y, vx, vy, T) {
return e;
}
function removeEnt(e) { entMark(e, 0); const k = ents.indexOf(e); if (k >= 0) ents.splice(k, 1); }
-// No transient tube state: even a loaded bar derives its ride from its eight cells.
-function entTubeAt(x, y) {
+// No transient screw state: even a loaded bar derives its ride from its eight cells.
+function entScrewAt(x, y) {
if (x < 0 || y < 0 || x + BAR_W > W || y + BAR_H > H) return 0;
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) {
- const p = part[yy * W + xx]; if (!IS_TUBE[p]) return 0;
+ const p = part[yy * W + xx]; if (!IS_SCREW[p]) return 0;
}
return part[(y + 1) * W + x + 1];
}
-function entTubeFree(x, y, tubeOnly = false) {
+function entScrewFree(x, y, screwOnly = false) {
if (x < 0 || y < 0 || x + BAR_W > W || y + BAR_H > H) return false;
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) {
- const j = yy * W + xx, p = part[j], tube = IS_TUBE[p];
- // gas and liquid don't stop a bar, in a tube or in the open
- if (occ[j] || (mat[j] !== EMPTY && PH[mat[j]] !== PH_GAS && PH[mat[j]] !== PH_LIQUID) || (tubeOnly ? !tube : !tube && entSolidCell(j))) return false;
+ const j = yy * W + xx, p = part[j], screw = IS_SCREW[p];
+ // gas and liquid don't stop a bar, in a screw or in the open
+ if (occ[j] || (mat[j] !== EMPTY && PH[mat[j]] !== PH_GAS && PH[mat[j]] !== PH_LIQUID) || (screwOnly ? !screw : !screw && entSolidCell(j))) return false;
}
return true;
}
// Check the swept rectangle too: a snap must never jump through a grain or another bar.
-function entTubeMove(e, x, y, tubeOnly = false, mouth = false) {
- if (!entTubeFree(x, y, tubeOnly)) return false;
+function entScrewMove(e, x, y, screwOnly = false, mouth = false) {
+ if (!entScrewFree(x, y, screwOnly)) return false;
if (mouth) {
// Go around the receiving corner, checking both legs before moving.
const acrossY = y > e.iy ? e.iy : y, downX = y > e.iy ? x : e.ix;
- for (let yy = Math.min(e.iy, y); yy <= Math.max(e.iy, y); yy++) if (!entTubeFree(downX, yy)) return false;
- for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entTubeFree(xx, acrossY)) return false;
+ for (let yy = Math.min(e.iy, y); yy <= Math.max(e.iy, y); yy++) if (!entScrewFree(downX, yy)) return false;
+ for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entScrewFree(xx, acrossY)) return false;
} else for (let yy = Math.min(e.iy, y); yy <= Math.max(e.iy, y); yy++)
- for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entTubeFree(xx, yy)) return false;
+ for (let xx = Math.min(e.ix, x); xx <= Math.max(e.ix, x); xx++) if (!entScrewFree(xx, yy)) return false;
e.x = e.ix = x; e.y = e.iy = y; e.vx = e.vy = 0; return true;
}
-function entTubeLane(y, iy) { return Math.max(y & ~3, Math.min((y & ~3) + 2, iy)); }
-function entTubeEdge(e, dx, dy, k) {
+function entScrewLane(y, iy) { return Math.max(y & ~3, Math.min((y & ~3) + 2, iy)); }
+function entScrewEdge(e, dx, dy, k) {
const x = e.ix + (dx > 0 ? BAR_W : dx < 0 ? -1 : k), y = e.iy + (dy > 0 ? BAR_H : dy < 0 ? -1 : k);
return x < 0 || y < 0 || x >= W || y >= H ? -1 : y * W + x;
}
-function entTubeEnter(e, dx, dy, mouth = false) {
+function entScrewEnter(e, dx, dy, mouth = false) {
for (let k = 0; k < (dx ? BAR_H : BAR_W); k++) {
- const i = entTubeEdge(e, dx, dy, k);
+ const i = entScrewEdge(e, dx, dy, k);
if (i < 0) continue;
const xx = i % W, yy = (i / W) | 0;
- const p = part[i]; if (!IS_TUBE[p] || (!dx && TDX[p]) || dx * TDX[p] + dy * TDY[p] < 0) continue;
+ const p = part[i]; if (!IS_SCREW[p] || (!dx && SCREW_DX[p]) || dx * SCREW_DX[p] + dy * SCREW_DY[p] < 0) continue;
// Vertical screws require the full width; horizontal screws use the nearest two-row lane.
- const x = (xx & ~3), y = entTubeLane(yy, e.iy);
- if (entTubeMove(e, x, TDX[p] || dy ? y : e.iy, true, mouth)) return true;
+ const x = (xx & ~3), y = entScrewLane(yy, e.iy);
+ if (entScrewMove(e, x, SCREW_DX[p] || dy ? y : e.iy, true, mouth)) return true;
}
return false;
}
-function entTubeTick(e, p) {
+function entScrewTick(e, p) {
e.vx = e.vy = 0; e.x = e.ix; e.y = e.iy;
const ci = (e.iy + 1) * W + e.ix + 1;
if ((tick & 1) || netSat(ci) <= 0 || rnd() > netSat(ci)) return;
- const dx = TDX[p], dy = TDY[p];
- if (dx && dy) { entDiagonalTubeTick(e, p); return; }
+ const dx = SCREW_DX[p], dy = SCREW_DY[p];
+ if (dx && dy) { entDiagonalScrewTick(e, p); return; }
let x = e.ix + dx, y = e.iy + dy, next = 0, ax = 0, ay = 0;
for (let k = 0; k < (dx ? BAR_H : BAR_W); k++) {
- const i = entTubeEdge(e, dx, dy, k);
+ const i = entScrewEdge(e, dx, dy, k);
if (i < 0) return;
const xx = i % W, yy = (i / W) | 0;
const q = part[i];
- if (IS_TUBE[q] && q !== p) { next = q; ax = xx; ay = yy; break; }
+ if (IS_SCREW[q] && q !== p) { next = q; ax = xx; ay = yy; break; }
}
if (next) {
- if (TDX[next] === -dx && TDY[next] === -dy) return;
- x = ax & ~3; y = entTubeLane(ay, e.iy);
- if (entTubeMove(e, x, y, true)) stats.moved++;
+ if (SCREW_DX[next] === -dx && SCREW_DY[next] === -dy) return;
+ x = ax & ~3; y = entScrewLane(ay, e.iy);
+ if (entScrewMove(e, x, y, true)) stats.moved++;
return;
}
- if (entTubeAt(x, y)) { if (entTubeMove(e, x, y, true)) stats.moved++; return; }
+ if (entScrewAt(x, y)) { if (entScrewMove(e, x, y, true)) stats.moved++; return; }
// Clear the mouth in one move so the next tick can derive ordinary falling from position.
x = e.ix + dx * BAR_W; y = e.iy + dy * BAR_H;
- if (!entTubeFree(x, y) || entTubeAt(x, y)) return;
+ if (!entScrewFree(x, y) || entScrewAt(x, y)) return;
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) if (part[yy * W + xx]) return;
- if (entTubeMove(e, x, y)) { e.vx = dx ? dx * 0.5 : (rnd() - 0.5) * 0.4; e.vy = dy * 0.6; stats.moved++; }
+ if (entScrewMove(e, x, y)) { e.vx = dx ? dx * 0.5 : (rnd() - 0.5) * 0.4; e.vy = dy * 0.6; stats.moved++; }
}
// A horizontal bar stays whole: one diagonal block per four screw beats.
-function entDiagonalTubeTick(e, p) {
+function entDiagonalScrewTick(e, p) {
if (tick & 7) return;
- const dx = TDX[p], dy = TDY[p], bx = e.ix & ~3, by = e.iy & ~3, lane = e.iy & 3;
+ const dx = SCREW_DX[p], dy = SCREW_DY[p], bx = e.ix & ~3, by = e.iy & ~3, lane = e.iy & 3;
for (const [ox, oy] of [[dx, dy], [dx, 0], [0, dy]]) {
- const x = bx + ox * BS, y = by + oy * BS + lane, q = entTubeAt(x, y);
+ const x = bx + ox * BS, y = by + oy * BS + lane, q = entScrewAt(x, y);
if (!q) continue;
- if (TDX[q] * dx + TDY[q] * dy < 0) return;
- if (entTubeMove(e, x, y, true)) stats.moved++;
+ if (SCREW_DX[q] * dx + SCREW_DY[q] * dy < 0) return;
+ if (entScrewMove(e, x, y, true)) stats.moved++;
return;
}
const x = bx + dx * BS;
let y = by + dy * BS + lane;
const belt = y >= 0 && y < H ? mat[y * W + x] : EMPTY;
if (dy > 0 && (belt === BELTL || belt === BELTR)) y = (y & ~3) - BAR_H;
- if (!entTubeFree(x, y)) return;
+ if (!entScrewFree(x, y)) return;
for (let yy = y; yy < y + BAR_H; yy++) for (let xx = x; xx < x + BAR_W; xx++) if (part[yy * W + xx]) return;
- if (entTubeMove(e, x, y, false, dy < 0)) { e.vx = dx * 0.5; e.vy = dy * 0.6; stats.moved++; }
+ if (entScrewMove(e, x, y, false, dy < 0)) { e.vx = dx * 0.5; e.vy = dy * 0.6; stats.moved++; }
}
// Bars stay level; their highest contact follows the face one grain per belt beat.
function entSlopeBeltTick(e) {
@@ -158,7 +158,7 @@ function entSlopeBeltTick(e) {
if (tick & 1) return true;
const y = face === H ? e.iy : face - BAR_H, x = e.ix + d;
if (entFreeAt(x, y)) { e.vx = d * 0.5; e.vy = (y - e.iy) * 0.5; e.x = e.ix = x; e.y = e.iy = y; stats.moved++; }
- else if (!entTubeEnter(e, d, 0)) entTubeEnter(e, d, Math.sign(y - e.iy), true);
+ else if (!entScrewEnter(e, d, 0)) entScrewEnter(e, d, Math.sign(y - e.iy), true);
return true;
}
function entTick() {
@@ -167,8 +167,8 @@ function entTick() {
entMark(e, 0);
entCurM = e.m;
let ix = e.ix; const iy = e.iy;
- const tube = entTubeAt(ix, iy);
- if (tube) entTubeTick(e, tube);
+ const screw = entScrewAt(ix, iy);
+ if (screw) entScrewTick(e, screw);
else if (!entSlopeBeltTick(e)) {
// what is underneath?
let belt = 0, bsat = 0, transport = false, support = iy + BAR_H >= H;
@@ -205,10 +205,10 @@ function entTick() {
e.x += e.vx; e.y += e.vy;
// step toward the new rounded position one cell at a time
let tx = Math.round(e.x), ty = Math.round(e.y), cx = ix, cy = iy;
- while (cx !== tx) { const s = Math.sign(tx - cx); if (entFreeAt(cx + s, cy)) cx += s; else if (belt && entTubeEnter(e, s, 0)) { cx = e.ix; cy = e.iy; e.x = cx; e.y = cy; break; } else { e.vx = 0; e.x = cx; break; } }
- while (cy !== ty) { const s = Math.sign(ty - cy); if (entTubeAt(cx, cy)) break; if (entFreeAt(cx, cy + s)) cy += s; else if (entTubeEnter(e, 0, s)) { cx = e.ix; cy = e.iy; break; } else { e.vy = 0; e.y = cy; break; } }
- if (support && !belt && entTubeEnter(e, 0, 1)) { cx = e.ix; cy = e.iy; }
- if (!entTubeAt(cx, cy) && !entFreeAt(cx, cy)) {
+ while (cx !== tx) { const s = Math.sign(tx - cx); if (entFreeAt(cx + s, cy)) cx += s; else if (belt && entScrewEnter(e, s, 0)) { cx = e.ix; cy = e.iy; e.x = cx; e.y = cy; break; } else { e.vx = 0; e.x = cx; break; } }
+ while (cy !== ty) { const s = Math.sign(ty - cy); if (entScrewAt(cx, cy)) break; if (entFreeAt(cx, cy + s)) cy += s; else if (entScrewEnter(e, 0, s)) { cx = e.ix; cy = e.iy; break; } else { e.vy = 0; e.y = cy; break; } }
+ if (support && !belt && entScrewEnter(e, 0, 1)) { cx = e.ix; cy = e.iy; }
+ if (!entScrewAt(cx, cy) && !entFreeAt(cx, cy)) {
// something moved into us (a grain fell in): try to step up out of it
let freed = false;
for (let u = 1; u < 6; u++) if (entFreeAt(cx, cy - u)) { cy -= u; e.y = cy; freed = true; break; }
diff --git a/src/sim/machine-tick.js b/src/sim/machine-tick.js
index b600b61..1b11019 100644
--- a/src/sim/machine-tick.js
+++ b/src/sim/machine-tick.js
@@ -1,4 +1,4 @@
-// Per-tick machine work: steam engines, fans, belts, filters, screw tubes (machinesTick).
+// Per-tick machine work: steam engines, fans, belts, filters, screw conveyors (machinesTick).
function engineIntake(mm) {
for (const i of mm.intake) {
if (mat[i] === STEAM) {
@@ -69,7 +69,7 @@ function machinesTick() {
for (let n = 1; n < nets.length; n++) {
const net = nets[n];
let s = 0; for (const p of net.prods) s += p.out;
- let d = net.nb * BELT_POWER + net.nt * TUBE_POWER;
+ let d = net.nb * BELT_POWER + net.nt * SCREW_POWER;
for (const f of net.fans) if (f.on) d += FAN_POWER;
net.supply = s; net.demand = d;
net.sat = POWER_FREE ? 1 : d > 0 ? Math.min(1, s / d) : 0;
@@ -91,16 +91,16 @@ function machinesTick() {
if (filtersDirty) rebuildFilters();
carryBeltsAndFilters(beltL, filterL, -1, BELTL, (tick & 1) === 0);
carryBeltsAndFilters(beltR, filterR, 1, BELTR, (tick & 1) === 0);
- // tubes: every loose grain inside steps one cell along, front of the line first, on the same
+ // screws: every loose grain inside steps one cell along, front of the line first, on the same
// beat as belts (every other tick), so a block-wide screw carries as much as a block-deep belt
- if ((tick & 1) === 0) for (let p = 1; p < IS_TUBE.length; p++) {
- if (!IS_TUBE[p]) continue;
- const list = tubeLists[p], dx = TDX[p], dy = TDY[p], dj = dy * W + dx, flat = dy === 0;
+ if ((tick & 1) === 0) for (let p = 1; p < IS_SCREW.length; p++) {
+ if (!IS_SCREW[p]) continue;
+ const list = screwLists[p], dx = SCREW_DX[p], dy = SCREW_DY[p], dj = dy * W + dx, flat = dy === 0;
for (let k = 0; k < list.length; k++) {
const i = list[k];
if (part[i] !== p || occ[i]) continue;
const m = mat[i];
- if (m === EMPTY || PH[m] !== PH_POWDER) { if (m === EMPTY || PH[m] !== PH_SOLID) tubeIntake(i, p, dx, dy); continue; }
+ if (m === EMPTY || PH[m] !== PH_POWDER) { if (m === EMPTY || PH[m] !== PH_SOLID) screwIntake(i, p, dx, dy); continue; }
if (stamp[i] === tickStamp) continue;
if (temp[i] > 1000 && rnd() < 0.02) {
part[i] = 0; markPow(i, false); emit({ t: 'warn', x: i % W, y: (i / W) | 0, text: 'melted' }); wakeIdx(i);
@@ -128,26 +128,26 @@ function machinesTick() {
if (shifted) continue;
}
if (x + dx < 0 || x + dx >= W || y + dy < 0 || y + dy >= H) continue;
- const dest = dx && dy ? diagonalTubeNext(i, p) : i + dj;
+ const dest = dx && dy ? diagonalScrewNext(i, p) : i + dj;
if (dest < 0) continue;
const nx = dest % W, ny = (dest / W) | 0;
if (nx < 0 || ny < 0 || nx >= W || ny >= H) continue;
const j = dest;
if (occ[j]) continue;
- const pj = part[j], tube = IS_TUBE[pj];
- if (tube && TDX[pj] === -dx && TDY[pj] === -dy) continue;
+ const pj = part[j], screw = IS_SCREW[pj];
+ if (screw && SCREW_DX[pj] === -dx && SCREW_DY[pj] === -dy) continue;
const t = mat[j];
if (!roomFor(j)) continue;
if (pj === P_GRATE || pj === P_INTAKE || (pj === P_FILTER && !filterPassAt(m, j))) continue;
- if (!tube && !pj && t === EMPTY) {
+ if (!screw && !pj && t === EMPTY) {
// out of the mouth: the screw spits it clear, so the next grain is never blocked by the last
launchGrain(i, nx, ny, dy && !dx ? (rnd() - 0.5) * (dy < 0 ? 0.9 : 0.1) : dx * (0.42 + rnd() * 0.16), dy ? dy * (0.5 + rnd() * 0.2) : rnd() * 0.08);
stats.moved++; continue;
}
move(i, j, x, y, nx, ny); stats.moved++;
- if (tube && pj !== p) {
- // turning a corner: keep going across the new tube's width so every lane fills
- tubeSpread(j, nx, ny, dx, dy);
+ if (screw && pj !== p) {
+ // turning a corner: keep going across the new screw's width so every lane fills
+ screwSpread(j, nx, ny, dx, dy);
}
}
}
diff --git a/src/sim/machines.js b/src/sim/machines.js
index 5fd1594..6ed66d9 100644
--- a/src/sim/machines.js
+++ b/src/sim/machines.js
@@ -10,7 +10,7 @@ let nextPowerBuild = 0;
function rebuildPower() {
powerDirty = false;
for (const i of pcells) netId[i] = 0;
- nets = [null]; beltL = []; beltR = []; tubeLists = Array.from({ length: IS_TUBE.length }, () => []);
+ nets = [null]; beltL = []; beltR = []; screwLists = Array.from({ length: IS_SCREW.length }, () => []);
const stack = [];
for (const s of pcells) {
if (netId[s]) continue;
@@ -23,11 +23,11 @@ function rebuildPower() {
const i = stack.pop();
const m = mat[i], p = part[i];
if (m === BELTL) { net.nb++; beltL.push(i); } else if (m === BELTR) { net.nb++; beltR.push(i); }
- if (IS_TUBE[p]) { net.nt++; tubeLists[p].push(i); }
+ if (IS_SCREW[p]) { net.nt++; screwLists[p].push(i); }
const x = i % W, y = (i / W) | 0;
// Corner-touching screws share power, without connecting unrelated diagonal machinery.
- if (IS_TUBE[p] && TDX[p] && TDY[p]) for (let d = -1; d <= 1; d += 2) {
- const xx = x + d * TDX[p], yy = y + d * TDY[p], j = yy * W + xx;
+ if (IS_SCREW[p] && SCREW_DX[p] && SCREW_DY[p]) for (let d = -1; d <= 1; d += 2) {
+ const xx = x + d * SCREW_DX[p], yy = y + d * SCREW_DY[p], j = yy * W + xx;
if (xx >= 0 && xx < W && yy >= 0 && yy < H && part[j] === p && isPow[j] && !netId[j]) { netId[j] = id; stack.push(j); }
}
let j;
@@ -45,10 +45,10 @@ function rebuildPower() {
if (mm.type === 'fan') net.fans.push(mm); else if (mm.type === 'wheel' || mm.type === 'engine') net.prods.push(mm);
}
beltL.sort((a, b) => (a % W) - (b % W) || a - b); beltR.sort((a, b) => (b % W) - (a % W) || b - a);
- for (const p of TUBE_PARTS) {
- tubeLists[p].sort((a, b) => {
- if (TDX[p] && TDY[p]) return ((b % W) - (a % W)) * TDX[p];
- return (TDY[p] || TDX[p]) < 0 ? a - b : b - a;
+ for (const p of SCREW_PARTS) {
+ screwLists[p].sort((a, b) => {
+ if (SCREW_DX[p] && SCREW_DY[p]) return ((b % W) - (a % W)) * SCREW_DX[p];
+ return (SCREW_DY[p] || SCREW_DX[p]) < 0 ? a - b : b - a;
});
}
}
diff --git a/src/sim/material-tables.js b/src/sim/material-tables.js
index 12a9181..4a7dee0 100644
--- a/src/sim/material-tables.js
+++ b/src/sim/material-tables.js
@@ -28,12 +28,12 @@ const SOLIDMETAL = new Uint8Array(NMAT); for (const m of [COPPER, IRON, STEEL, T
// ---------- Flags & parts ----------
const F_NAT = 1, F_CAST = 2, F_BURN = 4;
-const P_NONE = 0, P_TU = 1, P_TD = 2, P_TL = 3, P_TR = 4, P_GRATE = 5, P_GOPEN = 6, P_WHEEL = 7, P_INTAKE = 8, P_FILTER = 9, P_PIPE = 10, P_TUR = 11, P_TDR = 12, P_TDL = 13, P_TUL = 14;
-const TUBE_PARTS = [P_TU, P_TR, P_TD, P_TL, P_TUR, P_TDR, P_TDL, P_TUL];
-const TUBE_DIRS = [[0, -1], [1, 0], [0, 1], [-1, 0], [1, -1], [1, 1], [-1, 1], [-1, -1]];
-const IS_TUBE = new Uint8Array(15), TDX = new Int8Array(15), TDY = new Int8Array(15);
-for (let v = 0; v < TUBE_PARTS.length; v++) {
- const p = TUBE_PARTS[v]; IS_TUBE[p] = 1; TDX[p] = TUBE_DIRS[v][0]; TDY[p] = TUBE_DIRS[v][1];
+const P_NONE = 0, P_SCREW_U = 1, P_SCREW_D = 2, P_SCREW_L = 3, P_SCREW_R = 4, P_GRATE = 5, P_GOPEN = 6, P_WHEEL = 7, P_INTAKE = 8, P_FILTER = 9, P_PIPE = 10, P_SCREW_UR = 11, P_SCREW_DR = 12, P_SCREW_DL = 13, P_SCREW_UL = 14;
+const SCREW_PARTS = [P_SCREW_U, P_SCREW_R, P_SCREW_D, P_SCREW_L, P_SCREW_UR, P_SCREW_DR, P_SCREW_DL, P_SCREW_UL];
+const SCREW_DIRS = [[0, -1], [1, 0], [0, 1], [-1, 0], [1, -1], [1, 1], [-1, 1], [-1, -1]];
+const IS_SCREW = new Uint8Array(15), SCREW_DX = new Int8Array(15), SCREW_DY = new Int8Array(15);
+for (let v = 0; v < SCREW_PARTS.length; v++) {
+ const p = SCREW_PARTS[v]; IS_SCREW[p] = 1; SCREW_DX[p] = SCREW_DIRS[v][0]; SCREW_DY[p] = SCREW_DIRS[v][1];
}
// ---------- Tool tiers ----------
diff --git a/src/sim/materials.js b/src/sim/materials.js
index bc671e0..b1b04ee 100644
--- a/src/sim/materials.js
+++ b/src/sim/materials.js
@@ -46,7 +46,7 @@ def(SLAG, { name: 'Slag', ph: PH_POWDER, col: [76, 66, 64], v: 34, dens: 2.4, k:
def(MSLAG, { name: 'Molten slag', ph: PH_LIQUID, col: [232, 112, 40], v: 16, dens: 3.6, k: 0.02, cap: 1.2, hard: 0, freeze: 1000, freezeTo: SLAG, fluid: 0.5, spread: 10, liqT: 1150, desc: 'Smelting waste. Trickles down through the charge but is too thick to pass a grate, so it pools there and sets into slag. Knocking out the mold below rakes it away.' });
def(BRICK, { name: 'Brick', col: [168, 76, 56], v: 16, k: 0.004, hard: 3, melt: 1750, meltTo: MAGMA, yld: RBRICK, desc: 'Fired clay. Insulates and survives 1750°C.' });
def(GLASS, { name: 'Glass', col: [178, 216, 224], v: 6, k: 0.006, hard: 2, melt: 1300, meltTo: MGL, alpha: 0.45, yld: CULLET, desc: 'Clear. Lets light through.' });
-def(COPPER, { name: 'Copper', col: [200, 122, 76], v: 8, k: 0.04, cap: 1.5, hard: 4, melt: 1085, meltTo: MCU, yld: SCRCU, desc: 'Conducts heat well. Belts, tubes, fans, engines.' });
+def(COPPER, { name: 'Copper', col: [200, 122, 76], v: 8, k: 0.04, cap: 1.5, hard: 4, melt: 1085, meltTo: MCU, yld: SCRCU, desc: 'Conducts heat well. Belts, screw conveyors, fans, engines.' });
def(IRON, { name: 'Iron', col: [128, 131, 138], v: 8, k: 0.035, cap: 1.5, hard: 5, melt: 1538, meltTo: MFE, yld: SCRFE, desc: 'Strong metal. Melts at 1538°C.' });
def(STEEL, { name: 'Steel', col: [172, 180, 192], v: 6, k: 0.035, cap: 1.5, hard: 6, melt: 1450, meltTo: MST, yld: SCRST, desc: 'Iron refined by blowing air through it while molten. The best bit material.' });
def(WATER, { name: 'Water', ph: PH_LIQUID, col: [44, 100, 194], v: 6, dens: 1, k: 0.02, cap: 4, hard: 0, boil: 100, boilTo: STEAM, fluid: 1, spread: 48, alpha: 0.78, liqT: 20, bag: true, desc: 'Boils at 100°C. Steam that rises into the open sky comes back down as rain.' });
@@ -77,7 +77,7 @@ def(CULLET, { name: 'Glass shards', ph: PH_POWDER, col: [196, 228, 236], v: 22,
def(SCRCU, { name: 'Copper scrap', ph: PH_POWDER, col: [192, 118, 74], v: 36, dens: 5, k: 0.035, cap: 1.5, hard: 0, stick: 0.7, melt: 1085, meltTo: MCU, bag: true, desc: 'Copper that set outside a mold. Melt it again and cast it.' });
def(SCRFE, { name: 'Iron scrap', ph: PH_POWDER, col: [124, 126, 132], v: 30, dens: 5, k: 0.03, cap: 1.5, hard: 0, stick: 0.7, melt: 1538, meltTo: MFE, bag: true, desc: 'Iron that set outside a mold. Melt it again and cast it.' });
def(SCRST, { name: 'Steel scrap', ph: PH_POWDER, col: [168, 176, 188], v: 28, dens: 5, k: 0.03, cap: 1.5, hard: 0, stick: 0.7, melt: 1450, meltTo: MST, bag: true, desc: 'Steel that set outside a mold. Melt it again and cast it.' });
-def(BARCU, { name: 'Copper bar', ph: PH_ITEM, col: [214, 132, 82], v: 0, dens: 8.9, k: 0, hard: 0, melt: 1085, meltTo: MCU, bag: true, desc: 'A cast bar. Belts, screw tubes, fans and engines are made from these.' });
+def(BARCU, { name: 'Copper bar', ph: PH_ITEM, col: [214, 132, 82], v: 0, dens: 8.9, k: 0, hard: 0, melt: 1085, meltTo: MCU, bag: true, desc: 'A cast bar. Belts, screw conveyors, fans and engines are made from these.' });
def(BARFE, { name: 'Iron bar', ph: PH_ITEM, col: [140, 143, 150], v: 0, dens: 7.9, k: 0, hard: 0, melt: 1538, meltTo: MFE, bag: true, desc: 'A cast bar of iron.' });
def(BARST, { name: 'Steel bar', ph: PH_ITEM, col: [184, 192, 204], v: 0, dens: 7.9, k: 0, hard: 0, melt: 1450, meltTo: MST, bag: true, desc: 'A cast bar of steel.' });
def(ACORN, { name: 'Acorn', ph: PH_POWDER, col: [150, 104, 56], v: 22, dens: 0.9, k: 0.006, hard: 0, stick: 0.6, ign: 300, flame: 700, flameF: 900, fuel: 30, burnTo: ASH, ashP: 0.3, bag: true, desc: 'A tree seed. Left on dirt or grass under open sky, it sprouts and grows into a tree.' });
diff --git a/src/sim/pieces.js b/src/sim/pieces.js
index f4d98d3..81e3a7d 100644
--- a/src/sim/pieces.js
+++ b/src/sim/pieces.js
@@ -4,7 +4,7 @@
// Everything you build snaps to a grid of 4x4-grain blocks. The world itself
// stays grain-level; only built pieces snap.
const BS = 4;
-const BP_WALL = 1, BP_BELT = 2, BP_TUBE = 3, BP_SHAFT = 4, BP_GRATE = 5, BP_GATE = 6, BP_FILTER = 7, BP_PIPE = 8, BP_PUMP = 9, BP_OUTLET = 10, BP_GRIND = 11, BP_SAW = 12, BP_PRESS = 13, BP_RACK = 14;
+const BP_WALL = 1, BP_BELT = 2, BP_SCREW = 3, BP_SHAFT = 4, BP_GRATE = 5, BP_GATE = 6, BP_FILTER = 7, BP_PIPE = 8, BP_PUMP = 9, BP_OUTLET = 10, BP_GRIND = 11, BP_SAW = 12, BP_PRESS = 13, BP_RACK = 14;
const BP_SLOPE_BL = 15, BP_SLOPE_BR = 16, BP_SLOPE_TL = 17, BP_SLOPE_TR = 18;
// The shape is independent of the direction: reversing a belt never flips its face.
const BP_BELT_UP = 19, BP_BELT_DOWN = 20, BP_FILTER_UP = 21, BP_FILTER_DOWN = 22;
@@ -71,8 +71,8 @@ const PIECES = [
desc: 'A solid block. Diagonal drags make a sloped face; T flips it. For one block, T cycles square and four slope corners. R picks the material: a square costs 16 grains or 2 bars; a slope costs 8 grains or 1 bar.' },
{ id: 'belt', code: BP_BELT, name: 'Belt', kind: 'block', cost: [[CASH, 12]], dirs: ['left', 'right'],
desc: 'Carries loose grains and bars resting on top of it, left or right, in a layer up to a block deep. Water and other liquids drain through it. Drag diagonally to make a sloped belt that carries up or down.' },
- { id: 'tube', code: BP_TUBE, name: 'Screw tube', kind: 'block', cost: [[CASH, 25]], dirs: ['up', 'right', 'down', 'left', 'up-right', 'down-right', 'down-left', 'up-left'],
- desc: 'A turning screw that draws in loose grains and whole bars and pushes them along, four grains wide. Water and other liquids run straight through it; the screw never carries them. Drag a line to set its direction, including diagonals. R turns a single tube. Join either end to tubes or belts. Copper softens around 1000°C.' },
+ { id: 'screw', code: BP_SCREW, name: 'Screw conveyor', kind: 'block', cost: [[CASH, 25]], dirs: ['up', 'right', 'down', 'left', 'up-right', 'down-right', 'down-left', 'up-left'],
+ desc: 'A turning screw inside a tube that draws in loose grains and whole bars and pushes them along, four grains wide. Water and other liquids run straight through it; the screw never carries them. Drag a line to set its direction, including diagonals. R turns a single screw conveyor. Join either end to screw conveyors or belts. Copper softens around 1000°C.' },
{ id: 'filter', code: BP_FILTER, name: 'Filter', kind: 'block', cost: [[CASH, 40]], dirs: ['left', 'right'], filter: true,
desc: 'A stretch of belt with a mesh in it. Put it in a line of belts and it joins them as one belt. Pick materials and a mode: drop them through and carry everything else along, or carry them and drop the rest. Liquids drain through. Drag diagonally to make a sloped filter that carries up or down.' },
{ id: 'shaft', code: BP_SHAFT, name: 'Shaft', kind: 'block', cost: [[LWOOD, 8]], hidden: POWER_FREE,
@@ -100,7 +100,7 @@ const PIECES = [
{ id: 'fan', name: 'Fan', kind: 'machine', bw: 1, bh: 1, cost: [[CASH, 60]], dirs: ['right', 'down', 'left', 'up'],
desc: 'Blows gas and forces air into fires, making them far hotter: its stream reaches 28 grains and passes through grates but not walls. Its copper body melts at 1085°C, so keep it outside the furnace, a block clear of the wall, blowing in through a grate. Press F to switch it on or off.' },
{ id: 'silo', name: 'Silo', kind: 'machine', bw: 3, bh: 6, cost: [[CASH, 180]],
- desc: 'A steel bin on legs for one loose material: up to 4,000 grains go in through the hatch in its roof (drop them in from a belt or tube). Its gauge shows how full it is. Press F to open the gate in its cone and let it out underneath, about 60 grains a second; F again shuts it. The first material in decides what it holds until it is empty.' },
+ desc: 'A steel bin on legs for one loose material: up to 4,000 grains go in through the hatch in its roof (drop them in from a belt or screw conveyor). Its gauge shows how full it is. Press F to open the gate in its cone and let it out underneath, about 60 grains a second; F again shuts it. The first material in decides what it holds until it is empty.' },
{ id: 'engine', name: 'Steam engine', kind: 'machine', bw: 3, bh: 2, cost: [[BARCU, 10]], dirs: ['exhaust right', 'exhaust left'], hidden: POWER_FREE,
desc: 'Steam rising into the intake slots on its underside drives it. Condensate drips out the exhaust side.' },
{ id: 'dock', name: 'Trade dock', kind: 'machine', bw: 6, bh: 4, cost: [], hidden: true, fixed: true,
@@ -117,8 +117,8 @@ PIECES.forEach((p, i) => { p.index = i; PIECE_BY_ID[p.id] = p; if (p.code) PIECE
for (let c = BP_SLOPE_BL; c <= BP_SLOPE_TR; c++) PIECE_BY_CODE[c] = PIECE_BY_ID.wall;
for (const c of [BP_BELT_UP, BP_BELT_DOWN]) PIECE_BY_CODE[c] = PIECE_BY_ID.belt;
for (const c of [BP_FILTER_UP, BP_FILTER_DOWN]) PIECE_BY_CODE[c] = PIECE_BY_ID.filter;
-// power per grain of belt / tube (a block is 16 grains)
-const BELT_POWER = 0.03, TUBE_POWER = 0.125, FAN_POWER = 6;
+// power per grain of belt / screw (a block is 16 grains)
+const BELT_POWER = 0.03, SCREW_POWER = 0.125, FAN_POWER = 6;
const WHEEL_K = 0.32, ENGINE_K = 40;
const MOLDS = {
diff --git a/src/sim/progress.js b/src/sim/progress.js
index 19dff3a..8e1ca53 100644
--- a/src/sim/progress.js
+++ b/src/sim/progress.js
@@ -30,14 +30,14 @@ const MILESTONES = [
{ id: 'copper', title: 'Molten copper', msg: 'Molten copper sinks to the bottom of the charge. Get it into a mold before it sets.', hint: 'Copper ore melts at 1085°C. An open fire loses too much heat, so wall coal and ore in with blocks and leave a small vent at the top for smoke. Mix ore and coal about half and half: Ctrl+click coal and copper ore in the bag to blow a mix. Right click blows an even mix.' },
{ id: 'bars', title: 'Copper bars', msg: 'Bars sell for far more than the ore in them, and make metal wall blocks.', hint: 'Build an Ingot mold (Build, 3) right under the furnace with a floor of clay Grates between them: molten copper drips through, while the charge and the thick slag stay up. When the bars set, they drop out of the bottom of the mold onto whatever is under it; a belt carries them off. Leave space below, or pick up the bars with the Pneumatic nozzle. Ingot molds empty themselves: bars, junk and scrap drop below, slag is thrown away and the grate above is raked. Removing any mold returns its set metal to your bag: whole bars as bars, the rest as scrap. Copper that sets in the furnace melts again next firing.' },
{ id: 'bit1', title: 'Copper drill bit', hint: 'Cast copper in a Bit mold. Building the furnace right on top works. A Grate over the funnel keeps the charge out. Let it set, then press F.', reward: 'Copper bit. You can now dig iron ore.' },
- { id: 'auto', title: 'Moving parts', msg: 'Machines can now haul for you. Feed furnaces automatically, and lift ore out of your mines.', hint: 'Belts carry grains and bars resting on them. Screw tubes push loose material along (never liquids); stacked pointing up they lift it out of a mine. A Filter lets one material fall through and carries the rest along, so you can sort ore from rubble. Move 300 grains with machines.' },
+ { id: 'auto', title: 'Moving parts', msg: 'Machines can now haul for you. Feed furnaces automatically, and lift ore out of your mines.', hint: 'Belts carry grains and bars resting on them. Screw conveyors push loose material along (never liquids); stacked pointing up they lift it out of a mine. A Filter lets one material fall through and carries the rest along, so you can sort ore from rubble. Move 300 grains with machines.' },
{ id: 'glass', title: 'Glass', hint: 'Sand melts near 1300°C. Still air will not get a fire that hot: point a Fan into the fuel. Molten glass runs through a grate like metal, so cast it in an Ingot mold for glass bricks, which sell well.', reward: 'Flask unlocked. The nozzle can suck up and blow out liquids.' },
{ id: 'iron', title: 'Molten iron', msg: 'Iron is hard to keep molten. A brick mold right under the hot zone works best.', hint: 'Iron ore melts at 1538°C. That needs forced air, and stone slumps at 1250°C, so build the furnace from brick.' },
{ id: 'bit2', title: 'Iron drill bit', hint: 'Cast iron in a Bit mold. A stone mold melts at iron temperatures, so build it from brick.', reward: 'Iron bit. Granite is now diggable.' },
{ id: 'steel', title: 'Steel', msg: 'Cast it in a brick Bit mold for the best drill bit there is.', hint: 'Blow air through molten iron. Aim a Fan at a pool of it and the excess carbon burns off, turning it into steel.' },
{ id: 'bit3', title: 'Steel drill bit', hint: 'Cast steel in a brick Bit mold.', reward: 'Steel bit. Basalt is now diggable.' },
{ id: 'tank', title: 'Bigger bag', hint: 'Cast any metal in a Tank mold and knock it out to carry more.' },
- { id: 'sale', title: 'First sale', msg: 'Money buys machines: belts, screw tubes and fans. Refined goods pay far more than raw.', hint: 'Blow, belt or lift goods into the trade dock near where you started. They are paid for the moment they drop through the grate in its floor.' },
+ { id: 'sale', title: 'First sale', msg: 'Money buys machines: belts, screw conveyors and fans. Refined goods pay far more than raw.', hint: 'Blow, belt or lift goods into the trade dock near where you started. They are paid for the moment they drop through the grate in its floor.' },
{ id: 'order', title: 'First order filled', msg: 'Bigger buyers notice you as you fill more orders.', hint: 'Buyers post orders on the Market page: an amount of one good by a deadline, at well over market price. Goods dropped into the dock fill an open order first.' },
{ id: 'alloy', title: 'An alloy', msg: 'Alloys sell for far more than the metals that went into them.', hint: 'Tin ore lies a little deeper than copper, zinc deeper again. Melt tin into a pool of molten copper and it mixes into bronze; zinc makes brass.' },
{ id: 'precious', title: 'Precious metal', hint: 'Silver lies deep in the granite, gold deeper still. Their ore sells high even raw, and much higher as bars.' },
diff --git a/src/sim/save.js b/src/sim/save.js
index b0427a8..fa69ced 100644
--- a/src/sim/save.js
+++ b/src/sim/save.js
@@ -3,7 +3,7 @@
const SAVE_VERSION = 2;
const TOFF = 64; // temperatures are saved with an offset so sub-zero snow and ice survive
function serializeWorld() {
- // grains in flight from a tube or belt land where they are (rain in the air simply carries on)
+ // grains in flight from a screw or belt land where they are (rain in the air simply carries on)
for (const f of flyers) if (!f.bag && !f.rain) landFlyer(f, Math.max(0, Math.min(W - 1, Math.floor(f.x))), Math.max(0, Math.min(H - 1, Math.floor(f.y))));
flyers = flyers.filter(f => f.bag || f.rain);
const t16 = new Uint16Array(N);
@@ -86,7 +86,7 @@ function deserializeWorld(meta, buffers) {
const p = savedPart[i];
if (p === P_WHEEL || p === P_INTAKE) continue;
part[i] = p;
- if (IS_TUBE[p]) markPow(i, true);
+ if (IS_SCREW[p]) markPow(i, true);
}
for (let i = 0; i < N; i++) { const m = mat[i]; if (m === BELTL || m === BELTR || m === SHAFT) markPow(i, true); }
// Old machines removed from the game are refunded.
diff --git a/src/sim/vacuum.js b/src/sim/vacuum.js
index f4e055e..1c472ce 100644
--- a/src/sim/vacuum.js
+++ b/src/sim/vacuum.js
@@ -22,7 +22,7 @@ function toolVacuum(cx, cy, r, pick) {
const m = mat[i], ph = PH[m];
if (!(ph === PH_POWDER || (ph === PH_LIQUID && player.flask))) return;
if (!pick(m)) return;
- if (IS_TUBE[part[i]]) return;
+ if (IS_SCREW[part[i]]) return;
if (bagTotal >= bagCap()) { full = true; return false; }
if (!first) first = m;
bagAdd(m, 1); setCell(i, EMPTY, AMB[y], 0); got++;
diff --git a/src/sim/world.js b/src/sim/world.js
index 33362b9..d60322d 100644
--- a/src/sim/world.js
+++ b/src/sim/world.js
@@ -17,7 +17,7 @@ let awake, hot, hotLvl, powderArea, roofDirty;
let loadWakeOrder = null, loadWakePending = null, loadWakeIndex = 0;
let tick = 0, tickStamp = 1;
let machines = [null], molds = [], nets = [null], pcells = new Set(), powerDirty = true;
-let beltL = [], beltR = [], tubeLists = Array.from({ length: IS_TUBE.length }, () => []);
+let beltL = [], beltR = [], screwLists = Array.from({ length: IS_SCREW.length }, () => []);
let brokenMachines = [];
let ghostMachines = [], ghostBlocked = new Set();
const EV = [];
@@ -55,7 +55,7 @@ function alloc(w, h) {
roofDirty = new Uint8Array(CW).fill(1);
powderArea = new Uint8Array(NCH).fill(1); // unknown areas get one visit before being ruled out
machines = [null]; molds = []; nets = [null]; pcells = new Set(); powerDirty = true;
- beltL = []; beltR = []; tubeLists = Array.from({ length: IS_TUBE.length }, () => []); brokenMachines = [];
+ beltL = []; beltR = []; screwLists = Array.from({ length: IS_SCREW.length }, () => []); brokenMachines = [];
ghostMachines = []; ghostBlocked = new Set();
tick = 0; tickStamp = 1; drillPulses = []; lastPulseTick = -99; undoStack = []; undoRec = null;
pipeNets = []; pipeNetAt = null; pipesDirty = true;
diff --git a/src/ui/actions.js b/src/ui/actions.js
index 4f3535e..af89df0 100644
--- a/src/ui/actions.js
+++ b/src/ui/actions.js
@@ -95,14 +95,14 @@ function doUndo() {
}
// right-click take-backs while the button is held count as one thing to undo
function endRmbRecord() { if (GAME.rmbRec) { const r = GAME.rmbRec; GAME.rmbRec = null; const n = r.ops.length; r.label = `taking back ${plural(n, 'piece')}`; undoPush(r); } }
-// Belts, filters and tubes run the way you drag them; a single click uses the direction R sets
+// Belts, filters and screws run the way you drag them; a single click uses the direction R sets
function dragDirFor(P, a, b) {
- if (!a || !P.dirs || !(P.id === 'belt' || P.id === 'tube' || P.filter)) return -1;
+ if (!a || !P.dirs || !(P.id === 'belt' || P.id === 'screw' || P.filter)) return -1;
const dx = b.bx - a.bx, dy = b.by - a.by;
if (!dx && !dy) return -1;
if ((P.id === 'belt' || P.filter) && diagonalDrag(dx, dy)) return dx > 0 ? 1 : 0;
- if (P.id === 'tube' && diagonalDrag(dx, dy)) return dx > 0 ? (dy < 0 ? 4 : 5) : (dy > 0 ? 6 : 7);
- if (P.id === 'tube') return Math.abs(dx) >= Math.abs(dy) ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0);
+ if (P.id === 'screw' && diagonalDrag(dx, dy)) return dx > 0 ? (dy < 0 ? 4 : 5) : (dy > 0 ? 6 : 7);
+ if (P.id === 'screw') return Math.abs(dx) >= Math.abs(dy) ? (dx > 0 ? 1 : 3) : (dy > 0 ? 2 : 0);
return Math.abs(dx) >= Math.abs(dy) ? (dx > 0 ? 1 : 0) : -1;
}
function blockLabel(P, rec) {
@@ -165,7 +165,7 @@ function primaryRelease(shift) {
}
if (t === 'build' && GAME.drag) {
const P = piece(), b = aimBlock(), code = pieceCode(P, b);
- const list = shift ? blockRect(GAME.drag.bx, GAME.drag.by, b.bx, b.by) : blockLine(GAME.drag.bx, GAME.drag.by, b.bx, b.by, isWall(code) || isSlopeBelt(code) || code === BP_TUBE);
+ const list = shift ? blockRect(GAME.drag.bx, GAME.drag.by, b.bx, b.by) : blockLine(GAME.drag.bx, GAME.drag.by, b.bx, b.by, isWall(code) || isSlopeBelt(code) || code === BP_SCREW);
const dd = dragDirFor(P, GAME.drag, b);
if (dd >= 0) GAME.pieceDir[P.id] = dd;
const v = pieceV(P), ex = pieceExtra(P);
diff --git a/src/ui/build-menu.js b/src/ui/build-menu.js
index edd351a..eb5ccc8 100644
--- a/src/ui/build-menu.js
+++ b/src/ui/build-menu.js
@@ -2,7 +2,7 @@
// ---------- Build menu ----------
const BUILD_CATS = [
['Structure', ['wall', 'grate', 'gate']],
- ['Moving', ['belt', 'filter', 'tube']],
+ ['Moving', ['belt', 'filter', 'screw']],
['Liquids', ['pipe', 'pump', 'outlet']],
['Machines', ['fan', 'grinder', 'saw', 'press', 'rack', 'silo']],
['Molds', ['ingotmold', 'bitmold', 'tankmold']],
@@ -14,7 +14,7 @@ const PIECE_ICONS = {
gate: '',
belt: '',
filter: '',
- tube: '',
+ screw: '',
pipe: '',
pump: '',
outlet: '',
@@ -29,9 +29,9 @@ const PIECE_ICONS = {
tankmold: '',
};
function pieceIcon(id) {
- const v = GAME.drag && GAME.dragDir >= 0 ? GAME.dragDir : GAME.pieceDir.tube || 0;
+ const v = GAME.drag && GAME.dragDir >= 0 ? GAME.dragDir : GAME.pieceDir.screw || 0;
const body = PIECE_ICONS[id] || '';
- const icon = id === 'tube' && v >= 4 ? `${body}` : body;
+ const icon = id === 'screw' && v >= 4 ? `${body}` : body;
return ``;
}
function catOf(id) { const c = BUILD_CATS.findIndex(([, ids]) => ids.includes(id)); return c < 0 ? 0 : c; }
@@ -49,7 +49,7 @@ function cycleBuildCat(d) {
}
function buildVariantChips(P) {
const dir = P.dirs ? P.dirs[GAME.pieceDir[P.id] || 0] : null;
- // while dragging a line of belts, filters or tubes, the direction is the drag's
+ // while dragging a line of belts, filters or screws, the direction is the drag's
const dragging = GAME.drag && GAME.dragDir >= 0 && P.dirs;
const glyph = d => ({ left: '←', right: '→', up: '↑', down: '↓', 'up-right': '↗', 'down-right': '↘', 'down-left': '↙', 'up-left': '↖' })[d] || '';
const code = pieceCode(P), direction = dragging ? GAME.dragDir : GAME.pieceDir[P.id] || 0;
diff --git a/src/ui/info.js b/src/ui/info.js
index c610bc0..2e98013 100644
--- a/src/ui/info.js
+++ b/src/ui/info.js
@@ -121,7 +121,7 @@ function hoverInfo(ax, ay) {
const pb = ((ay / BS) | 0) * BW + ((ax / BS) | 0), pn = pipeNetOf(pb);
t1 = 'Pipe' + (m !== EMPTY ? ` · ${MD[m].name} flowing past` : '');
t2.push(pipeContentsText(pn));
- } else if (IS_TUBE[p]) t1 = 'Screw tube ' + PIECE_BY_ID.tube.dirs[TUBE_PARTS.indexOf(p)] + (m !== EMPTY ? ` · ${MD[m].name}` : '');
+ } else if (IS_SCREW[p]) t1 = 'Screw conveyor ' + PIECE_BY_ID.screw.dirs[SCREW_PARTS.indexOf(p)] + (m !== EMPTY ? ` · ${MD[m].name}` : '');
else if (p) t1 = ({ 5: 'Grate', 6: 'Gate (open)', 7: 'Water wheel', 8: 'Engine intake' })[p] + (m !== EMPTY ? ` · ${MD[m].name}` : '');
else if (m === BELTL || m === BELTR) { const owner = slopeSealOwner(i), fb = owner >= 0 ? owner : (ay >> 2) * BW + (ax >> 2); t1 = 'Belt ' + beltDirection(blockP[fb], blockV[fb]); }
else if (m === PIPE && isPipeCode(b)) {
diff --git a/src/ui/input.js b/src/ui/input.js
index 7cc04b4..4637e0a 100644
--- a/src/ui/input.js
+++ b/src/ui/input.js
@@ -34,7 +34,7 @@ window.addEventListener('keydown', e => {
case 'f': doInteract(); break;
case 'g': GAME.replace = true; hudDirty = true; break;
case 'h': toggleLens(); break;
- case 'i': GAME.infoOverlay = !GAME.infoOverlay; playSfx('ui'); toast(GAME.infoOverlay ? 'Info overlay on: what every filter does, and which way belts and tubes run. I hides it.' : 'Info overlay off.'); break;
+ case 'i': GAME.infoOverlay = !GAME.infoOverlay; playSfx('ui'); toast(GAME.infoOverlay ? 'Info overlay on: what every filter does, and which way belts and screw conveyors run. I hides it.' : 'Info overlay off.'); break;
case 'l': bot.lamp = !bot.lamp; playSfx('ui'); break;
case 'n': openNotebook(); break;
case 'b': openNotebook('Blueprints'); break;
diff --git a/src/ui/notebook.js b/src/ui/notebook.js
index a40e47c..16865f9 100644
--- a/src/ui/notebook.js
+++ b/src/ui/notebook.js
@@ -60,7 +60,7 @@ function controlsHtml() {
['Fly', 'W A S D'], ['Boost while flying', 'Shift'], ['Tool modifier (while not flying)', 'Shift'], ['Use tool', 'Left mouse'], ['Second action', 'Right mouse'],
['Tools', '1 – 4'], ['Select one to blow · piece', 'Q / E'], ['Switch build tab', 'Shift + Q / E'], ['Variant: material · direction · flip', 'R'], ['Wall: cycle shape · flip diagonal drag', 'T'], ['Wall or mold material menu (Build)', 'F'],
['Pick up selection or what you click (Pneumatic nozzle)', 'Left drag · Shift: everything'], ['Select (Pneumatic nozzle)', 'Click a bag name · Q/E'], ['Add to selection (Pneumatic nozzle)', 'Ctrl+click a bag name'], ['Show everything found (Pneumatic nozzle)', 'Hold Ctrl in the bag'], ['Clear selection (Pneumatic nozzle)', 'Clear in the bag'], ['Blow everything loose in the bag', 'Shift + right click'], ['Nozzle or brush size', '[ ] or Shift + wheel'],
- ['Replace what is built (Build)', 'Hold G'], ['Copy a box of what you built', 'Hold C, drag'], ['Cut a box (refunded, paste it back)', 'Hold X, drag'], ['Paste again', 'V'], ['Undo building', 'Ctrl + Z'], ['Blueprint library', 'B'], ['Zoom', 'Mouse wheel'], ['Interact (gates, molds, fans, filters)', 'F'], ['Thermal lens', 'H'], ['Info overlay: filters, belt and tube directions', 'I'], ['Bot lamp', 'L'],
+ ['Replace what is built (Build)', 'Hold G'], ['Copy a box of what you built', 'Hold C, drag'], ['Cut a box (refunded, paste it back)', 'Hold X, drag'], ['Paste again', 'V'], ['Undo building', 'Ctrl + Z'], ['Blueprint library', 'B'], ['Zoom', 'Mouse wheel'], ['Interact (gates, molds, fans, filters)', 'F'], ['Thermal lens', 'H'], ['Info overlay: filters, belt and screw conveyor directions', 'I'], ['Bot lamp', 'L'],
['Pick the piece under the cursor (with its direction and settings)', 'Middle mouse'], ['Notebook', 'N'], ['Clear all blueprint ghosts', 'Delete'], ['Save', 'Ctrl + S'], ['Pause · menu', 'Esc'], ['Mute', 'Shift + M'], ['Full map', 'M'], ['Minimap zoom', '+ / − on minimap'],
];
const pad = [
@@ -71,7 +71,7 @@ function controlsHtml() {
const tools = [
['1 Resonance drill', 'Hold left click to send pulses from the bot toward the cursor: rock they shake hard enough shatters and falls as rubble, ore, coal or scrap, and trees come apart into wood chunks and acorns. Q and E set the pulse width: narrow bores a tunnel fast, wide clears a big cone slowly, medium is in between. R over a material tunes the drill to it, so only that shatters (great for pulling a vein out of the rock); R again untunes. Built blocks are never harmed. Better bits dig harder rock, faster and further.'],
['2 Pneumatic nozzle', "Left drag picks up the selection, or what you click if nothing is selected. Right click blows the selection. Click a bag name or Q/E selects one; Ctrl+click mixes. Hold Ctrl to see everything found. Clear removes the selection. Bars blow alone. Blowing out the last of a material deselects it. Shift while not flying takes everything; Shift + right click blows everything loose in the bag."],
- ['3 Build', 'Places pieces on a grid of 4×4 blocks: wall blocks, belts, tubes, filters, grates, gates, pipes, pumps, outlets, Grinders, Saws, Brick presses and Bar racks, machines (fans and silos) and molds. Drag for a line, hold Shift while dragging for a filled rectangle. A diagonal wall drag makes a sealed sloped face, solid below; T flips it. For one wall block, T cycles square and four slope corners. Diagonal belt and filter drags make sloped runs that carry up or down, following the drag. Their faces always point up. Right click takes a piece back. Your bot flies through anything you build.'],
+ ['3 Build', 'Places pieces on a grid of 4×4 blocks: wall blocks, belts, screw conveyors, filters, grates, gates, pipes, pumps, outlets, Grinders, Saws, Brick presses and Bar racks, machines (fans and silos) and molds. Drag for a line, hold Shift while dragging for a filled rectangle. A diagonal wall drag makes a sealed sloped face, solid below; T flips it. For one wall block, T cycles square and four slope corners. Diagonal belt and filter drags make sloped runs that carry up or down, following the drag. Their faces always point up. Right click takes a piece back. Your bot flies through anything you build.'],
['4 Igniter', 'Heats a small spot to 640°C, and its flame reaches a few grains into a pile. Enough to light coal and wood that touch air.'],
['Copy, cut and paste', 'Hold C (copy) or X (cut) and drag a box over what you built; let go and it is picked up, ready to paste. Cut takes it down and refunds it to your bag. Click to place ghosts: your bot builds them from your bag when it is close. R flips, V pastes again, right click or Esc stops. Ctrl+Z undoes building. The notebook (B) keeps saved blueprints and a few starter designs.'],
];
@@ -84,8 +84,8 @@ function controlsHtml() {
3. Build (3) a chamber of stone wall blocks with a one-block vent in the roof, a floor of clay Grates, and an Ingot mold right under the grates. The notebook's Blueprints tab has this as a starter design.
4. Ctrl+click coal and copper ore in the bag to blow a mix. Fly inside (the bot passes through what you build) and right click to blow an even mix onto the grates: about 50 of each. (Ore first and coal on top works too; coal buried under ore will not light.) Light a coal grain on top with the Igniter.
5. When the ingot sets, bars drop out of the bottom of the mold onto whatever is under it. Leave one open block under it: a belt below that carries them off, or pick them up with the nozzle. Leftover metal stays for the next firing. Ingot molds empty themselves: bars, junk and scrap drop below, slag is thrown away and the grate above is raked. Removing any mold returns its set metal to your bag: whole bars as bars, the rest as scrap.
- Screw tubes carry grains and whole bars: feed bars in from a belt, turn corners and unload at the mouth. Drag at about 45° for a diagonal run; R cycles eight directions.
- 6. Blow the bars into the trade dock: they sell the moment they drop through its grate, and bars pay far more than ore. Watch the Market page for orders that pay double. Money buys belts, screw tubes and fans; pay down the claim there too.
+ Screw conveyors carry grains and whole bars: feed bars in from a belt, turn corners and unload at the mouth. Drag at about 45° for a diagonal run; R cycles eight directions.
+ 6. Blow the bars into the trade dock: they sell the moment they drop through its grate, and bars pay far more than ore. Watch the Market page for orders that pay double. Money buys belts, screw conveyors and fans; pay down the claim there too.
7. From there: deeper ores (tin, zinc, silver, gold), alloys, fans for hotter fires, iron and steel.
`;
}
diff --git a/src/ui/preview-selection.js b/src/ui/preview-selection.js
index 5ef2143..5eb5561 100644
--- a/src/ui/preview-selection.js
+++ b/src/ui/preview-selection.js
@@ -73,7 +73,7 @@ function drawPastePreview() {
ctx.strokeStyle = 'rgba(92,186,166,0.85)'; ctx.setLineDash([zp, zp]); ctx.lineWidth = Math.max(1, zp * 0.3);
ctx.strokeRect(x, y, w, h); ctx.setLineDash([]);
const planned = new Map(bp.blocks.map(([dx, dy, code, v]) => [(oy + dy) * BW + ox + dx, [code, v]]));
- for (const [dx, dy, code, v] of bp.blocks) { const [bx2, by2, bw2, bh2] = blockRectPx(ox + dx, oy + dy, 1, 1); ctx.fillStyle = `rgba(${previewColor(code, v)},0.5)`; previewBlock(code, bx2, by2, bw2, bh2, false, code === BP_TUBE ? v : -1, ox + dx, oy + dy, planned); }
+ for (const [dx, dy, code, v] of bp.blocks) { const [bx2, by2, bw2, bh2] = blockRectPx(ox + dx, oy + dy, 1, 1); ctx.fillStyle = `rgba(${previewColor(code, v)},0.5)`; previewBlock(code, bx2, by2, bw2, bh2, false, code === BP_SCREW ? v : -1, ox + dx, oy + dy, planned); }
for (const [type, dx, dy] of bp.machines) { const P = PIECE_BY_ID[type]; const [mx, my, mw, mh] = blockRectPx(ox + dx, oy + dy, P.bw, P.bh); ctx.strokeStyle = 'rgba(255,210,122,0.8)'; ctx.strokeRect(mx, my, mw, mh); }
for (const [k, dx, dy] of bp.molds) { const md = MOLDS[k]; const [mx, my, mw, mh] = blockRectPx(ox + dx, oy + dy, md.bw, md.bh); ctx.strokeStyle = 'rgba(255,190,90,0.8)'; ctx.strokeRect(mx, my, mw, mh); }
ctx.restore();
diff --git a/src/ui/previews.js b/src/ui/previews.js
index 45b5457..2371ecd 100644
--- a/src/ui/previews.js
+++ b/src/ui/previews.js
@@ -17,7 +17,7 @@ function previewColor(code, v) {
if (isWall(code)) { const m = WALL_OF[v]; return `${COLR[m]},${COLG[m]},${COLB[m]}`; }
if (isFilter(code)) return '96,100,110';
if (isBelt(code)) return '110,114,124';
- return { [BP_BELT]: '110,114,124', [BP_TUBE]: '120,132,150', [BP_SHAFT]: '160,118,70', [BP_GRATE]: '170,90,66', [BP_GATE]: '160,84,64', [BP_PIPE]: '176,112,72', [BP_PUMP]: '104,122,142', [BP_OUTLET]: '170,106,66' }[code] || '120,200,190';
+ return { [BP_BELT]: '110,114,124', [BP_SCREW]: '120,132,150', [BP_SHAFT]: '160,118,70', [BP_GRATE]: '170,90,66', [BP_GATE]: '160,84,64', [BP_PIPE]: '176,112,72', [BP_PUMP]: '104,122,142', [BP_OUTLET]: '170,106,66' }[code] || '120,200,190';
}
// Air swirls into the pickup ring or spreads from the nozzle toward the cursor.
function drawAirStreaks(ax, ay, p, r, blowing) {
@@ -105,12 +105,12 @@ function drawPreviews() {
if (P.kind === 'block') {
const v = pieceV(P);
col = previewColor(code, v);
- cells = GAME.drag ? (modShift() ? blockRect(GAME.drag.bx, GAME.drag.by, b.bx, b.by) : blockLine(GAME.drag.bx, GAME.drag.by, b.bx, b.by, isWall(code) || isSlopeBelt(code) || code === BP_TUBE)) : [[b.bx, b.by]];
+ cells = GAME.drag ? (modShift() ? blockRect(GAME.drag.bx, GAME.drag.by, b.bx, b.by) : blockLine(GAME.drag.bx, GAME.drag.by, b.bx, b.by, isWall(code) || isSlopeBelt(code) || code === BP_SCREW)) : [[b.bx, b.by]];
const dd = GAME.drag ? dragDirFor(P, GAME.drag, b) : -1;
if (dd !== GAME.dragDir) { GAME.dragDir = dd; hudDirty = true; }
const dIdx = dd >= 0 ? dd : (GAME.pieceDir[P.id] || 0);
const afford = canAfford(pieceCostNow(P), cells.length);
- const planned = code === BP_TUBE && dIdx >= 4 ? new Map(cells.map(([x, y]) => [y * BW + x, [BP_TUBE, dIdx]])) : null;
+ const planned = code === BP_SCREW && dIdx >= 4 ? new Map(cells.map(([x, y]) => [y * BW + x, [BP_SCREW, dIdx]])) : null;
for (const [bx, by] of cells) {
const err = canPlaceBlock(bx, by, code), rb = by * BW + bx;
const swap = GAME.replace && !!blockP[rb] && /already built/.test(err) && !blockIs(rb, code, v, pieceExtra(P));
@@ -118,13 +118,13 @@ function drawPreviews() {
const ok = (!err || swap) && !ghost && afford;
const [x, y, w, h] = blockRectPx(bx, by, 1, 1);
ctx.fillStyle = ghost ? 'rgba(90,190,230,0.22)' : ok ? (swap ? 'rgba(255,190,90,0.5)' : `rgba(${col},0.55)`) : 'rgba(239,106,85,0.35)';
- previewBlock(code, x, y, w, h, false, P.id === 'tube' ? dIdx : -1, bx, by, planned);
+ previewBlock(code, x, y, w, h, false, P.id === 'screw' ? dIdx : -1, bx, by, planned);
ctx.strokeStyle = ghost ? 'rgba(90,190,230,0.8)' : ok ? 'rgba(236,226,210,0.8)' : 'rgba(239,106,85,0.9)'; ctx.lineWidth = Math.max(1, zp * 0.25);
if (ghost) ctx.setLineDash([zp, zp * 0.7]);
- previewBlock(code, x, y, w, h, true, P.id === 'tube' ? dIdx : -1, bx, by, planned); ctx.setLineDash([]);
- if (P.dirs && (P.id === 'belt' || P.id === 'tube' || P.filter || P.id === 'pump' || P.id === 'outlet')) {
+ previewBlock(code, x, y, w, h, true, P.id === 'screw' ? dIdx : -1, bx, by, planned); ctx.setLineDash([]);
+ if (P.dirs && (P.id === 'belt' || P.id === 'screw' || P.filter || P.id === 'pump' || P.id === 'outlet')) {
// a chevron on every block, pointing the way it will carry (or pump, or pour)
- const [dx, dy] = isSlopeBelt(code) ? [dIdx ? 1 : -1, beltGrade(code) * (dIdx ? 1 : -1)] : P.id === 'tube' ? TUBE_DIRS[dIdx] : P.id === 'pump' ? PUMP_V[dIdx] : P.id === 'outlet' ? OUTLET_V[dIdx] : [[-1, 0], [1, 0]][dIdx];
+ const [dx, dy] = isSlopeBelt(code) ? [dIdx ? 1 : -1, beltGrade(code) * (dIdx ? 1 : -1)] : P.id === 'screw' ? SCREW_DIRS[dIdx] : P.id === 'pump' ? PUMP_V[dIdx] : P.id === 'outlet' ? OUTLET_V[dIdx] : [[-1, 0], [1, 0]][dIdx];
const cx = x + w / 2, cy = y + h / 2, k = w * 0.22, px_ = -dy, py_ = dx;
ctx.strokeStyle = 'rgba(20,16,12,0.55)'; ctx.lineWidth = Math.max(2, zp * 0.9); ctx.lineCap = 'round'; ctx.lineJoin = 'round';
const chev = () => { ctx.beginPath(); ctx.moveTo(cx - dx * k * 0.6 + px_ * k, cy - dy * k * 0.6 + py_ * k); ctx.lineTo(cx + dx * k * 0.7, cy + dy * k * 0.7); ctx.lineTo(cx - dx * k * 0.6 - px_ * k, cy - dy * k * 0.6 - py_ * k); ctx.stroke(); };
@@ -180,12 +180,12 @@ function drawPreviews() {
function previewBlock(code, x, y, w, h, stroke = false, direction = -1, bx = -1, by = -1, planned = null) {
const ctx = R.ctx;
- if (code === BP_TUBE && direction >= 4) {
- const joins = bx >= 0 ? diagonalTubeJoins(bx, by, direction, planned) : 0;
+ if (code === BP_SCREW && direction >= 4) {
+ const joins = bx >= 0 ? diagonalScrewJoins(bx, by, direction, planned) : 0;
const points = [];
for (let end = 0; end < 2; end++) {
const join = (joins >> (end * 3)) & 7, side = end ? 1 : -1;
- const d = join ? TUBE_DIRS[join - 1] : [side, side * (direction === 4 || direction === 6 ? -1 : 1)];
+ const d = join ? SCREW_DIRS[join - 1] : [side, side * (direction === 4 || direction === 6 ? -1 : 1)];
points.push([d[0] * w / 2, d[1] * h / 2]);
}
const r = w / 2, a = points[0], b = points[1];
diff --git a/test/play.py b/test/play.py
index 72e0db1..68f60f7 100644
--- a/test/play.py
+++ b/test/play.py
@@ -698,21 +698,21 @@ with sync_playwright() as p:
check('Wall diagonal defaults back to solid below', ev(f'Array.from({{length:4}}, (_,k)=>blockP[({bby}-12+k)*BW+{bbx}-5+k]).every(c=>c===BP_SLOPE_BL)'))
pg.keyboard.press('Control+z'); settle(60)
- # Diagonal tubes use real mouse placement, preview arrows, R and picking.
- check('select screw tube', select('3', "piece().id === 'tube'"))
- ev('GAME.pieceDir.tube = 0')
+ # Diagonal screws use real mouse placement, preview arrows, R and picking.
+ check('select screw conveyor', select('3', "piece().id === 'screw'"))
+ ev('GAME.pieceDir.screw = 0')
directions = []
for _ in range(8):
- directions.append(ev('GAME.pieceDir.tube'))
+ directions.append(ev('GAME.pieceDir.screw'))
pg.keyboard.press('r'); settle(40)
- check('R cycles eight tube directions', directions == list(range(8)) and ev('GAME.pieceDir.tube') == 0)
+ check('R cycles eight screw directions', directions == list(range(8)) and ev('GAME.pieceDir.screw') == 0)
drag(lambda: blk(bbx - 5, bby - 12), lambda: blk(bbx - 2, bby - 9))
- check('diagonal tube drag places four down-right blocks', ev(f'Array.from({{length:4}}, (_,k)=>[blockP[({bby}-12+k)*BW+{bbx}-5+k],blockV[({bby}-12+k)*BW+{bbx}-5+k]]).every(([c,v])=>c===BP_TUBE && v===5)'))
+ check('diagonal screw drag places four down-right blocks', ev(f'Array.from({{length:4}}, (_,k)=>[blockP[({bby}-12+k)*BW+{bbx}-5+k],blockV[({bby}-12+k)*BW+{bbx}-5+k]]).every(([c,v])=>c===BP_SCREW && v===5)'))
pg.mouse.move(*blk(bbx - 4, bby - 11)); settle(60)
check('diagonal hover names its direction', ev(f"hoverInfo(({bbx}-4)*4+1,({bby}-11)*4+1).t1.includes('down-right')"))
pg.mouse.click(*blk(bbx - 4, bby - 11), button='middle'); settle(60)
- check('picking a diagonal tube keeps its direction', ev("piece().id === 'tube' && GAME.pieceDir.tube === 5"))
- pg.screenshot(path=str(out / '04-diagonal-tubes.png'))
+ check('picking a diagonal screw keeps its direction', ev("piece().id === 'screw' && GAME.pieceDir.screw === 5"))
+ pg.screenshot(path=str(out / '04-diagonal-screws.png'))
pg.keyboard.press('Control+z'); settle(60)
# 6. ingot mold: click to place, pour, bars drop below, vacuum them
diff --git a/test/t_conveyors.js b/test/t_conveyors.js
index 5110670..4ccaafb 100644
--- a/test/t_conveyors.js
+++ b/test/t_conveyors.js
@@ -1,9 +1,9 @@
-// Screw tubes that never carry liquid (it flows through), tube and belt throughput, filters.
+// Screw conveyors that never carry liquid (it flows through), screw and belt throughput, filters.
const { load, suite, flatWorld, rect, ticks, count } = require('./harness');
const { run } = load();
const { check, done } = suite();
function fresh() { flatWorld(run, 320, 200, 120); run('bot.x = 20; bot.y = 40; money = 1e6; globalThis.__got = 0;'); }
-const tubeLiquid = () => run('(() => { let n = 0; for (let i = 0; i < N; i++) if (part[i] >= P_TU && part[i] <= P_TR && PH[mat[i]] === PH_LIQUID) n++; return n; })()');
+const screwLiquid = () => run('(() => { let n = 0; for (let i = 0; i < N; i++) if (part[i] >= P_SCREW_U && part[i] <= P_SCREW_R && PH[mat[i]] === PH_LIQUID) n++; return n; })()');
// grains that land in the sink region are counted and taken away, so outlets never back up
function rate(x0, x1, y0, y1, warm, meas) {
const sink = `for (let y=${y0};y<=${y1};y++) for (let x=${x0};x<=${x1};x++) { const i=y*W+x; if (mat[i]===SAND) { mat[i]=EMPTY; __got++; } }`;
@@ -13,42 +13,42 @@ function rate(x0, x1, y0, y1, warm, meas) {
return run('__got') / (meas / 60);
}
-// --- tubes are sealed: no water in, no water carried
+// --- screws are sealed: no water in, no water carried
fresh();
run('for (let y = 120; y < 170; y++) for (let x = 100; x < 124; x++) setCell(y*W+x, EMPTY, AMB[y], 0)');
rect(run, 100, 150, 123, 169, run('SAND'));
rect(run, 100, 130, 123, 149, run('WATER'));
-run('for (let by = 22; by <= 41; by++) placeBlock(BP_TUBE, 0, 27, by, true); for (let bx = 27; bx <= 34; bx++) placeBlock(BP_TUBE, 1, bx, 21, true);');
+run('for (let by = 22; by <= 41; by++) placeBlock(BP_SCREW, 0, 27, by, true); for (let bx = 27; bx <= 34; bx++) placeBlock(BP_SCREW, 1, bx, 21, true);');
const water0 = count(run, run('WATER'));
ticks(run, 900);
const wOut = count(run, run('WATER'), 124, 0, 319, 119), sOut = count(run, run('SAND'), 124, 0, 319, 119);
-check('a lift standing in a flooded pit brings up the sand but not the water', sOut > 150 && wOut === 0, `sand out ${sOut}, water out ${wOut}, water standing in the tubes ${tubeLiquid()}`);
+check('a lift standing in a flooded pit brings up the sand but not the water', sOut > 150 && wOut === 0, `sand out ${sOut}, water out ${wOut}, water standing in the screws ${screwLiquid()}`);
const pitClouds = run('weather.moist.reduce((a, v) => a + v, 0)');
const pitSteam = count(run, run('STEAM')); // the pit's floor is hot: some is steam on its way up at any moment
check('the pit keeps its water (bar what steams off into the clouds)', water0 - count(run, run('WATER')) - pitSteam - pitClouds <= 6, `${water0} -> ${count(run, run('WATER'))}, ${pitSteam} steam, ${pitClouds.toFixed(0)} in the clouds`);
fresh();
-run('for (let bx = 20; bx < 26; bx++) placeBlock(BP_TUBE, 1, bx, 25, true);');
+run('for (let bx = 20; bx < 26; bx++) placeBlock(BP_SCREW, 1, bx, 25, true);');
rect(run, 84, 60, 95, 79, run('WATER'));
ticks(run, 300);
-check('water poured on a tube runs straight through it', count(run, run('WATER'), 0, 0, 319, 99) === 0 && count(run, run('WATER'), 0, 104, 319, 119) > 200 && tubeLiquid() < 10, `${count(run, run('WATER'), 0, 104, 319, 119)} below, ${tubeLiquid()} still inside`);
+check('water poured on a screw runs straight through it', count(run, run('WATER'), 0, 0, 319, 99) === 0 && count(run, run('WATER'), 0, 104, 319, 119) > 200 && screwLiquid() < 10, `${count(run, run('WATER'), 0, 104, 319, 119)} below, ${screwLiquid()} still inside`);
fresh();
-// a vertical tube pointing up, its top open to a pool: the water drains down through it, against the screw
-run('for (let by = 20; by <= 26; by++) placeBlock(BP_TUBE, 0, 25, by, true); for (let y = 60; y < 80; y++) { setCell(y*W+99, STONE, AMB[y], 0); setCell(y*W+104, STONE, AMB[y], 0); }');
+// a vertical screw pointing up, its top open to a pool: the water drains down through it, against the screw
+run('for (let by = 20; by <= 26; by++) placeBlock(BP_SCREW, 0, 25, by, true); for (let y = 60; y < 80; y++) { setCell(y*W+99, STONE, AMB[y], 0); setCell(y*W+104, STONE, AMB[y], 0); }');
rect(run, 100, 60, 103, 79, run('WATER'));
ticks(run, 400);
check('water runs down through an upward screw, which never pushes it back', count(run, run('WATER'), 0, 0, 319, 79) === 0, count(run, run('WATER'), 0, 0, 319, 79));
-// Intake itself fills the far lanes first, for either tube axis.
+// Intake itself fills the far lanes first, for either screw axis.
for (const horizontal of [false, true]) {
fresh();
- run(`placeBlock(BP_TUBE, ${horizontal ? 1 : 0}, 25, 20, true);`);
+ run(`placeBlock(BP_SCREW, ${horizontal ? 1 : 0}, 25, 20, true);`);
const filled = run(`(() => {
const lanes = [];
for (let k = 0; k < BS; k++) {
tickStamp++;
const x = ${horizontal ? 100 : 99}, y = ${horizontal ? 79 : 80};
setCell(y*W+x, SAND, AMB[y], 0);
- tubeIntake(80*W+100, ${horizontal ? 'P_TR, 1, 0' : 'P_TU, 0, -1'});
+ screwIntake(80*W+100, ${horizontal ? 'P_SCREW_R, 1, 0' : 'P_SCREW_U, 0, -1'});
for (let lane = 0; lane < BS; lane++) if (mat[(80+${horizontal ? 'lane' : '0'})*W+100+${horizontal ? '0' : 'lane'}] === SAND && !lanes.includes(lane)) lanes.push(lane);
}
return lanes;
@@ -59,7 +59,7 @@ for (const horizontal of [false, true]) {
// A finite heap rides into the side of a six-block lift; remove each mouth flyer once.
const sideSeed = run('RS');
fresh();
-run('RS = 7; for (let bx = 16; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true); for (let by = 19; by <= 24; by++) placeBlock(BP_TUBE, 0, 32, by, true);');
+run('RS = 7; for (let bx = 16; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true); for (let by = 19; by <= 24; by++) placeBlock(BP_SCREW, 0, 32, by, true);');
rect(run, 64, 40, 119, 99, run('SAND'));
const sideLift = run(`(() => {
let out = 0; const lanes = [0, 0, 0, 0];
@@ -79,7 +79,7 @@ check('the side-fed lift conserves its sand, including mouth flyers', sideLift.b
// The same belt and heap deliver 2243 into an open drop, the most the lift can be given.
check('a side-fed lift carries at least 90% of what its belt brings (it was 65%)', sideLift.out >= Math.floor(0.9 * 2243), sideLift.out);
-// Same belt and finite heap, with a non-blocking sink at the tube side.
+// Same belt and finite heap, with a non-blocking sink at the screw side.
run(`RS = ${sideSeed}`);
fresh();
run('RS = 7; for (let bx = 16; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true);');
@@ -100,24 +100,24 @@ console.log('Belt delivery to a non-blocking sink, 3000 ticks: ' + beltDelivery)
// --- throughput
fresh();
-run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_TUBE, 1, bx, 20, true);');
+run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_SCREW, 1, bx, 20, true);');
run('for (let y = 0; y < 80; y++) { setCell(y*W+79, STONE, AMB[y], 0); setCell(y*W+96, STONE, AMB[y], 0); }');
rect(run, 80, 0, 95, 79, run('SAND'));
ticks(run, 60);
const lanes = run('(() => { const s = new Set(); for (let y = 80; y < 84; y++) for (let x = 100; x < 120; x++) if (mat[y*W+x] === SAND) s.add(y); return s.size; })()');
-check('a sideways tube fed from above (its side) fills more than one row', lanes > 1, lanes);
-check('a sideways tube fed from a hopper runs full, all four grains wide', lanes === 4, lanes);
+check('a sideways screw fed from above (its side) fills more than one row', lanes > 1, lanes);
+check('a sideways screw fed from a hopper runs full, all four grains wide', lanes === 4, lanes);
fresh();
-run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_TUBE, 1, bx, 20, true);');
+run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_SCREW, 1, bx, 20, true);');
run('for (let y = 0; y < 80; y++) { setCell(y*W+79, STONE, AMB[y], 0); setCell(y*W+96, STONE, AMB[y], 0); }');
rect(run, 80, 0, 95, 79, run('SAND'));
const tr = rate(120, 319, 84, 119, 90, 300);
-// tubes run at belt pace (every other tick), so a belt under a tube's mouth keeps up with it
-check('a sideways tube moves around a hundred grains a second', tr > 80 && tr < 150, `${tr.toFixed(0)}/s`);
+// screws run at belt pace (every other tick), so a belt under a screw's mouth keeps up with it
+check('a sideways screw moves around a hundred grains a second', tr > 80 && tr < 150, `${tr.toFixed(0)}/s`);
fresh();
run('for (let y = 120; y < 196; y++) for (let x = 90; x < 130; x++) setCell(y*W+x, EMPTY, AMB[y], 0)');
rect(run, 90, 130, 129, 195, run('SAND'));
-run('for (let by = 20; by <= 37; by++) placeBlock(BP_TUBE, 0, 26, by, true); for (let bx = 26; bx <= 40; bx++) placeBlock(BP_TUBE, 1, bx, 19, true);');
+run('for (let by = 20; by <= 37; by++) placeBlock(BP_SCREW, 0, 26, by, true); for (let bx = 26; bx <= 40; bx++) placeBlock(BP_SCREW, 1, bx, 19, true);');
const lr = rate(164, 319, 80, 119, 400, 300);
const liftLanes = run('(() => { const s = new Set(); for (let y = 76; y < 80; y++) for (let x = 120; x < 160; x++) if (mat[y*W+x] === SAND) s.add(y); return s.size; })()');
check('a lift turns its corner without squeezing down to one lane', liftLanes === 4 && lr > 30, `${lr.toFixed(0)}/s, ${liftLanes} lanes at the top`);
@@ -151,7 +151,7 @@ check('the load rides a belt up to a block deep', depth >= 3, depth);
// --- filters
fresh();
// a filter for copper ore under a mixed pile; everything else rides right and off the end
-// fed from a walled hopper over its left end, the way a tube or belt would drop a mixed load on it
+// fed from a walled hopper over its left end, the way a screw or belt would drop a mixed load on it
run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_FILTER, 128, bx, 25, true, { m: [CUORE] });');
run('for (let y = 30; y < 120; y++) setCell(y*W+79, STONE, AMB[y], 0); for (let y = 30; y < 96; y++) setCell(y*W+88, STONE, AMB[y], 0);');
run('for (let y = 40; y < 96; y++) for (let x = 80; x < 88; x++) setCell(y*W+x, (x + y) % 3 ? SAND : CUORE, AMB[y], 0)');
@@ -236,7 +236,7 @@ run(`globalThis.__carry = carryColumn;
for (const lift of [true, false]) {
fresh();
run('RS = 7; __shear = 0; for (let bx = 20; bx < 32; bx++) placeBlock(BP_BELT, 1, bx, 25, true);');
- if (lift) run('for (let by = 15; by <= 24; by++) placeBlock(BP_TUBE, 0, 32, by, true);');
+ if (lift) run('for (let by = 15; by <= 24; by++) placeBlock(BP_SCREW, 0, 32, by, true);');
else run('for (let by = 10; by <= 25; by++) placeBlock(BP_WALL, RSTONE, 32, by, true);');
rect(run, 88, 94, 119, 99, run('SAND'));
rect(run, 88, 70, 119, 93, run('CUORE'));
@@ -251,60 +251,60 @@ for (const lift of [true, false]) {
}
run('carryColumn = __carry;');
-// --- whole bars in screw tubes; position an existing bar inside for mid-ride cases
+// --- whole bars in screw conveyors; position an existing bar inside for mid-ride cases
function putBar(x, y) { run(`{ spawnBar(BARCU, 20, 40, 0, 0); const e=ents[0]; entMark(e,0); e.x=e.ix=${x}; e.y=e.iy=${y}; entMark(e,1); }`); }
-function barTube(corner = false) {
+function barScrew(corner = false) {
fresh();
- run(`for (let by = 20; by < 24; by++) placeBlock(BP_TUBE, 0, 25, by, true);
- ${corner ? 'for (let bx = 25; bx < 29; bx++) placeBlock(BP_TUBE, 1, bx, 19, true);' : ''}`);
+ run(`for (let by = 20; by < 24; by++) placeBlock(BP_SCREW, 0, 25, by, true);
+ ${corner ? 'for (let bx = 25; bx < 29; bx++) placeBlock(BP_SCREW, 1, bx, 19, true);' : ''}`);
putBar(100,94);
}
function watchBar(n, condition) { return run(`(() => { for (let k=0;k<${n};k++) { simTick(); if (${condition}) return true; } return false; })()`); }
fresh();
-run('for (let bx=20;bx<25;bx++) placeBlock(BP_BELT,1,bx,25,true); for(let by=21;by<25;by++) placeBlock(BP_TUBE,0,25,by,true); spawnBar(BARCU,84,98,0,0);');
+run('for (let bx=20;bx<25;bx++) placeBlock(BP_BELT,1,bx,25,true); for(let by=21;by<25;by++) placeBlock(BP_SCREW,0,25,by,true); spawnBar(BARCU,84,98,0,0);');
check('a belt feeds a whole copper bar into a lift and out its top', watchBar(200, 'ents.length===1 && ents[0].iy+BAR_H<=84'), run('JSON.stringify(ents)'));
fresh();
-run('for(let bx=20;bx<24;bx++) placeBlock(BP_TUBE,1,bx,20,true);'); putBar(80,82);
-check('a whole bar leaves a right tube', watchBar(100,'ents.length===1 && ents[0].ix>=96'));
-// steam and smoke drift through tubes near furnaces: a bar passes them as it does in the open
-barTube();
-check('steam and smoke in a tube do not stop a bar', run(`(() => { for (let k = 0; k < 120; k++) {
+run('for(let bx=20;bx<24;bx++) placeBlock(BP_SCREW,1,bx,20,true);'); putBar(80,82);
+check('a whole bar leaves a right screw', watchBar(100,'ents.length===1 && ents[0].ix>=96'));
+// steam and smoke drift through screws near furnaces: a bar passes them as it does in the open
+barScrew();
+check('steam and smoke in a screw do not stop a bar', run(`(() => { for (let k = 0; k < 120; k++) {
for (let y = 80; y < 94; y++) for (let x = 100; x < 104; x++) if (mat[y*W+x] === EMPTY && !occ[y*W+x]) setCell(y*W+x, (x + y) & 1 ? STEAM : SMOKE, 120, 0);
simTick(); if (ents.length === 1 && ents[0].iy + BAR_H <= 80) return true; } return false; })()`), run('JSON.stringify(ents)'));
-barTube(true);
-check('a bar turns from an up tube into a right tube and leaves', watchBar(200,'ents.length===1 && ents[0].ix>=116'));
+barScrew(true);
+check('a bar turns from an up screw into a right screw and leaves', watchBar(200,'ents.length===1 && ents[0].ix>=116'));
fresh();
-run('for(let bx=20;bx<24;bx++) placeBlock(BP_TUBE,1,bx,20,true);'); putBar(80,82);
+run('for(let bx=20;bx<24;bx++) placeBlock(BP_SCREW,1,bx,20,true);'); putBar(80,82);
rect(run,96,80,99,83,run('STONE'));
ticks(run,100);
check('a blocked mouth holds the whole bar inside without wall overlap',run('ents.length===1 && ents[0].ix===92 && !mat.some((m,i)=>m===STONE && occ[i])'),run('JSON.stringify(ents)'));
rect(run,96,80,99,83,run('EMPTY'));
check('the bar leaves when the mouth wall is removed',watchBar(80,'ents[0].ix>=96'));
-barTube();
+barScrew();
rect(run,100,88,103,89,run('SAND'));
const shared = run(`(() => { let overlap=false, rose=false; const n=mat.reduce((n,m)=>n+(m===SAND),0); for(let k=0;k<160;k++) { simTick(); if(mat.some((m,i)=>m===SAND && occ[i])) overlap=true; if(ents[0].iy<90) rose=true; } return { overlap, rose, n, after:mat.reduce((n,m)=>n+(m===SAND),0)+flyers.filter(f=>f.m===SAND).length }; })()`);
check('grains and a moving bar share a lift without overlap or lost sand',!shared.overlap && shared.rose && shared.n===shared.after,JSON.stringify(shared));
-barTube(); ticks(run,8);
+barScrew(); ticks(run,8);
run('globalThis.__barSave=serializeWorld(); deserializeWorld(__barSave.meta,__barSave.buffers);');
-check('a bar saved mid-tube continues out of its mouth',watchBar(100,'ents.length===1 && ents[0].iy+BAR_H<=80'));
+check('a bar saved mid-screw continues out of its mouth',watchBar(100,'ents.length===1 && ents[0].iy+BAR_H<=80'));
fresh();
run('for(let bx=20;bx<24;bx++) placeBlock(BP_BELT,1,bx,25,true); placeBlock(BP_WALL,RSTONE,24,25,true); placeBlock(BP_WALL,RSTONE,24,24,true); spawnBar(BARCU,80,98,0,0);');
ticks(run,100);
check('a normal belt still carries a bar and stops at its blocked end',run('ents.length===1 && ents[0].ix===92 && ents[0].iy===98'));
-fresh(); run('placeBlock(BP_TUBE,1,25,25,true); spawnBar(BARCU,100,98,0,0);'); ticks(run,100);
-check('a bar resting on a horizontal tube stays on top',run('ents[0].ix===100 && ents[0].iy===98'));
+fresh(); run('placeBlock(BP_SCREW,1,25,25,true); spawnBar(BARCU,100,98,0,0);'); ticks(run,100);
+check('a bar resting on a horizontal screw stays on top',run('ents[0].ix===100 && ents[0].iy===98'));
for (const dir of [0, 2]) {
- fresh(); run(`placeBlock(BP_TUBE,${dir},25,20,true); spawnBar(BARCU,100,${dir ? 78 : 84},0,${dir ? 0 : -0.4});`);
- check(dir ? 'a falling bar enters a down tube from above' : 'an upward moving bar enters the bottom of an up tube',watchBar(20,'entTubeAt(ents[0].ix,ents[0].iy)!==0'));
+ fresh(); run(`placeBlock(BP_SCREW,${dir},25,20,true); spawnBar(BARCU,100,${dir ? 78 : 84},0,${dir ? 0 : -0.4});`);
+ check(dir ? 'a falling bar enters a down screw from above' : 'an upward moving bar enters the bottom of an up screw',watchBar(20,'entScrewAt(ents[0].ix,ents[0].iy)!==0'));
}
-fresh(); run('for(let bx=20;bx<24;bx++) placeBlock(BP_TUBE,1,bx,20,true); for(let by=21;by<25;by++) placeBlock(BP_TUBE,2,23,by,true);'); putBar(80,82);
-check('a bar turns from a right tube down into a vertical lane',watchBar(160,'ents[0].iy>=100'));
-barTube(true); rect(run,100,76,103,79,run('STONE')); ticks(run,100);
+fresh(); run('for(let bx=20;bx<24;bx++) placeBlock(BP_SCREW,1,bx,20,true); for(let by=21;by<25;by++) placeBlock(BP_SCREW,2,23,by,true);'); putBar(80,82);
+check('a bar turns from a right screw down into a vertical lane',watchBar(160,'ents[0].iy>=100'));
+barScrew(true); rect(run,100,76,103,79,run('STONE')); ticks(run,100);
check('a blocked corner waits without overlapping stone',run('ents[0].iy===80 && !mat.some((m,i)=>m===STONE && occ[i])'));
rect(run,100,76,103,79,run('EMPTY'));
check('a blocked corner resumes when cleared',watchBar(120,'ents[0].ix>=116'));
-barTube(); run('ents[0].T=1200; for(let y=94;y<96;y++) for(let x=100;x<104;x++) temp[y*W+x]=1200; entTick();');
-check('a bar melting inside a tube becomes eight molten grains',run('ents.length===0') && count(run,run('MCU'))===8);
+barScrew(); run('ents[0].T=1200; for(let y=94;y<96;y++) for(let x=100;x<104;x++) temp[y*W+x]=1200; entTick();');
+check('a bar melting inside a screw becomes eight molten grains',run('ents.length===0') && count(run,run('MCU'))===8);
done();
diff --git a/test/t_corner_seal.js b/test/t_corner_seal.js
index 986d0ed..9fbb981 100644
--- a/test/t_corner_seal.js
+++ b/test/t_corner_seal.js
@@ -42,13 +42,13 @@ for (const material of ['SAND', 'WATER', 'STEAM']) for (const mirror of [false,
// A screw mouth must not draw sand through two external wall cells either.
for (const direction of [4,5,6,7]) {
flatWorld(run,160,160,140);
- run(`placeBlock(BP_TUBE,${direction},20,20,true);
- var dx=TDX[TUBE_PARTS[${direction}]],dy=TDY[TUBE_PARTS[${direction}]];
+ run(`placeBlock(BP_SCREW,${direction},20,20,true);
+ var dx=SCREW_DX[SCREW_PARTS[${direction}]],dy=SCREW_DY[SCREW_PARTS[${direction}]];
var x=dx>0?80:83,y=dy>0?80:83;
setCell((y-dy)*W+x-dx,SAND,20,0);
setCell((y-dy)*W+x,STONE,20,0);setCell(y*W+x-dx,STONE,20,0);
- tubeIntake(y*W+x,part[y*W+x],dx,dy);`);
- check(`diagonal tube ${direction} cannot pull through a solid corner`,run('mat[(y-dy)*W+x-dx]===SAND'));
+ screwIntake(y*W+x,part[y*W+x],dx,dy);`);
+ check(`diagonal screw ${direction} cannot pull through a solid corner`,run('mat[(y-dy)*W+x-dx]===SAND'));
}
// A free flyer can take the same diagonal shortcut between wall cells.
diff --git a/test/t_diag_tubes.js b/test/t_diag_screws.js
similarity index 80%
rename from test/t_diag_tubes.js
rename to test/t_diag_screws.js
index 6f64a50..d019ef7 100644
--- a/test/t_diag_tubes.js
+++ b/test/t_diag_screws.js
@@ -6,11 +6,11 @@ const { check, done } = suite();
function fresh() { flatWorld(run, 256, 192, 176); run('RS=7; nextPowerBuild=0; money=10000; AMB.fill(20); temp.fill(20); hot.fill(0);'); }
function line(v, chain = false) {
fresh();
- const [dx, dy] = run(`TUBE_DIRS[${v}]`), bx = dx > 0 ? 16 : 36, by = dy < 0 ? 32 : 12;
- run(`for(let k=0;k<10;k++) placeBlock(BP_TUBE,${v},${bx}+k*${dx},${by}+k*${dy},true);`);
+ const [dx, dy] = run(`SCREW_DIRS[${v}]`), bx = dx > 0 ? 16 : 36, by = dy < 0 ? 32 : 12;
+ run(`for(let k=0;k<10;k++) placeBlock(BP_SCREW,${v},${bx}+k*${dx},${by}+k*${dy},true);`);
let tail = bx, head = bx + 9 * dx;
if (chain) {
- run(`for(let k=1;k<=3;k++) { placeBlock(BP_TUBE,${dx > 0 ? 1 : 3},${bx}-k*${dx},${by},true); placeBlock(BP_TUBE,${dx > 0 ? 1 : 3},${head}+k*${dx},${by + 9 * dy},true); }`);
+ run(`for(let k=1;k<=3;k++) { placeBlock(BP_SCREW,${dx > 0 ? 1 : 3},${bx}-k*${dx},${by},true); placeBlock(BP_SCREW,${dx > 0 ? 1 : 3},${head}+k*${dx},${by + 9 * dy},true); }`);
tail -= 3 * dx; head += 3 * dx;
}
return { dx, dy, bx, by, tail, head, hy: by + 9 * dy };
@@ -21,7 +21,7 @@ function drain(s, steps, feed = false) {
for(let k=0;k<${steps};k++) {
${feed ? `for(let l=0;l<4;l++) { const i=(${s.by}*4+l)*W+${s.tail}*4+${s.dx > 0 ? 0 : 3}; if(mat[i]===EMPTY) {setCell(i,SAND,20,0);added++;} }` : ''}
simTick();
- for(let i=0;i0 ? x>=${(s.head + 1) * 4} : x<${s.head * 4}) out++; else leaks++;
setCell(i,EMPTY,20,0);
}
@@ -37,7 +37,7 @@ for (const v of [4, 5, 6, 7]) {
const s = line(v);
check(`direction ${v} uses appended part ids and marks power`,run(`part[${s.by * 4}*W+${s.bx * 4}]===${v + 7} && blockV[${s.by}*BW+${s.bx}]===${v} && pcells.size===160`));
// Fill every lane, in every block: a corner or side leak is counted before it can fall away.
- run('for(let i=0;i {
@@ -107,7 +107,7 @@ for(const v of [4,5,6,7]) for(const chain of [false,true]) {
// Vertical straight joins reach the same tails and heads too.
for (const v of [4,5,6,7]) {
const s=line(v), vv=s.dy<0?0:2;
- run(`for(let k=1;k<=3;k++){placeBlock(BP_TUBE,${vv},${s.bx},${s.by}-k*${s.dy},true);placeBlock(BP_TUBE,${vv},${s.head},${s.hy}+k*${s.dy},true);}`);
+ run(`for(let k=1;k<=3;k++){placeBlock(BP_SCREW,${vv},${s.bx},${s.by}-k*${s.dy},true);placeBlock(BP_SCREW,${vv},${s.head},${s.hy}+k*${s.dy},true);}`);
putBar(s.bx*4,(s.by-3*s.dy)*4+1);
const r=run(`(()=>{let out=false;for(let k=0;k<500;k++){simTick();if(${s.dy}<0?ents[0].iy+2<=${(s.hy-3)*4}:ents[0].iy>=${(s.hy+4)*4})out=true;}return {out,overlap:mat.some((m,i)=>occ[i] && PH[m]===PH_SOLID)};})()`);
check(`a bar crosses vertical joins at diagonal ${v}`,r.out && !r.overlap,JSON.stringify(r));
@@ -140,11 +140,11 @@ const blocked=line(4);
putBar(blocked.bx*4,blocked.by*4+1);
rect(run,(blocked.head+1)*4,(blocked.hy-1)*4,(blocked.head+2)*4-1,blocked.hy*4-1,run('STONE'));
ticks(run,300);
-check('a blocked diagonal mouth holds its bar without wall overlap',run('ents.length===1 && entTubeAt(ents[0].ix,ents[0].iy) && !mat.some((m,i)=>m===STONE && occ[i])'));
+check('a blocked diagonal mouth holds its bar without wall overlap',run('ents.length===1 && entScrewAt(ents[0].ix,ents[0].iy) && !mat.some((m,i)=>m===STONE && occ[i])'));
rect(run,(blocked.head+1)*4,(blocked.hy-1)*4,(blocked.head+2)*4-1,blocked.hy*4-1,run('EMPTY'));
check('the diagonal bar resumes after the mouth clears',watchBars(blocked).out===1);
for(const v of [4,5,6,7]) {
- fresh(); run(`placeBlock(BP_TUBE,${v},20,20,true);`);
+ fresh(); run(`placeBlock(BP_SCREW,${v},20,20,true);`);
run('globalThis.saved=serializeWorld();deserializeWorld(saved.meta,saved.buffers);');
check(`save/load keeps direction ${v} and power`,run(`blockV[20*BW+20]===${v} && part[80*W+80]===${v+7} && isPow[80*W+80]`));
run('globalThis.clip=copyRegion(80,80,83,83);bot.x=100;bot.y=80;undoRecord(\'paste\',()=>pasteGhost(clip,24,20));ghostTick();');
@@ -156,42 +156,42 @@ for(const v of [4,5,6,7]) {
run('undoRecord(\'cut\',()=>cutRegion(80,80,83,83));undoLast();');
check(`cut and undo restore direction ${v}`,run(`blockV[20*BW+20]===${v} && blockIntegrity(20*BW+20)===16`));
run('money=100;removeBlock(20,20);');
- check(`diagonal ${v} refunds the straight tube price`,run('money')===125);
+ check(`diagonal ${v} refunds the straight screw price`,run('money')===125);
}
// Exercise the actual UI functions without starting a browser or simulating a replacement.
run(fs.readFileSync('src/ui/selection.js','utf8'));
run(fs.readFileSync('src/ui/actions.js','utf8'));
-run('globalThis.GAME={pieceDir:{tube:0}};globalThis.hudDirty=false;globalThis.toolId=()=>\'build\';globalThis.piece=()=>PIECE_BY_ID.tube;globalThis.playSfx=()=>{};');
-const cycle=run('(()=>{const ds=[];for(let k=0;k<8;k++){ds.push(GAME.pieceDir.tube);rotate();}return ds;})()');
-check('R cycles all eight tube directions then wraps',cycle.join(',')==='0,1,2,3,4,5,6,7' && run('GAME.pieceDir.tube')===0,cycle);
+run('globalThis.GAME={pieceDir:{screw:0}};globalThis.hudDirty=false;globalThis.toolId=()=>\'build\';globalThis.piece=()=>PIECE_BY_ID.screw;globalThis.playSfx=()=>{};');
+const cycle=run('(()=>{const ds=[];for(let k=0;k<8;k++){ds.push(GAME.pieceDir.screw);rotate();}return ds;})()');
+check('R cycles all eight screw directions then wraps',cycle.join(',')==='0,1,2,3,4,5,6,7' && run('GAME.pieceDir.screw')===0,cycle);
for(const [dx,dy,v] of [[5,-4,4],[4,5,5],[-5,4,6],[-4,-5,7]]) {
- check(`a diagonal drag picks direction ${v}`,run(`dragDirFor(PIECE_BY_ID.tube,{bx:20,by:20},{bx:${20+dx},by:${20+dy}})`)===v);
+ check(`a diagonal drag picks direction ${v}`,run(`dragDirFor(PIECE_BY_ID.screw,{bx:20,by:20},{bx:${20+dx},by:${20+dy}})`)===v);
check(`direction ${v} lays a corner-touching diagonal run`,run(`blockLine(20,20,${20+dx},${20+dy},true).every(([x,y],k)=>x===20+k*${Math.sign(dx)} && y===20+k*${Math.sign(dy)})`));
}
// End elbows follow the adjoining straight piece's axis, regardless of flow direction.
run(fs.readFileSync('src/render/world.js', 'utf8'));
-for (const v of [4,5,6,7]) for (let dir=0;dir<4;dir++) for (const code of ['BP_TUBE','BP_BELT','BP_FILTER']) {
- if (code !== 'BP_TUBE' && !(dir & 1)) continue;
+for (const v of [4,5,6,7]) for (let dir=0;dir<4;dir++) for (const code of ['BP_SCREW','BP_BELT','BP_FILTER']) {
+ if (code !== 'BP_SCREW' && !(dir & 1)) continue;
fresh();
- const [dx,dy]=run(`TUBE_DIRS[${dir}]`);
- run(`placeBlock(BP_TUBE,${v},20,20,true);placeBlock(${code},${dir},${20+dx},${20+dy},true);`);
+ const [dx,dy]=run(`SCREW_DIRS[${dir}]`);
+ run(`placeBlock(BP_SCREW,${v},20,20,true);placeBlock(${code},${dir},${20+dx},${20+dy},true);`);
const end = dx ? dx>0 : (v===4 || v===6 ? dy<0 : dy>0);
- check(`diagonal ${v} bends toward ${code} direction ${dir}`,run(`(diagonalTubeJoins(20,20,${v}) >> ${end?3:0} & 7)`)===dir+1);
- check(`planned ${code} direction ${dir} bends the diagonal ${v} ghost too`,run(`(()=>{removeBlock(${20+dx},${20+dy});const planned=new Map([[${20+dy}*BW+${20+dx},[${code},${dir}]]]);const j=diagonalTubeJoins(20,20,${v},planned);placeBlock(${code},${dir},${20+dx},${20+dy},true);return (j >> ${end?3:0} & 7)===${dir+1};})()`));
- check(`diagonal ${v} meets ${code} direction ${dir} across its centred edge`,run(`(()=>{const b=20*BW+20,j=diagonalTubeJoins(20,20,${v});return [0.5,1.5,2.5,3.5].every(t=>diagonalTubeShape(${dx ? 82+dx*1.99 : '80+t'},${dy ? 82+dy*1.99 : '80+t'},b,${v},j)<=0);})()`));
+ check(`diagonal ${v} bends toward ${code} direction ${dir}`,run(`(diagonalScrewJoins(20,20,${v}) >> ${end?3:0} & 7)`)===dir+1);
+ check(`planned ${code} direction ${dir} bends the diagonal ${v} ghost too`,run(`(()=>{removeBlock(${20+dx},${20+dy});const planned=new Map([[${20+dy}*BW+${20+dx},[${code},${dir}]]]);const j=diagonalScrewJoins(20,20,${v},planned);placeBlock(${code},${dir},${20+dx},${20+dy},true);return (j >> ${end?3:0} & 7)===${dir+1};})()`));
+ check(`diagonal ${v} meets ${code} direction ${dir} across its centred edge`,run(`(()=>{const b=20*BW+20,j=diagonalScrewJoins(20,20,${v});return [0.5,1.5,2.5,3.5].every(t=>diagonalScrewShape(${dx ? 82+dx*1.99 : '80+t'},${dy ? 82+dy*1.99 : '80+t'},b,${v},j)<=0);})()`));
}
// The drawing can bridge a joint only in unoccupied air; transport parts stay square.
for (const v of [4,5,6,7]) {
flatWorld(run,240,192,176);
const rising = v===4 || v===6;
- run(`placeBlock(BP_TUBE,${v},20,20,true);placeBlock(BP_TUBE,${v},21,${rising ? 19 : 21},true);`);
+ run(`placeBlock(BP_SCREW,${v},20,20,true);placeBlock(BP_SCREW,${v},21,${rising ? 19 : 21},true);`);
const x=84, y=rising ? 80 : 83;
- check(`diagonal ${v} draws over the empty corner joint`,run(`diagonalTubeBlock(${x},${y},false)>=0 && part[${y}*W+${x}]===0`));
+ check(`diagonal ${v} draws over the empty corner joint`,run(`diagonalScrewBlock(${x},${y},false)>=0 && part[${y}*W+${x}]===0`));
for(const obstacle of ['mat[j]=STONE','mat[j]=SAND','part[j]=P_PIPE','mach[j]=1','occ[j]=1','blockP[(j/W>>2)*BW+(j%W>>2)]=BP_WALL']) {
- check(`diagonal ${v} leaves ${obstacle} visible`,run(`(()=>{const j=${y}*W+${x};${obstacle};const hidden=diagonalTubeBlock(${x},${y},true);mat[j]=EMPTY;part[j]=0;mach[j]=0;occ[j]=0;blockP[(${y}>>2)*BW+(${x}>>2)]=0;return hidden<0;})()`));
+ check(`diagonal ${v} leaves ${obstacle} visible`,run(`(()=>{const j=${y}*W+${x};${obstacle};const hidden=diagonalScrewBlock(${x},${y},true);mat[j]=EMPTY;part[j]=0;mach[j]=0;occ[j]=0;blockP[(${y}>>2)*BW+(${x}>>2)]=0;return hidden<0;})()`));
}
- check(`diagonal ${v} keeps its continuation uncapped`,run(`(diagonalTubeJoins(20,20,${v}) & 128)===0 && (diagonalTubeJoins(21,${rising ? 19 : 21},${v}) & 64)===0`));
+ check(`diagonal ${v} keeps its continuation uncapped`,run(`(diagonalScrewJoins(20,20,${v}) & 128)===0 && (diagonalScrewJoins(21,${rising ? 19 : 21},${v}) & 64)===0`));
run(`removeBlock(21,${rising ? 19 : 21});`);
- check(`diagonal ${v} has no stray corner after its end`,run(`diagonalTubeBlock(${x+1},${rising ? y-1 : y+1},false)===-1`));
+ check(`diagonal ${v} has no stray corner after its end`,run(`diagonalScrewBlock(${x+1},${rising ? y-1 : y+1},false)===-1`));
}
done();
diff --git a/test/t_light.js b/test/t_light.js
index b84f48d..c887f40 100644
--- a/test/t_light.js
+++ b/test/t_light.js
@@ -21,7 +21,7 @@ run(`
placeBlock(BP_WALL, RSTONE, 29, by, true);
}
placeBlock(BP_BELT, 1, 12, 30, true);
- placeBlock(BP_TUBE, 0, 14, 30, true);
+ placeBlock(BP_SCREW, 0, 14, 30, true);
addMachine('silo', 18, 30, 0);
addMold('bit', 24, 30, RSTONE, true);
for (let y = 80; y < 88; y++) for (let x = 180; x < 280; x++) setCell(y * W + x, STONE, AMB[y], F_NAT);
@@ -37,7 +37,7 @@ for (const mode of ['full', 'fast']) {
check(`${mode} ${channel}: natural overhang still shades`, rock < open * 0.95, `${rock} / ${open}`);
}
// Directly check transmission as daylight can hide an opaque piece in the open.
- for (const [name, x, y] of [['wall', 32, 120], ['belt', 48, 120], ['tube', 56, 120], ['machine', 72, 120], ['mold', 96, 120]]) {
+ for (const [name, x, y] of [['wall', 32, 120], ['belt', 48, 120], ['screw', 56, 120], ['machine', 72, 120], ['mold', 96, 120]]) {
const a = run(`lightAt(R.latt, ${x}, ${y})`);
check(`${mode}: ${name} transmits like air`, Math.abs(a - Math.pow(0.978, mode === 'fast' ? 4 : 2)) < 1e-6, a);
}
@@ -99,8 +99,8 @@ for (const [name, support, depth, gap, expected] of [
['natural ledge', 'mat[i] = STONE; flags[i] = F_NAT', 3, '', false],
['deep natural heap', 'mat[i] = SAND; flags[i] = F_NAT', 13, '', false],
['wall floor', 'mat[i] = STONE', 3, '', true],
- ['tube', 'part[i] = P_TR', 3, '', true],
- ['loaded tube', 'mat[i] = SAND; part[i] = P_TR', 3, '', true],
+ ['screw', 'part[i] = P_SCREW_R', 3, '', true],
+ ['loaded screw', 'mat[i] = SAND; part[i] = P_SCREW_R', 3, '', true],
]) {
run(`
for (let y = 40; y <= 53; y++) { const i = y * W + 300; mat[i] = SAND; part[i] = 0; flags[i] = 0; }
diff --git a/test/t_racks.js b/test/t_racks.js
index 5d91004..388c7a4 100644
--- a/test/t_racks.js
+++ b/test/t_racks.js
@@ -64,10 +64,10 @@ check('missing rack metadata defaults to empty and closed', run('rackState.get(2
fresh(3);
run('rackPush(rackState.get(20 * BW + 10), BARFE, 3); rackPush(rackState.get(22 * BW + 10), BARCU, 1); interactAt(40, 84); for (let t = 0; t < 20; t++) rackTick();');
check('refilling crosses an empty middle block without losing FIFO order', run('ents[0].m === BARCU && rackState.get(22 * BW + 10).n === 3 && rackState.get(20 * BW + 10).n === 0'));
-for (const delivery of ['belt', 'tube', 'thrown', 'mold']) {
+for (const delivery of ['belt', 'screw', 'thrown', 'mold']) {
fresh();
if (delivery === 'belt') run('for (let bx = 6; bx <= 9; bx++) placeBlock(BP_BELT, 1, bx, 20, true); spawnBar(BARCU, 30, 78);');
- if (delivery === 'tube') run('placeBlock(BP_TUBE, 2, 10, 17, true); placeBlock(BP_TUBE, 2, 10, 18, true); spawnBar(BARCU, 40, 68);');
+ if (delivery === 'screw') run('placeBlock(BP_SCREW, 2, 10, 17, true); placeBlock(BP_SCREW, 2, 10, 18, true); spawnBar(BARCU, 40, 68);');
if (delivery === 'thrown') run('spawnBar(BARCU, 34, 66, 0.35, -0.3);');
if (delivery === 'mold') run("addMold('ingot', 10, 17, RSTONE, true); for (const i of molds[0].cavs[0].slice(0, 8)) setCell(i, COPPER, 20, F_CAST); checkMolds();");
run('for (let t = 0; t < 300; t++) simTick();');
diff --git a/test/t_slope_belt_art.js b/test/t_slope_belt_art.js
index 3df4b94..e2423d7 100644
--- a/test/t_slope_belt_art.js
+++ b/test/t_slope_belt_art.js
@@ -62,9 +62,9 @@ check('free end rounds its corners and stops at the end', run(`(() => {
blockP[b] = BP_BELT_DOWN;
return slopeBeltShape(x + 0.8, y + 0.8, b, 0) < 0 && slopeBeltShape(x - 1, y - 1, b, 0) > 0;
})()`));
-check('flat belts still meet diagonal tube elbows without an end cap', run(`(() => {
+check('flat belts still meet diagonal screw elbows without an end cap', run(`(() => {
blockP[artB - 1] = BP_BELT;
- blockP[artB] = BP_TUBE; blockV[artB] = 4;
+ blockP[artB] = BP_SCREW; blockV[artB] = 4;
const joined = !(flatBeltCaps(artB - 1) & 2);
blockP[artB] = BP_BELT_DOWN;
return joined;
diff --git a/test/t_slope_belts.js b/test/t_slope_belts.js
index 71caa67..60519e4 100644
--- a/test/t_slope_belts.js
+++ b/test/t_slope_belts.js
@@ -71,14 +71,14 @@ for(const grade of [-1,1]) {
check(`grade ${grade} keeps a bar moving forward off an open end`,barLaunch);
for(const diagonal of [false,true]) {
line(grade);
- run(`for(let bx=26;bx<32;bx++)removeBlock(bx,${s.outlet});for(let k=0;k<3;k++)placeBlock(BP_TUBE,${diagonal?(grade<0?4:5):1},26+k,${s.by+9*grade+(grade<0?-1:0)}+k*${diagonal?grade:0},true);`);
+ run(`for(let bx=26;bx<32;bx++)removeBlock(bx,${s.outlet});for(let k=0;k<3;k++)placeBlock(BP_SCREW,${diagonal?(grade<0?4:5):1},26+k,${s.by+9*grade+(grade<0?-1:0)}+k*${diagonal?grade:0},true);`);
rect(run,40,s.inlet*4-8,55,s.inlet*4-1,run('SAND'));
rect(run,39,20,39,s.inlet*4-1,run('STONE'));
const r=collect(s,1000,false,116);
- check(`grade ${grade} unloads grains into a ${diagonal?'diagonal':'straight'} tube`,r.out===128 && r.left===0,JSON.stringify(r));
+ check(`grade ${grade} unloads grains into a ${diagonal?'diagonal':'straight'} screw`,r.out===128 && r.left===0,JSON.stringify(r));
run(`spawnBar(BARCU,44,${s.inlet*4-2},0,0);`);
ticks(run,600);
- check(`grade ${grade} unloads a bar into a ${diagonal?'diagonal':'straight'} tube`,run('ents.length===1 && ents[0].ix>=116 && !mat.some((m,i)=>occ[i] && PH[m]===PH_SOLID)'),run('ents.map(e=>[e.ix,e.iy])'));
+ check(`grade ${grade} unloads a bar into a ${diagonal?'diagonal':'straight'} screw`,run('ents.length===1 && ents[0].ix>=116 && !mat.some((m,i)=>occ[i] && PH[m]===PH_SOLID)'),run('ents.map(e=>[e.ix,e.iy])'));
}
}
diff --git a/test/t_world.js b/test/t_world.js
index 7e42890..631db5d 100644
--- a/test/t_world.js
+++ b/test/t_world.js
@@ -216,16 +216,16 @@ check('blown grains never come back into the bag', run('bag[SAND]') === 0 && cou
// --- round 6: lifts, falling trees, machines without power, the bot and walls
fresh();
-// a shaft 16 wide, a column of up-tubes from its floor, turned sideways at the top to unload onto the ground
+// a shaft 16 wide, a column of up-screws from its floor, turned sideways at the top to unload onto the ground
run('for (let y = 100; y < 132; y++) for (let x = 120; x < 128; x++) setCell(y*W+x, EMPTY, AMB[y], 0)');
run('bot.x = 110; bot.y = 90;');
// unload well above the ground: a pile that reaches the outlet would block it
-run('for (let by = 19; by <= 32; by++) placeBlock(BP_TUBE, 0, 30, by, true); for (let bx = 30; bx <= 35; bx++) placeBlock(BP_TUBE, 1, bx, 18, true);');
+run('for (let by = 19; by <= 32; by++) placeBlock(BP_SCREW, 0, 30, by, true); for (let bx = 30; bx <= 35; bx++) placeBlock(BP_SCREW, 1, bx, 18, true);');
box(120, 116, 127, 131, run('SAND'));
const sand0 = count(run, run('SAND'));
ticks(run, 1800);
const lifted = count(run, run('SAND'), 136, 0, 239, 99);
-check('a column of upward screw tubes lifts sand out of a shaft, unpowered', lifted > sand0 * 0.8, `${lifted} of ${sand0} grains brought up and out`);
+check('a column of upward screw conveyors lifts sand out of a shaft, unpowered', lifted > sand0 * 0.8, `${lifted} of ${sand0} grains brought up and out`);
fresh();
run('for (let bx = 25; bx <= 30; bx++) placeBlock(BP_BELT, 1, bx, 22, true); rebuildPower();');
box(101, 84, 104, 87, run('SAND'));
diff --git a/tools/experiments/flow.js b/tools/experiments/flow.js
index 47c55eb..a3ab92b 100644
--- a/tools/experiments/flow.js
+++ b/tools/experiments/flow.js
@@ -1,4 +1,4 @@
-// Throughput of belts and screw tubes: grains delivered per second. Whatever lands
+// Throughput of belts and screw conveyors: grains delivered per second. Whatever lands
// past the outlet is taken away, so the output pile never backs up into the line.
const { load, flatWorld, rect, ticks, count } = require('../../test/harness');
const { run } = load();
@@ -11,18 +11,18 @@ function rate(x0, x1, y0, y1) {
for (let k = 0; k < 300; k++) run(`simTick(); ${sink}`);
return run('__got') / 5;
}
-// sideways tube fed by a walled hopper sitting on its first two blocks
+// sideways screw fed by a walled hopper sitting on its first two blocks
fresh();
-run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_TUBE, 1, bx, 20, true);');
+run('for (let bx = 20; bx < 30; bx++) placeBlock(BP_SCREW, 1, bx, 20, true);');
run('for (let y = 0; y < 80; y++) { setCell(y*W+79, STONE, AMB[y], 0); setCell(y*W+96, STONE, AMB[y], 0); }');
rect(run, 80, 0, 95, 79, run('SAND'));
-res.tubeSideways = rate(120, 319, 84, 119);
-res.tubeLanes = run('(() => { const s = new Set(); for (let y = 80; y < 84; y++) for (let x = 100; x < 120; x++) if (mat[y*W+x] === SAND) s.add(y); return s.size; })()');
-// lift: a column of up-tubes in a pit of sand, turned sideways at the top
+res.screwSideways = rate(120, 319, 84, 119);
+res.screwLanes = run('(() => { const s = new Set(); for (let y = 80; y < 84; y++) for (let x = 100; x < 120; x++) if (mat[y*W+x] === SAND) s.add(y); return s.size; })()');
+// lift: a column of up-screws in a pit of sand, turned sideways at the top
fresh();
run('for (let y = 120; y < 196; y++) for (let x = 90; x < 130; x++) setCell(y*W+x, EMPTY, AMB[y], 0)');
rect(run, 90, 130, 129, 195, run('SAND'));
-run('for (let by = 20; by <= 37; by++) placeBlock(BP_TUBE, 0, 26, by, true); for (let bx = 26; bx <= 40; bx++) placeBlock(BP_TUBE, 1, bx, 19, true);');
+run('for (let by = 20; by <= 37; by++) placeBlock(BP_SCREW, 0, 26, by, true); for (let bx = 26; bx <= 40; bx++) placeBlock(BP_SCREW, 1, bx, 19, true);');
res.lift = rate(164, 319, 80, 119);
res.liftLanes = run('(() => { const s = new Set(); for (let y = 76; y < 80; y++) for (let x = 120; x < 160; x++) if (mat[y*W+x] === SAND) s.add(y); return s.size; })()');
// belt: 12 blocks, a pile dropped on its back end
diff --git a/tools/scenes/bar-lift.js b/tools/scenes/bar-lift.js
index 149252b..e068f7f 100644
--- a/tools/scenes/bar-lift.js
+++ b/tools/scenes/bar-lift.js
@@ -8,8 +8,8 @@ for (let x = (bx - 3) * BS; x < (bx + 27) * BS; x++) surfY[x] = (by + 4) * BS;
const mo = addMold('ingot', bx, by - 4, RSTONE, true);
const beltY = Math.ceil((mo.y + mo.h) / BS) + 1;
for (let x = bx; x < bx + 9; x++) placeBlock(BP_BELT, 1, x, beltY, true);
-for (let y = by - 11; y < beltY; y++) placeBlock(BP_TUBE, 0, bx + 9, y, true);
-for (let x = bx + 9; x < bx + 16; x++) placeBlock(BP_TUBE, 1, x, by - 12, true);
+for (let y = by - 11; y < beltY; y++) placeBlock(BP_SCREW, 0, bx + 9, y, true);
+for (let x = bx + 9; x < bx + 16; x++) placeBlock(BP_SCREW, 1, x, by - 12, true);
for (let x = bx + 16; x < bx + 25; x++) placeBlock(BP_BELT, 1, x, by - 11, true);
placeBlock(BP_WALL, RSTONE, bx + 25, by - 12, true);
mo.cavs[0].forEach(i => setCell(i, MCU, 1090, 0));
diff --git a/tools/scenes/belt-heap.js b/tools/scenes/belt-heap.js
index 948452a..9ab4dd5 100644
--- a/tools/scenes/belt-heap.js
+++ b/tools/scenes/belt-heap.js
@@ -3,7 +3,7 @@
// python3 tools/shot.py /tmp/belt-heap.png --setup tools/scenes/clear-sky.js --js "$(cat tools/scenes/belt-heap.js)" --ticks 240 --zoom 7
const bx = BX + 6, by = BY;
for (let x = bx; x < bx + 12; x++) placeBlock(BP_BELT, 1, x, by, true);
-for (let y = by - 10; y <= by; y++) placeBlock(BP_TUBE, 0, bx + 12, y, true);
+for (let y = by - 10; y <= by; y++) placeBlock(BP_SCREW, 0, bx + 12, y, true);
for (let y = by * 4 - 30; y < by * 4; y++) for (let x = (bx + 2) * 4; x < (bx + 10) * 4; x++)
setCell(y * W + x, y >= by * 4 - 6 ? SAND : CUORE, AMB[y], 0);
window.CAM = [(bx + 7) * 4, (by - 5) * 4];
diff --git a/tools/scenes/t35-diagonal.js b/tools/scenes/t35-diagonal.js
index ac660d8..9e7eb14 100644
--- a/tools/scenes/t35-diagonal.js
+++ b/tools/scenes/t35-diagonal.js
@@ -1,7 +1,7 @@
// Run after clear-sky.js: a belt takes sand from a pile, up a diagonal screw to an upper belt.
const tx = BX + 12, ty = BY - 1;
for (let x = BX + 3; x < tx; x++) placeBlock(BP_BELT, 1, x, BY, true);
-for (let k = 0; k < 10; k++) placeBlock(BP_TUBE, 4, tx + k, ty - k, true);
+for (let k = 0; k < 10; k++) placeBlock(BP_SCREW, 4, tx + k, ty - k, true);
for (let x = tx + 10; x < tx + 18; x++) placeBlock(BP_BELT, 1, x, ty - 9, true);
for (let y = BY * 4 - 22; y < BY * 4; y++) for (let x = (BX + 4) * 4; x < (BX + 11) * 4; x++) setCell(y * W + x, SAND, 20, 0);
window.CAM = [(tx + 4) * 4, (ty - 4) * 4];
diff --git a/tools/scenes/t35-joins.js b/tools/scenes/t35-joins.js
index 49bc6d1..a5fc358 100644
--- a/tools/scenes/t35-joins.js
+++ b/tools/scenes/t35-joins.js
@@ -1,13 +1,13 @@
// Run after clear-sky.js. Paused, empty pipes make casing, flights and mouths easy to inspect.
const jx = BX + 5, jy = BY - 3;
-for (let k = 1; k <= 4; k++) placeBlock(BP_TUBE, 0, jx, jy + k, true);
-for (let k = 0; k < 6; k++) placeBlock(BP_TUBE, 4, jx + k, jy - k, true);
-for (let k = 1; k <= 4; k++) placeBlock(BP_TUBE, 0, jx + 5, jy - 5 - k, true);
+for (let k = 1; k <= 4; k++) placeBlock(BP_SCREW, 0, jx, jy + k, true);
+for (let k = 0; k < 6; k++) placeBlock(BP_SCREW, 4, jx + k, jy - k, true);
+for (let k = 1; k <= 4; k++) placeBlock(BP_SCREW, 0, jx + 5, jy - 5 - k, true);
const hx = BX + 21, hy = BY - 3;
for (let k = 1; k <= 4; k++) placeBlock(BP_BELT, 1, hx - k, hy, true);
-for (let k = 0; k < 6; k++) placeBlock(BP_TUBE, 4, hx + k, hy - k, true);
+for (let k = 0; k < 6; k++) placeBlock(BP_SCREW, 4, hx + k, hy - k, true);
for (let k = 1; k <= 4; k++) placeBlock(BP_BELT, 1, hx + 5 + k, hy - 5, true);
-for (let k = 0; k < 4; k++) placeBlock(BP_TUBE, 5, BX + 32 + k, BY - 10 + k, true);
+for (let k = 0; k < 4; k++) placeBlock(BP_SCREW, 5, BX + 32 + k, BY - 10 + k, true);
window.JOINTS = {
verticalIn: [jx * 4 + 2, jy * 4 + 4],
verticalOut: [(jx + 5) * 4 + 2, (jy - 5) * 4],