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: '
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.