Stop the artwork breakout drawing its screen into two sprites

It captured the HUD strip into sprite 1 and the whole play field into sprite 2,
and its own header called them "the screen". That was never a choice: a drawing
lasted one frame, so a sprite was the only thing the interpreter would redraw for
nothing. Both slots are free now, and five things went with them.

**`WINDOW 0, 35, 49, 36` is the first executable line.** The text layer repaints
every row it owns and by default owns the whole window; moved to two rows at the
bottom, it leaves the other thirty-five to the drawing verbs, and what the
program draws stays.

**`PACE` is gone.** It spun on `TI#` until a batch boundary so an `SSHAPE`
capture would not be cut in half by a present. Nothing is captured, so a drawing
spanning two batches simply arrives over two frames and the layer keeps both
halves. The 256-line drawing deadline that shaped every draw routine in this
listing does not apply to it any more.

**The flattened live list is gone** -- `BUILDLIVE`, `BUILDROW`, `STAMPROW` and
the four arrays behind them. They existed so a full-field redraw could be one
stamp and an advance per brick, and a full-field redraw existed because removing
one brick meant rebuilding all sixty. A broken brick is now erased in place with
a blank stamp, so the field is drawn once per level and not again.

**`BALLBRICKS` and `TESTCELL` are gone** -- about sixty lines that turned the
ball's box into a range of candidate cells, walked them with a nested `DO`, and
computed an overlap rectangle to pick an axis. `SOLID` registers each brick as
the level is laid out, `COLLISION 2` fires, and `RCOLLISION` says which brick,
which way out, how far, and which axis to reverse.

**The `SSHAPE` pool accounting is gone.** `SHN#` counted slots because every
frame's capture spent another and `GRAPHIC 5` had to throw them all away
periodically; eight are now spent once and never again.

Three mistakes worth recording, because each was silent:

- **The bricks were registered and then immediately thrown away.**
  `GOSUB BUILDLIVE` sat *after* the fill loop in `SETUPLEVEL` and meant "rebuild
  the list of what is standing"; replaced in place by a bare `SOLID`, it meant
  "retire everything" and ran after all sixty had been registered. Nothing
  failed -- the ball simply passed through the wall.
- **The handler tested the wrong bits.** `BUMP` is a mask by *sprite*, and the
  balls are sprites 5, 6 and 7, so their bits are 16, 32 and 64. Testing 1, 2 and
  4 asked about the HUD and field sprites that no longer exist.
- **The eraser stamps were undeclared**, so `SSHAPE` filled them inside
  `DRAWPROTOS` and they were empty everywhere else -- exactly the scope trap
  chapter 17 Step 3 is about, found by printing the handle inside the routine and
  again outside it.

Verified by driving it: a copy with a tracking paddle and auto-relaunch breaks
thirteen bricks in sixty seconds with no error line, and the shipped listing runs
clean. Both suites green.

Chapter 18 still describes the architecture this commit removed; that is the rest
of section 6 item 39.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EwxGB6TdoVvZ11KQQME9cL
This commit is contained in:
2026-08-02 12:58:08 -04:00
parent 8da8d717c8
commit 39d1d0c80c
2 changed files with 171 additions and 188 deletions

View File

@@ -3,43 +3,45 @@ REM BREAKOUT, for the AKGL BASIC interpreter, using sprite artwork.
REM
REM Run it with the SDL build: build-akgl/basic breakout-sprites.bas
REM
REM Five things about this interpreter shape everything below, and they
REM are worth knowing before the first line stops making sense.
REM Four things about this interpreter shape everything below, and they are
REM worth knowing before the first line stops making sense.
REM
REM 1. The text layer repaints every row of the window opaque black after
REM the program's steps have run, so by default anything DRAW or BOX
REM put on the screen is wiped before it is ever presented. Sprites are
REM drawn after the text layer, so a sprite is the one thing that is
REM seen for nothing. Everything drawn here is therefore captured with
REM SSHAPE and installed as a sprite with SPRSAV -- that is what
REM sprites 1 and 2 are. They are the screen.
REM 1. The text layer repaints every row it owns, opaque, after the
REM program's steps have run -- and by default it owns the whole window,
REM so anything DRAW or BOX put on the screen would be wiped before it
REM was ever seen. WINDOW moves it to two rows at the bottom, which is
REM the first executable line of this program, and everything below
REM then draws into a layer the frame composites and *keeps*.
REM
REM (WINDOW can shrink the text area and hand the rest of the window to
REM the drawing verbs; it could not be reached from this build when
REM this was written. It would not help here anyway: a drawing has to
REM be re-issued every frame and fit inside one batch, and a sprite has
REM neither constraint.)
REM Nothing here is captured into a sprite. It used to be: sprites 1
REM and 2 held an SSHAPE of the HUD strip and of the whole play field,
REM because a sprite was the one thing drawn from state the interpreter
REM kept and a drawing lasted exactly one frame. Both slots are free
REM now, and the field is drawn once per level rather than rebuilt.
REM
REM 2. The host runs 256 source lines and then presents the frame, and a
REM present throws the drawing buffer away. So all the drawing between
REM a GRAPHIC 1, 1 and its SSHAPE has to fit inside one of those
REM batches or the capture comes back half empty. Waiting for TI# to
REM tick lands us on a batch boundary -- see PACE -- and every draw
REM routine below is written to stay under about two hundred lines.
REM Arithmetic is free; only drawing has a deadline. That is why the
REM lettering is turned into a list of strokes when a number changes
REM and merely replayed when it is time to draw.
REM 2. Nothing has a drawing deadline. The host runs 256 source lines and
REM then presents, and a present used to throw the drawing buffer away
REM -- so a capture spanning that boundary came back half empty, and a
REM PACE routine spun on TI# to start each frame's drawing at the top
REM of a batch. With nothing captured, a drawing that spans two batches
REM simply arrives over two frames and the layer keeps both halves.
REM
REM 3. There are eight sprites and no more. Two are the screen, five are
REM the artwork, one is the gem. That is the whole budget, and it is
REM why the bricks are drawn rather than made of art.
REM What survives from that era is worth keeping anyway: the lettering
REM is turned into a list of strokes when a number changes and merely
REM replayed when it is drawn. Arithmetic away from the frame is free.
REM
REM 3. There are eight sprites and no more. Five are the artwork, one is
REM the gem, and two are spare. The bricks are still drawn rather than
REM made of art -- sixty of them would not fit in eight slots however
REM many are free -- but they are *collidable* without being sprites,
REM because SOLID registers a rectangle and costs no slot.
REM
REM 4. There are 128 variables and 64 labels, and this program is close
REM enough to both that a constant is spelled out rather than named
REM and several routines that would read better as subroutines are
REM BEGIN blocks instead.
REM
REM 5. Five things in this dialect did not do what they looked like they
REM And five things in this dialect did not do what they looked like they
REM did, and each one cost an evening. THREE OF THEM HAVE SINCE BEEN
REM FIXED, and are marked below; the shape of this listing is still
REM theirs, which is why they are still written down:
@@ -138,13 +140,8 @@ GMY% = 0
REM --------------------------------------------------------------- bricks
DIM BRK#(60)
DIM LX#(60)
DIM LY#(60)
DIM RS#(6)
DIM RC#(6)
DIM BRC#(6)
DIM BRV#(6)
LN# = 0
BRN# = 0
REM ------------------------------------------------------- shapes and dirt
@@ -165,7 +162,6 @@ S5$ = ""
Z$ = ""
REM 99 means the stamps are gone and have to be rebuilt before anything
REM can be drawn with them.
SHN# = 99
DHUD# = 1
DPLAY# = 1
BAN$ = ""
@@ -244,6 +240,19 @@ HIT# = 0
SEED# = 1
RNMAX# = 2
RNVAL# = 0
REM The collision handler answers through these, so they are created out here
REM like everything else: a name first seen inside a GOSUB dies at its RETURN.
MB# = 0
AX# = 0
DP% = 0.0
SLX# = 0
SLY# = 0
SLI# = 0
REM The two eraser stamps. Declared here for exactly the same reason -- built
REM inside DRAWPROTOS and left undeclared, they were SHAPE:6 and SHAPE:7 inside
REM it and empty everywhere else.
BL$ = ""
HBL$ = ""
REM =====================================================================
REM Start up
@@ -252,6 +261,14 @@ REM Tables before font, and the order is not a preference: READ walks one
REM cursor through every DATA item in the program in the order they appear
REM in the file, so whichever loader runs first gets the DATA that is
REM written first. The tables are written first.
REM The text layer repaints every row it owns, opaque, so it has to be moved
REM out of the way before anything drawn can be seen. Two rows at the bottom is
REM enough for the final score, and hands the other thirty-five to the drawing
REM verbs. Everything this game draws then simply stays there -- a drawing goes
REM into a layer the frame composites, so nothing here is captured into a sprite
REM and nothing is redrawn every frame.
WINDOW 0, 35, 49, 36
GOSUB LOADTABLES
GOSUB LOADFONT
SEED# = MOD(1 + TI# * 7919, 2147483648)
@@ -283,6 +300,11 @@ VOL 8
ENVELOPE 0, 0, 6, 0, 4
TEMPO 12
COLLISION 2, BRICKHIT
REM The stamps, once. They used to be rebuilt whenever the SSHAPE pool ran dry,
REM because every frame's capture spent another slot; nothing captures now, so
REM eight slots are spent here and never again.
GOSUB DRAWPROTOS
GOSUB TITLESCREEN
REM =====================================================================
@@ -292,8 +314,11 @@ REM PACE first, because it is what puts the drawing at the top of a host
REM batch; then at most one capture, because a capture is the only thing
REM with a deadline; then the game, which may take as long as it likes.
REM =====================================================================
REM No pacing. PACE spun on TI# until a batch boundary so that an SSHAPE
REM capture would not be cut in half by a present; nothing is captured now, so
REM a drawing that spans two batches simply arrives over two frames and the
REM layer keeps both halves.
LABEL FRAME
GOSUB PACE
GOSUB DRAWJOB
GOSUB READKEYS
IF STATE# = 0 THEN GOSUB TITLETICK
@@ -312,30 +337,6 @@ IF SCORE# > HISCORE# THEN HISCORE# = SCORE#
PRINT "FINAL SCORE " + SCORE# + " BEST " + HISCORE#
END
REM =====================================================================
REM Pacing, and the batch boundary it buys
REM
REM TI# is refreshed from the host's clock once per batch, so the step on
REM which it changes is the first step of a batch. Spinning here until it
REM moves is the only way a program in this dialect can find out where a
REM frame boundary is, and a capture that starts anywhere else risks being
REM cut in half by a present. Two jiffies is thirty frames a second.
REM
REM **LASTT# is sampled here rather than carried over from the last
REM frame**, and that is the whole correctness of it. Carried over, a
REM frame whose work ran long would find two jiffies already gone and
REM return on whatever step it happened to be on -- somewhere in the
REM middle of a batch -- and the capture that followed would come back
REM holding half of the frame before it. Sampling here means the loop
REM always sees TI# change under it, and a change is only ever seen on
REM the first step of a batch.
REM =====================================================================
LABEL PACE
LASTT# = TI#
LABEL PACEEDGE
IF TI# - LASTT# < 2 THEN GOTO PACEEDGE
RETURN
REM =====================================================================
REM One capture per frame, and only one
REM
@@ -346,10 +347,10 @@ REM Labels rather than BEGIN blocks here. When this was written it was not
REM a style choice -- a loop inside a block that was skipped left the
REM interpreter with no GOSUB to return from and stopped the program. That
REM is fixed; the shape is kept because it reads the same either way.
REM The stamps are built once now rather than counted and rebuilt. Nothing
REM captures per frame any more, so the sixteen SSHAPE slots are spent once and
REM stay spent -- eight of them -- and GRAPHIC 5 is never called.
LABEL DRAWJOB
IF SHN# < 14 THEN GOTO DRAWJOB2
GOSUB DRAWPROTOS
RETURN
LABEL DRAWJOB2
IF DPLAY# = 0 THEN GOTO DRAWJOB3
GOSUB DRAWPLAY
@@ -368,10 +369,8 @@ REM hundred and seventy a rule-at-a-time loop would take. PAINT would be
REM one statement instead of sixteen and is not an option: it costs nearly
REM four milliseconds a call, which is an eighth of a frame.
LABEL DRAWPROTOS
GRAPHIC 5
GRAPHIC 1, 1
WIDTH 1
SHN# = 99
FOR R# = 0 TO 5
COLOR 1, BRC#(R#)
T1# = R# * 20
@@ -380,17 +379,41 @@ FOR R# = 0 TO 5
DRAW 1, 0, T1# + K# TO 67, T1# + K# : DRAW 1, 0, T2# + K# TO 67, T2# + K#
NEXT K#
NEXT R#
REM A seventh stamp, in the background colour, is how a broken brick is taken
REM off the screen. There is no filled-rectangle verb and PAINT costs four
REM milliseconds a call, so erasing is a GSHAPE of something blank -- one line
REM instead of sixteen, and it reuses machinery that is already here.
COLOR 1, 1
FOR K# = 0 TO 15
DRAW 1, 0, 130 + K# TO 67, 130 + K#
NEXT K#
REM And an eighth the width of the HUD strip, for the same reason: the strip is
REM rewritten whenever a number in it changes, and the old digits have to go
REM somewhere first.
FOR K# = 0 TO 59
DRAW 1, 0, 160 + K# TO 799, 160 + K#
NEXT K#
SSHAPE Z$, 0, 0, 68, 16 : S0$ = Z$
SSHAPE Z$, 0, 20, 68, 36 : S1$ = Z$
SSHAPE Z$, 0, 40, 68, 56 : S2$ = Z$
SSHAPE Z$, 0, 60, 68, 76 : S3$ = Z$
SSHAPE Z$, 0, 80, 68, 96 : S4$ = Z$
SSHAPE Z$, 0, 100, 68, 116 : S5$ = Z$
SHN# = 6
SSHAPE Z$, 0, 130, 68, 146 : BL$ = Z$
SSHAPE Z$, 0, 160, 800, 220 : HBL$ = Z$
GRAPHIC 1, 1
DPLAY# = 1
DHUD# = 1
RETURN
REM Take one brick off the screen: stamp the blank over it. Called when a brick
REM breaks, so the field is never redrawn as a whole during play -- which is
REM what lets the whole live-list machinery go.
LABEL ERASEBRICK
Z$ = BL$
GSHAPE Z$, BRKX# + C# * 72, BRKY# + R# * 24
RETURN
REM ---------------------------------------------------------------------
REM The field: walls, whatever bricks are still standing, and whatever
REM lettering belongs on the field. Nothing here computes anything it
@@ -401,53 +424,43 @@ WIDTH 2
COLOR 0, 1 : COLOR 1, 4 : COLOR 2, 8 : COLOR 3, 5
COLOR 4, 11 : COLOR 5, 16 : COLOR 6, 6
BOX 5, 2, 62, 797, 597 : BOX 1, 6, 66, 793, 593
REM Six runs of stamps, one per row colour, because picking the stamp per
REM brick would cost a line per brick inside the one routine in this
REM program that has a deadline. The live list is built row by row, so a
REM row is a contiguous run of it: RS# is where the run starts and RC# how
REM long it is.
Z$ = S0$ : T1# = RS#(0) : T2# = RC#(0) : GOSUB STAMPROW
Z$ = S1$ : T1# = RS#(1) : T2# = RC#(1) : GOSUB STAMPROW
Z$ = S2$ : T1# = RS#(2) : T2# = RC#(2) : GOSUB STAMPROW
Z$ = S3$ : T1# = RS#(3) : T2# = RC#(3) : GOSUB STAMPROW
Z$ = S4$ : T1# = RS#(4) : T2# = RC#(4) : GOSUB STAMPROW
Z$ = S5$ : T1# = RS#(5) : T2# = RC#(5) : GOSUB STAMPROW
REM A skipped BEGIN block holding a FOR used to break the RETURN at the end
REM of the routine holding it, so this loop is guarded by a GOTO instead.
REM Fixed since; left alone because a GOTO guard costs nothing.
REM Every brick still standing, stamped where it belongs. This runs when a
REM level is laid out and not again -- a broken brick is erased in place by
REM ERASEBRICK, so there is no reason to redraw sixty of them to remove one.
REM That is what took away the flattened live list, its four arrays, and the
REM run-per-row loop that walked it.
FOR R# = 0 TO 5
IF R# = 0 THEN Z$ = S0$
IF R# = 1 THEN Z$ = S1$
IF R# = 2 THEN Z$ = S2$
IF R# = 3 THEN Z$ = S3$
IF R# = 4 THEN Z$ = S4$
IF R# = 5 THEN Z$ = S5$
FOR C# = 0 TO 9
IF BRK#(R# * 10 + C#) > 0 THEN GSHAPE Z$, BRKX# + C# * 72, BRKY# + R# * 24
NEXT C#
NEXT R#
IF PN# = 1 THEN DRAW PC#(0), PX1#(0), PY1#(0) TO PX2#(0), PY2#(0)
IF PN# < 2 THEN GOTO PLDONE
FOR I# = 0 TO PN# - 1
DRAW PC#(I#), PX1#(I#), PY1#(I#) TO PX2#(I#), PY2#(I#)
NEXT I#
LABEL PLDONE
SSHAPE Z$, 0, 60, 800, 600
SPRSAV Z$, 2
SPRITE 2, 1, 2
MOVSPR 2, 0, 60
SHN# = SHN# + 1
RETURN
REM Stamp T2# bricks starting at T1# with the shape in Z$. The count of
REM one is spelled out because a FOR whose bounds are equal does not run
REM its body in this dialect, and one brick left in a row is exactly that.
LABEL STAMPROW
IF T2# < 1 THEN RETURN
IF T2# = 1 THEN GSHAPE Z$, LX#(T1#), LY#(T1#)
IF T2# < 2 THEN RETURN
FOR I# = T1# TO T1# + T2# - 1
GSHAPE Z$, LX#(I#), LY#(I#)
NEXT I#
RETURN
REM ---------------------------------------------------------------------
REM The HUD strip. Same shape as DRAWPLAY: one pass over a stroke list
REM that was built on some earlier frame, when there was time.
LABEL DRAWHUD
GRAPHIC 1, 1
GRAPHIC 1, 0
WIDTH 1
COLOR 0, 1 : COLOR 1, 4 : COLOR 2, 8 : COLOR 3, 5
COLOR 4, 11 : COLOR 5, 16 : COLOR 6, 6
REM The old strip goes first. Nothing here clears the screen -- a drawing
REM stays, which is the whole point -- so the digits that were there have to be
REM stamped over before the new ones are drawn.
Z$ = HBL$
GSHAPE Z$, 0, 0
BOX 5, 0, 56, 799, 57
IF HN# = 1 THEN DRAW HC#(0), HX1#(0), HY1#(0) TO HX2#(0), HY2#(0)
IF HN# < 2 THEN GOTO HUDONE
@@ -455,11 +468,6 @@ FOR I# = 0 TO HN# - 1
DRAW HC#(I#), HX1#(I#), HY1#(I#) TO HX2#(I#), HY2#(I#)
NEXT I#
LABEL HUDONE
SSHAPE Z$, 0, 0, 800, 60
SPRSAV Z$, 1
SPRITE 1, 1, 2
MOVSPR 1, 0, 0
SHN# = SHN# + 1
RETURN
REM =====================================================================
@@ -580,7 +588,6 @@ IF BLST#(B#) = 0 THEN BEGIN
BEND
IF BLON#(B#) = 0 THEN RETURN
IF BLST#(B#) = 0 THEN BEGIN
GOSUB BALLBRICKS
GOSUB BALLPADDLE
BEND
MOVSPR 5 + B#, BLX%(B#), BLY%(B#)
@@ -610,70 +617,58 @@ SPRITE 5 + B#, 0
RETURN
REM ---------------------------------------------------------------------
REM Brick collision. A ball can only be over four cells at once, so the
REM cells are worked out from its box rather than by walking sixty
REM bricks. DO rather than FOR throughout: a FOR whose bounds are equal
REM does not run its body in this dialect, and a ball inside one column is
REM exactly that case.
LABEL BALLBRICKS
IF BRN# = 0 THEN RETURN
LFT# = BLX%(B#)
TOP# = BLY%(B#)
RGT# = LFT# + 21
BOT# = TOP# + 21
IF BOT# < BRKY# THEN RETURN
IF TOP# > BRKY# + 6 * 24 THEN RETURN
CC1# = (LFT# - BRKX#) / 72
CC2# = (RGT# - BRKX#) / 72
RR1# = (TOP# - BRKY#) / 24
RR2# = (BOT# - BRKY#) / 24
IF CC1# < 0 THEN CC1# = 0
IF CC2# > 9 THEN CC2# = 9
IF RR1# < 0 THEN RR1# = 0
IF RR2# > 5 THEN RR2# = 5
IF CC2# < CC1# THEN RETURN
IF RR2# < RR1# THEN RETURN
HIT# = 0
R# = RR1#
DO
C# = CC1#
DO
IF HIT# = 0 THEN GOSUB TESTCELL
C# = C# + 1
LOOP UNTIL C# > CC2# OR HIT# = 1
R# = R# + 1
LOOP UNTIL R# > RR2# OR HIT# = 1
REM Brick collision, all of it.
REM
REM COLLISION 2 fires between source lines when a ball overlaps one of the
REM rectangles SOLID registered, and RCOLLISION says which and which way out.
REM That replaced a bounding-box narrowing that turned the ball's box into a
REM range of candidate cells, a nested DO walking them, and a cell test that
REM computed an overlap rectangle and compared its width to its height to pick
REM an axis -- about sixty lines.
REM
REM The handler cannot know which ball hit, so it asks each live one. Three is
REM not a loop worth writing.
REM The mask is by *sprite*, and the balls are sprites 5, 6 and 7 -- so their
REM bits are 16, 32 and 64, not 1, 2 and 4. Ball B# is sprite 5 + B#, which is
REM the same arithmetic MOVSPR and RCOLLISION use for them everywhere else.
LABEL BRICKHIT
MB# = BUMP(2)
IF MB# = 0 THEN RETURN
B# = 0
IF (MB# AND 16) <> 0 THEN GOSUB ONEBRICK
B# = 1
IF (MB# AND 32) <> 0 THEN GOSUB ONEBRICK
B# = 2
IF (MB# AND 64) <> 0 THEN GOSUB ONEBRICK
RETURN
REM Reflect off whichever face the ball has less of itself past: the
REM standard box resolution, and the reason a ball clipping the end of a
REM row goes sideways instead of straight back down. T1# to T4# are the
REM overlapping rectangle; its width against its height is the whole test.
LABEL TESTCELL
N# = R# * 10 + C#
REM One ball against whatever it hit. B# is which ball; sprite 5 + B# is its
REM slot, and RCOLLISION is keyed on the sprite number.
LABEL ONEBRICK
IF BLON#(B#) = 0 THEN RETURN
N# = RCOLLISION(5 + B#, 1)
IF N# < 1 THEN RETURN
N# = N# - 1
IF BRK#(N#) = 0 THEN RETURN
BX1# = BRKX# + C# * 72
BY1# = BRKY# + R# * 24
IF RGT# < BX1# THEN RETURN
IF LFT# > BX1# + 67 THEN RETURN
IF BOT# < BY1# THEN RETURN
IF TOP# > BY1# + 15 THEN RETURN
T1# = RGT# : IF BX1# + 67 < T1# THEN T1# = BX1# + 67
T2# = LFT# : IF BX1# > T2# THEN T2# = BX1#
T3# = BOT# : IF BY1# + 15 < T3# THEN T3# = BY1# + 15
T4# = TOP# : IF BY1# > T4# THEN T4# = BY1#
IF T1# - T2# < T3# - T4# THEN BLVX%(B#) = 0.0 - BLVX%(B#)
IF T1# - T2# >= T3# - T4# THEN BLVY%(B#) = 0.0 - BLVY%(B#)
DP% = RCOLLISION(5 + B#, 4)
BLX%(B#) = BLX%(B#) + (RCOLLISION(5 + B#, 2) * DP%)
BLY%(B#) = BLY%(B#) + (RCOLLISION(5 + B#, 3) * DP%)
AX# = RCOLLISION(5 + B#, 7)
IF AX# = 1 THEN BLVX%(B#) = 0.0 - BLVX%(B#)
IF AX# = 2 THEN BLVY%(B#) = 0.0 - BLVY%(B#)
R# = N# / 10
C# = MOD(N#, 10)
BRK#(N#) = 0
SOLID N# + 1
BRN# = BRN# - 1
SCORE# = SCORE# + BRV#(R#)
REM The top row rings at C6 and each row down drops about a third, so the
REM wall plays itself down a scale as it comes apart.
SOUND 1, 17175 - R# * 2100, 4
HIT# = 1
GOSUB BUILDLIVE
BX1# = BRKX# + C# * 72
BY1# = BRKY# + R# * 24
GOSUB ERASEBRICK
GOSUB HUDTEXT
DPLAY# = 1
IF GMON# = 1 THEN RETURN
RNMAX# = 7
GOSUB NEXTRAND
@@ -915,7 +910,7 @@ BRN# = 0
FOR I# = 0 TO 59
BRK#(I#) = 0
NEXT I#
GOSUB BUILDLIVE
SOLID
SPRITE 3, 0
SPRITE 5, 0
PN# = 0
@@ -962,7 +957,7 @@ BRN# = 0
FOR I# = 0 TO 59
BRK#(I#) = 0
NEXT I#
GOSUB BUILDLIVE
SOLID
SPRITE 3, 0
SPRITE 5, 0
SPRITE 6, 0
@@ -1006,10 +1001,15 @@ BRN# = 0
FOR I# = 0 TO 59
BRK#(I#) = 0
NEXT I#
REM Retire the previous level's rectangles *before* laying this one out, not
REM after. This is where GOSUB BUILDLIVE used to sit, and it meant something
REM different: rebuild the list of what is standing. Left in place, the clear
REM ran after FILLROW had registered all sixty and threw them all away again --
REM which cost nothing visible and simply stopped the ball hitting anything.
SOLID
FOR R# = 0 TO 5
IF R# < T3# THEN GOSUB FILLROW
NEXT R#
GOSUB BUILDLIVE
GOSUB RESETBALL
GOSUB HUDTEXT
GMTYP# = 0
@@ -1020,10 +1020,17 @@ DPLAY# = 1
RETURN
REM Again a subroutine rather than a block, for the same reason.
REM One row of bricks: the array, and the rectangle the interpreter collides
REM against. The id is the array index plus one, so RCOLLISION hands back a
REM number that indexes BRK#() with no lookup at all.
LABEL FILLROW
FOR C# = 0 TO 9
BRK#(R# * 10 + C#) = 1
BRN# = BRN# + 1
SLX# = BRKX# + C# * 72
SLY# = BRKY# + R# * 24
SLI# = (R# * 10 + C#) + 1
SOLID SLI#, SLX#, SLY#, SLX# + 68, SLY# + 16
NEXT C#
RETURN
@@ -1032,30 +1039,6 @@ SPD% = 5.6 + 0.45 * LEVEL#
IF SPD% > 11.0 THEN SPD% = 11.0
RETURN
REM Flatten the brick grid into the list DRAWPLAY walks. Doing it here
REM rather than in the draw routine is the whole trick: this costs four
REM lines a brick and has all the time in the world, the draw costs two
REM and has to finish before the host presents.
LABEL BUILDLIVE
LN# = 0
FOR R# = 0 TO 5
RS#(R#) = LN#
RC#(R#) = 0
GOSUB BUILDROW
NEXT R#
RETURN
LABEL BUILDROW
FOR C# = 0 TO 9
IF BRK#(R# * 10 + C#) > 0 THEN BEGIN
LX#(LN#) = BRKX# + C# * 72
LY#(LN#) = BRKY# + R# * 24
LN# = LN# + 1
RC#(R#) = RC#(R#) + 1
BEND
NEXT C#
RETURN
LABEL RESETBALL
FOR B# = 0 TO 2
BLON#(B#) = 0