Thruster flames, so a watcher can see what the pilot is doing

Every reading on this screen is a number drawn as a bar - how fast
sideways, how fast down, how much sky, how much tank - and all of it says
what is happening TO the lander. None of it says what the pilot is doing
about it, so somebody watching over a shoulder has to read gauges to work
out that a thruster is even lit.

A plume hangs off whichever side the engine is pushing from: under the
lander to lift, over it to retro, and on the far side from the way it is
being pushed sideways, since that is the side the gas leaves. One tile
does up and down, because a vertical flip turns one into the other, and a
second does the sides, because a flip cannot rotate a tile a quarter
turn. Twenty four pixels each, on purpose, so a count of them means
something.

HELD, NOT FIRED. The engine fires one frame in ten, because that is the
tick gravity is applied on, and a flame that honest would be one frame of
light six times a second - a fault lamp, not a rocket. What is drawn is
the button being down, which is the truthful answer to "is the pilot
burning": the tick is how the sum gets done, not what is happening.

An empty tank draws nothing, and nor does the retro thruster on the
ground, because in both cases the button really is doing nothing. The
second of those was a lie the first version told.

Red, and that is by elimination again: white is the ceiling warning, cyan
the landing pads, magenta the instruments, blue the station, yellow the
lander itself - and the lander is counted as exactly forty pixels of
yellow, so a yellow flame would have broken it.

Three checks, each seen to fail on its own break. Two things the fixtures
taught: a lander placed at the world's origin sits BEHIND the two row
window, which reads exactly like a flame that is not drawn; and red has
to be looked for in a box round the lander rather than a column, because
the speed bars go red and one of the four pads is red too.
This commit is contained in:
Anachronaut
2026-09-04 11:49:38 -04:00
parent 418631a221
commit c408fc6cf6
13 changed files with 404 additions and 12 deletions
+88
View File
@@ -2515,6 +2515,94 @@ grep -q "Wrecked against the station" "$BUILD/wreck.out" \
&& result ok "and the same approach unmatched is a wreck" "64 sixteenths, eight times the limit" \
|| result no "and the same approach unmatched is a wreck" "no wreck in the transcript"
# ---- Thruster flames ----
#
# Every other reading here is a number drawn as a bar: how fast sideways, how fast down, how
# much sky, how much tank. All of it says what is happening TO the lander and none of it says
# what the PILOT is doing, so somebody watching over a shoulder has to read gauges to work out
# that a thruster is even lit.
#
# A plume hangs off whichever side the engine is pushing from - under the lander to go up,
# over it to go down, and on the far side from the way it is being pushed sideways. Twenty
# four pixels of it, which is the whole tile, so the count says it is not clipped.
#
# Held rather than fired. The engine fires one frame in ten, because that is the tick gravity
# is applied on, and a flame that honest would be one frame of light six times a second - a
# fault lamp, not a rocket. The button being down is the thing being shown.
python3 -c "
import struct
# The same place every time, with an empty tank for the last of them. Down 512 sixteenths and
# not nought: the world's origin is the top of the screen, and the two row window is drawn OVER
# the map there, so a lander placed at the origin is behind the fuel gauge and its flame with
# it - which reads exactly like a flame that is not being drawn.
for name, fuel in (('flamefull', 255), ('flamedry', 0)):
open('$BUILD/%s.state' % name, 'wb').write(struct.pack('<hhhhhhBBBB',
0x14A0, 512, 0, 0, 0x14A0, 2600, fuel, 0, 0, 0))
"
for which in flamefull flamedry; do
cp "$ROOT/Tests/build/disks/cosmos.img" "$BUILD/$which.img"
"$ROOT/SplitDisk" put "$BUILD/$which.img" "$BUILD/$which.state" /lander.state > /dev/null
done
for held in none up down right left dry; do
case "$held" in
none) byte='\x00'; disk=flamefull ;;
up) byte='\x08'; disk=flamefull ;;
down) byte='\x04'; disk=flamefull ;;
right) byte='\x01'; disk=flamefull ;;
left) byte='\x02'; disk=flamefull ;;
dry) byte='\x08'; disk=flamedry ;;
esac
python3 -c "open('$BUILD/flame$held.pad','wb').write(b'$byte' * 40000)"
timeout 60 "$EMU" --fast --cycles 1500000 --keyboard "$BUILD/fly.keys" \
--pad "$BUILD/flame$held.pad" --screen "$BUILD/flame$held.ppm" \
--disk "$BUILD/$disk.img" --ram-disk 2048 \
"$BUILD/cosmos.bin" > "$BUILD/flame$held.out" 2>&1 || true
done
read -r FNONE FUP FDOWN FRIGHT FLEFT FDRY <<EOT
$(python3 -c "
def flame(path):
# Red, and IN A BOX ROUND THE LANDER. The speed bars go red when they say a landing would
# not be survived, and one of the four landing pads is red as well - both of them turned
# up inside a filter that only looked at the column.
d = open(path, 'rb').read()
width = int(d[:40].split()[1])
px = d[d.index(b'255\n') + 4:]
def where(colour):
c = bytes.fromhex(colour)
return [(i % width, i // width) for i in range(len(px) // 3)
if px[i * 3:i * 3 + 3] == c]
ship = where('d8c048')
if not ship:
return 'noship'
sx, sy = min(x for x, y in ship), min(y for x, y in ship)
lit = [(x, y) for x, y in where('d04038')
if sx - 12 <= x <= sx + 20 and sy - 12 <= y <= sy + 20]
if not lit:
return '0'
# Which side of the lander it came out on, which is the half that matters.
fx, fy = min(x for x, y in lit), min(y for x, y in lit)
side = 'below' if fy > sy else 'above'
if fx < sx: side = 'left'
if fx > sx + 4: side = 'right'
return '%d%s' % (len(lit), side)
print(flame('$BUILD/flamenone.ppm'), flame('$BUILD/flameup.ppm'),
flame('$BUILD/flamedown.ppm'), flame('$BUILD/flameright.ppm'),
flame('$BUILD/flameleft.ppm'), flame('$BUILD/flamedry.ppm'))
" 2>/dev/null || echo "x x x x x x")
EOT
[ "$FNONE" = "0" ] && [ "$FUP" = "24below" ] && [ "$FDOWN" = "24above" ] \
&& result ok "a lifting thruster shows a flame under the lander" "none held draws none, up draws 24 below, down 24 above" \
|| result no "a lifting thruster shows a flame under the lander" "none $FNONE, up $FUP, down $FDOWN"
# Sideways is the one that is easy to get backwards: pushing RIGHT means the plume comes out
# of the LEFT of the lander, because that is the side the gas leaves from.
[ "$FRIGHT" = "24left" ] && [ "$FLEFT" = "24right" ] \
&& result ok "and comes out the far side going sideways" "right draws left, left draws right" \
|| result no "and comes out the far side going sideways" "right $FRIGHT, left $FLEFT"
# An empty tank draws nothing, because then the button really is doing nothing.
[ "$FDRY" = "0" ] \
&& result ok "and an empty tank draws none at all" "the button is doing nothing, and shows it" \
|| result no "and an empty tank draws none at all" "$FDRY with a dry tank"
# ---- Clearing puts the cursor back at the top ----
#
# A screen with nothing on it and a cursor half way down it is not a cleared screen. This