A crash takes the lander apart, instead of just saying so
The verdict used to be the whole of it: a line of text and a lander still sitting there in one piece, so somebody watching a recording had to read the words to know what had happened. That is the same problem the flames were for. The lander goes, and both flames with it, and six pieces of it leave in a rough hexagon at its own colour for about a second. Then they go too, rather than hanging over the words. The world is not running while it plays: it is a loop of its own, so nothing else in the program has to know how to be half destroyed. Yellow, which is the lander's own colour, because it IS the lander - and it is only free to use because the lander itself is hidden by then. The check that counts exactly forty pixels of yellow looks at a flying frame. ---- Two mistakes worth keeping ---- The block went in between touchdownCrash and touchdownStop, so a crash fell into the explosion and returned from there - no verdict, no end of run, and the lander sitting there being crashed into the ground again every frame. The linter caught it as a subroutine nothing called walking into, which is exactly what it was. And copyWord goes DP0 to DP1, so setting the pieces off from the lander's position had the pointers the wrong way round: it copied the empty pieces OVER ShipX. Since that is in the view block, it took the lander's own column with it, and the one piece that could be seen drifted out of the top left corner of the screen. Three checks, each seen to fail on its own break. They measure the SPREAD and not the count: two of the six leave the top of the screen on the way, so the count drops from twenty four to sixteen, which is correct and would make an exact count a check that breaks the day a lander crashes somewhere else.
This commit is contained in:
@@ -1826,6 +1826,7 @@ touchdown:
|
||||
touchdownCrash:
|
||||
SETD.0 CrashedText
|
||||
touchdownStop:
|
||||
CALL explode ; Before the verdict, so there is something to watch.
|
||||
; ---- The view settled before anything is said ----
|
||||
;
|
||||
; The console draws into the map, and the map is what is being scrolled - so a message
|
||||
@@ -2321,6 +2322,201 @@ putFlame:
|
||||
OUTA 0xE9 ; And no depth.
|
||||
RET
|
||||
|
||||
; ---- Coming apart, which is the only thing on this screen that is not a number ----
|
||||
;
|
||||
; A crash used to be a line of text and a lander still sitting there in one piece. The verdict
|
||||
; was the whole of it, and somebody watching a recording had to read the words to know what had
|
||||
; happened - the same problem the flames were for.
|
||||
;
|
||||
; The lander goes and six pieces of it leave in a rough hexagon, at its own colour, for half a
|
||||
; second. The world is not running while this happens: it is a loop of its own, so nothing has
|
||||
; to know how to be half destroyed.
|
||||
explode:
|
||||
RSTA
|
||||
CALL hideSprite ; The lander itself.
|
||||
INIA 0x70
|
||||
CALL hideSprite
|
||||
INIA 0x80
|
||||
CALL hideSprite ; And both flames, which would otherwise hang in the air.
|
||||
|
||||
; Every piece starts where the lander was.
|
||||
RSTA
|
||||
SETD.0 SparkAt
|
||||
STA.0
|
||||
INIA 0d6
|
||||
SETD.0 SparkCount
|
||||
STA.0
|
||||
explodeStart:
|
||||
; DP0 is what copyWord reads and DP1 what it writes, so the lander's place is DP0. Written
|
||||
; the other way round first, which copied the empty sparks OVER ShipX - and since that is
|
||||
; the view block, the lander's own column with it.
|
||||
SETD.0 ShipX
|
||||
SETD.1 Spark
|
||||
SETD.2 SparkAt
|
||||
LDA.2
|
||||
DPUA.1
|
||||
CALL copyWord
|
||||
SETD.0 ShipTopLow
|
||||
SETD.1 Spark
|
||||
SETD.2 SparkAt
|
||||
LDA.2
|
||||
INCA
|
||||
INCA
|
||||
DPUA.1
|
||||
CALL copyWord
|
||||
SETD.0 SparkAt
|
||||
LDA.0
|
||||
INIB 0d4
|
||||
CCF
|
||||
ADD
|
||||
STQ.0
|
||||
SETD.0 SparkCount
|
||||
LDA.0
|
||||
DECA
|
||||
STA.0
|
||||
BNA explodeStart
|
||||
|
||||
INIA 0d48
|
||||
SETD.0 SparkLeft
|
||||
STA.0 ; Getting on for a second, which is long enough to watch.
|
||||
explodeFrame:
|
||||
CALL waitFrame
|
||||
RSTA
|
||||
SETD.0 SparkAt
|
||||
STA.0
|
||||
INIA 0x90
|
||||
SETD.0 SparkSlot
|
||||
STA.0
|
||||
INIA 0d6
|
||||
SETD.0 SparkCount
|
||||
STA.0
|
||||
explodeOne:
|
||||
SETD.0 Spark
|
||||
SETD.2 SparkAt
|
||||
LDA.2
|
||||
DPUA.0
|
||||
SETD.1 SparkV
|
||||
DPUA.1
|
||||
CALL addWord ; Across, by however fast this piece is going.
|
||||
SETD.0 Spark
|
||||
SETD.2 SparkAt
|
||||
LDA.2
|
||||
INCA
|
||||
INCA
|
||||
DPUA.0
|
||||
SETD.1 SparkV
|
||||
DPUA.1
|
||||
CALL addWord ; And down.
|
||||
CALL putSpark
|
||||
|
||||
SETD.0 SparkAt
|
||||
LDA.0
|
||||
INIB 0d4
|
||||
CCF
|
||||
ADD
|
||||
STQ.0
|
||||
SETD.0 SparkSlot
|
||||
LDA.0
|
||||
INIB 0x10
|
||||
CCF
|
||||
ADD
|
||||
STQ.0
|
||||
SETD.0 SparkCount
|
||||
LDA.0
|
||||
DECA
|
||||
STA.0
|
||||
BNA explodeOne
|
||||
|
||||
SETD.0 SparkLeft
|
||||
LDA.0
|
||||
DECA
|
||||
STA.0
|
||||
BNA explodeFrame
|
||||
|
||||
; And away, or they would sit there under the verdict.
|
||||
INIA 0x90
|
||||
SETD.0 SparkSlot
|
||||
STA.0
|
||||
INIA 0d6
|
||||
SETD.0 SparkCount
|
||||
STA.0
|
||||
explodeClear:
|
||||
SETD.0 SparkSlot
|
||||
LDA.0
|
||||
CALL hideSprite
|
||||
SETD.0 SparkSlot
|
||||
LDA.0
|
||||
INIB 0x10
|
||||
CCF
|
||||
ADD
|
||||
STQ.0
|
||||
SETD.0 SparkCount
|
||||
LDA.0
|
||||
DECA
|
||||
STA.0
|
||||
BNA explodeClear
|
||||
RET
|
||||
|
||||
; One piece, where the pointers say it now is.
|
||||
putSpark:
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0xC0
|
||||
OUTA 0xE4
|
||||
SETD.0 SparkSlot
|
||||
LDA.0
|
||||
OUTA 0xE5
|
||||
INIA 0xC8
|
||||
OUTA 0xE9 ; The solid block, at two pixels by two.
|
||||
INIA 0x03
|
||||
OUTA 0xE9 ; Yellow: it is the lander that is coming apart.
|
||||
SETD.0 Spark
|
||||
SETD.2 SparkAt
|
||||
LDA.2
|
||||
DPUA.0
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE9
|
||||
INCD.0
|
||||
LDA.0
|
||||
OUTA 0xE9 ; Across and down, four bytes in a row of them.
|
||||
INIA 0x11
|
||||
OUTA 0xE9
|
||||
RSTA
|
||||
OUTA 0xE9 ; No flags.
|
||||
INIA 0d2
|
||||
OUTA 0xE9
|
||||
RSTA
|
||||
OUTA 0xE9
|
||||
INIA 0d2
|
||||
OUTA 0xE9
|
||||
RSTA
|
||||
OUTA 0xE9 ; Two by two, which is a piece and not a lander.
|
||||
OUTA 0xE9 ; And no depth.
|
||||
RET
|
||||
|
||||
; Byte six of a sprite is its size, and nought there is the off switch.
|
||||
hideSprite:
|
||||
SETD.0 HideSlot
|
||||
STA.0
|
||||
INIA 0d4
|
||||
OUTA 0xE3
|
||||
INIA 0xC0
|
||||
OUTA 0xE4
|
||||
LDA.0
|
||||
INIB 0d6
|
||||
CCF
|
||||
ADD
|
||||
OUTQ 0xE5
|
||||
RSTA
|
||||
OUTA 0xE9
|
||||
RET
|
||||
|
||||
; ---- A key, or a button ----
|
||||
;
|
||||
; Somebody flying on a controller should not have to reach for the keyboard to say "yes, I
|
||||
@@ -3437,6 +3633,30 @@ ShipTopLow:
|
||||
ShipTopHigh:
|
||||
0x00
|
||||
|
||||
; ---- Six pieces of lander, and where each is going ----
|
||||
;
|
||||
; Interleaved, four bytes to a piece: where it is across, then where it is down. The speeds
|
||||
; beside them are a rough hexagon, so it comes apart evenly rather than in a line.
|
||||
Spark:
|
||||
#Reserve 0d24
|
||||
SparkV:
|
||||
0x02 0x00 0x00 0x00
|
||||
0x01 0x00 0x02 0x00
|
||||
0xFF 0xFF 0x02 0x00
|
||||
0xFE 0xFF 0x00 0x00
|
||||
0xFF 0xFF 0xFE 0xFF
|
||||
0x01 0x00 0xFE 0xFF
|
||||
SparkAt:
|
||||
0x00
|
||||
SparkSlot:
|
||||
0x00
|
||||
SparkCount:
|
||||
0x00
|
||||
SparkLeft:
|
||||
0x00
|
||||
HideSlot:
|
||||
0x00
|
||||
|
||||
; What a flame is being asked to be, before it is written out.
|
||||
FlameSlot:
|
||||
0x00
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -25,7 +25,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -15,7 +15,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -32,7 +32,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -22,7 +22,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -22,7 +22,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -108,7 +108,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -27,7 +27,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -15,7 +15,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -13,7 +13,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -22,7 +22,7 @@ Mode.sbx 48
|
||||
Flip.sbx 173
|
||||
Sprite.sbx 442
|
||||
Depth.sbx 672
|
||||
Lander.sbx 7477
|
||||
Lander.sbx 7913
|
||||
Pad.sbx 264
|
||||
Crash.sbx 632
|
||||
vars.script 50
|
||||
|
||||
@@ -2603,6 +2603,64 @@ EOT
|
||||
&& 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"
|
||||
|
||||
# ---- Coming apart ----
|
||||
#
|
||||
# A crash used to be a line of text and a lander still sitting there in one piece: the verdict
|
||||
# was the whole of it, and somebody watching a recording had to read the words to know what had
|
||||
# happened. The lander goes now, and six pieces of it leave in a rough hexagon at its own
|
||||
# colour for about a second.
|
||||
#
|
||||
# THE SPREAD is what is measured, not the count. Two of the six leave the top of the screen on
|
||||
# the way, so the count drops from twenty four to sixteen - which is correct and would make an
|
||||
# exact count a check that breaks the day the lander crashes somewhere else. How far apart they
|
||||
# have got only ever goes up.
|
||||
#
|
||||
# The animation runs about 45 frames from the crash, which here lands between 1.05 and 1.85
|
||||
# million cycles; the samples sit at 1.2 and 1.6. If this ever fails, check that window before
|
||||
# believing the code is broken.
|
||||
python3 -c "
|
||||
import struct
|
||||
# Well above the ground and coming down far too fast to survive it.
|
||||
open('$BUILD/crash.state','wb').write(struct.pack('<hhhhhhBBBB',
|
||||
0x14A0, 200, 0, 60, 0x14A0, 2600, 255, 0, 0, 0))
|
||||
"
|
||||
cp "$ROOT/Tests/build/disks/cosmos.img" "$BUILD/crash.img"
|
||||
"$ROOT/SplitDisk" put "$BUILD/crash.img" "$BUILD/crash.state" /lander.state > /dev/null
|
||||
for when in 1200000 1600000 2200000; do
|
||||
timeout 60 "$EMU" --fast --cycles $when --keyboard "$BUILD/fly.keys" \
|
||||
--pad "$BUILD/none.pad" --screen "$BUILD/burst$when.ppm" \
|
||||
--disk "$BUILD/crash.img" --ram-disk 2048 \
|
||||
"$BUILD/cosmos.bin" > "$BUILD/burst$when.out" 2>&1 || true
|
||||
done
|
||||
read -r EARLYBITS EARLYWIDE LATEBITS LATEWIDE AFTERBITS <<EOT
|
||||
$(python3 -c "
|
||||
def bits(path):
|
||||
d = open(path, 'rb').read()
|
||||
width = int(d[:40].split()[1])
|
||||
px = d[d.index(b'255\n') + 4:]
|
||||
at = [(i % width, i // width) for i in range(len(px) // 3)
|
||||
if px[i * 3:i * 3 + 3] == bytes.fromhex('d8c048')]
|
||||
if not at:
|
||||
return 0, 0
|
||||
xs = [x for x, y in at]
|
||||
return len(at), max(xs) - min(xs)
|
||||
e = bits('$BUILD/burst1200000.ppm'); l = bits('$BUILD/burst1600000.ppm')
|
||||
print(e[0], e[1], l[0], l[1], bits('$BUILD/burst2200000.ppm')[0])
|
||||
" 2>/dev/null || echo "0 0 0 0 -1")
|
||||
EOT
|
||||
# Forty pixels in one eight wide block is the lander; anything else in its colour is what is
|
||||
# left of it. Both samples have to have SOMETHING, or the next check compares two nothings.
|
||||
[ "$EARLYBITS" -gt 0 ] && [ "$EARLYBITS" != "40" ] && [ "$LATEBITS" -gt 0 ] \
|
||||
&& result ok "a crash breaks the lander into pieces" "$EARLYBITS pixels of it, not the forty it was" \
|
||||
|| result no "a crash breaks the lander into pieces" "$EARLYBITS early, $LATEBITS later"
|
||||
[ "$LATEWIDE" -gt "$EARLYWIDE" ] \
|
||||
&& result ok "and they carry on outwards" "$EARLYWIDE pixels apart, then $LATEWIDE" \
|
||||
|| result no "and they carry on outwards" "$EARLYWIDE pixels apart, then $LATEWIDE"
|
||||
# And they are gone by the time the verdict is up, rather than hanging over the words.
|
||||
[ "$AFTERBITS" = "0" ] && grep -q "Crashed" "$BUILD/burst2200000.out" \
|
||||
&& result ok "and are gone before the verdict" "nothing left over the words" \
|
||||
|| result no "and are gone before the verdict" "$AFTERBITS pixels still there"
|
||||
|
||||
# ---- 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
|
||||
|
||||
Reference in New Issue
Block a user